Author: brane
Date: Fri Jul 18 12:47:56 2025
New Revision: 1927308

URL: http://svn.apache.org/viewvc?rev=1927308&view=rev
Log:
Implement a prototype libunbound-based asynchronous resolver.
EXPERIMENTAL, does not include proper discovery of libunbound.

* CMakeLists.txt: Add some manually configurable bits to find libunbound.
   Show the result in the summary.
* build/SerfGenClangd.cmake: Add the unbound include directory.

* serf_private.h
  (serf_context_t):
    - resolve_init_status renamed from resolve_guard_status;
    - added resolve_context, which is implementation-specific.
  (serf__create_resolve_context): New prototype.

* src/context.c
  (serf_context_create_ex): Initialize the resolve_context and track the
   status in resolve_init_status, along with the mutex status.
* src/resolve.c:
   - include <unbound.h> when enabled;
   - include APR_WANT_BYTEFUNC through <apr_want.h>, used for logging
      resolve results. Seems to work fine on Windows/Fedora/Debian,
      needs testing on various *BSDs etc.
   - Add implementaiton for libunbound.
  (SERF_HAVE_ASYNC_RESOLVER): Renamed from SERF_USE_ASYNC_RESOLVER. Again.
  (serf__create_resolve_context): Implement here.

* test/test_context.c
  (test_async_resolve): Check the status before the connection pointer,
   otherwise we don't see the status in the results if the pointer is NULL.

Modified:
    serf/trunk/CMakeLists.txt
    serf/trunk/build/SerfGenClangd.cmake
    serf/trunk/serf_private.h
    serf/trunk/src/context.c
    serf/trunk/src/resolve.c
    serf/trunk/test/test_context.c

Modified: serf/trunk/CMakeLists.txt
URL: 
http://svn.apache.org/viewvc/serf/trunk/CMakeLists.txt?rev=1927308&r1=1927307&r2=1927308&view=diff
==============================================================================
--- serf/trunk/CMakeLists.txt (original)
+++ serf/trunk/CMakeLists.txt Fri Jul 18 12:47:56 2025
@@ -28,6 +28,7 @@
 # ZLIB_ROOT         - Path to zlib's install area
 # Brotli_ROOT       - Path to Brotli's install area
 # GSSAPI_ROOT       - Path to GSSAPI's install area
+# Unbound_ROOT      - Path to libunbound's install area
 # ===================================================================
 
 cmake_minimum_required(VERSION 3.12)
@@ -261,6 +262,21 @@ else()
   endif()
 endif()
 
+
+# FIXME: VERYTEMPORARY, figure out FindUnbound.cmake first.
+set(Unbound_FOUND FALSE)
+if (Unbound_FOUND)
+  set(UNBOUND_INCLUDE_DIR "/opt/homebrew/opt/unbound/include")
+  set(UNBOUND_LIBRARIES "/opt/homebrew/opt/unbound/lib/libunbound.dylib")
+  # set(UNBOUND_LIBRARIES "/usr/lib/aarch64-linux-gnu/libunbound.so")
+  # set(UNBOUND_LIBRARIES "/usr/lib64/libunbound.so")
+  add_library(Unbound::Unbound UNKNOWN IMPORTED)
+  set_target_properties(Unbound::Unbound PROPERTIES
+    INTERFACE_INCLUDE_DIRECTORIES "${UNBOUND_INCLUDE_DIR}")
+  set_target_properties(Unbound::Unbound PROPERTIES
+    IMPORTED_LOCATION "${UNBOUND_LIBRARIES}")
+endif(Unbound_FOUND)
+
 # Calculate the set of private and public targets
 set(SERF_PRIVATE_TARGETS OpenSSL::Crypto OpenSSL::SSL ZLIB::ZLIB)
 if(Brotli_FOUND)
@@ -270,6 +286,11 @@ if(GSSAPI_FOUND)
   list(APPEND SERF_C_DEFINES "SERF_HAVE_GSSAPI")
   list(APPEND SERF_PRIVATE_TARGETS KRB5::GSSAPI)
 endif()
