Author: brane
Date: Fri Jul 25 14:25:43 2025
New Revision: 1927469
Log:
On the user-defined-authn branch: sync with trunk r1927468.
Modified:
serf/branches/user-defined-authn/ (props changed)
serf/branches/user-defined-authn/CMakeLists.txt
serf/branches/user-defined-authn/README
serf/branches/user-defined-authn/buckets/ssl_buckets.c
serf/branches/user-defined-authn/build/FindAPR.cmake
serf/branches/user-defined-authn/build/FindAPRUtil.cmake
serf/branches/user-defined-authn/build/SerfGenClangd.cmake
serf/branches/user-defined-authn/serf.h
serf/branches/user-defined-authn/serf_private.h
serf/branches/user-defined-authn/src/context.c
serf/branches/user-defined-authn/src/outgoing.c
serf/branches/user-defined-authn/src/resolve.c
serf/branches/user-defined-authn/test/MockHTTPinC/CMakeLists.txt
serf/branches/user-defined-authn/test/test_context.c
serf/branches/user-defined-authn/test/test_ssl.c
serf/branches/user-defined-authn/test/test_util.c
Modified: serf/branches/user-defined-authn/CMakeLists.txt
==============================================================================
--- serf/branches/user-defined-authn/CMakeLists.txt Fri Jul 25 13:56:10
2025 (r1927468)
+++ serf/branches/user-defined-authn/CMakeLists.txt Fri Jul 25 14:25:43
2025 (r1927469)
@@ -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)
@@ -263,6 +264,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)
@@ -272,6 +288,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)
@@ -497,6 +518,8 @@ if(NOT SERF_WINDOWS)
${APRUTIL_LDFLAGS}
${APRUTIL_LIBRARIES}
${APRUTIL_EXTRALIBS}
+ ${BROTLI_COMMON_LIBRARY}
+ ${BROTLI_DECODE_LIBRARY}
${GSSAPI_LIBRARIES}
${ZLIB_LIBRARIES}
)
@@ -532,6 +555,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 +582,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 +601,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/branches/user-defined-authn/README
==============================================================================
--- serf/branches/user-defined-authn/README Fri Jul 25 13:56:10 2025
(r1927468)
+++ serf/branches/user-defined-authn/README Fri Jul 25 14:25:43 2025
(r1927469)
@@ -31,7 +31,7 @@ create a symlink for 'scons' in your PAT
Fetch the scons-local package:
http://prdownloads.sourceforge.net/scons/scons-local-2.3.5.tar.gz
-
+
Alternatively create a virtual Python environment and install SCons via
pip (replace the path ~/scons_venv with your preferred path):
@@ -105,6 +105,11 @@ $ scons --help
$ scons check
+Some tests take a long time to run, for example a 4GB request. These can
+be enabled by passing ENABLE_SLOW_TESTS on the command line:
+
+$ scons check ENABLE_SLOW_TESTS=1
+
1.1.4 Installing Apache Serf
@@ -236,6 +241,11 @@ or, on Windows using the Visual Studio g
$ cmake --build out --config Release --target run_tests
)
+Some tests take a long time to run, for example a 4GB request. These can
+be enabled by passing ENABLE_SLOW_TESTS on the command line:
+
+$ cmake -DENABLE_SLOW_TESTS=ON
+
1.2.5 Installing Apache Serf
Modified: serf/branches/user-defined-authn/buckets/ssl_buckets.c
==============================================================================
--- serf/branches/user-defined-authn/buckets/ssl_buckets.c Fri Jul 25
13:56:10 2025 (r1927468)
+++ serf/branches/user-defined-authn/buckets/ssl_buckets.c Fri Jul 25
14:25:43 2025 (r1927469)
@@ -1608,7 +1608,6 @@ static int ssl_pass_cb(UI *ui, UI_STRING
static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
{
serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
- unsigned long err = 0;
#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
STACK_OF(X509) *leaves;
STACK_OF(X509) *intermediates;
@@ -1919,7 +1918,7 @@ static int ssl_need_client_cert(SSL *ssl
return 1;
}
else {
- err = ERR_get_error();
+ unsigned long err = ERR_get_error();
ERR_clear_error();
if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
Modified: serf/branches/user-defined-authn/build/FindAPR.cmake
==============================================================================
--- serf/branches/user-defined-authn/build/FindAPR.cmake Fri Jul 25
13:56:10 2025 (r1927468)
+++ serf/branches/user-defined-authn/build/FindAPR.cmake Fri Jul 25
14:25:43 2025 (r1927469)
@@ -42,7 +42,7 @@ cmake_minimum_required(VERSION 3.12)
# APR_FOUND - True if APR was found.
# APR_VERSION - The version of APR found (x.y.z)
# APR_CONTAINS_APRUTIL - True if the APR major version is 2 or greater.
-# APR_INCLUDES - Where to find apr.h, etc.
+# APR_INCLUDE_DIR - Where to find apr.h, etc.
# APR_LIBRARIES - Linker switches to use with ld to link against APR
#
# ::
@@ -211,7 +211,7 @@ if(NOT _apru_include_only_utilities)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
- _apru_find_win_version("apr" APR_INCLUDES
+ _apru_find_win_version("apr" APR_INCLUDE_DIR
APR_VERSION _apr_major _apr_minor)
set(_apr_name "apr-${_apr_major}")
@@ -229,12 +229,11 @@ if(NOT _apru_include_only_utilities)
_apru_config(${APR_CONFIG_EXECUTABLE} ${_varname} "${_regexp}" "${ARGN}")
endmacro(_apr_invoke)
- _apr_invoke(APR_CFLAGS "(^| )-[gOW][^ ]*" --cppflags --cflags)
- _apr_invoke(APR_INCLUDES "(^| )-I" --includes)
- _apr_invoke(APR_LDFLAGS "" --ldflags)
- _apr_invoke(APR_LIBRARIES "" --link-ld)
- _apr_invoke(APR_EXTRALIBS "" --libs)
- _apr_invoke(APR_VERSION "" --version)
+ _apr_invoke(APR_CFLAGS "(^| )-[gOW][^ ]*" --cppflags --cflags)
+ _apr_invoke(APR_INCLUDE_DIR "" --includedir)
+ _apr_invoke(APR_LIBRARIES "" --link-ld)
+ _apr_invoke(APR_EXTRALIBS "" --libs)
+ _apr_invoke(APR_VERSION "" --version)
string(REGEX REPLACE "^([0-9]+)\\..*$" "\\1" _apr_major "${APR_VERSION}")
endif() # NOT Windows
@@ -248,7 +247,7 @@ if(NOT _apru_include_only_utilities)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
APR
- REQUIRED_VARS APR_LIBRARIES APR_INCLUDES
+ REQUIRED_VARS APR_LIBRARIES APR_INCLUDE_DIR
VERSION_VAR APR_VERSION)
if(APR_FOUND)
@@ -257,7 +256,7 @@ if(NOT _apru_include_only_utilities)
if(APR_LIBRARIES AND APR_RUNTIME_LIBS)
add_library(APR::APR SHARED IMPORTED)
set_target_properties(APR::APR PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${APR_INCLUDES}"
+ INTERFACE_INCLUDE_DIRECTORIES "${APR_INCLUDE_DIR}"
IMPORTED_LOCATION "${APR_RUNTIME_LIBS}"
IMPORTED_IMPLIB "${APR_LIBRARIES}")
endif()
@@ -267,9 +266,10 @@ if(NOT _apru_include_only_utilities)
add_library(APR::APR_static STATIC IMPORTED)
set_target_properties(APR::APR_static PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "APR_DECLARE_STATIC"
- INTERFACE_INCLUDE_DIRECTORIES "${APR_INCLUDES}"
- IMPORTED_INTERFACE_LINK_LIBRARIES "${_apr_extra}"
+ INTERFACE_INCLUDE_DIRECTORIES "${APR_INCLUDE_DIR}"
IMPORTED_LOCATION "${_apr_static}")
+ target_link_libraries(APR::APR_static
+ INTERFACE ${_apr_extra})
endif()
else() # NOT Windows
@@ -277,9 +277,10 @@ if(NOT _apru_include_only_utilities)
_apru_location(_apr_library _apr_extra "${APR_LIBRARIES}")
add_library(APR::APR UNKNOWN IMPORTED)
set_target_properties(APR::APR PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${APR_INCLUDES}"
- INTERFACE_LINK_LIBRARIES
"${APR_LDFLAGS};${APR_EXTRALIBS};${_apr_extra}"
+ INTERFACE_INCLUDE_DIRECTORIES "${APR_INCLUDE_DIR}"
IMPORTED_LOCATION "${_apr_library}")
+ target_link_libraries(APR::APR
+ INTERFACE ${APR_EXTRALIBS} ${_apr_extra})
endif() # NOT Windows
endif(APR_FOUND)
Modified: serf/branches/user-defined-authn/build/FindAPRUtil.cmake
==============================================================================
--- serf/branches/user-defined-authn/build/FindAPRUtil.cmake Fri Jul 25
13:56:10 2025 (r1927468)
+++ serf/branches/user-defined-authn/build/FindAPRUtil.cmake Fri Jul 25
14:25:43 2025 (r1927469)
@@ -41,7 +41,7 @@ cmake_minimum_required(VERSION 3.12)
#
# APRUtil_FOUND - True if APR-Util was found
# APRUTIL_VERSION - The version of APR-Util found (x.y.z)
-# APRUTIL_INCLUDES - Where to find apr.h, etc.
+# APRUTIL_INCLUDE_DIR - Where to find apr.h, etc.
# APRUTIL_LIBRARIES - Linker switches to use with ld to link against APR
#
# ::
@@ -83,7 +83,7 @@ else(APR_CONTAINS_APRUTIL)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
- _apru_find_win_version("apu" APRUTIL_INCLUDES
+ _apru_find_win_version("apu" APRUTIL_INCLUDE_DIR
APRUTIL_VERSION _apu_major _apu_minor)
set(_apu_name "aprutil-${_apu_major}")
@@ -122,18 +122,17 @@ else(APR_CONTAINS_APRUTIL)
_apru_config(${APRUTIL_CONFIG_EXECUTABLE} ${_varname} "${_regexp}"
"${ARGN}")
endmacro(_apu_invoke)
- _apu_invoke(APRUTIL_INCLUDES "(^| )-I" --includes)
- _apu_invoke(APRUTIL_EXTRALIBS "" --libs)
- _apu_invoke(APRUTIL_LIBRARIES "" --link-ld)
- _apu_invoke(APRUTIL_LDFLAGS "" --ldflags)
- _apu_invoke(APRUTIL_VERSION "" --version)
+ _apu_invoke(APRUTIL_INCLUDE_DIR "" --includedir)
+ _apu_invoke(APRUTIL_EXTRALIBS "" --libs)
+ _apu_invoke(APRUTIL_LIBRARIES "" --link-ld)
+ _apu_invoke(APRUTIL_VERSION "" --version)
endif() # NOT Windows
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
APRUtil
- REQUIRED_VARS APRUTIL_LIBRARIES APRUTIL_INCLUDES
+ REQUIRED_VARS APRUTIL_LIBRARIES APRUTIL_INCLUDE_DIR
VERSION_VAR APRUTIL_VERSION)
if(APRUtil_FOUND)
@@ -142,7 +141,7 @@ else(APR_CONTAINS_APRUTIL)
if(APRUTIL_LIBRARIES AND APRUTIL_RUNTIME_LIBS)
add_library(APR::APRUtil SHARED IMPORTED)
set_target_properties(APR::APRUtil PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${APRUTIL_INCLUDES}"
+ INTERFACE_INCLUDE_DIRECTORIES "${APRUTIL_INCLUDE_DIR}"
IMPORTED_LOCATION "${APRUTIL_RUNTIME_LIBS}"
IMPORTED_IMPLIB "${APRUTIL_LIBRARIES}")
if(TARGET APR::APR)
@@ -159,7 +158,7 @@ else(APR_CONTAINS_APRUTIL)
add_library(APR::APRUtil_static STATIC IMPORTED)
set_target_properties(APR::APRUtil_static PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "APU_DECLARE_STATIC"
- INTERFACE_INCLUDE_DIRECTORIES "${APRUTIL_INCLUDES}"
+ INTERFACE_INCLUDE_DIRECTORIES "${APRUTIL_INCLUDE_DIR}"
IMPORTED_LOCATION "${_apu_static}")
target_link_libraries(APR::APRUtil_static
INTERFACE ${_apu_extra})
@@ -170,10 +169,10 @@ else(APR_CONTAINS_APRUTIL)
_apru_location(_apu_library _apu_extra "${APRUTIL_LIBRARIES}")
add_library(APR::APRUtil UNKNOWN IMPORTED)
set_target_properties(APR::APRUtil PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${APRUTIL_INCLUDES}"
+ INTERFACE_INCLUDE_DIRECTORIES "${APRUTIL_INCLUDE_DIR}"
IMPORTED_LOCATION "${_apu_library}")
target_link_libraries(APR::APRUtil
- INTERFACE ${APRUTIL_LDFLAGS};${APRUTIL_EXTRALIBS};${_apu_extra})
+ INTERFACE ${APRUTIL_EXTRALIBS} ${_apu_extra})
endif() # NOT Windows
endif(APRUtil_FOUND)
Modified: serf/branches/user-defined-authn/build/SerfGenClangd.cmake
==============================================================================
--- serf/branches/user-defined-authn/build/SerfGenClangd.cmake Fri Jul 25
13:56:10 2025 (r1927468)
+++ serf/branches/user-defined-authn/build/SerfGenClangd.cmake Fri Jul 25
14:25:43 2025 (r1927469)
@@ -55,9 +55,9 @@ function(SerfGenClangd)
write_flags("--language=c")
write_includes("${CMAKE_SOURCE_DIR}")
- list(APPEND includes ${APR_INCLUDES})
+ list(APPEND includes ${APR_INCLUDE_DIR})
if(NOT APR_CONTAINS_APRUTIL)
- list(APPEND includes ${APRUTIL_INCLUDES})
+ list(APPEND includes ${APRUTIL_INCLUDE_DIR})
endif()
list(APPEND includes ${OPENSSL_INCLUDE_DIR})
list(APPEND includes ${ZLIB_INCLUDE_DIR})
@@ -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/branches/user-defined-authn/serf.h
==============================================================================
--- serf/branches/user-defined-authn/serf.h Fri Jul 25 13:56:10 2025
(r1927468)
+++ serf/branches/user-defined-authn/serf.h Fri Jul 25 14:25:43 2025
(r1927469)
@@ -551,7 +551,7 @@ apr_status_t serf_connection_create3(
* The @a ctx and @a resolved_baton arguments are the same that were passed
* to serf_address_resolve_async().
*
- * @a status contains the result of the address resolution. If it is notably
+ * @a status contains the result of the address resolution. If it is not
* @c APR_SUCCESS, then @a host_address is invalid and should be ignored.
*
* The resolved @a host_address is ephemeral, allocated iun @a pool and lives
@@ -597,6 +597,56 @@ apr_status_t serf_address_resolve_async(
apr_pool_t *pool);
+/**
+ * Notification callback when a connection hae been created.
+ *
+ * The @a ctx and @a created_baton arguments are the same that were passed
+ * to serf_connection_create_async().
+ *
+ * @a status contains the result of the connection creation. If it is not
+ * @c APR_SUCCESS, then @a conn is invalid and should be ignored.
+ *
+ * The created @a conn is allocated in the pool that was passed to
+ * serf_connection_create_async(); this is @b not the same as @a pool.
+ *
+ * All temporary allocations should be made in @a pool.
+ *
+ * @since New in 1.4.
+ */
+/* FIXME: EXPERIMENTAL */
+typedef void (*serf_connection_created_t)(
+ serf_context_t *ctx,
+ void *created_baton,
+ serf_connection_t *conn,
+ apr_status_t status,
+ apr_pool_t *pool);
+
+/**
+ * Asyncchronously create a new connection associated with
+ * the @a ctx serf context.
+ *
+ * Like serf_connection_create3() with @a host_address set to @c NULL,
+ * except that address resolution is performed asynchronously, similarly to
+ * serf_address_resolve_async().
+ *
+ * The @a created callback with @a created_baton is called when the connection
+ * is created but before it is opened.
+ *
+ * @since New in 1.4.
+ */
+/* FIXME: EXPERIMENTAL */
+apr_status_t serf_connection_create_async(
+ serf_context_t *ctx,
+ apr_uri_t host_info,
+ serf_connection_created_t created,
+ void *created_baton,
+ serf_connection_setup_t setup,
+ void *setup_baton,
+ serf_connection_closed_t closed,
+ void *closed_baton,
+ apr_pool_t *pool);
+
+
typedef apr_status_t (*serf_accept_client_t)(
serf_context_t *ctx,
serf_listener_t *l,
Modified: serf/branches/user-defined-authn/serf_private.h
==============================================================================
--- serf/branches/user-defined-authn/serf_private.h Fri Jul 25 13:56:10
2025 (r1927468)
+++ serf/branches/user-defined-authn/serf_private.h Fri Jul 25 14:25:43
2025 (r1927469)
@@ -440,17 +440,6 @@ serf__config_store_remove_host(serf__con
const char *hostname_port);
-typedef struct serf__resolve_result_t serf__resolve_result_t;
-struct serf__resolve_result_t
-{
- apr_sockaddr_t *host_address;
- apr_status_t status;
- serf_address_resolved_t resolved;
- void *resolved_baton;
- apr_pool_t *result_pool;
- serf__resolve_result_t *next;
-};
-
struct serf_context_t {
/* the pool used for self and for other allocations */
apr_pool_t *pool;
@@ -498,11 +487,10 @@ struct serf_context_t {
serf_config_t *config;
- /* The results of asynchronous address resolution. */
-#if APR_HAS_THREADS
- apr_thread_mutex_t *resolve_guard;
-#endif
- serf__resolve_result_t *resolve_head;
+ /* Support for asynchronous address resolution. */
+ void *volatile resolve_head;
+ apr_status_t resolve_init_status;
+ void *resolve_context;
};
struct serf_listener_t {
@@ -684,6 +672,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/branches/user-defined-authn/src/context.c
==============================================================================
--- serf/branches/user-defined-authn/src/context.c Fri Jul 25 13:56:10
2025 (r1927468)
+++ serf/branches/user-defined-authn/src/context.c Fri Jul 25 14:25:43
2025 (r1927469)
@@ -136,6 +136,20 @@ void serf_config_authn_types(serf_contex
}
+#ifdef BROKEN_WSAPOLL
+/* APR 1.4.x switched to using WSAPoll() on Win32, but it does not
+ * properly handle errors on a non-blocking sockets (such as
+ * connecting to a server where no listener is active).
+ *
+ * So, sadly, we must force using select() on Win32.
+ *
+ *
http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%[email protected]%3E
+ */
+#define PLATFORM_POLLSET_METHOD APR_POLLSET_SELECT
+#else
+#define PLATFORM_POLLSET_METHOD APR_POLLSET_DEFAULT
+#endif
+
serf_context_t *serf_context_create_ex(
void *user_baton,
serf_socket_add_t addf,
@@ -160,20 +174,8 @@ serf_context_t *serf_context_create_ex(
### Probably move creation of the pollset to later when we have
### the possibility of returning status to the caller.
*/
-#ifdef BROKEN_WSAPOLL
- /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not
- * properly handle errors on a non-blocking sockets (such as
- * connecting to a server where no listener is active).
- *
- * So, sadly, we must force using select() on Win32.
- *
- *
http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%[email protected]%3E
- */
(void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0,
- APR_POLLSET_SELECT);
-#else
- (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0);
-#endif
+ PLATFORM_POLLSET_METHOD);
ctx->pollset_baton = ps;
ctx->pollset_add = pollset_add;
ctx->pollset_rm = pollset_rm;
@@ -193,13 +195,12 @@ serf_context_t *serf_context_create_ex(
ctx->server_authn_info = apr_hash_make(pool);
/* Initialize async resolver result queue. */
-#if APR_HAS_THREADS
- /* FIXME: Ignore the status? */
- apr_thread_mutex_create(&ctx->resolve_guard,
- APR_THREAD_MUTEX_DEFAULT,
- ctx->pool);
-#endif
ctx->resolve_head = NULL;
+ 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/branches/user-defined-authn/src/outgoing.c
==============================================================================
--- serf/branches/user-defined-authn/src/outgoing.c Fri Jul 25 13:56:10
2025 (r1927468)
+++ serf/branches/user-defined-authn/src/outgoing.c Fri Jul 25 14:25:43
2025 (r1927469)
@@ -1362,6 +1362,78 @@ apr_status_t serf_connection_create3(
return status;
}
+
+struct async_create_baton
+{
+ apr_uri_t host_info;
+ serf_connection_created_t created;
+ void *created_baton;
+ serf_connection_setup_t setup;
+ void *setup_baton;
+ serf_connection_closed_t closed;
+ void *closed_baton;
+ apr_pool_t *conn_pool;
+};
+
+static void async_conn_create(serf_context_t *ctx,
+ void *resolved_baton,
+ apr_sockaddr_t *host_address,
+ apr_status_t status,
+ apr_pool_t *scratch_pool)
+{
+ struct async_create_baton *const baton = resolved_baton;
+ serf_connection_t *conn = NULL;
+
+ if (status == APR_SUCCESS)
+ {
+ 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,
+ host_address,
+ baton->setup, baton->setup_baton,
+ baton->closed,
baton->closed_baton,
+ baton->conn_pool);
+ }
+ }
+
+ baton->created(ctx, baton->created_baton, conn, status, scratch_pool);
+}
+
+apr_status_t serf_connection_create_async(
+ serf_context_t *ctx,
+ apr_uri_t host_info,
+ serf_connection_created_t created,
+ void *created_baton,
+ serf_connection_setup_t setup,
+ void *setup_baton,
+ serf_connection_closed_t closed,
+ void *closed_baton,
+ apr_pool_t *pool)
+{
+ 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);
+ apr_pool_destroy(scratch_pool);
+ return status;
+}
+
+
apr_status_t serf_connection_reset(
serf_connection_t *conn)
{
Modified: serf/branches/user-defined-authn/src/resolve.c
==============================================================================
--- serf/branches/user-defined-authn/src/resolve.c Fri Jul 25 13:56:10
2025 (r1927468)
+++ serf/branches/user-defined-authn/src/resolve.c Fri Jul 25 14:25:43
2025 (r1927469)
@@ -19,29 +19,49 @@
*/
#include <apr.h>
+#include <apr_version.h>
+
+/* Include the headers needed for inet_ntop and related structs.
+ On Windows, we'll always get <Winsock2.h> from <apr.h>. */
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
#include <apr_errno.h>
-#include <apr_network_io.h>
#include <apr_pools.h>
+#include <apr_atomic.h>
+#include <apr_network_io.h>
#include <apr_thread_mutex.h>
#include <apr_thread_pool.h>
+/* Third-party resolver headers. */
+#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
+
#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)
/*
* FIXME: EXPERIMENTAL
* TODO:
- * - Add cleanup function for in-flight resolve tasks if their owning
- * context is destroyed. This function should be called from the
- * context's pool cleanup handler.
- * - 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.
+ * - Wake the poll/select in serf_context_run() when new resolve
+ * results are available.
+ *
+ * TODO for Unbound:
+ * - Convert unbound results to apr_sockaddr_t.
*/
@@ -51,7 +71,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);
@@ -73,6 +93,10 @@ apr_status_t serf_address_resolve_async(
{
apr_pool_t *resolve_pool;
+ if (ctx->resolve_init_status != APR_SUCCESS) {
+ return ctx->resolve_init_status;
+ }
+
apr_pool_create(&resolve_pool, ctx->pool);
/* See serf_connection_create3(): if there's a proxy configured in the
@@ -105,10 +129,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,
@@ -122,13 +152,360 @@ 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 /* 0 */
+
+#if SERF_HAVE_UNBOUND
+
+/* DNS classes and record types.
+ https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
+#define RR_CLASS_IN 1 /* Internet */
+#define RR_TYPE_A 1 /* IPv4 address */
+#define RR_TYPE_AAAA 28 /* IPv6 address */
+
+
+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;
+ }
+}
+
+
+struct resolve_context
+{
+ struct ub_ctx *ub_ctx;
+ volatile apr_uint32_t tasks;
+};
+
+static apr_status_t cleanup_resolve_context(void *baton)
+{
+ struct resolve_context *const rctx = baton;
+ ub_ctx_delete(rctx->ub_ctx);
+ return APR_SUCCESS;
+}
+
+static apr_status_t create_resolve_context(serf_context_t *ctx)
+{
+ struct resolve_context *const rctx = apr_palloc(ctx->pool, sizeof(*rctx));
+ int err;
+
+ rctx->ub_ctx = ub_ctx_create();
+ rctx->tasks = 0;
+ if (!rctx->ub_ctx)
+ return APR_ENOMEM;
+
+ err = ub_ctx_resolvconf(rctx->ub_ctx, NULL);
+ if (!err)
+ err = ub_ctx_hosts(rctx->ub_ctx, NULL);
+ if (!err)
+ err = ub_ctx_async(rctx->ub_ctx, 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));
+ cleanup_resolve_context(rctx);
+ return status;
+ }
+
+ ctx->resolve_context = rctx;
+ /* 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, rctx, cleanup_resolve_context);
+ return APR_SUCCESS;
+}
+
+
+/* Task data for the Unbound resolver. */
+typedef struct unbound_resolve_task resolve_task_t;
+
+struct resolve_result
+{
+ int err;
+ apr_status_t status;
+ struct ub_result* ub_result;
+ resolve_task_t *task;
+ const char *qtype;
+};
+
+struct unbound_resolve_task
+{
+ serf_context_t *ctx;
+ apr_port_t host_port;
+
+ /* There can be one or two pending results, depending on whether
+ we resolve for IPv6 as well as IPv4. */
+ volatile apr_uint32_t pending_results;
+ struct resolve_result results[2];
+
+ serf_address_resolved_t resolved;
+ void *resolved_baton;
+ apr_pool_t *resolve_pool;
+};
+
+static void resolve_finalize(resolve_task_t *task)
+{
+ /* TODO: Convert ub_result to apr_sockaddr_t */
+ if (task->results[0].ub_result)
+ ub_resolve_free(task->results[0].ub_result);
+ if (task->results[1].ub_result)
+ ub_resolve_free(task->results[1].ub_result);
+
+ push_resolve_result(task->ctx, NULL, APR_EAFNOSUPPORT,
+ task->resolved, task->resolved_baton,
+ task->resolve_pool);
+}
+
+static void resolve_callback(void* baton, int err,
+ struct ub_result* ub_result)
+{
+ struct resolve_result *const resolve_result = baton;
+ resolve_task_t *const task = resolve_result->task;
+ apr_status_t status = err_to_status(err);
+
+ struct resolve_context *const rctx = task->ctx->resolve_context;
+ apr_atomic_dec32(&rctx->tasks);
+
+ if (err)
+ {
+ /* TODO: Error callback */
+ serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+ "unbound resolve: [%s] error %s\n",
+ resolve_result->qtype, ub_strerror(err));
+ }
+ else if (!ub_result->havedata)
+ {
+ if (ub_result->nxdomain) {
+ /* TODO: Error callback */
+ serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+ "unbound resolve: [%s] NXDOMAIN [%d]\n",
+ resolve_result->qtype, ub_result->rcode);
+ if (status == APR_SUCCESS)
+ status = APR_ENOENT;
+ }
+ if (ub_result->bogus) {
+ /* TODO: Error callback */
+ serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+ "unbound resolve: [%s] BOGUS [%d]%s%s\n",
+ resolve_result->qtype, ub_result->rcode,
+ ub_result->why_bogus ? " " : "",
+ ub_result->why_bogus ? ub_result->why_bogus : "");
+ if (status == APR_SUCCESS)
+ status = APR_EINVAL;
+ }
+ if (ub_result->was_ratelimited) {
+ /* TODO: Error callback */
+ serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, task->ctx->config,
+ "unbound resolve: [%s] SERVFAIL [%d]\n",
+ resolve_result->qtype, ub_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: [%s] no data [%d]\n",
+ resolve_result->qtype, ub_result->rcode);
+ status = APR_ENOENT;
+ }
+ }
+
+ resolve_result->err = err;
+ resolve_result->status = status;
+ resolve_result->ub_result = ub_result;
+
+ if (status == APR_SUCCESS
+ && serf__log_enabled(LOGLVL_DEBUG, LOGCOMP_CONN, task->ctx->config))
+ {
+ char buf[INET6_ADDRSTRLEN];
+ const socklen_t len = sizeof(buf);
+ int i;
+
+ for (i = 0; ub_result->data && ub_result->data[i]; ++i) {
+ const char *address = "(AF-unknown)";
+
+ if (ub_result->len[i] == sizeof(struct in_addr))
+ address = inet_ntop(AF_INET, ub_result->data[i], buf, len);
+ else if (ub_result->len[i] == sizeof(struct in6_addr))
+ address = inet_ntop(AF_INET6, ub_result->data[i], buf, len);
+ serf__log(LOGLVL_DEBUG, LOGCOMP_CONN,
+ __FILE__, task->ctx->config,
+ "unbound resolve: [%s] %s: %s\n",
+ resolve_result->qtype, ub_result->qname, address);
+ }
+ }
+
+ /* The last pending task combines and publishes the results. */
+ if (apr_atomic_dec32(&task->pending_results) == 1)
+ resolve_finalize(task);
+}
+
+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 resolve_context *const rctx = ctx->resolve_context;
+ resolve_task_t *const task = apr_palloc(resolve_pool, sizeof(*task));
+ apr_status_t status = APR_SUCCESS;
+ int err4 = 0, err6 = 0;
+
+ task->ctx = ctx;
+ task->host_port = host_info.port;
+
+#if APR_HAVE_IPV6
+ task->pending_results = 2;
+#else
+ task->pending_results = 1;
#endif
+ task->results[0].err = task->results[1].err = 0;
+ task->results[0].status = task->results[1].status = APR_SUCCESS;
+ task->results[0].ub_result = task->results[1].ub_result = NULL;
+ task->results[0].task = task->results[1].task = task;
+ task->results[0].qtype = task->results[1].qtype = "??";
+
+ task->resolved = resolved;
+ task->resolved_baton = resolved_baton;
+ task->resolve_pool = resolve_pool;
+
+ task->results[0].qtype = "v4";
+ err4 = ub_resolve_async(rctx->ub_ctx, host_info.hostname,
+ RR_TYPE_A, RR_CLASS_IN,
+ &task->results[0], resolve_callback, NULL);
+ if (!err4) {
+ apr_atomic_inc32(&rctx->tasks);
+ }
+
+#if APR_HAVE_IPV6
+ task->results[1].qtype = "v6";
+ err6 = ub_resolve_async(rctx->ub_ctx, host_info.hostname,
+ RR_TYPE_AAAA, RR_CLASS_IN,
+ &task->results[1], resolve_callback, NULL);
+ if (!err6) {
+ apr_atomic_inc32(&rctx->tasks);
+ }
+#endif /* APR_HAVE_IPV6 */
+
+ if (err4 || err6)
+ {
+ apr_uint32_t pending_results = -1;
-#else /* !SERF_USE_ASYNC_RESOLVER */
+ if (err4) {
+ pending_results = apr_atomic_dec32(&task->pending_results);
+ /* TODO: Error callback */
+ serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, ctx->config,
+ "unbound resolve start: [v4] %s\n", ub_strerror(err4));
+ status = err_to_status(err4);
+ }
+
+#if APR_HAVE_IPV6
+ if (err6) {
+ pending_results = apr_atomic_dec32(&task->pending_results);
+ /* TODO: Error callback */
+ serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, ctx->config,
+ "unbound resolve start: [v6] %s\n", ub_strerror(err6));
+ /* We have only one status to report. */
+ if (!err4)
+ status = err_to_status(err6);
+ }
+#endif /* APR_HAVE_IPV6 */
+
+ /* If one of the tasks failed and the other has already completed,
+ we have to do the result processing here. Note that the Unbound
+ callbacks can be called synchronously from ub_resolve_async(). */
+ if (pending_results == 1)
+ resolve_finalize(task);
+ }
+
+ return status;
+}
+
+static apr_status_t run_async_resolver_loop(serf_context_t *ctx)
+{
+ struct resolve_context *const rctx = ctx->resolve_context;
+
+ /* No need to poll if there are no in-flight tasks. */
+ if (apr_atomic_read32(&rctx->tasks))
+ {
+ if (ub_poll(rctx->ub_ctx)) {
+ const int err = ub_process(rctx->ub_ctx);
+ 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
@@ -136,9 +513,10 @@ static void run_async_resolver_loop(void
#define MAX_WORK_QUEUE_THREADS 50
static apr_pool_t *work_pool = NULL;
static apr_thread_pool_t *work_queue = NULL;
-static apr_status_t init_work_queue(void *baton)
+
+static apr_status_t do_init_work_queue(void *baton)
{
- serf_context_t *ctx = baton;
+ serf_context_t *const ctx = baton;
apr_status_t status;
apr_pool_create(&work_pool, NULL);
@@ -152,10 +530,35 @@ static apr_status_t init_work_queue(void
return status;
}
+static apr_status_t init_work_queue(serf_context_t *ctx)
+{
+ SERF__DECLARE_STATIC_INIT_ONCE_CONTEXT(init_ctx);
+ return serf__init_once(&init_ctx, do_init_work_queue, ctx);
+}
+
+
+static apr_status_t cleanup_resolve_tasks(void *baton)
+{
+ /* baton is serf_context_t */
+ return apr_thread_pool_tasks_cancel(work_queue, baton);
+}
+
+static apr_status_t create_resolve_context(serf_context_t *ctx)
+{
+ apr_status_t status;
+
+ ctx->resolve_context = NULL;
+ status = init_work_queue(ctx);
+ if (status == APR_SUCCESS)
+ apr_pool_pre_cleanup_register(ctx->pool, ctx, cleanup_resolve_tasks);
+
+ return status;
+}
+
/* 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;
@@ -176,6 +579,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);
@@ -190,10 +615,7 @@ static apr_status_t resolve_address_asyn
apr_pool_t *scratch_pool)
{
resolve_task_t *task;
- apr_status_t status;
- SERF__DECLARE_STATIC_INIT_ONCE_CONTEXT(init_ctx);
-
- status = serf__init_once(&init_ctx, init_work_queue, ctx);
+ apr_status_t status = init_work_queue(ctx);
if (status)
return status;
@@ -210,103 +632,93 @@ 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 */
/*******************************************************************/
/* The result queue implementation. */
#if HAVE_ASYNC_RESOLVER
-static apr_status_t lock_results(serf_context_t *ctx)
-{
-#if APR_HAS_THREADS
- apr_status_t status = apr_thread_mutex_lock(ctx->resolve_guard);
- if (status) {
- /* TODO: ctx->error_callback... */
- char buffer[256];
- serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, ctx->config,
- "Lock async resolve results: %s\n",
- apr_strerror(status, buffer, sizeof(buffer)));
- }
- return status;
-#else
- return APR_SUCCESS;
-#endif
-}
+#if APR_MAJOR_VERSION < 2
+/* NOTE: The atomic pointer prototypes in apr-1.x are just horribly
+ wrong. They're fixed in version 2.x and these macros deal
+ with the difference. */
+#define apr_atomic_casptr(mem, with, cmp) \
+ (apr_atomic_casptr)((volatile void**)(mem), (with), (cmp))
+#define apr_atomic_xchgptr(mem, with) \
+ (apr_atomic_xchgptr)((volatile void**)(mem), (with))
+#endif /* APR_MAJOR_VERSION < 2 */
+
-static apr_status_t unlock_results(serf_context_t *ctx)
+typedef struct resolve_result_t resolve_result_t;
+struct resolve_result_t
{
-#if APR_HAS_THREADS
- apr_status_t status = apr_thread_mutex_unlock(ctx->resolve_guard);
- if (status) {
- /* TODO: ctx->error_callback... */
- char buffer[256];
- serf__log(LOGLVL_ERROR, LOGCOMP_CONN, __FILE__, ctx->config,
- "Unlock async resolve results: %s\n",
- apr_strerror(status, buffer, sizeof(buffer)));
- }
- return status;
-#else
- return APR_SUCCESS;
-#endif
-}
+ apr_sockaddr_t *host_address;
+ apr_status_t status;
+ serf_address_resolved_t resolved;
+ void *resolved_baton;
+ apr_pool_t *result_pool;
+ resolve_result_t *next;
+};
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;
+ resolve_result_t *result;
+ void *head;
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)
- {
- result->next = ctx->resolve_head;
- ctx->resolve_head = result;
- lock_status = unlock_results(ctx);
- }
+ /* Atomic push this result to the result stack. This might look like
+ a potential priority inversion, however, it's not likely that we'll
+ resolve several tens of thousands of results per second in hundreds
+ of separate threads. */
+ head = apr_atomic_casptr(&ctx->resolve_head, NULL, NULL);
+ do {
+ result->next = head;
+ head = apr_atomic_casptr(&ctx->resolve_head, result, head);
+ } while(head != result->next);
+}
+
- /* TODO: if (lock_status) ... then what? */
+/* Internal API */
+apr_status_t serf__create_resolve_context(serf_context_t *ctx)
+{
+ return create_resolve_context(ctx);
}
/* Internal API */
apr_status_t serf__process_async_resolve_results(serf_context_t *ctx)
{
- serf__resolve_result_t *result = NULL;
- apr_status_t lock_status;
-
- run_async_resolver_loop();
+ resolve_result_t *result;
+ apr_status_t status;
- lock_status = lock_results(ctx);
- if (lock_status)
- return lock_status;
-
- result = ctx->resolve_head;
- ctx->resolve_head = NULL;
- lock_status = unlock_results(ctx);
-
- /* TODO: if (lock_status) ... then what? Shouldn't be possible. */
- /* if (lock_status) */
- /* return lock_status; */
+ status = run_async_resolver_loop(ctx);
+ if (status)
+ return status;
+ /* Grab the whole stack, leaving it empty, and process the contents. */
+ result = apr_atomic_xchgptr(&ctx->resolve_head, NULL);
while (result)
{
- serf__resolve_result_t *const next = result->next;
+ resolve_result_t *const next = result->next;
result->resolved(ctx, result->resolved_baton,
result->host_address, result->status,
result->result_pool);
Modified: serf/branches/user-defined-authn/test/MockHTTPinC/CMakeLists.txt
==============================================================================
--- serf/branches/user-defined-authn/test/MockHTTPinC/CMakeLists.txt Fri Jul
25 13:56:10 2025 (r1927468)
+++ serf/branches/user-defined-authn/test/MockHTTPinC/CMakeLists.txt Fri Jul
25 14:25:43 2025 (r1927469)
@@ -56,5 +56,7 @@ target_compile_options(mockhttpinc
target_compile_definitions(mockhttpinc
PUBLIC "MOCKHTTP_OPENSSL"
PRIVATE ${MockHTTPinC_DEFINES})
-target_include_directories(mockhttpinc SYSTEM BEFORE
- PRIVATE ${APR_INCLUDES} ${APRUTIL_INCLUDES})
+target_link_libraries(mockhttpinc
+ ${SERF_PRIVATE_TARGETS}
+ ${SERF_PUBLIC_TARGETS}
+ ${SERF_STANDARD_LIBRARIES})
Modified: serf/branches/user-defined-authn/test/test_context.c
==============================================================================
--- serf/branches/user-defined-authn/test/test_context.c Fri Jul 25
13:56:10 2025 (r1927468)
+++ serf/branches/user-defined-authn/test/test_context.c Fri Jul 25
14:25:43 2025 (r1927469)
@@ -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++) {
@@ -1058,6 +1058,44 @@ static void test_async_resolve(CuTest *t
handler_ctx, tb->pool);
}
+static void async_resolve_cancel_callback(serf_context_t *ctx,
+ void *resolved_baton,
+ apr_sockaddr_t *host_address,
+ apr_status_t status,
+ apr_pool_t *scratch_pool)
+{
+ *(int*)resolved_baton = 1;
+}
+
+static void test_async_resolve_cancel(CuTest *tc)
+{
+ test_baton_t *tb = tc->testBaton;
+ serf_context_t *ctx;
+ apr_pool_t *ctx_pool;
+ apr_status_t status;
+ apr_uri_t url;
+ int resolved = 0;
+
+ status = apr_uri_parse(tb->pool, "http://localhost:8080/", &url);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+ apr_pool_create(&ctx_pool, tb->pool);
+ CuAssertPtrNotNull(tc, ctx_pool);
+ ctx = serf_context_create(ctx_pool);
+ CuAssertPtrNotNull(tc, ctx);
+
+ status = serf_address_resolve_async(ctx, url,
+ async_resolve_cancel_callback,
+ &resolved, ctx_pool);
+
+ /* This would create and actual race in the test case: */
+ /* serf_context_prerun(ctx); */
+
+ apr_pool_destroy(ctx_pool);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+ CuAssertIntEquals(tc, 0, resolved);
+}
+
/*****************************************************************************/
CuSuite *test_context(void)
@@ -1086,5 +1124,6 @@ CuSuite *test_context(void)
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_resolve_cancel);
return suite;
}
Modified: serf/branches/user-defined-authn/test/test_ssl.c
==============================================================================
--- serf/branches/user-defined-authn/test/test_ssl.c Fri Jul 25 13:56:10
2025 (r1927468)
+++ serf/branches/user-defined-authn/test/test_ssl.c Fri Jul 25 14:25:43
2025 (r1927469)
@@ -31,6 +31,7 @@
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
+#include <openssl/opensslv.h>
#ifndef OPENSSL_NO_OCSP /* requires openssl 0.9.7 or later */
#include <openssl/ocsp.h>
#endif
@@ -2783,10 +2784,16 @@ static void test_ssl_ocsp_verify_respons
{
#ifndef OPENSSL_NO_OCSP
apr_status_t status = verify_ocsp_response(tc, 1, 0, 0, 0);
- /* OCSP responses MUST be signed, we can't even create one
- without a signature. This error doesn't come from response
- validation but because OCSP_response_create() fails. */
+#if OPENSSL_VERSION_NUMBER >= (3 << 28) /* OpenSSL 3.0.0 */
+ /* OCSP responses MUST be signed, and on newer versions of OpenSSL we
+ can't even create one without a signature. This error doesn't come
+ from response validation but because OCSP_response_create() fails. */
CuAssertIntEquals(tc, APR_EGENERAL, status);
+#else
+ /* But both LibreSSL and OpenSSL up to 1.1.1 do allow creating such
+ a response, and so our validation will return a different error. */
+ CuAssertIntEquals(tc, SERF_ERROR_SSL_OCSP_RESPONSE_INVALID, status);
+#endif
#endif /* OPENSSL_NO_OCSP */
}
Modified: serf/branches/user-defined-authn/test/test_util.c
==============================================================================
--- serf/branches/user-defined-authn/test/test_util.c Fri Jul 25 13:56:10
2025 (r1927468)
+++ serf/branches/user-defined-authn/test/test_util.c Fri Jul 25 14:25:43
2025 (r1927469)
@@ -142,44 +142,22 @@ apr_status_t use_new_connection(test_bat
return status;
}
-struct async_reolved_baton
+static void conn_created(serf_context_t *ctx,
+ void *resolved_baton,
+ serf_connection_t *conn,
+ apr_status_t status,
+ apr_pool_t *unused_scratch_pool)
{
- test_baton_t *tb;
- apr_uri_t url;
-};
-
-static void address_resolved(serf_context_t *ctx,
- void *resolved_baton,
- apr_sockaddr_t *host_address,
- apr_status_t status,
- apr_pool_t *unused_scratch_pool)
-{
- struct async_reolved_baton *baton = resolved_baton;
- test_baton_t *tb = baton->tb;
- serf_connection_t *conn;
- apr_pool_t *conn_pool = tb->pool;
+ test_baton_t *tb = resolved_baton;
if (tb->context != ctx)
REPORT_TEST_SUITE_ERROR();
if (status == APR_SUCCESS)
{
- status = apr_sockaddr_info_copy(&host_address, host_address,
conn_pool);
- if (status == APR_SUCCESS)
- status = serf_connection_create3(&conn, ctx,
- baton->url,
- host_address,
- tb->conn_setup,
- tb,
- default_closed_connection,
- tb,
- conn_pool);
- if (status == APR_SUCCESS)
- {
- tb->connection = conn;
- apr_pool_cleanup_register(conn_pool, tb->connection, cleanup_conn,
- apr_pool_cleanup_null);
- }
+ tb->connection = conn;
+ apr_pool_cleanup_register(tb->pool, tb->connection, cleanup_conn,
+ apr_pool_cleanup_null);
}
tb->user_status = status;
@@ -190,7 +168,6 @@ apr_status_t use_new_async_connection(te
{
apr_uri_t url;
apr_status_t status;
- struct async_reolved_baton *baton;
if (tb->connection)
cleanup_conn(tb->connection);
@@ -200,12 +177,12 @@ apr_status_t use_new_async_connection(te
if (status != APR_SUCCESS)
return status;
- baton = apr_palloc(pool, sizeof(*baton));
- baton->tb = tb;
- baton->url = url;
tb->user_status = APR_SUCCESS;
- return serf_address_resolve_async(tb->context, url,
- address_resolved, baton, pool);
+ return serf_connection_create_async(tb->context, url,
+ conn_created, tb,
+ tb->conn_setup, tb,
+ default_closed_connection, tb,
+ tb->pool);
}
static test_baton_t *initTestCtx(CuTest *tc, apr_pool_t *pool)