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; }