+if(Unbound_FOUND)
+  list(APPEND SERF_C_DEFINES "SERF_HAVE_ASYNC_RESOLVER=1")
+  list(APPEND SERF_C_DEFINES "SERF_HAVE_UNBOUND")
+  list(APPEND SERF_PRIVATE_TARGETS Unbound::Unbound)
+endif()
 
 if(APR_STATIC)
   if(SERF_WINDOWS)
@@ -532,6 +553,7 @@ set(_gen_dot_clangd OFF)
 set(_have_brotli OFF)
 set(_have_gssapi OFF)
 set(_have_sspi OFF)
+set(_have_unbound OFF)
 
 if(NOT SKIP_SHARED)
   set(_build_shared ON)
@@ -558,6 +580,9 @@ endif()
 if("SERF_HAVE_SSPI" IN_LIST SERF_C_DEFINES)
   set(_have_sspi ON)
 endif()
+if ("SERF_HAVE_UNBOUND" IN_LIST SERF_C_DEFINES)
+  set(_have_unbound "EXPERIMENTAL")
+endif()
 
 message(STATUS "Summary:")
 message(STATUS "  Version ................... : ${SERF_VERSION}")
@@ -574,6 +599,7 @@ message(STATUS "  Options:")
 message(STATUS "    Brotli .................. : ${_have_brotli}")
 message(STATUS "    GSSAPI .................. : ${_have_gssapi}")
 message(STATUS "    SSPI .................... : ${_have_sspi}")
+message(STATUS "    Unbound ................. : ${_have_unbound}")
 message(STATUS "  Install:")
 message(STATUS "    prefix: ................. : ${CMAKE_INSTALL_PREFIX}")
 message(STATUS "    headers: ................ : ${SERF_INSTALL_HEADERS}")

Modified: serf/trunk/build/SerfGenClangd.cmake
URL: 
http://svn.apache.org/viewvc/serf/trunk/build/SerfGenClangd.cmake?rev=1927308&r1=1927307&r2=1927308&view=diff
==============================================================================
--- serf/trunk/build/SerfGenClangd.cmake (original)
+++ serf/trunk/build/SerfGenClangd.cmake Fri Jul 18 12:47:56 2025
@@ -67,6 +67,9 @@ function(SerfGenClangd)
   if(GSSAPI_FOUND)
     list(APPEND includes ${GSSAPI_INCLUDES})
   endif()
+  if(Unbound_FOUND)
+    list(APPEND includes ${UNBOUND_INCLUDE_DIR})
+  endif()
   list(REMOVE_DUPLICATES includes)
   write_includes(${includes})
 

