Author: brane
Date: Mon Jun 16 03:19:00 2025
New Revision: 1926453

URL: http://svn.apache.org/viewvc?rev=1926453&view=rev
Log:
On the user-defined-authn branch: start figuring out how the public authn
callbacks will look like, and what sort of support from the implementation
they'll need.

* serf.h
  (SERF_AUTHN_FLAG_NONE,
   SERF_AUTHN_FLAG_PIPE,
   SERF_AUTHN_FLAG_CREDS): Bit mask constants for authn scheme requirements.
  (serf_authn_handle_func_t,
   serf_authn_handle_func_t,
   serf_authn_setup_request_func_t,
   serf_authn_validate_response_func_t): Declare callback types. Still mostly
   guesswork as to what the actual arguments will be like.
  (serf_authn_register_scheme): Add the flags and callback arguments.
  (serf_request_bucket_request_create): Fix typo in docstring.

* auth/auth.h
  (serf__authn_scheme_t::user_magic): Renamed from 'magic'.
  (serf__authn_scheme_t::user_baton): Renamed from baton'.
  (serf__authn_scheme_t::user_flags,
   serf__authn_scheme_t::user_init_conn_func,
   serf__authn_scheme_t::user_handle_func,
   serf__authn_scheme_t::user_setup_request_func,
   serf__authn_scheme_t::user_validate_response_func): New members.
* auth/auth.c
  (serf_authn_register_scheme): Add and store the new parameters
   to the authn descriptor struct.
* auth/auth_user_defined.c
  (validate_user_authn): Change magic -> user_magic.
  (get_authn_info): New helper function.
  (authn_baton_wrapper): New; the per-connection authentication baton.
  (serf__authn_user__init_conn): Implement connection initialization.
  (serf__authn_user__handle,
   serf__authn_user__setup_request,
   serf__authn_user__validate_response): Add logging to argument validation.

* serf_private.h
  (serf__connection_set_pipelining): Change the return type.
* src/outgoing.c
  (serf__connection_set_pipelining): Return the previous pipelining state.

* test/test_auth.c
  (test_authn_register_one,
   test_authn_register_two,
   test_authn_register_twice): Update calls to serf_authn_register_scheme.
  (user_authn_baton, user_authn_t): Baton type for user-defined authn tests.
  (USER_AUTHN_COUNT): Helper macro for counting callback invocations.
  (user_authn_make_baton): Allocation helper for user_authn_t.
  (user_authn_cache, user_authn_baton_t): Per-connection authn baton type.
  (user_authn_prefix): Helper string for validation.
  (user_authn_init_conn): Complete connection-init callback.
  (user_authn_handle,
   user_authn_setup_request,
   user_authn_validate_response): Placeholders for the other three callbacks.
  (user_authn_credentials_callback): Credentials callback for user-authn tests.
  (user_authentication): Partial test for user authentication.
  (test_user_authentication,
   test_user_authentication_keepalive_off): Two entry points for this test.
  (test_auth): Register only test_user_authentication for now.
* test/test_util.c
  (setup_test_context): Duh. Set up logging only once per new context.

Modified:
    serf/branches/user-defined-authn/auth/auth.c
    serf/branches/user-defined-authn/auth/auth.h
    serf/branches/user-defined-authn/auth/auth_user_defined.c
    serf/branches/user-defined-authn/serf.h
    serf/branches/user-defined-authn/serf_private.h
    serf/branches/user-defined-authn/src/outgoing.c
    serf/branches/user-defined-authn/test/test_auth.c
    serf/branches/user-defined-authn/test/test_util.c

Modified: serf/branches/user-defined-authn/auth/auth.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth.c?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth.c (original)
+++ serf/branches/user-defined-authn/auth/auth.c Mon Jun 16 03:19:00 2025
@@ -623,11 +623,14 @@ static unsigned int find_next_user_schem
     return avail & -avail;
 }
 
