Author: brane
Date: Sun Jan  4 10:28:45 2026
New Revision: 1931089

Log:
Tie in SSL context error reporting with the context/connection callbacks.

* serf.h
  (serf_context_error_callback_set,
   serf_connection_error_callback_set,
   serf_incoming_error_callback_set): Add a note about baton lifetimes.

* serf_bucket_types.h: Move the encrypt bucket declarations next to the
   decrypt bucket declarations, they belong together.
  (serf_bucket_ssl_encrypt_create,
   serf_bucket_ssl_encrypt_context_get,
   serf_bucket_ssl_decrypt_create,
   serf_bucket_ssl_decrypt_context_get): Add sorely missing docstrings.
  (serf_ssl_use_context_error_callback,
   serf_ssl_use_connection_error_callback,
   serf_ssl_use_incoming_error_callback): New prototypes.

* serf_private.h
  (serf__request_ssl_error,
   serf__response_ssl_error,
   serf__incoming_request_ssl_error,
   serf__incoming_response_ssl_error): Remove prototypes.

* src/error_callbacks.c
  (serf__request_ssl_error,
   serf__response_ssl_error,
   serf__incoming_request_ssl_error,
   serf__incoming_response_ssl_error): Remove unused functions.

* buckets/ssl_buckets.c
  (serf_ssl_context_t): Add err_ctx, an error context field. Replace almost
   all references to global_error_ctx with serf_ssl_context_t::err_ctx.
  (ssl_init_context): Initialize err_ctx from global_error_ctx.
  (serf_ssl_use_context_error_callback,
   serf_ssl_use_connection_error_callback,
   serf_ssl_use_incoming_error_callback): Implement here.

* test/serf_get.c: Include serf_bucket_types.h.
  (conn_setup): Make new SSL contexts use the connection's error callback.
  (global_error_callback,
   connection_error_callback): New error callback functions.
  (main): Register global and per-connection error callbacks.

Modified:
   serf/trunk/buckets/ssl_buckets.c
   serf/trunk/serf.h
   serf/trunk/serf_bucket_types.h
   serf/trunk/serf_private.h
   serf/trunk/src/error_callbacks.c
   serf/trunk/test/serf_get.c

Modified: serf/trunk/buckets/ssl_buckets.c
==============================================================================
--- serf/trunk/buckets/ssl_buckets.c    Sun Jan  4 09:20:25 2026        
(r1931088)
+++ serf/trunk/buckets/ssl_buckets.c    Sun Jan  4 10:28:45 2026        
(r1931089)
@@ -223,6 +223,9 @@ struct serf_ssl_context_t {
     void *protocol_userdata;
 
     serf_config_t *config;
+
+    /* The error callback context. */
+    serf__ssl_error_ctx_t err_ctx;
 };
 
 /* The fallback SSL error context which logs to the global error callback. */
@@ -231,6 +234,27 @@ static const serf__ssl_error_ctx_t globa
     NULL                        /* baton */
 };
 
+void serf_ssl_use_context_error_callback(serf_ssl_context_t *ssl_ctx,
+                                         serf_context_t *ctx)
+{
+    ssl_ctx->err_ctx.dispatch = serf__context_ssl_error;
+    ssl_ctx->err_ctx.baton = ctx;
+}
+
+void serf_ssl_use_connection_error_callback(serf_ssl_context_t *ssl_ctx,
+                                            serf_connection_t *conn)
+{
+    ssl_ctx->err_ctx.dispatch = serf__connection_ssl_error;
+    ssl_ctx->err_ctx.baton = conn;
+}
+
+void serf_ssl_use_incoming_error_callback(serf_ssl_context_t *ssl_ctx,
+                                          serf_incoming_t *client)
+{
+    ssl_ctx->err_ctx.dispatch = serf__incoming_ssl_error;
+    ssl_ctx->err_ctx.baton = client;
+}
+
 static apr_status_t dispatch_ssl_error(const serf__ssl_error_ctx_t *err_ctx,
                                        apr_status_t status,
                                        const char *message)