Modified: serf/trunk/serf_private.h
URL: 
http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1927308&r1=1927307&r2=1927308&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Fri Jul 18 12:47:56 2025
@@ -498,12 +498,13 @@ struct serf_context_t {
 
     serf_config_t *config;
 
-    /* The results of asynchronous address resolution. */
+    /* Support for asynchronous address resolution. */
+    apr_status_t resolve_init_status;
+    serf__resolve_result_t *resolve_head;
 #if APR_HAS_THREADS
     apr_thread_mutex_t *resolve_guard;
-    apr_status_t resolve_guard_status;
 #endif
-    serf__resolve_result_t *resolve_head;
+    void *resolve_context;
 };
 
 struct serf_listener_t {
@@ -685,6 +686,10 @@ struct serf_connection_t {
    up buckets that may still reference buckets of this request */
 void serf__connection_pre_cleanup(serf_connection_t *);
 
+/* Called from serf_context_create_ex() to set up the context-specific
+   asynchronous address resolver context. */
+apr_status_t serf__create_resolve_context(serf_context_t *ctx);
+
 /* Called from serf_context_prerun() before handling the connections.
    Processes the results of any asynchronously resolved addresses
    that were initiated for CTX. */

Modified: serf/trunk/src/context.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/src/context.c?rev=1927308&r1=1927307&r2=1927308&view=diff
==============================================================================
--- serf/trunk/src/context.c (original)
+++ serf/trunk/src/context.c Fri Jul 18 12:47:56 2025
@@ -195,14 +195,21 @@ serf_context_t *serf_context_create_ex(
     ctx->server_authn_info = apr_hash_make(pool);
 
     /* Initialize async resolver result queue. */
+    ctx->resolve_init_status = APR_SUCCESS;
+    ctx->resolve_head = NULL;
 #if APR_HAS_THREADS
-    ctx->resolve_guard_status = apr_thread_mutex_create(
+    ctx->resolve_init_status = apr_thread_mutex_create(
         &ctx->resolve_guard, APR_THREAD_MUTEX_DEFAULT, ctx->pool);
-    if (ctx->resolve_guard_status != APR_SUCCESS) {
+    if (ctx->resolve_init_status != APR_SUCCESS) {
         ctx->resolve_guard = NULL;
     }
 #endif
-    ctx->resolve_head = NULL;
+    if (ctx->resolve_init_status == APR_SUCCESS) {
+        ctx->resolve_init_status = serf__create_resolve_context(ctx);
+    }
+    if (ctx->resolve_init_status != APR_SUCCESS) {
+        ctx->resolve_context = NULL;
+    }
 
     /* Assume returned status is APR_SUCCESS */
     serf__config_store_init(ctx);

Modified: serf/trunk/src/resolve.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/src/resolve.c?rev=1927308&r1=1927307&r2=1927308&view=diff
==============================================================================
--- serf/trunk/src/resolve.c (original)
+++ serf/trunk/src/resolve.c Fri Jul 18 12:47:56 2025
@@ -20,16 +20,32 @@
 
 #include <apr.h>
 #include <apr_errno.h>
-#include <apr_network_io.h>
 #include <apr_pools.h>
+#include <apr_network_io.h>
 #include <apr_thread_mutex.h>
 #include <apr_thread_pool.h>
 
+/* This will include <netinet/in.h> and/or <arpa/inet.h>, which we'll
+   use for logging the resolver results. On Windows, we'll always get
+   <Winsock2.h> from <apr.h>. */
+#define APR_WANT_BYTEFUNC
+#include <apr_want.h>
+
 #include "serf.h"
 #include "serf_private.h"
 
 
-#define HAVE_ASYNC_RESOLVER (SERF_USE_ASYNC_RESOLVER || APR_HAS_THREADS)
+#define HAVE_ASYNC_RESOLVER (SERF_HAVE_ASYNC_RESOLVER || APR_HAS_THREADS)
+
+#if SERF_HAVE_ASYNC_RESOLVER
+#if SERF_HAVE_UNBOUND
+#include <unbound.h>
+#else
+/* Really shouldn't happen, but just in case it does, fall back
+   to the apr_thread_pool-based resolver. */
+#undef SERF_HAVE_ASYNC_RESOLVER
+#endif  /* SERF_HAVE_UNBOUND */
+#endif
 
 /*
  * FIXME: EXPERIMENTAL
@@ -41,13 +57,23 @@
  *  - Wake the poll/select in serf_context_run() when new resolve
  *    results are available.
  *
- *  - Add a way to cancel a resolve task.
+ *  - Add a way to cancel a resolve task?
  *
  *  - Figure out what to do if the lock/unlock calls return an error.
  *    This should not be possible unless we messed up the implementation,
  *    but there should be a way for clients to back out of this situation.
  *    Failed lock/unlock could potentially leave the context in an
  *    inconsistent state.
+ *
+ * TODO for Unbound:
+ *  - Convert unbound results to apr_sockaddr_t.
+ *
+ *  - Resolve both IPv4 and IPv6 addresses. This will require creating two
+ *    asynchronous resolve tasks and combining the results so that our
+ *    callback gets invoked only once both tasks are completed.
+ *
+ *  - Figure out how to use libunbound's event-based API, because it uses
+ *    true asynchronous I/O instead of background threads.
  */
 
 
@@ -57,7 +83,7 @@
    onto the context's result queue. */
 static void push_resolve_result(serf_context_t *ctx,
                                 apr_sockaddr_t *host_address,
-                                apr_status_t status,
+                                apr_status_t resolve_status,
                                 serf_address_resolved_t resolved,
                                 void *resolved_baton,
                                 apr_pool_t *resolve_pool);
@@ -80,8 +106,8 @@ apr_status_t serf_address_resolve_async(
     apr_pool_t *resolve_pool;
 
 #if APR_HAS_THREADS
-    if (ctx->resolve_guard_status != APR_SUCCESS) {
-        return ctx->resolve_guard_status;
+    if (ctx->resolve_init_status != APR_SUCCESS) {
+        return ctx->resolve_init_status;
     }
 #endif
 
@@ -117,10 +143,16 @@ apr_status_t serf_address_resolve_async(
 #endif  /* !HAVE_ASYNC_RESOLVER */
 
 
-#if SERF_USE_ASYNC_RESOLVER
+#if SERF_HAVE_ASYNC_RESOLVER
 
 /* TODO: Add implementation for one or more async resolver libraries. */
 #if 0
+/* Called during context creation. Must initialize ctx->resolver_context. */
+static apr_status_t create_resolve_context(serf_context_t *ctx)
+{
+    ...
+}
+
 static apr_status_t resolve_address_async(serf_context_t *ctx,
                                           apr_uri_t host_info,
                                           serf_address_resolved_t resolved,
@@ -134,13 +166,257 @@ static apr_status_t resolve_address_asyn
 /* Some asynchronous resolved libraries use event loop to harvest results.
    This function will be called from serf__process_async_resolve_results()
    so, in effect, from serf_context_prerun(). */
-static void run_async_resolver_loop(void)
+static apr_status_t run_async_resolver_loop(serf_context_t *ctx)
 {
     ...
 }
-#endif
+#endif  /* 0 */
+
+#if SERF_HAVE_UNBOUND
+
+static apr_status_t err_to_status(enum ub_ctx_err err)
+{
+    switch (err)
+    {
+    case UB_NOERROR:
+        /* no error */
+        return APR_SUCCESS;
+
+    case UB_SOCKET:
+        /* socket operation. Set to -1, so that if an error from _fd() is
+           passed (-1) it gives a socket error. */
+        if (errno)
+            return APR_FROM_OS_ERROR(errno);
+        return APR_ENOTSOCK;
+
+    case UB_NOMEM:
+        /* alloc failure */
+        return APR_ENOMEM;
+
+    case UB_SYNTAX:
+        /* syntax error */
+        return APR_EINIT;
+
+    case UB_SERVFAIL:
+        /* DNS service failed */
+        return APR_EAGAIN;
+
+    case UB_FORKFAIL:
+        /* fork() failed */
+        return APR_ENOMEM;
+
+    case UB_AFTERFINAL:
+        /* cfg change after finalize() */
+        return APR_EINIT;
+
+    case UB_INITFAIL:
+        /* initialization failed (bad settings) */
+        return APR_EINIT;
+
+    case UB_PIPE:
+        /* error in pipe communication with async bg worker */
+        return APR_EPIPE;
+
+    case UB_READFILE:
+        /* error reading from file (resolv.conf) */
+        if (errno)
+            return APR_FROM_OS_ERROR(errno);
+        return APR_ENOENT;
+
+    case UB_NOID:
+        /* error async_id does not exist or result already been delivered */
+        return APR_EINVAL;
+
+    default:
+        return APR_EGENERAL;
+    }
+}
+
+
+static apr_status_t cleanup_resolve_context(void *baton)
+{
+    struct ub_ctx *const resolve_context = baton;
+    ub_ctx_delete(resolve_context);
+    return APR_SUCCESS;
+}
+
+static apr_status_t create_resolve_context(serf_context_t *ctx)
+{
+    int err;
+    struct ub_ctx *const resolve_context = ub_ctx_create();
+    if (!resolve_context)
+        return APR_ENOMEM;
+
+    err = ub_ctx_resolvconf(resolve_context, NULL);
+    if (!err)
+        err = ub_ctx_hosts(resolve_context, NULL);
+    if (!err)
+        err = ub_ctx_async(resolve_context, true);
+
+    if (err) {
+        const apr_status_t status = err_to_status(err);
+        /* TODO: Error callback */
+        serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, ctx->config,
+                  "unbound ctx init: %s\n", ub_strerror(err));
+        return status;
+    }
+
+    ctx->resolve_context = resolve_context;
+    /* pre-cleanup because the live resolve tasks contain subpools of the
+       context pool and must be canceled before their pools go away. */
+    apr_pool_pre_cleanup_register(ctx->pool, resolve_context,
+                                  cleanup_resolve_context);
+    return APR_SUCCESS;
+}
+
+
+/* Task data for the Unbound resolver. */
+typedef struct unbound_resolve_task resolve_task_t;
+struct unbound_resolve_task
+{
+    serf_context_t *ctx;
+    apr_port_t host_port;
+    serf_address_resolved_t resolved;
+    void *resolved_baton;
+    apr_pool_t *resolve_pool;
+};
+
+static void resolve_callback(void* baton, int err,
+                             struct ub_result* result)
+{
+    resolve_task_t *const task = baton;
+    apr_status_t status = err_to_status(err);
+
+    if (err) {
+        /* TODO: Error callback */
+        serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+                  "unbound resolve: error %s\n", ub_strerror(err));
+    }
+    if (!result->havedata) {
+        if (result->nxdomain) {
+            /* TODO: Error callback */
+            serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+                      "unbound resolve: NXDOMAIN [%d]\n", result->rcode);
+            if (status == APR_SUCCESS)
+                status = APR_ENOENT;
+        }
+        if (result->bogus) {
+            /* TODO: Error callback */
+            serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+                      "unbound resolve: BOGUS [%d]%s%s\n", result->rcode,
+                      result->why_bogus ? " " : "",
+                      result->why_bogus ? result->why_bogus : "");
+            if (status == APR_SUCCESS)
+                status = APR_EINVAL;
+        }
+        if (result->was_ratelimited) {
+            /* TODO: Error callback */
+            serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+                      "unbound resolve: SERVFAIL [%d]\n", result->rcode);
+            if (status == APR_SUCCESS)
+                status = APR_EAGAIN;
+        }
+
+        /* This shouldn't happen, one of the previous checks should
+           have caught an error. */
+        if (status == APR_SUCCESS) {
+            /* TODO: Error callback */
+            serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+                      "unbound resolve: no data [%d]\n", result->rcode);
+            status = APR_ENOENT;
+        }
+    }
+
+    if (status)
+    {
+        push_resolve_result(task->ctx, NULL, status,
+                            task->resolved, task->resolved_baton,
+                            task->resolve_pool);
+    }
+    else
+    {
+        if (serf__log_enabled(LOGLVL_DEBUG, LOGCOMP_CONN,task->ctx->config))
+        {
+            int i;
+
+            for (i = 0; result->data && result->data[i]; ++i) {
+                char buf[INET6_ADDRSTRLEN];
+                const socklen_t len = sizeof(buf);
+                const char *address = "(AF-unknown)";
+
+                if (result->len[i] == sizeof(struct in_addr))
+                    address = inet_ntop(AF_INET, result->data[i], buf, len);
+                else if (result->len[i] == sizeof(struct in6_addr))
+                    address = inet_ntop(AF_INET6, result->data[i], buf, len);
+                serf__log(LOGLVL_DEBUG, LOGCOMP_CONN,
+                          __FILE__, task->ctx->config,
+                          "unbound resolve: %s: %s\n", result->qname, address);
+            }
+        }
+
+        /* TODO: Convert ub_result to apr_sockaddr_t */
+        push_resolve_result(task->ctx, NULL, APR_EAFNOSUPPORT,
+                            task->resolved, task->resolved_baton,
+                            task->resolve_pool);
+    }
 
-#else    /* !SERF_USE_ASYNC_RESOLVER */
+    ub_resolve_free(result);
+}
+
+static apr_status_t resolve_address_async(serf_context_t *ctx,
+                                          apr_uri_t host_info,
+                                          serf_address_resolved_t resolved,
+                                          void *resolved_baton,
+                                          apr_pool_t *resolve_pool,
+                                          apr_pool_t *scratch_pool)
+{
+    struct ub_ctx *const resolve_context = ctx->resolve_context;
+    resolve_task_t *const task = apr_palloc(resolve_pool, sizeof(*task));
+    apr_status_t status = APR_SUCCESS;
+    int err;
+
+    task->ctx = ctx;
+    task->host_port = host_info.port;
+    task->resolved = resolved;
+    task->resolved_baton = resolved_baton;
+    task->resolve_pool = resolve_pool;
+
+    /* FIXME: We should resolve both RRType 1 (A) and RRType 28 (AAAA). */
+    if ((err = ub_resolve_async(resolve_context, host_info.hostname,
+                                1,   /* rrtype: IPv4 host address (A) */
+                                1,   /* rrclass: IN(ternet) */
+                                task, resolve_callback, NULL)))
+    {
+        /* TODO: Error callback */
+        status = err_to_status(err);
+        serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, ctx->config,
+                  "unbound resolve start: %s\n", ub_strerror(err));
+    }
+
+    return status;
+}
+
+static apr_status_t run_async_resolver_loop(serf_context_t *ctx)
+{
+    struct ub_ctx *const resolve_context = ctx->resolve_context;
+
+    if (ub_poll(resolve_context)) {
+        const int err = ub_process(resolve_context);
+        if (err) {
+            const apr_status_t status = err_to_status(err);
+            /* TODO: Error callback */
+            serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, ctx->config,
+                      "unbound process: %s\n", ub_strerror(err));
+            return status;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+#endif  /* SERF_HAVE_UNBOUND */
+
+#else   /* !SERF_HAVE_ASYNC_RESOLVER */
 #if APR_HAS_THREADS
 
 /* This could be made configurable, but given that this is a fallback
@@ -165,9 +441,16 @@ static apr_status_t init_work_queue(void
 }
 
 
+static apr_status_t create_resolve_context(serf_context_t *ctx)
+{
+    ctx->resolve_context = NULL;
+    return APR_SUCCESS;
+}
+
+
 /* Task data for the thred pool resolver. */
-typedef struct resolve_task_t resolve_task_t;
-struct resolve_task_t
+typedef struct threadpool_resolve_task resolve_task_t;
+struct threadpool_resolve_task
 {
     serf_context_t *ctx;
     apr_uri_t host_info;
@@ -188,6 +471,28 @@ static void *APR_THREAD_FUNC resolve(apr
                                    APR_UNSPEC,
                                    task->host_info.port,
                                    0, task->resolve_pool);
+
+    if (status) {
+        host_address = NULL;
+    }
+    else if (serf__log_enabled(LOGLVL_DEBUG, LOGCOMP_CONN, task->ctx->config))
+    {
+        apr_sockaddr_t *addr = host_address;
+        while (addr)
+        {
+            char buf[INET6_ADDRSTRLEN];
+            const socklen_t len = sizeof(buf);
+            const char *address = "(AF-unknown)";
+
+            if (addr->family == APR_INET || addr->family == APR_INET6)
+                address = inet_ntop(addr->family, addr->ipaddr_ptr, buf, len);
+            serf__log(LOGLVL_DEBUG, LOGCOMP_CONN,
+                      __FILE__, task->ctx->config,
+                      "apr async resolve: %s: %s\n", addr->hostname, address);
+            addr = addr->next;
+        }
+    }
+
     push_resolve_result(task->ctx, host_address, status,
                         task->resolved, task->resolved_baton,
                         task->resolve_pool);
@@ -222,10 +527,13 @@ static apr_status_t resolve_address_asyn
 
 /* This is a no-op since we're using a thread pool that
    does its own task queue management. */
-static void run_async_resolver_loop(void) {}
+static apr_status_t run_async_resolver_loop(serf_context_t *ctx)
+{
+    return APR_SUCCESS;
+}
 
 #endif  /* !APR_HAS_THREADS */
-#endif  /* !SERF_USE_ASYNC_RESOLVER */
+#endif  /* !SERF_HAVE_ASYNC_RESOLVER */
 
 
 /*******************************************************************/
@@ -269,30 +577,37 @@ static apr_status_t unlock_results(serf_
 
 static void push_resolve_result(serf_context_t *ctx,
                                 apr_sockaddr_t *host_address,
-                                apr_status_t status,
+                                apr_status_t resolve_status,
                                 serf_address_resolved_t resolved,
                                 void *resolved_baton,
                                 apr_pool_t *resolve_pool)
 {
     serf__resolve_result_t *result;
-    apr_status_t lock_status;
+    apr_status_t status;
 
     result = apr_palloc(resolve_pool, sizeof(*result));
     result->host_address = host_address;
-    result->status = status;
+    result->status = resolve_status;
     result->resolved = resolved;
     result->resolved_baton = resolved_baton;
     result->result_pool = resolve_pool;
 
-    lock_status = lock_results(ctx);
-    if (!lock_status)
+    status = lock_results(ctx);
+    if (!status)
     {
         result->next = ctx->resolve_head;
         ctx->resolve_head = result;
-        lock_status = unlock_results(ctx);
+        status = unlock_results(ctx);
     }
 
-    /* TODO: if (lock_status) ... then what? */
+    /* TODO: if (status) ... then what? */
+}
+
+
+/* Internal API */
+apr_status_t serf__create_resolve_context(serf_context_t *ctx)
+{
+    return create_resolve_context(ctx);
 }
 
 
@@ -300,21 +615,23 @@ static void push_resolve_result(serf_con
 apr_status_t serf__process_async_resolve_results(serf_context_t *ctx)
 {
     serf__resolve_result_t *result = NULL;
-    apr_status_t lock_status;
+    apr_status_t status;
 
-    run_async_resolver_loop();
+    status = run_async_resolver_loop(ctx);
+    if (status)
+        return status;
 
-    lock_status = lock_results(ctx);
-    if (lock_status)
-        return lock_status;
+    status = lock_results(ctx);
+    if (status)
+        return status;
 
     result = ctx->resolve_head;
     ctx->resolve_head = NULL;
-    lock_status = unlock_results(ctx);
+    status = unlock_results(ctx);
 
-    /* TODO: if (lock_status) ... then what? Shouldn't be possible. */
-    /* if (lock_status) */
-    /*     return lock_status; */
+    /* TODO: if (status) ... then what? Shouldn't be possible. */
+    /* if (status) */
+    /*     return status; */
 
     while (result)
     {

Modified: serf/trunk/test/test_context.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/test/test_context.c?rev=1927308&r1=1927307&r2=1927308&view=diff
==============================================================================
--- serf/trunk/test/test_context.c (original)
+++ serf/trunk/test/test_context.c Fri Jul 18 12:47:56 2025
@@ -1046,8 +1046,8 @@ static void test_async_resolve(CuTest *t
         if (!APR_STATUS_IS_TIMEUP(status))
             CuAssertIntEquals(tc, APR_SUCCESS, status);
     }
-    CuAssertPtrNotNull(tc, tb->connection);
     CuAssertIntEquals(tc, APR_SUCCESS, tb->user_status);
+    CuAssertPtrNotNull(tc, tb->connection);
 
     /* Send some requests on the connections */
     for (i = 0 ; i < num_requests ; i++) {


Reply via email to