-apr_status_t serf_authn_register_scheme(serf_context_t *ctx,
-                                        const char *name,
-                                        void *baton,
-                                        apr_pool_t *result_pool,
-                                        int *type)
+apr_status_t serf_authn_register_scheme(
+    serf_context_t *ctx, const char *name, void *baton, int flags,
+    serf_authn_init_conn_func_t init_conn,
+    serf_authn_handle_func_t handle,
+    serf_authn_setup_request_func_t setup_request,
+    serf_authn_validate_response_func_t validate_response,
+    apr_pool_t *result_pool,
+    int *type)
 {
     serf__authn_scheme_t *authn_scheme;
     apr_status_t lock_status;
@@ -655,8 +658,13 @@ apr_status_t serf_authn_register_scheme(
     authn_scheme->validate_response_func = serf__authn_user__validate_response;
 
     /* User-defined scheme data. */
-    authn_scheme->magic = serf__authn_user__magic;
-    authn_scheme->baton = baton;
+    authn_scheme->user_magic = serf__authn_user__magic;
+    authn_scheme->user_baton = baton;
+    authn_scheme->user_flags = flags;
+    authn_scheme->user_init_conn_func = init_conn;
+    authn_scheme->user_handle_func = handle;
+    authn_scheme->user_setup_request_func = setup_request;
+    authn_scheme->user_validate_response_func = validate_response;
 
     lock_status = lock_authn_schemes(ctx->config);
     if (lock_status)

Modified: serf/branches/user-defined-authn/auth/auth.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth.h?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth.h (original)
+++ serf/branches/user-defined-authn/auth/auth.h Mon Jun 16 03:19:00 2025
@@ -114,10 +114,19 @@ struct serf__authn_scheme_t {
      */
 
     /* The magic number that helps verify the user-defined scheme data. */
-    apr_uint64_t magic;
+    apr_uint64_t user_magic;
+
+    /* The flags for this authentication scheme */
+    int user_flags;
 
     /* The baton used by the callbacks.  */
-    void *baton;
+    void *user_baton;
+
+    /* Authentication callbacks. */
+    serf_authn_init_conn_func_t user_init_conn_func;
+    serf_authn_handle_func_t user_handle_func;
+    serf_authn_setup_request_func_t user_setup_request_func;
+    serf_authn_validate_response_func_t user_validate_response_func;
 };
 
 

Modified: serf/branches/user-defined-authn/auth/auth_user_defined.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth_user_defined.c?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth_user_defined.c (original)
+++ serf/branches/user-defined-authn/auth/auth_user_defined.c Mon Jun 16 
03:19:00 2025
@@ -25,25 +25,101 @@
 #include "auth.h"
 
 
-static const bool
-validate_user_authn(const serf__authn_scheme_t *scheme)
+static bool validate_user_authn(const serf__authn_scheme_t *scheme)
 {
     return (scheme->type & *serf__authn_user__type_mask
-            && scheme->magic == serf__authn_user__magic);
+            && scheme->user_magic == serf__authn_user__magic);
 }
 
+
+static serf__authn_info_t *get_authn_info(int code, serf_connection_t *conn)
+{
+    switch (code)
+    {
+    case SERF_AUTHN_CODE_HOST:
+        return serf__get_authn_info_for_server(conn);
+    case SERF_AUTHN_CODE_PROXY:
+        return &conn->ctx->proxy_authn_info;
+    }
+
+    /* FIXME: ??? Shouldn't be possible. */
+    return NULL;
+}
+
+
+/* Used for serf__authn_info_t::baton  */
+struct callback_authn_baton {
+    /* The connection's pipelining state before we changed it. */
+    int pipelining;
+
+    /* The user-defined scheme's per-connection baton. */
+    void *user_authn_baton;
+};
+
+
 apr_status_t
 serf__authn_user__init_conn(const serf__authn_scheme_t *scheme,
                             int code,
                             serf_connection_t *conn,
                             apr_pool_t *pool)
 {
-    if (!validate_user_authn(scheme))
+    serf__authn_info_t *const authn_info = get_authn_info(code, conn);
+    struct callback_authn_baton *authn_baton;
+    apr_status_t status = APR_SUCCESS;
+
+    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+              "User-defined scheme %s: callback: init-conn\n",
+              scheme->name);
+
+    if (!validate_user_authn(scheme)) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "Not a user-defined scheme: %s\n", scheme->name);
         return APR_EINVAL;