@@ -1102,7 +1126,7 @@ static apr_status_t status_from_ssl_erro
                     ctx->fatal_err = SERF_ERROR_SSL_COMM_FAILED;
 
                 status = ctx->fatal_err;
-                log_ssl_error(&global_error_ctx, status);
+                log_ssl_error(&ctx->err_ctx, status);
             }
             break;
 
@@ -1116,7 +1140,7 @@ static apr_status_t status_from_ssl_erro
 
         default:
             status = ctx->fatal_err = SERF_ERROR_SSL_COMM_FAILED;
-            log_ssl_error(&global_error_ctx, status);
+            log_ssl_error(&ctx->err_ctx, status);
             break;
     }
 
@@ -1223,7 +1247,7 @@ static apr_status_t ssl_decrypt(void *ba
         } else {
             /* A fatal error occurred. */
             ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
-            log_ssl_error(&global_error_ctx, status);
+            log_ssl_error(&ctx->err_ctx, status);
         }
     } else {
         *len = ssl_len;
@@ -1656,9 +1680,9 @@ static int ssl_read_client_cert_uri(serf
         char ebuf[1024];
         ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
         apr_snprintf(ebuf, sizeof(ebuf), "could not open URI: %s", cert_uri);
-        dispatch_ssl_error(&global_error_ctx, ctx->fatal_err, ebuf);
+        dispatch_ssl_error(&ctx->err_ctx, ctx->fatal_err, ebuf);
 
-        log_ssl_error(&global_error_ctx, ctx->fatal_err);
+        log_ssl_error(&ctx->err_ctx, ctx->fatal_err);
         UI_destroy_method(ui_method);
         return 0;
     }
@@ -1688,7 +1712,7 @@ static int ssl_read_client_cert_uri(serf
             char ebuf[1024];
             ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
             apr_snprintf(ebuf, sizeof(ebuf), "could not read URI: %s", 
cert_uri);
-            dispatch_ssl_error(&global_error_ctx, ctx->fatal_err, ebuf);
+            dispatch_ssl_error(&ctx->err_ctx, ctx->fatal_err, ebuf);
             store_error = 1;
             break;
         }
@@ -1739,7 +1763,7 @@ static int ssl_read_client_cert_uri(serf
     OSSL_STORE_close(store);
 
     if (store_error || ERR_peek_error()) {
-        log_ssl_error(&global_error_ctx, ctx->fatal_err);
+        log_ssl_error(&ctx->err_ctx, ctx->fatal_err);
         result = 0;
         goto cleanup;
     }
@@ -1801,7 +1825,7 @@ static int ssl_read_client_cert_uri(serf
     }
 
     if (ERR_peek_error()) {
-        log_ssl_error(&global_error_ctx, ctx->fatal_err);
+        log_ssl_error(&ctx->err_ctx, ctx->fatal_err);
         result = -1;
         goto cleanup;
     }
@@ -1902,9 +1926,9 @@ static int ssl_need_client_cert(SSL *ssl
         if (status) {
             char ebuf[1024];
             apr_snprintf(ebuf, sizeof(ebuf), "could not open PKCS12: %s", 
cert_path);
-            dispatch_ssl_error(&global_error_ctx, status, ebuf);
+            dispatch_ssl_error(&ctx->err_ctx, status, ebuf);
             apr_strerror(status, ebuf, sizeof(ebuf));
-            dispatch_ssl_error(&global_error_ctx, status, ebuf);
+            dispatch_ssl_error(&ctx->err_ctx, status, ebuf);
 
             ctx->fatal_err = status;
             return -1;
@@ -1989,9 +2013,9 @@ static int ssl_need_client_cert(SSL *ssl
                         else {
                             ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
                             apr_snprintf(ebuf, sizeof(ebuf), "could not parse 
PKCS12: %s", cert_path);
-                            dispatch_ssl_error(&global_error_ctx, 
ctx->fatal_err, ebuf);
+                            dispatch_ssl_error(&ctx->err_ctx, ctx->fatal_err, 
ebuf);
 
-                            log_ssl_error(&global_error_ctx, ctx->fatal_err);
+                            log_ssl_error(&ctx->err_ctx, ctx->fatal_err);
                             return -1;
                         }
                     }
@@ -2001,9 +2025,9 @@ static int ssl_need_client_cert(SSL *ssl
 
                 ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
                 apr_snprintf(ebuf, sizeof(ebuf), "PKCS12 needs a password: 
%s", cert_path);
-                dispatch_ssl_error(&global_error_ctx, ctx->fatal_err, ebuf);
+                dispatch_ssl_error(&ctx->err_ctx, ctx->fatal_err, ebuf);
 
-                log_ssl_error(&global_error_ctx, ctx->fatal_err);
+                log_ssl_error(&ctx->err_ctx, ctx->fatal_err);
                 return -1;
             }
             else {
@@ -2011,9 +2035,9 @@ static int ssl_need_client_cert(SSL *ssl
                 bio_meth_free(biom);
 
                 ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
-                dispatch_ssl_error(&global_error_ctx, ctx->fatal_err, ebuf);
+                dispatch_ssl_error(&ctx->err_ctx, ctx->fatal_err, ebuf);
 
-                log_ssl_error(&global_error_ctx, ctx->fatal_err);
+                log_ssl_error(&ctx->err_ctx, ctx->fatal_err);
                 return -1;
             }
         }
@@ -2198,6 +2222,8 @@ static serf_ssl_context_t *ssl_init_cont
     ssl_ctx->handshake_done = FALSE;
     ssl_ctx->hit_eof = FALSE;
 
+    ssl_ctx->err_ctx = global_error_ctx;
+
     return ssl_ctx;
 }
 
@@ -2402,6 +2428,8 @@ apr_status_t serf_ssl_load_cert_file(
         return APR_SUCCESS;
     }
 
+    /* We don't have an ssl_context_t here, so log the errors to the global
+       callback. It's better than ignoring them. */
     status = SERF_ERROR_SSL_CERT_FAILED;
     log_ssl_error(&global_error_ctx, status);
     return status;
@@ -2466,7 +2494,7 @@ apr_status_t serf_ssl_add_crl_from_file(
     result = X509_STORE_add_crl(store, crl);
     if (!result) {
         ssl_ctx->fatal_err = status = SERF_ERROR_SSL_CERT_FAILED;
-        log_ssl_error(&global_error_ctx, status);
+        log_ssl_error(&ssl_ctx->err_ctx, status);
         return status;
     }
 

Modified: serf/trunk/serf.h
==============================================================================
--- serf/trunk/serf.h   Sun Jan  4 09:20:25 2026        (r1931088)
+++ serf/trunk/serf.h   Sun Jan  4 10:28:45 2026        (r1931089)
@@ -262,6 +262,8 @@ void serf_global_error_callback_set(
  * context @a ctx and, since contexts may not be accessed from multiple
  * threads, serialization is not a concern.
  *
+ * The lifetime of @a baton must be longer than the lifetime of the context.
+ *
  * @since New in 1.5.
  */
 void serf_context_error_callback_set(
@@ -274,6 +276,8 @@ void serf_context_error_callback_set(
  *
  * Like serf_context_error_callback_set() but for connections.
  *
+ * The lifetime of @a baton must be longer than the lifetime of the connection.
+ *
  * @since New in 1.5.
  */
 void serf_connection_error_callback_set(
@@ -286,6 +290,9 @@ void serf_connection_error_callback_set(
  *
  * Like serf_context_error_callback_set() but for incoming connections.
  *
+ * The lifetime of @a baton must be longer than the lifetime of the
+ * incoming connection.
+ *
  * @since New in 1.5.
  */
 void serf_incoming_error_callback_set(

Modified: serf/trunk/serf_bucket_types.h
==============================================================================
--- serf/trunk/serf_bucket_types.h      Sun Jan  4 09:20:25 2026        
(r1931088)
+++ serf/trunk/serf_bucket_types.h      Sun Jan  4 10:28:45 2026        
(r1931089)
@@ -585,9 +585,6 @@ serf_bucket_t *serf_bucket_limit_create(
 
 #define SERF_SSL_SIGNATURE_FAILURE      0x0800
 
-extern const serf_bucket_type_t serf_bucket_type_ssl_encrypt;
-#define SERF_BUCKET_IS_SSL_ENCRYPT(b) SERF_BUCKET_CHECK((b), ssl_encrypt)
-
 typedef struct serf_ssl_context_t serf_ssl_context_t;
 typedef struct serf_ssl_certificate_t serf_ssl_certificate_t;
 
@@ -846,14 +843,6 @@ apr_status_t serf_ssl_use_compression(
     serf_ssl_context_t *ssl_ctx,
     int enabled);
 
-serf_bucket_t *serf_bucket_ssl_encrypt_create(
-    serf_bucket_t *stream,
-    serf_ssl_context_t *ssl_context,
-    serf_bucket_alloc_t *allocator);
-
-serf_ssl_context_t *serf_bucket_ssl_encrypt_context_get(
-    serf_bucket_t *bucket);
-
 /* ==================================================================== */
 
 /**
@@ -1009,17 +998,85 @@ apr_status_t serf_ssl_ocsp_response_veri
 
 /* ==================================================================== */
 
+extern const serf_bucket_type_t serf_bucket_type_ssl_encrypt;
+#define SERF_BUCKET_IS_SSL_ENCRYPT(b) SERF_BUCKET_CHECK((b), ssl_encrypt)
+
+/**
+ * Create an SSL encryption bucket that wraps @a stream.
+ *
+ * If @a ssl_context is not provided, a new one will be created.
+ */
+serf_bucket_t *serf_bucket_ssl_encrypt_create(
+    serf_bucket_t *stream,
+    serf_ssl_context_t *ssl_context,
+    serf_bucket_alloc_t *allocator);
+
+/**
+ * Return the SSL context from an encryption bucket.
+ */
+serf_ssl_context_t *serf_bucket_ssl_encrypt_context_get(
+    serf_bucket_t *bucket);
+
 extern const serf_bucket_type_t serf_bucket_type_ssl_decrypt;
 #define SERF_BUCKET_IS_SSL_DECRYPT(b) SERF_BUCKET_CHECK((b), ssl_decrypt)
 
+/**
+ * Create an SSL encryption bucket that wraps @a stream.
+ *
+ * If @a ssl_context is not provided, a new one will be created.
+ */
 serf_bucket_t *serf_bucket_ssl_decrypt_create(
     serf_bucket_t *stream,
     serf_ssl_context_t *ssl_context,
     serf_bucket_alloc_t *allocator);
 
+/**
+ * Return the SSL context from an decryption bucket.
+ */
 serf_ssl_context_t *serf_bucket_ssl_decrypt_context_get(
     serf_bucket_t *bucket);
 
+/**
+ * Configure the SSL context to use the error cllback set on @a ctx.
+ *
+ * By default, new SSL contexts send error messages to the global
+ * error callback.
+ *
+ * @see serf_global_error_callback_set()
+ * @see serf_context_error_callback_set()
+ *
+ * @since New in 1.5.
+ */
+void serf_ssl_use_context_error_callback(serf_ssl_context_t *ssl_ctx,
+                                         serf_context_t *ctx);
+
+/**
+ * Configure the SSL context to use the error callback set on @a conn.
+ *
+ * By default, new SSL contexts send error messages to the global
+ * error callback.
+ *
+ * @see serf_global_error_callback_set()
+ * @see serf_connection_error_callback_set()
+ *
+ * @since New in 1.5.
+ */
+void serf_ssl_use_connection_error_callback(serf_ssl_context_t *ssl_ctx,
+                                            serf_connection_t *conn);
+
+/**
+ * Configure the SSL context to use the error callback set on @a client.
+ *
+ * By default, new SSL contexts send error messages to the global
+ * error callback.
+ *
+ * @see serf_global_error_callback_set()
+ * @see serf_incoming_error_callback_set()
+ *
+ * @since New in 1.5.
+ */
+void serf_ssl_use_incoming_error_callback(serf_ssl_context_t *ssl_ctx,
+                                          serf_incoming_t *client);
 
 /* ==================================================================== */
 

Modified: serf/trunk/serf_private.h
==============================================================================
--- serf/trunk/serf_private.h   Sun Jan  4 09:20:25 2026        (r1931088)
+++ serf/trunk/serf_private.h   Sun Jan  4 10:28:45 2026        (r1931089)
@@ -178,21 +178,9 @@ apr_status_t serf__context_ssl_error(con
 apr_status_t serf__connection_ssl_error(const void *baton,
                                         apr_status_t status,
                                         const char *message);
-apr_status_t serf__request_ssl_error(const void *baton,
-                                     apr_status_t status,
-                                     const char *message);
-apr_status_t serf__response_ssl_error(const void *baton,
-                                      apr_status_t status,
-                                      const char *message);
 apr_status_t serf__incoming_ssl_error(const void *baton,
                                       apr_status_t status,
                                       const char *message);
-apr_status_t serf__incoming_request_ssl_error(const void *baton,
-                                              apr_status_t status,
-                                              const char *message);
-apr_status_t serf__incoming_response_ssl_error(const void *baton,
-                                               apr_status_t status,
-                                               const char *message);
 
 /*** Logging facilities ***/
 

Modified: serf/trunk/src/error_callbacks.c
==============================================================================
--- serf/trunk/src/error_callbacks.c    Sun Jan  4 09:20:25 2026        
(r1931088)
+++ serf/trunk/src/error_callbacks.c    Sun Jan  4 10:28:45 2026        
(r1931089)
@@ -208,28 +208,6 @@ apr_status_t serf__connection_ssl_error(
                                     conn, status, message);
 }
 
-apr_status_t serf__request_ssl_error(const void *baton,
-                                     apr_status_t status,
-                                     const char *message)
-{
-    const serf_request_t *const req = baton;
-    return process_connection_error(SERF_ERROR_CB_SSL_CONTEXT
-                                    | SERF_ERROR_CB_OUTGOING
-                                    | SERF_ERROR_CB_REQUEST,
-                                    req->conn, status, message);
-}
-
-apr_status_t serf__response_ssl_error(const void *baton,
-                                      apr_status_t status,
-                                      const char *message)
-{
-    const serf_request_t *const req = baton;
-    return process_connection_error(SERF_ERROR_CB_SSL_CONTEXT
-                                    | SERF_ERROR_CB_OUTGOING
-                                    | SERF_ERROR_CB_RESPONSE,
-                                    req->conn, status, message);
-}
-
 apr_status_t serf__incoming_ssl_error(const void *baton,
                                       apr_status_t status,
                                       const char *message)
@@ -239,25 +217,3 @@ apr_status_t serf__incoming_ssl_error(co
                                   | SERF_ERROR_CB_INCOMING,
                                   client, status, message);
 }
-
-apr_status_t serf__incoming_request_ssl_error(const void *baton,
-                                              apr_status_t status,
-                                              const char *message)
-{
-    const serf_incoming_request_t *const req = baton;
-    return process_incoming_error(SERF_ERROR_CB_SSL_CONTEXT
-                                  | SERF_ERROR_CB_INCOMING
-                                  | SERF_ERROR_CB_REQUEST,
-                                  req->incoming, status, message);
-}
-
-apr_status_t serf__incoming_response_ssl_error(const void *baton,
-                                               apr_status_t status,
-                                               const char *message)
-{
-    const serf_incoming_request_t *const req = baton;
-    return process_incoming_error(SERF_ERROR_CB_SSL_CONTEXT
-                                  | SERF_ERROR_CB_INCOMING
-                                  | SERF_ERROR_CB_RESPONSE,
-                                  req->incoming, status, message);
-}

Modified: serf/trunk/test/serf_get.c
==============================================================================
--- serf/trunk/test/serf_get.c  Sun Jan  4 09:20:25 2026        (r1931088)
+++ serf/trunk/test/serf_get.c  Sun Jan  4 10:28:45 2026        (r1931089)
@@ -29,6 +29,7 @@
 #include <apr_version.h>
 
 #include "serf.h"
+#include "serf_bucket_types.h"
 
 /* Add Connection: close header to each request. */
 /* #define CONNECTION_CLOSE_HDR */
@@ -230,6 +231,7 @@ static apr_status_t conn_setup(apr_socke
         c = serf_bucket_ssl_decrypt_create(c, conn_ctx->ssl_ctx, 
ctx->bkt_alloc);
         if (!conn_ctx->ssl_ctx) {
             conn_ctx->ssl_ctx = serf_bucket_ssl_decrypt_context_get(c);
+            serf_ssl_use_connection_error_callback(conn_ctx->ssl_ctx, 
conn_ctx->conn);
         }
         serf_ssl_server_cert_chain_callback_set(conn_ctx->ssl_ctx,
                                                 ignore_all_cert_errors,
@@ -509,6 +511,32 @@ credentials_callback(char **username,
     }
 }
 
+static apr_status_t
+global_error_callback(void *baton,
+                      unsigned source,
+                      apr_status_t status,
+                      const char *message)
+{
+    fprintf(stderr, "ERROR%s <%d> %s\n",
+            ((source & SERF_ERROR_CB_SSL_CONTEXT) ? " (SSL)" : ""),
+            status, message);
+    return APR_SUCCESS;
+}
+
+static apr_status_t
+connection_error_callback(void *baton,
+                          unsigned source,
+                          apr_status_t status,
+                          const char *message)
+{
+    const char *const conn_id = baton;
+    fprintf(stderr, "ERROR conn[%s]%s <%d> %s\n",
+            conn_id,
+            ((source & SERF_ERROR_CB_SSL_CONTEXT) ? " (SSL)" : ""),
+            status, message);
+    return APR_SUCCESS;
+}
+
 /* Value for 'no short code' should be > 255 */
 #define CERTFILE 256
 #define CERTPWD  257
@@ -817,7 +845,7 @@ int main(int argc, const char **argv)
 
     serf_config_credentials_callback(context, credentials_callback);
 
-    /* Setup debug logging */
+    /* Setup debug logging and error callbacks */
     if (debug)
     {
         serf_log_output_t *output;
@@ -832,6 +860,8 @@ int main(int argc, const char **argv)
 
         if (!status)
             serf_logging_add_output(context, output);
+
+        serf_global_error_callback_set(global_error_callback, NULL);
     }
 
     /* ### Connection or Context should have an allocator? */
@@ -857,6 +887,12 @@ int main(int argc, const char **argv)
         conn_ctx->conn = connections[i];
 
         serf_connection_set_max_outstanding_requests(connections[i], inflight);
+        if (debug)
+        {
+            serf_connection_error_callback_set(connections[i],
+                                               connection_error_callback,
+                                               apr_psprintf(pool, "%02d", i));
+        }
     }
 
     handler_ctx.completed_requests = 0;

Reply via email to