Author: brane
Date: Fri Jul 25 23:55:47 2025
New Revision: 1927472
Log:
Follow up to 1927464: If we're using a proxy, we don't have to resolve the
host address, so the "asynchronous" connection creation becomes synchronous.
* serf.h:
(serf_connection_create_async): update the docstring.
* src/outgoing.c
(async_conn_create): Only copy the host address if it's not null.
(serf_connection_create_async): If we have a proxy, create the connection
immediately without addres resolution.
* test/test_context.c
(test_async_connection): Renamed from test_async_resolve.
(test_async_proxy_connection): New test.
(test_context): Register test_async_proxy_connection.
Modified:
serf/trunk/serf.h
serf/trunk/src/outgoing.c
serf/trunk/test/test_context.c
Modified: serf/trunk/serf.h
==============================================================================
--- serf/trunk/serf.h Fri Jul 25 18:45:01 2025 (r1927471)
+++ serf/trunk/serf.h Fri Jul 25 23:55:47 2025 (r1927472)
@@ -625,7 +625,9 @@ typedef void (*serf_connection_created_t
* serf_address_resolve_async().
*
* The @a created callback with @a created_baton is called when the connection
- * is created but before it is opened.
+ * is created but before it is opened. Note that depending on the configuration
+ * of @a ctx,the connection may be created and this callback be invoked
+ * synchronously during the scope of this function call.
*
* @since New in 1.4.
*/
Modified: serf/trunk/src/outgoing.c
==============================================================================
--- serf/trunk/src/outgoing.c Fri Jul 25 18:45:01 2025 (r1927471)
+++ serf/trunk/src/outgoing.c Fri Jul 25 23:55:47 2025 (r1927472)
@@ -1386,8 +1386,10 @@ static void async_conn_create(serf_conte
if (status == APR_SUCCESS)
{
- status = apr_sockaddr_info_copy(&host_address, host_address,
- baton->conn_pool);
+ if (host_address) {
+ status = apr_sockaddr_info_copy(&host_address, host_address,
+ baton->conn_pool);
+ }
if (status == APR_SUCCESS) {
status = serf_connection_create3(&conn, ctx,
baton->host_info,
@@ -1415,20 +1417,38 @@ apr_status_t serf_connection_create_asyn
apr_pool_t *scratch_pool;
apr_status_t status;
- struct async_create_baton *const baton = apr_palloc(pool, sizeof(*baton));
- baton->host_info = host_info;
- baton->created = created;
- baton->created_baton = created_baton;
- baton->setup = setup;
- baton->setup_baton = setup_baton;
- baton->closed = closed;
- baton->closed_baton = closed_baton;
- baton->conn_pool = pool;
-
apr_pool_create(&scratch_pool, pool);
- status = serf_address_resolve_async(ctx, host_info,
- async_conn_create, baton,
- scratch_pool);
+ if (ctx->proxy_address)
+ {
+ /* If we're using a proxy, we do *not* resolve the host
+ (see serf_connection_create3(), above), so just create
+ the connection immediately. */
+ serf_connection_t *conn;
+ status = serf_connection_create3(&conn, ctx,
+ host_info, NULL,
+ setup, setup_baton,
+ closed, closed_baton,
+ pool);
+ if (status == APR_SUCCESS)
+ created(ctx, created_baton, conn, status, scratch_pool);
+ }
+ else
+ {
+ struct async_create_baton *const baton = apr_palloc(pool,
sizeof(*baton));
+ baton->host_info = host_info;
+ baton->created = created;
+ baton->created_baton = created_baton;
+ baton->setup = setup;
+ baton->setup_baton = setup_baton;
+ baton->closed = closed;
+ baton->closed_baton = closed_baton;
+ baton->conn_pool = pool;
+
+ status = serf_address_resolve_async(ctx, host_info,
+ async_conn_create, baton,
+ scratch_pool);
+ }
+
apr_pool_destroy(scratch_pool);
return status;
}
Modified: serf/trunk/test/test_context.c
==============================================================================
--- serf/trunk/test/test_context.c Fri Jul 25 18:45:01 2025
(r1927471)
+++ serf/trunk/test/test_context.c Fri Jul 25 23:55:47 2025
(r1927472)
@@ -1018,7 +1018,7 @@ static void test_outgoing_request_err(Cu
}
/* Test that asynchronus name resolution happens. */
-static void test_async_resolve(CuTest *tc)
+static void test_async_connection(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
apr_status_t status;
@@ -1058,6 +1058,54 @@ static void test_async_resolve(CuTest *t
handler_ctx, tb->pool);
}
+/* Test that async connection to proxy short-circuits. */
+static void test_async_proxy_connection(CuTest *tc)
+{
+ test_baton_t *tb = tc->testBaton;
+ handler_baton_t handler_ctx[2];
+ const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]);
+ apr_status_t status;
+ int i;
+
+ /* Set up a test context with a proxy */
+ setup_test_mock_server(tb);
+ status = setup_test_mock_proxy(tb);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+ status = setup_test_client_context_with_proxy(tb, NULL, tb->pool);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+ Given(tb->mh)
+ RequestsReceivedByProxy
+ GETRequest(
+ URLEqualTo(apr_psprintf(tb->pool, "http://%s", tb->serv_host)),
+ HeaderEqualTo("Host", tb->serv_host),
+ ChunkedBodyEqualTo("1"))
+ Respond(WithCode(200), WithChunkedBody(""))
+ GETRequest(
+ URLEqualTo(apr_psprintf(tb->pool, "http://%s", tb->serv_host)),
+ HeaderEqualTo("Host", tb->serv_host),
+ ChunkedBodyEqualTo("2"))
+ Respond(WithCode(200), WithChunkedBody(""))
+ EndGiven
+
+ status = use_new_async_connection(tb, tb->pool);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+ /* Because we use a proxy, the connection creation is actually synchronous,
+ because we don't resolve the host address -- that's the proxy's job.
+ The connection-created callback was called immediately.*/
+ CuAssertIntEquals(tc, APR_SUCCESS, tb->user_status);
+ CuAssertPtrNotNull(tc, tb->connection);
+
+ /* Send some requests on the connections */
+ for (i = 0 ; i < num_requests ; i++) {
+ create_new_request(tb, &handler_ctx[i], "GET", "/", i+1);
+ }
+
+ run_client_and_mock_servers_loops_expect_ok(tc, tb, num_requests,
+ handler_ctx, tb->pool);
+}
+
static void async_resolve_cancel_callback(serf_context_t *ctx,
void *resolved_baton,
apr_sockaddr_t *host_address,
@@ -1123,7 +1171,8 @@ CuSuite *test_context(void)
SUITE_ADD_TEST(suite, test_connection_large_request);
SUITE_ADD_TEST(suite, test_max_keepalive_requests);
SUITE_ADD_TEST(suite, test_outgoing_request_err);
- SUITE_ADD_TEST(suite, test_async_resolve);
+ SUITE_ADD_TEST(suite, test_async_connection);
+ SUITE_ADD_TEST(suite, test_async_proxy_connection);
SUITE_ADD_TEST(suite, test_async_resolve_cancel);
return suite;
}