-
-    return APR_ENOTIMPL;
+    }
+    if (!scheme->user_init_conn_func) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s: missing callback: init-conn\n",
+                  scheme->name);
+        return APR_ENOTIMPL;
+    }
+    if (!authn_info) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s init-conn: invalid code: %d\n",
+                  scheme->name, code);
+        return APR_ERANGE;
+    }
+
+    authn_baton = authn_info->baton;
+    if (authn_baton == NULL) {
+        apr_pool_t *scratch_pool;
+
+        serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s: create authn baton\n",
+                  scheme->name);
+
+        apr_pool_create(&scratch_pool, pool);
+        authn_baton = apr_pcalloc(pool, sizeof(*authn_baton));
+        status = scheme->user_init_conn_func(scheme->user_baton, code,
+                                             pool, scratch_pool,
+                                             &authn_baton->user_authn_baton);
+        apr_pool_destroy(scratch_pool);
+
+        if (status == APR_SUCCESS)
+            authn_info->baton = authn_baton;
+    }
+
+    /* Turn off pipelining if the scheme requires it. */
+    if (status == APR_SUCCESS
+        && !(scheme->user_flags & SERF_AUTHN_FLAG_PIPE)) {
+        serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s: pipelining off for %s\n",
+                  scheme->name, conn->host_url);
+        authn_baton->pipelining = serf__connection_set_pipelining(conn, 0);
+    }
+    return status;
 }
 
+
 apr_status_t
 serf__authn_user__handle(const serf__authn_scheme_t *scheme,
                          int code,
@@ -53,12 +129,26 @@ serf__authn_user__handle(const serf__aut
                          const char *auth_attr,
                          apr_pool_t *pool)
 {
-    if (!validate_user_authn(scheme))
+    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, request->conn->config,
+              "User-defined scheme %s: callback: handle-auth\n",
+              scheme->name);
+
+    if (!validate_user_authn(scheme)) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, request->conn->config,
+                  "Not a user-defined scheme: %s\n", scheme->name);
         return APR_EINVAL;
+    }
+    if (!scheme->user_handle_func) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, request->conn->config,
+                  "User-defined scheme %s: missing callback: handle-auth\n",
+                  scheme->name);
+        return APR_ENOTIMPL;
+    }
 
     return APR_ENOTIMPL;
 }
 
+
 apr_status_t
 serf__authn_user__setup_request(const serf__authn_scheme_t *scheme,
                                 peer_t peer,
@@ -69,12 +159,26 @@ serf__authn_user__setup_request(const se
                                 const char *uri,
                                 serf_bucket_t *hdrs_bkt)
 {
-    if (!validate_user_authn(scheme))
+    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+              "User-defined scheme %s: callback: setup-request\n",
+              scheme->name);
+
+    if (!validate_user_authn(scheme)) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "Not a user-defined scheme: %s\n", scheme->name);
         return APR_EINVAL;
+    }
+    if (!scheme->user_setup_request_func) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s: missing callback: setup-request\n",
+                  scheme->name);
+        return APR_ENOTIMPL;
+    }
 
     return APR_ENOTIMPL;
 }
 
+
 apr_status_t
 serf__authn_user__validate_response(const serf__authn_scheme_t *scheme,
                                     peer_t peer,
@@ -84,8 +188,22 @@ serf__authn_user__validate_response(cons
                                     serf_bucket_t *response,
                                     apr_pool_t *pool)
 {
-    if (!validate_user_authn(scheme))
+    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+              "User-defined scheme %s: callback: validate-response\n",
+              scheme->name);
+
+    if (!validate_user_authn(scheme)) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "Not a user-defined scheme: %s\n", scheme->name);
         return APR_EINVAL;
+    }
+    if (!scheme->user_validate_response_func) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s:"
+                  " missing callback: validate-response\n",
+                  scheme->name);
+        return APR_ENOTIMPL;
+    }
 
     return APR_ENOTIMPL;
 }

Modified: serf/branches/user-defined-authn/serf.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf.h?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/serf.h (original)
+++ serf/branches/user-defined-authn/serf.h Mon Jun 16 03:19:00 2025
@@ -966,18 +966,73 @@ serf_bucket_t *serf_request_bucket_reque
 #define SERF_AUTHN_CODE_HOST  401 /**< Authentication request from a host */
 #define SERF_AUTHN_CODE_PROXY 407 /**< Authentication requset from a proxy */
 
