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

Reply via email to