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++) {