+/* Flags returned from the init-connection callback. */
+#define SERF_AUTHN_FLAG_NONE  0x00 /**< Authn flags: None */
+#define SERF_AUTHN_FLAG_PIPE  0x01 /**< Authn flags: Allow pipelining */
+#define SERF_AUTHN_FLAG_CREDS 0x02 /**< Authn flags: Require credentials */
+
+/** TODO:  */
+typedef apr_status_t
+(*serf_authn_init_conn_func_t)(void *baton, int code,
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool,
+                               void **authn_baton);
+
+/** TODO:  */
+typedef apr_status_t
+(*serf_authn_handle_func_t)(void *baton,
+                            int code,
+                            serf_request_t *request,
+                            serf_bucket_t *response,
+                            const char *auth_hdr,
+                            const char *auth_attr,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool);
+
+/** TODO:  */
+typedef apr_status_t
+(*serf_authn_setup_request_func_t)(void *baton,
+                                   int peer,
+                                   int code,
+                                   serf_connection_t *conn,
+                                   serf_request_t *request,
+                                   const char *method,
+                                   const char *uri,
+                                   serf_bucket_t *headers,
+                                   apr_pool_t *scratch_pool);
+
+/** TODO:  */
+typedef apr_status_t
+(*serf_authn_validate_response_func_t)(void *baton,
+                                       int peer,
+                                       int code,
+                                       serf_connection_t *conn,
+                                       serf_request_t *request,
+                                       serf_bucket_t *response,
+                                       apr_pool_t *scratch_pool);
+
 /**
  * Register an autehtication scheme.
  *
- * The number returned in @a type can be used as a bit mask in
- * serf_config_authn_types(). If an error occurs during registration,
- * @a type will be set to @c SERF_AUTHN_NONE.
+ * The context in @a ctx is used for logging.
  *
  * The @a name is the name of the authentication scheme as it appears in the
  * authorization headers. It must be a valid token as defined in RFC-9110
  * (see reference, below).
  *
- * The context in @a ctx is used for logging.
+ * @a baton will be passed unchanged to the callbacks.
+ *
+ * @a flags is a bitmask of @c APR_AUTHN_FLAG_* constants that define the
+ * scheme's requirements; e.g., whether the credentials callback should be
+ * invoked, or whether pipelining should be disabled while the authentication
+ * handshake is in progress.
+ *
+ * @a init_conn, @a handle, @a setup_request and @a validate_response are the
+ * callbacks that implement the authentication handshake for this scheme.
+ *
+ * The number returned in @a type can be used as a bit mask in
+ * serf_config_authn_types(). If an error occurs during registration,
+ * @a type will be set to @c SERF_AUTHN_NONE.
  *
  * Internal structures related to this provider will be allocated from
  * @a result_pool, so take care that it lives as long as the autehtication
@@ -986,11 +1041,14 @@ serf_bucket_t *serf_request_bucket_reque
  * @see https://www.rfc-editor.org/rfc/rfc9110#section-11.1
  * @since New in 1.4
  */
-apr_status_t serf_authn_register_scheme(serf_context_t *ctx,
-                                        const char *name,
-                                        void *baton,
-                                        apr_pool_t *result_pool,
-                                        int *type);
+apr_status_t serf_authn_register_scheme(
+    serf_context_t *ctx, const char *name, void *baton, int flags,
+    serf_authn_init_conn_func_t init_conn,
+    serf_authn_handle_func_t handle,
+    serf_authn_setup_request_func_t setup_request,
+    serf_authn_validate_response_func_t validate_response,
+    apr_pool_t *result_pool,
+    int *type);
 
 /* FIXME: Think some more about whether unregistering schemes makes sense. */
 /**
@@ -999,8 +1057,8 @@ apr_status_t serf_authn_register_scheme(
  * Removes the scheme, identified by @a type that was returned from and
  * @a name that was supplied to serf_authn_register_scheme(), from the
  * list of supported authentication schemes. Uses @a scratch_pool for
- * temporary allocations; this pool can be destroyed afterthe function
- * returns.
+ * temporary allocations; this pool can be destroyed after the function
+ * has returned.
  *
  * The context in @a ctx is used for logging.
  *

Modified: serf/branches/user-defined-authn/serf_private.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf_private.h?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/serf_private.h (original)
+++ serf/branches/user-defined-authn/serf_private.h Mon Jun 16 03:19:00 2025
@@ -741,7 +741,7 @@ apr_status_t serf__conn_update_pollset(s
 serf_request_t *serf__ssltunnel_request_create(serf_connection_t *conn,
                                                serf_request_setup_t setup,
                                                void *setup_baton);
-void serf__connection_set_pipelining(serf_connection_t *conn, int enabled);
+int serf__connection_set_pipelining(serf_connection_t *conn, int enabled);
 apr_status_t serf__connection_flush(serf_connection_t *conn,
                                     bool fetch_new);
 

Modified: serf/branches/user-defined-authn/src/outgoing.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/src/outgoing.c?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/src/outgoing.c (original)
+++ serf/branches/user-defined-authn/src/outgoing.c Mon Jun 16 03:19:00 2025
@@ -1460,9 +1460,11 @@ void serf_connection_set_max_outstanding
    HTTP pipelining can achieve this by calling:
      serf_connection_set_max_outstanding_requests(conn, 1) .
  */
-void serf__connection_set_pipelining(serf_connection_t *conn, int enabled)
+int serf__connection_set_pipelining(serf_connection_t *conn, int enabled)
 {
+    int pipelining = conn->pipelining;
     conn->pipelining = enabled;
+    return pipelining;
 }
 
 void serf_connection_set_async_responses(

Modified: serf/branches/user-defined-authn/test/test_auth.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/test_auth.c?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/test_auth.c (original)
+++ serf/branches/user-defined-authn/test/test_auth.c Mon Jun 16 03:19:00 2025
@@ -531,8 +531,10 @@ static void test_authn_register_one(CuTe
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
     /* Register an authentication scheme */
-    status = serf_authn_register_scheme(tb->context,
-                                        "Fizzle", baton, tb->pool, &type);
+    status = serf_authn_register_scheme(tb->context, "Fizzle", baton,
+                                        SERF_AUTHN_FLAG_NONE,
+                                        NULL, NULL, NULL, NULL,
+                                        tb->pool, &type);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type != SERF_AUTHN_NONE);
 
@@ -554,13 +556,17 @@ static void test_authn_register_two(CuTe
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
     /* Register the schemes */
-    status = serf_authn_register_scheme(tb->context,
-                                        "Tweedledee", baton1, tb->pool, 
&type1);
+    status = serf_authn_register_scheme(tb->context, "Tweedledee", baton1,
+                                        SERF_AUTHN_FLAG_NONE,
+                                        NULL, NULL, NULL, NULL,
+                                        tb->pool, &type1);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type1 != SERF_AUTHN_NONE);
 
-    status = serf_authn_register_scheme(tb->context,
-                                        "Tweedledum", baton2, tb->pool, 
&type2);
+    status = serf_authn_register_scheme(tb->context, "Tweedledum", baton2,
+                                        SERF_AUTHN_FLAG_NONE,
+                                        NULL, NULL, NULL, NULL,
+                                        tb->pool, &type2);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type2 != SERF_AUTHN_NONE);
     CuAssertTrue(tc, type2 != type1);
@@ -585,13 +591,17 @@ static void test_authn_register_twice(Cu
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
     /* Register an authentication scheme */
-    status = serf_authn_register_scheme(tb->context,
-                                        "Tweens", baton, tb->pool, &type);
+    status = serf_authn_register_scheme(tb->context, "Tweens", baton,
+                                        SERF_AUTHN_FLAG_NONE,
+                                        NULL, NULL, NULL, NULL,
+                                        tb->pool, &type);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type != SERF_AUTHN_NONE);
 
-    status = serf_authn_register_scheme(tb->context,
-                                        "Tweens", baton, tb->pool, &epyt);
+    status = serf_authn_register_scheme(tb->context, "Tweens", baton,
+                                        SERF_AUTHN_FLAG_NONE,
+                                        NULL, NULL, NULL, NULL,
+                                        tb->pool, &epyt);
     CuAssertIntEquals(tc, APR_EEXIST, status);
     CuAssertTrue(tc, epyt == SERF_AUTHN_NONE);
 
@@ -615,6 +625,213 @@ static void test_authn_unregister_unknow
     CuAssertIntEquals(tc, APR_ENOENT, status);
 }
 
+
+typedef struct user_authn_baton user_authn_t;
+struct user_authn_baton {
+    const char *name;
+    int all_count;
+    int init_conn_count;
+    int handle_count;
+    int setup_request_count;
+    int validate_response_count;
+};
+
+#define USER_AUTHN_COUNT(baton, callback)  \
+    user_authn_t *const b = (baton);       \
+    do {                                   \
+        ++b->callback##_count;             \
+        ++b->all_count;                    \
+    } while(0)
+
+static user_authn_t *user_authn_make_baton(const char* name, apr_pool_t *pool)
+{
+    user_authn_t *baton = apr_pcalloc(pool, sizeof(*baton));
+    baton->name = apr_pstrdup(pool, name);
+    return baton;
+}
+
+typedef struct user_authn_cache user_authn_baton_t;
+struct user_authn_cache {
+    const char *header;
+    const char *value;
+};
+
+static const char *const user_authn_prefix = "Tweedle";
+
+static apr_status_t user_authn_init_conn(void *baton, int code,
+                                         apr_pool_t *result_pool,
+                                         apr_pool_t *scratch_pool,
+                                         void **authn_baton)
+{
+    USER_AUTHN_COUNT(baton, init_conn);
+
+    if (strncmp(user_authn_prefix, b->name, strlen(user_authn_prefix)) != 0)
+        return SERF_ERROR_AUTHN_INITALIZATION_FAILED;
+
+    *authn_baton = apr_pcalloc(result_pool, sizeof(user_authn_baton_t));
+    return APR_SUCCESS;
+}
+
+static apr_status_t user_authn_handle(void *baton,
+                                      int code,
+                                      serf_request_t *request,
+                                      serf_bucket_t *response,
+                                      const char *auth_hdr,
+                                      const char *auth_attr,
+                                      apr_pool_t *result_pool,
+                                      apr_pool_t *scratch_pool)
+{
+    USER_AUTHN_COUNT(baton, handle);
+    return APR_SUCCESS;
+}
+
+static apr_status_t user_authn_setup_request(void *baton,
+                                             int peer,
+                                             int code,
+                                             serf_connection_t *conn,
+                                             serf_request_t *request,
+                                             const char *method,
+                                             const char *uri,
+                                             serf_bucket_t *headers,
+                                             apr_pool_t *scratch_pool)
+{
+    USER_AUTHN_COUNT(baton, setup_request);
+    return APR_SUCCESS;
+}
+
+static apr_status_t user_authn_validate_response(void *baton,
+                                                 int peer,
+                                                 int code,
+                                                 serf_connection_t *conn,
+                                                 serf_request_t *request,
+                                                 serf_bucket_t *response,
+                                                 apr_pool_t *scratch_pool)
+{
+    USER_AUTHN_COUNT(baton, validate_response);
+    return APR_SUCCESS;
+}
+#undef USER_AUTHN_COUNT
+
+static apr_status_t
+user_authn_credentials_callback(char **username,
+                    char **password,
+                    serf_request_t *request, void *baton,
+                    int code, const char *authn_type,
+                    const char *scope,
+                    apr_pool_t *pool)
+{
+    handler_baton_t *handler_ctx = baton;
+    test_baton_t *tb = handler_ctx->tb;
+
+    tb->result_flags |= TEST_RESULT_AUTHNCB_CALLED;
+
+    if (code != SERF_AUTHN_CODE_HOST)
+        return REPORT_TEST_SUITE_ERROR();
+    if (strncmp(user_authn_prefix, authn_type, strlen(user_authn_prefix)) != 0)
+        return REPORT_TEST_SUITE_ERROR();
+    if (strcmp("Alice", scope) != 0)
+        return REPORT_TEST_SUITE_ERROR();
+
+    *username = NULL;
+    *password = apr_pstrdup(pool, authn_type);
+
+    return APR_SUCCESS;
+}
+
+static void user_authentication(CuTest *tc, int close_conn)
+{
+    test_baton_t *tb = tc->testBaton;
+    handler_baton_t handler_ctx[2];
+    int num_requests_sent;
+    apr_status_t status;
+    int typedee, typedum;
+    user_authn_t *const tdee = user_authn_make_baton("TweedleDee", tb->pool);
+    user_authn_t *const tdum = user_authn_make_baton("TweedleDum", tb->pool);
+
+    status = setup_test_context(tb, tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+    status = serf_authn_register_scheme(tb->context, tdee->name, tdee,
+                                        SERF_AUTHN_FLAG_CREDS
+                                        | SERF_AUTHN_FLAG_PIPE,
+                                        user_authn_init_conn,
+                                        user_authn_handle,
+                                        user_authn_setup_request,
+                                        user_authn_validate_response,
+                                        tb->pool, &typedee);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertTrue(tc, typedee != SERF_AUTHN_NONE);
+
+    status = serf_authn_register_scheme(tb->context, tdum->name, tdum,
+                                        SERF_AUTHN_FLAG_CREDS
+                                        | SERF_AUTHN_FLAG_PIPE,
+                                        user_authn_init_conn,
+                                        user_authn_handle,
+                                        user_authn_setup_request,
+                                        user_authn_validate_response,
+                                        tb->pool, &typedum);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertTrue(tc, typedum != SERF_AUTHN_NONE);
+    CuAssertTrue(tc, typedum != typedee);
+
+    /* Test that a request is retried and authentication
+       headers are set correctly. */
+    num_requests_sent = 1;
+
+    /* Set up a test context with a server */
+    setup_test_mock_server(tb);
+    status = setup_test_client_context(tb, NULL, tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+    serf_config_authn_types(tb->context, typedee | typedum);
+    serf_config_credentials_callback(tb->context, 
user_authn_credentials_callback);
+
+    /* Use non-standard case WWW-Authenticate header and scheme name to test
+       for case insensitive comparisons. */
+    Given(tb->mh)
+      GETRequest(URLEqualTo("/"), HeaderNotSet("Authorization"))
+        Respond(WithCode(SERF_AUTHN_CODE_HOST),WithChunkedBody("1"),
+                WithHeader("www-Authenticate", "tweeDlEdee scope=Alice"),
+                OnConditionThat(close_conn, WithConnectionCloseHeader))
+      GETRequest(URLEqualTo("/"),
+                 HeaderEqualTo("Authorization", "TweedleDee TweedleDee"))
+        Respond(WithCode(200),WithChunkedBody(""))
+    Expect
+      AllRequestsReceivedInOrder
+    EndGiven
+
+    create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
+    status = run_client_and_mock_servers_loops(tb, num_requests_sent,
+                                               handler_ctx, tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertTrue(tc, tb->result_flags & TEST_RESULT_AUTHNCB_CALLED);
+    Verify(tb->mh)
+      CuAssertTrue(tc, VerifyAllExpectationsOk);
+    EndVerify
+
+    CuAssertIntEquals(tc, 4, tdee->all_count);
+    CuAssertIntEquals(tc, 0, tdum->all_count);
+
+    status = serf_authn_unregister_scheme(tb->context,
+                                          typedum, tdum->name, tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    status = serf_authn_unregister_scheme(tb->context,
+                                          typedee, tdee->name, tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+}
+
+static void test_user_authentication(CuTest *tc)
+{
+    user_authentication(tc, 0 /* don't close connection */);
+}
+
+static void test_user_authentication_keepalive_off(CuTest *tc)
+{
+    user_authentication(tc, 1);
+}
+
+
+
 /*****************************************************************************/
 CuSuite *test_auth(void)
 {
@@ -635,5 +852,8 @@ CuSuite *test_auth(void)
     SUITE_ADD_TEST(suite, test_authn_register_two);
     SUITE_ADD_TEST(suite, test_authn_register_twice);
     SUITE_ADD_TEST(suite, test_authn_unregister_unknown);
+    SUITE_ADD_TEST(suite, test_user_authentication);
+    /* SUITE_ADD_TEST(suite, test_user_authentication_keepalive_off); */
+
     return suite;
 }

Modified: serf/branches/user-defined-authn/test/test_util.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/test_util.c?rev=1926453&r1=1926452&r2=1926453&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/test_util.c (original)
+++ serf/branches/user-defined-authn/test/test_util.c Mon Jun 16 03:19:00 2025
@@ -427,17 +427,18 @@ setup_test_context(test_baton_t *tb, apr
     serf_log_output_t *output;
     apr_status_t status = APR_SUCCESS;
 
-    if (!tb->context)
+    if (!tb->context) {
         tb->context = serf_context_create(pool);
 
-    if (TEST_VERBOSE) {
-        status = serf_logging_create_stream_output(&output, tb->context,
-                                                   SERF_LOG_DEBUG,
-                                                   SERF_LOGCOMP_ALL,
-                                                   SERF_LOG_DEFAULT_LAYOUT,
-                                                   stderr, pool);
-        if (status == APR_SUCCESS)
-            status = serf_logging_add_output(tb->context, output);
+        if (TEST_VERBOSE) {
+            status = serf_logging_create_stream_output(&output, tb->context,
+                                                       SERF_LOG_DEBUG,
+                                                       SERF_LOGCOMP_ALL,
+                                                       SERF_LOG_DEFAULT_LAYOUT,
+                                                       stderr, pool);
+            if (status == APR_SUCCESS)
+                status = serf_logging_add_output(tb->context, output);
+        }
     }
 
     return status;


Reply via email to