Author: brane
Date: Wed Jun 25 20:20:05 2025
New Revision: 1926725

URL: http://svn.apache.org/viewvc?rev=1926725&view=rev
Log:
On the user-defined-authn branch: Parse authentication parameters so that
users don't have to bother with that (and most likely get it wrong, too).

* CMakeLists.txt (SOURCES): Add src/syntax.c.

* serf.h: Include apr_hash.h
  (serf_authn_get_realm_func_t,
   serf_authn_handle_func_t): Change authn_parameters to a dictionary.
   Update the docstrings.

* serf_private.h
  (serf__parse_authn_parameters,
   serf__tolower_inplace, serf__tolower): Declare prototypes.

* auth/auth.c
  (store_header_in_dict): Use serf__tolower_inplace() to fold the scheme.
  (serf_authn_register_scheme,
   serf_authn_unregister_scheme): Use serf__tolower() to make the scheme key.

* auth/auth_user_defined.c
  (serf__authn_user__handle): Parse the incoming authentication parameters.

* src/genctype.py: New; stolen from Subversion.
* src/syntax.c: New; implements serf__parse_authn_parameters(),
   serf__tolower_inplace() and serf__tolower().

* test/test_auth.c
  (user_authn_get_realm,
   user_authn_handle,
   user_authn_credentials): Update test callbacks.
  (user_authentication): Accept a 'scope' instead of a 'tweak'.

* test/test_internal.c
  (struct expected_attrs, parse_parameters): New; common test implementation.
  (test_parse_parameters,
   test_parse_bad_parameters,
   test_parse_repeated_parameters,
   test_parameter_case_folding): New test cases.
  (test_internal): Register test cases.

Added:
    serf/branches/user-defined-authn/src/genctype.py   (with props)
    serf/branches/user-defined-authn/src/syntax.c   (with props)
Modified:
    serf/branches/user-defined-authn/CMakeLists.txt
    serf/branches/user-defined-authn/auth/auth.c
    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/test/test_auth.c
    serf/branches/user-defined-authn/test/test_internal.c
    serf/branches/user-defined-authn/test/test_serf.h

Modified: serf/branches/user-defined-authn/CMakeLists.txt
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/CMakeLists.txt?rev=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/CMakeLists.txt (original)
+++ serf/branches/user-defined-authn/CMakeLists.txt Wed Jun 25 20:20:05 2025
@@ -137,6 +137,7 @@ list(APPEND SOURCES
     "src/outgoing_request.c"
     "src/pump.c"
     "src/ssltunnel.c"
+    "src/syntax.c"
     "auth/auth.c"
     "auth/auth_basic.c"
     "auth/auth_digest.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=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth.c (original)
+++ serf/branches/user-defined-authn/auth/auth.c Wed Jun 25 20:20:05 2025
@@ -264,9 +264,7 @@ static int store_header_in_dict(void *ba
         auth_name = apr_pstrmemdup(ab->pool, header, strlen(header));
 
     /* Convert scheme name to lower case to enable case insensitive matching. 
*/
-    for (c = auth_name; *c != '\0'; c++)
-        *c = (char)apr_tolower(*c);
-
+    serf__tolower_inplace(auth_name);
     apr_hash_set(ab->hdrs, auth_name, APR_HASH_KEY_STRING,
                  apr_pstrdup(ab->pool, header));
 
@@ -673,7 +671,6 @@ apr_status_t serf_authn_register_scheme(
     apr_status_t status;
     unsigned int scheme_type;
     const char *key;
-    char *cp;
     int index;
 
     serf__log(LOGLVL_INFO, LOGCOMP_AUTHN, __FILE__, config,
@@ -683,11 +680,8 @@ apr_status_t serf_authn_register_scheme(
     authn_scheme = apr_palloc(result_pool, sizeof(*authn_scheme));
 
     /* Generate a lower-case key for the scheme. */
-    key = cp = apr_pstrdup(result_pool, name);
-    while (*cp) {
-        *cp = apr_tolower(*cp);
-        ++cp;
-    }
+    key = serf__tolower(name, result_pool);
+
     authn_scheme->name = apr_pstrdup(result_pool, name);
     authn_scheme->key = key;
     /* user_scheme->type = ?; Will be updated later, under lock. */
@@ -780,18 +774,13 @@ apr_status_t serf_authn_unregister_schem
     apr_status_t lock_status;
     apr_status_t status;
     const char *key;
-    char *cp;
     int index;
 
     serf__log(LOGLVL_INFO, LOGCOMP_AUTHN, __FILE__, config,
               "Unregistering user-defined scheme %s", name);
 
     /* Generate a lower-case key for the scheme. */
-    key = cp = apr_pstrdup(scratch_pool, name);
-    while (*cp) {
-        *cp = apr_tolower(*cp);
-        ++cp;
-    }
+    key = serf__tolower(name, scratch_pool);
 
     lock_status = lock_authn_schemes(config);
     if (lock_status) {

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=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth_user_defined.c (original)
+++ serf/branches/user-defined-authn/auth/auth_user_defined.c Wed Jun 25 
20:20:05 2025
@@ -158,6 +158,7 @@ serf__authn_user__handle(const serf__aut
     struct authn_baton_wrapper *authn_baton;
     char *username, *password;
     apr_pool_t *scratch_pool;
+    apr_hash_t *auth_param;
     apr_status_t status;
 
     status = validate_handler(conn->config, scheme, code, "handle-auth",
@@ -198,12 +199,13 @@ serf__authn_user__handle(const serf__aut
     apr_pool_create(&scratch_pool, pool);
 
     status = APR_SUCCESS;
+    auth_param = serf__parse_authn_parameters(auth_attr, scratch_pool);
     if (scheme->user_flags & SERF_AUTHN_FLAG_CREDS) {
         const char *realm_name;
         status = scheme->user_get_realm_func(&realm_name,
                                              scheme->user_baton,
                                              authn_baton->user_authn_baton,
-                                             auth_hdr, auth_attr,
+                                             auth_hdr, auth_param,
                                              scratch_pool, scratch_pool);
         if (!status) {
             const char *const realm = serf__construct_realm(
@@ -222,7 +224,7 @@ serf__authn_user__handle(const serf__aut
 
     status = scheme->user_handle_func(scheme->user_baton,
                                       authn_baton->user_authn_baton, code,
-                                      auth_hdr, auth_attr,
+                                      auth_hdr, auth_param,
                                       SERF__HEADER_FROM_CODE(code),
                                       username, password,
                                       request, response,

Modified: serf/branches/user-defined-authn/serf.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf.h?rev=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/serf.h (original)
+++ serf/branches/user-defined-authn/serf.h Wed Jun 25 20:20:05 2025
@@ -30,6 +30,7 @@
 #include <apr_errno.h>
 #include <apr_allocator.h>
 #include <apr_pools.h>
+#include <apr_hash.h>
 #include <apr_network_io.h>
 #include <apr_time.h>
 #include <apr_poll.h>
@@ -1029,11 +1030,12 @@ typedef apr_status_t
  *
  * @a authn_baton is the pointer returned from the init-connection callback.
  *
- * @a authn_header and @a authn_attributes contain the value of the
- * authentication header (WWW-Authenticate or Proxy-Authenticate) recevied in
- * a server response. @a authn_header contains the scheme name, i.e., "scheme
- * <atributes>", whereas @a authn_attributes contains only the attributes
- * without the scheme name.
+ * @a authn_header and @a authn_parameters come from the authentication header
+ * (WWW-Authenticate or Proxy-Authenticate) recevied in a server response.
+ * @a authn_header is the header value, i.e., "scheme <parameters>";
+ * @a authn_parameters is a dictionary of the authentication parameters
+ * and their values, e.g., `realm="Wonderland"`. The keys are always folded
+ * to lower case.
  *
  * If the scheme flag @a SERF_AUTHN_FLAG_PIPE is *not* set, pipelining will be
  * disabled on the connection after this callback succeeds.
@@ -1047,7 +1049,7 @@ typedef apr_status_t
                                void *baton,
                                void *authn_baton,
                                const char *authn_header,
-                               const char *authn_attributes,
+                               apr_hash_t *authn_parameters,
                                apr_pool_t *result_pool,
                                apr_pool_t *scratch_pool);
 
@@ -1057,7 +1059,7 @@ typedef apr_status_t
  * Called after the init-conn function has succeeded to prepare (cache) the
  * credentials for this connection, usually in @a auth_baton.
  *
- * @a baton, @a authn_baton, @a authn_header and @a authn_attributers have the
+ * @a baton, @a authn_baton, @a authn_header and @a authn_parameters have the
  * same meaning as in the get-realm function; @a code is the same as in the
  * init-conn function.
  *
@@ -1080,7 +1082,7 @@ typedef apr_status_t
                             void *authn_baton,
                             int code,
                             const char *authn_header,
-                            const char *authn_attributes,
+                            apr_hash_t *authn_parameters,
                             const char *response_header,
                             const char *username,
                             const char *password,

Modified: serf/branches/user-defined-authn/serf_private.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf_private.h?rev=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/serf_private.h (original)
+++ serf/branches/user-defined-authn/serf_private.h Wed Jun 25 20:20:05 2025
@@ -747,6 +747,23 @@ apr_status_t serf__handle_auth_response(
    able to cleanup stale objects from time to time. */
 serf__authn_info_t *serf__get_authn_info_for_server(serf_connection_t *conn);
 
+
+/* Parse authentication scheme parameters from a WWW-Authenticate or
+   Proxy-Authenticate header. Splits the comma-separated token=value
+   or token="quoted \" value" pairs into a dictionary.
+
+   The keys in the dictionary will be folded to lowercase.
+
+   See: https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6 */
+apr_hash_t *serf__parse_authn_parameters(const char *attrs, apr_pool_t *pool);
+
+/* Fold ASCII uppercase letters to lowercase, in place, using the same
+   case-folding table as serf__parse_authn_attributes() does for keys.*/
+void serf__tolower_inplace(char *dst);
+
+/* Like serf__tolower_inplace, but allocates a new string from the pool. */
+const char *serf__tolower(const char *src, apr_pool_t *pool);
+
 /* from context.c */
 void serf__context_progress_delta(void *progress_baton, apr_off_t read,
                                   apr_off_t written);

Added: serf/branches/user-defined-authn/src/genctype.py
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/src/genctype.py?rev=1926725&view=auto
==============================================================================
--- serf/branches/user-defined-authn/src/genctype.py (added)
+++ serf/branches/user-defined-authn/src/genctype.py Wed Jun 25 20:20:05 2025
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# ======================================================================
+# This file was stolen and slightly modified from Subversion @ r882257.
+
+"""getctype.py - Generate the character classification table.
+"""
+
+# Table of ASCII character names
+names = ('nul', 'soh', 'stx', 'etx', 'eot', 'enq', 'ack', 'bel',
+         'bs',  'ht',  'nl',  'vt',  'np',  'cr',  'so',  'si',
+         'dle', 'dc1', 'dc2', 'dc3', 'dc4', 'nak', 'syn', 'etb',
+         'can', 'em',  'sub', 'esc', 'fs',  'gs',  'rs',  'us',
+         'sp',  '!',   '"',   '#',   '$',   '%',   '&',   '\'',
+         '(',   ')',   '*',   '+',   ',',   '-',   '.',   '/',
+         '0',   '1',   '2',   '3',   '4',   '5',   '6',   '7',
+         '8',   '9',   ':',   ';',   '<',   '=',   '>',   '?',
+         '@',   'A',   'B',   'C',   'D',   'E',   'F',   'G',
+         'H',   'I',   'J',   'K',   'L',   'M',   'N',   'O',
+         'P',   'Q',   'R',   'S',   'T',   'U',   'V',   'W',
+         'X',   'Y',   'Z',   '[',   '\\',  ']',   '^',   '_',
+         '`',   'a',   'b',   'c',   'd',   'e',   'f',   'g',
+         'h',   'i',   'j',   'k',   'l',   'm',   'n',   'o',
+         'p',   'q',   'r',   's',   't',   'u',   'v',   'w',
+         'x',   'y',   'z',   '{',   '|',   '}',   '~',   'del')
+
+# All whitespace characters:
+#   horizontal tab, vertical tab, new line, form feed, carriage return, space
+whitespace = frozenset((9, 10, 11, 12, 13, 32))
+
+# Bytes not valid in UTF-8 sequences
+utf8_invalid = frozenset((0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF))
+
+# HTTP classes: whitespace and token punctuation
+http_space = (9, 32)
+http_token_punct = frozenset((
+    33, 35, 36, 37, 38, 39, 42, 43, 45, 46, 94, 95, 96, 124, 126))
+#    !   #   $   %   &   '   *   +   -   .   ^   _   `   |    ~
+
+print('    /* **** DO NOT EDIT! ****')
+print('       This table was generated by genctype.py, make changes there. */')
+
+for c in range(256):
+    bits = []
+
+    # Ascii subrange
+    if c < 128:
+        bits.append('CT_ASCII')
+
+        if len(names[c]) == 1:
+            name = names[c].center(3)
+        else:
+            name = names[c].ljust(3)
+
+        # Control characters
+        if c < 32 or c == 127:
+            bits.append('CT_CNTRL')
+
+        # Whitespace characters
+        if c in whitespace:
+            bits.append('CT_SPACE')
+            if c in http_space:
+                bits.append('CT_HTTP_SPACE')
+
+        # Punctuation marks
+        if c >= 33 and c < 48 \
+           or c >= 58 and c < 65 \
+           or c >= 91 and c < 97 \
+           or c >= 123 and c < 127:
+            bits.append('CT_PUNCT')
+            if c in http_token_punct:
+                bits.append('CT_HTTP_TOKEN')
+
+        # Decimal digits
+        elif c >= 48 and c < 58:
+            bits.append('CT_DIGIT')
+            bits.append('CT_HTTP_TOKEN')
+
+        # Uppercase letters
+        elif c >= 65 and c < 91:
+            bits.append('CT_UPPER')
+            bits.append('CT_HTTP_TOKEN')
+            # Hexadecimal digits
+            if c <= 70:
+                bits.append('CT_XALPHA')
+
+        # Lowercase letters
+        elif c >= 97 and c < 123:
+            bits.append('CT_LOWER')
+            bits.append('CT_HTTP_TOKEN')
+            # Hexadecimal digits
+            if c <= 102:
+                bits.append('CT_XALPHA')
+
+    # UTF-8 multibyte sequences
+    else:
+        name = hex(c)[1:]
+
+        # Lead bytes (start of sequence)
+        if c > 0xC0 and c < 0xFE and c not in utf8_invalid:
+            bits.append('CT_UTF8_LEAD')
+
+        # Continuation bytes
+        elif (c & 0xC0) == 0x80:
+            bits.append('CT_UTF8_CONT')
+
+    if len(bits) == 0:
+        flags = '0'
+    else:
+        flags = ' | '.join(bits)
+    print('    /* %s */ %s,' % (name, flags))

Propchange: serf/branches/user-defined-authn/src/genctype.py
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: serf/branches/user-defined-authn/src/genctype.py
------------------------------------------------------------------------------
    svn:executable = *

Added: serf/branches/user-defined-authn/src/syntax.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/src/syntax.c?rev=1926725&view=auto
==============================================================================
--- serf/branches/user-defined-authn/src/syntax.c (added)
+++ serf/branches/user-defined-authn/src/syntax.c Wed Jun 25 20:20:05 2025
@@ -0,0 +1,506 @@
+/* ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <apr_pools.h>
+#include <apr_hash.h>
+
+#include "serf.h"
+#include "serf_private.h"
+
+
+/* Character classes */
+#define CT_ASCII      0x0001
+#define CT_CNTRL      0x0002
+#define CT_SPACE      0x0004
+#define CT_PUNCT      0x0008
+#define CT_DIGIT      0x0010
+#define CT_UPPER      0x0020
+#define CT_LOWER      0x0040
+#define CT_XALPHA     0x0080
+#define CT_UTF8_CONT  0x0100
+#define CT_UTF8_LEAD  0x0200
+
+/* Only space and the horizontal tab are treated as whitespace. */
+#define CT_HTTP_SPACE 0x1000
+
+/* Characters that are allowed in tokens, accorting to RFC 9110, are the
+   ASCII digits, lowercase and uppercase letters, and the following
+   punctiation marks:
+
+       ! # $ % & ' * + - . ^ _ ` | ~
+
+   Whitespace and all other printable ASCII characters are delimiters. */
+#define CT_HTTP_TOKEN 0x2000
+
+
+/* ASCII + UTF-8 character table, stolen wholesale from Subversion. */
+static const unsigned short char_table[256] =
+{
+    /* **** DO NOT EDIT! ****
+       This table was generated by genctype.py, make changes there. */
+    /* nul */ CT_ASCII | CT_CNTRL,
+    /* soh */ CT_ASCII | CT_CNTRL,
+    /* stx */ CT_ASCII | CT_CNTRL,
+    /* etx */ CT_ASCII | CT_CNTRL,
+    /* eot */ CT_ASCII | CT_CNTRL,
+    /* enq */ CT_ASCII | CT_CNTRL,
+    /* ack */ CT_ASCII | CT_CNTRL,
+    /* bel */ CT_ASCII | CT_CNTRL,
+    /* bs  */ CT_ASCII | CT_CNTRL,
+    /* ht  */ CT_ASCII | CT_CNTRL | CT_SPACE | CT_HTTP_SPACE,
+    /* nl  */ CT_ASCII | CT_CNTRL | CT_SPACE,
+    /* vt  */ CT_ASCII | CT_CNTRL | CT_SPACE,
+    /* np  */ CT_ASCII | CT_CNTRL | CT_SPACE,
+    /* cr  */ CT_ASCII | CT_CNTRL | CT_SPACE,
+    /* so  */ CT_ASCII | CT_CNTRL,
+    /* si  */ CT_ASCII | CT_CNTRL,
+    /* dle */ CT_ASCII | CT_CNTRL,
+    /* dc1 */ CT_ASCII | CT_CNTRL,
+    /* dc2 */ CT_ASCII | CT_CNTRL,
+    /* dc3 */ CT_ASCII | CT_CNTRL,
+    /* dc4 */ CT_ASCII | CT_CNTRL,
+    /* nak */ CT_ASCII | CT_CNTRL,
+    /* syn */ CT_ASCII | CT_CNTRL,
+    /* etb */ CT_ASCII | CT_CNTRL,
+    /* can */ CT_ASCII | CT_CNTRL,
+    /* em  */ CT_ASCII | CT_CNTRL,
+    /* sub */ CT_ASCII | CT_CNTRL,
+    /* esc */ CT_ASCII | CT_CNTRL,
+    /* fs  */ CT_ASCII | CT_CNTRL,
+    /* gs  */ CT_ASCII | CT_CNTRL,
+    /* rs  */ CT_ASCII | CT_CNTRL,
+    /* us  */ CT_ASCII | CT_CNTRL,
+    /* sp  */ CT_ASCII | CT_SPACE | CT_HTTP_SPACE,
+    /*  !  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  "  */ CT_ASCII | CT_PUNCT,
+    /*  #  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  $  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  %  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  &  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  '  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  (  */ CT_ASCII | CT_PUNCT,
+    /*  )  */ CT_ASCII | CT_PUNCT,
+    /*  *  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  +  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  ,  */ CT_ASCII | CT_PUNCT,
+    /*  -  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  .  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  /  */ CT_ASCII | CT_PUNCT,
+    /*  0  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  1  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  2  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  3  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  4  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  5  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  6  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  7  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  8  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  9  */ CT_ASCII | CT_DIGIT | CT_HTTP_TOKEN,
+    /*  :  */ CT_ASCII | CT_PUNCT,
+    /*  ;  */ CT_ASCII | CT_PUNCT,
+    /*  <  */ CT_ASCII | CT_PUNCT,
+    /*  =  */ CT_ASCII | CT_PUNCT,
+    /*  >  */ CT_ASCII | CT_PUNCT,
+    /*  ?  */ CT_ASCII | CT_PUNCT,
+    /*  @  */ CT_ASCII | CT_PUNCT,
+    /*  A  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  B  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  C  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  D  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  E  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  F  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  G  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  H  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  I  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  J  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  K  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  L  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  M  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  N  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  O  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  P  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  Q  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  R  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  S  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  T  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  U  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  V  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  W  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  X  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  Y  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  Z  */ CT_ASCII | CT_UPPER | CT_HTTP_TOKEN,
+    /*  [  */ CT_ASCII | CT_PUNCT,
+    /*  \  */ CT_ASCII | CT_PUNCT,
+    /*  ]  */ CT_ASCII | CT_PUNCT,
+    /*  ^  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  _  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  `  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  a  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  b  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  c  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  d  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  e  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  f  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN | CT_XALPHA,
+    /*  g  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  h  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  i  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  j  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  k  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  l  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  m  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  n  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  o  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  p  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  q  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  r  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  s  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  t  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  u  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  v  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  w  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  x  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  y  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  z  */ CT_ASCII | CT_LOWER | CT_HTTP_TOKEN,
+    /*  {  */ CT_ASCII | CT_PUNCT,
+    /*  |  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /*  }  */ CT_ASCII | CT_PUNCT,
+    /*  ~  */ CT_ASCII | CT_PUNCT | CT_HTTP_TOKEN,
+    /* del */ CT_ASCII | CT_CNTRL,
+    /* x80 */ CT_UTF8_CONT,
+    /* x81 */ CT_UTF8_CONT,
+    /* x82 */ CT_UTF8_CONT,
+    /* x83 */ CT_UTF8_CONT,
+    /* x84 */ CT_UTF8_CONT,
+    /* x85 */ CT_UTF8_CONT,
+    /* x86 */ CT_UTF8_CONT,
+    /* x87 */ CT_UTF8_CONT,
+    /* x88 */ CT_UTF8_CONT,
+    /* x89 */ CT_UTF8_CONT,
+    /* x8a */ CT_UTF8_CONT,
+    /* x8b */ CT_UTF8_CONT,
+    /* x8c */ CT_UTF8_CONT,
+    /* x8d */ CT_UTF8_CONT,
+    /* x8e */ CT_UTF8_CONT,
+    /* x8f */ CT_UTF8_CONT,
+    /* x90 */ CT_UTF8_CONT,
+    /* x91 */ CT_UTF8_CONT,
+    /* x92 */ CT_UTF8_CONT,
+    /* x93 */ CT_UTF8_CONT,
+    /* x94 */ CT_UTF8_CONT,
+    /* x95 */ CT_UTF8_CONT,
+    /* x96 */ CT_UTF8_CONT,
+    /* x97 */ CT_UTF8_CONT,
+    /* x98 */ CT_UTF8_CONT,
+    /* x99 */ CT_UTF8_CONT,
+    /* x9a */ CT_UTF8_CONT,
+    /* x9b */ CT_UTF8_CONT,
+    /* x9c */ CT_UTF8_CONT,
+    /* x9d */ CT_UTF8_CONT,
+    /* x9e */ CT_UTF8_CONT,
+    /* x9f */ CT_UTF8_CONT,
+    /* xa0 */ CT_UTF8_CONT,
+    /* xa1 */ CT_UTF8_CONT,
+    /* xa2 */ CT_UTF8_CONT,
+    /* xa3 */ CT_UTF8_CONT,
+    /* xa4 */ CT_UTF8_CONT,
+    /* xa5 */ CT_UTF8_CONT,
+    /* xa6 */ CT_UTF8_CONT,
+    /* xa7 */ CT_UTF8_CONT,
+    /* xa8 */ CT_UTF8_CONT,
+    /* xa9 */ CT_UTF8_CONT,
+    /* xaa */ CT_UTF8_CONT,
+    /* xab */ CT_UTF8_CONT,
+    /* xac */ CT_UTF8_CONT,
+    /* xad */ CT_UTF8_CONT,
+    /* xae */ CT_UTF8_CONT,
+    /* xaf */ CT_UTF8_CONT,
+    /* xb0 */ CT_UTF8_CONT,
+    /* xb1 */ CT_UTF8_CONT,
+    /* xb2 */ CT_UTF8_CONT,
+    /* xb3 */ CT_UTF8_CONT,
+    /* xb4 */ CT_UTF8_CONT,
+    /* xb5 */ CT_UTF8_CONT,
+    /* xb6 */ CT_UTF8_CONT,
+    /* xb7 */ CT_UTF8_CONT,
+    /* xb8 */ CT_UTF8_CONT,
+    /* xb9 */ CT_UTF8_CONT,
+    /* xba */ CT_UTF8_CONT,
+    /* xbb */ CT_UTF8_CONT,
+    /* xbc */ CT_UTF8_CONT,
+    /* xbd */ CT_UTF8_CONT,
+    /* xbe */ CT_UTF8_CONT,
+    /* xbf */ CT_UTF8_CONT,
+    /* xc0 */ 0,
+    /* xc1 */ CT_UTF8_LEAD,
+    /* xc2 */ CT_UTF8_LEAD,
+    /* xc3 */ CT_UTF8_LEAD,
+    /* xc4 */ CT_UTF8_LEAD,
+    /* xc5 */ CT_UTF8_LEAD,
+    /* xc6 */ CT_UTF8_LEAD,
+    /* xc7 */ CT_UTF8_LEAD,
+    /* xc8 */ CT_UTF8_LEAD,
+    /* xc9 */ CT_UTF8_LEAD,
+    /* xca */ CT_UTF8_LEAD,
+    /* xcb */ CT_UTF8_LEAD,
+    /* xcc */ CT_UTF8_LEAD,
+    /* xcd */ CT_UTF8_LEAD,
+    /* xce */ CT_UTF8_LEAD,
+    /* xcf */ CT_UTF8_LEAD,
+    /* xd0 */ CT_UTF8_LEAD,
+    /* xd1 */ CT_UTF8_LEAD,
+    /* xd2 */ CT_UTF8_LEAD,
+    /* xd3 */ CT_UTF8_LEAD,
+    /* xd4 */ CT_UTF8_LEAD,
+    /* xd5 */ CT_UTF8_LEAD,
+    /* xd6 */ CT_UTF8_LEAD,
+    /* xd7 */ CT_UTF8_LEAD,
+    /* xd8 */ CT_UTF8_LEAD,
+    /* xd9 */ CT_UTF8_LEAD,
+    /* xda */ CT_UTF8_LEAD,
+    /* xdb */ CT_UTF8_LEAD,
+    /* xdc */ CT_UTF8_LEAD,
+    /* xdd */ CT_UTF8_LEAD,
+    /* xde */ CT_UTF8_LEAD,
+    /* xdf */ CT_UTF8_LEAD,
+    /* xe0 */ 0,
+    /* xe1 */ CT_UTF8_LEAD,
+    /* xe2 */ CT_UTF8_LEAD,
+    /* xe3 */ CT_UTF8_LEAD,
+    /* xe4 */ CT_UTF8_LEAD,
+    /* xe5 */ CT_UTF8_LEAD,
+    /* xe6 */ CT_UTF8_LEAD,
+    /* xe7 */ CT_UTF8_LEAD,
+    /* xe8 */ CT_UTF8_LEAD,
+    /* xe9 */ CT_UTF8_LEAD,
+    /* xea */ CT_UTF8_LEAD,
+    /* xeb */ CT_UTF8_LEAD,
+    /* xec */ CT_UTF8_LEAD,
+    /* xed */ CT_UTF8_LEAD,
+    /* xee */ CT_UTF8_LEAD,
+    /* xef */ CT_UTF8_LEAD,
+    /* xf0 */ 0,
+    /* xf1 */ CT_UTF8_LEAD,
+    /* xf2 */ CT_UTF8_LEAD,
+    /* xf3 */ CT_UTF8_LEAD,
+    /* xf4 */ CT_UTF8_LEAD,
+    /* xf5 */ CT_UTF8_LEAD,
+    /* xf6 */ CT_UTF8_LEAD,
+    /* xf7 */ CT_UTF8_LEAD,
+    /* xf8 */ 0,
+    /* xf9 */ CT_UTF8_LEAD,
+    /* xfa */ CT_UTF8_LEAD,
+    /* xfb */ CT_UTF8_LEAD,
+    /* xfc */ 0,
+    /* xfd */ CT_UTF8_LEAD,
+    /* xfe */ 0,
+    /* xff */ 0
+};
+
+/* Character class lookup */
+static APR_INLINE int ct_isspace(char c)
+{
+    return char_table[(unsigned char)c] & CT_HTTP_SPACE;
+}
+
+static APR_INLINE int ct_istoken(char c)
+{
+    return char_table[(unsigned char)c] & CT_HTTP_TOKEN;
+}
+
+
+/* Uppercase-lowercase table, also stolen wholesale from Subversion.
+   Folds uppercase ASCII letters into lowercase and ignores the rest. */
+static const unsigned char casefold_table[256] =
+  {
+    /* Identity, except {65:90} => {97:122} */
+      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+     16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+     32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+     48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+     64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+    112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
+     96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+    112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+    128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+    144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+    160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+    176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+    192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+    208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+    224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+    240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+  };
+
+/* Fold ASCII to lowercase. */
+static APR_INLINE char ct_tolower(char c)
+{
+    return (char) casefold_table[(unsigned char)c];
+}
+
+
+static const char *skip_space(const char *src)
+{
+    while (ct_isspace(*src))
+        ++src;
+    return src;
+}
+
+#if 0
+static const char *skip_comment(const char *src)
+{
+    if (*src != '(')
+        return NULL;
+
+    ++src;
+    while (*src && *src != ')')
+    {
+        if (*src == '(') {
+            src = skip_comment(src);
+            if (!src)
+                return NULL;
+        }
+        else {
+            if (*src == '\\')
+                ++src;          /* Skip escape from quoted-pair */
+            if (*src)
+                ++src;
+        }
+    }
+
+    if (!*src)
+        return NULL;
+    return src + 1;
+}
+#endif
+
+static const char *copy_quoted_string(char **dst, const char *src)
+{
+    if (*src != '"')
+        return NULL;
+
+    ++src;
+    while (*src && *src != '"')
+    {
+        if (*src == '\\')
+            ++src;              /* Skip escape from quoted-pair */
+        if (*src)
+            *((*dst)++) = *src++;
+    }
+
+    if (!*src)
+        return NULL;
+    return src + 1;
+}
+
+static const char *copy_token_key(char **dst, const char *src)
+{
+    if (!ct_istoken(*src))
+        return NULL;
+
+    do { *((*dst)++) = ct_tolower(*src++); } while (ct_istoken(*src));
+    return src;
+}
+
+static const char *copy_token(char **dst, const char *src)
+{
+    if (!ct_istoken(*src))
+        return NULL;
+
+    do { *((*dst)++) = *src++; } while (ct_istoken(*src));
+    return src;
+}
+
+
+apr_hash_t *serf__parse_authn_parameters(const char *attrs, apr_pool_t *pool)
+{
+    apr_hash_t *dict = apr_hash_make(pool);
+    char *dst = apr_palloc(pool, strlen(attrs));
+    const char *src = attrs;
+    bool want_comma = false;
+
+    while (src && *src)
+    {
+        const char *key;
+        apr_ssize_t keylen;
+        const char *value;
+
+        /* Skip spaces and the separator. */
+        src = skip_space(src);
+        if (want_comma) {
+            if (*src != ',')
+                break;
+            ++src;
+            if (*src)
+                src = skip_space(src);
+        }
+        if (!*src)
+            break;
+
+        /* Pars the key, a lower-cased token. */
+        key = dst;
+        src = copy_token_key(&dst, src);
+        if (!src || key == dst || *src != '=')
+            break;
+        keylen = dst - key;
+        *dst++ = '\0';
+
+        /* Parse the value, either a token or a quoted string. */
+        ++src;
+        value = dst;
+        if (*src == '"')
+            src = copy_quoted_string(&dst, src);
+        else if (ct_istoken(*src))
+            src = copy_token(&dst, src);
+        if (!src || value == dst)
+            break;
+        *dst++ = '\0';
+
+        /* Must be at the end of the string or at a valid separator. */
+        if (*src && *src != ',' && !ct_isspace(*src))
+            break;
+
+        /* Put the new key/value pair into the dict. */
+        apr_hash_set(dict, key, keylen, value);
+        want_comma = true;
+    }
+
+    return dict;
+}
+
+
+void serf__tolower_inplace(char *dst)
+{
+    while(*dst) {
+        *dst = ct_tolower(*dst);
+        ++dst;
+    }
+}
+
+
+const char *serf__tolower(const char *src, apr_pool_t *pool)
+{
+    const size_t len = strlen(src) + 1;
+    size_t i;
+
+    char *dst = apr_palloc(pool, len);
+    for (i = 0; i < len; ++i)
+        dst[i] = ct_tolower(src[i]); /* The NUL byte is copied, too. */
+    return dst;
+}

Propchange: serf/branches/user-defined-authn/src/syntax.c
------------------------------------------------------------------------------
    svn:eol-style = native

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=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/test_auth.c (original)
+++ serf/branches/user-defined-authn/test/test_auth.c Wed Jun 25 20:20:05 2025
@@ -707,33 +707,26 @@ static apr_status_t user_authn_get_realm
                                          void *baton,
                                          void *authn_baton,
                                          const char *authn_header,
-                                         const char *authn_attributes,
+                                         apr_hash_t *authn_parameters,
                                          apr_pool_t *result_pool,
                                          apr_pool_t *scratch_pool)
 {
-    const char *end;
-    apr_size_t length;
+    const char *name;
     USER_AUTHN_COUNT(baton, get_realm);
 
     test__log(TEST_VERBOSE, __FILE__,
-              "user_authn_get_realm, header %s, attrs %s\n",
-              authn_header, authn_attributes);
+              "user_authn_get_realm, header %s\n", authn_header);
 
     if (strncasecmp(authn_header, "TweedleDee", 10)) {
         *realm_name = "Wonderland";
         return APR_SUCCESS;
     }
 
-    if (strncasecmp(authn_attributes, "scope=", 6))
+    name = apr_hash_get(authn_parameters, "scope", 5);
+    if (!name)
         return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
 
-    authn_attributes += 6;
-    if ((end = strchr(authn_attributes, ' '))) {
-        length = end - authn_attributes;
-    } else {
-        length = strlen(authn_attributes);
-    }
-    *realm_name = apr_pstrndup(result_pool, authn_attributes, length);
+    *realm_name = apr_pstrdup(result_pool, name);
     return APR_SUCCESS;
 }
 
@@ -741,7 +734,7 @@ static apr_status_t user_authn_handle(vo
                                       void *authn_baton,
                                       int code,
                                       const char *authn_header,
-                                      const char *authn_attributes,
+                                      apr_hash_t *authn_parameters,
                                       const char *response_header,
                                       const char *username,
                                       const char *password,
@@ -821,15 +814,17 @@ user_authn_credentials(char **username,
     if (strncmp(user_authn_prefix, authn_type, strlen(user_authn_prefix)) != 0)
         return REPORT_TEST_SUITE_ERROR();
 
-    realm_name = realm + strlen(realm) - strlen("Alice");
-    if (strcmp("Alice", realm_name) != 0)
+    realm_name = strrchr(realm, ' ');
+    if (!realm_name
+        || (strcmp(" Alice", realm_name)
+            && strcmp(" Cheshire", realm_name)))
         return REPORT_TEST_SUITE_ERROR();
 
     *username = NULL;
     *password = apr_pstrdup(pool, authn_type);
     test__log(TEST_VERBOSE, __FILE__,
               "user credentials, realm %s, password %s\n",
-              realm, *password);
+              realm_name + 1, *password);
 
     return APR_SUCCESS;
 }
@@ -837,7 +832,7 @@ user_authn_credentials(char **username,
 static void user_authentication(CuTest *tc,
                                 int close_conn,
                                 int use_pipelining,
-                                const char *tweak)
+                                const char *scope)
 {
     test_baton_t *tb = tc->testBaton;
     handler_baton_t handler_ctx;
@@ -889,9 +884,9 @@ static void user_authentication(CuTest *
     serf_config_credentials_callback(tb->context, user_authn_credentials);
 
     /* Adjust the authentication header. */
-    if (tweak)
+    if (scope)
         hdr_value = apr_pstrcat(tb->pool, hdr_value,
-                                " tweak=", tweak, NULL);
+                                ", scope=", scope, NULL);
 
     /* Use non-standard case WWW-Authenticate header and scheme name to test
        for case insensitive comparisons. */
@@ -966,7 +961,7 @@ static void test_user_authentication(CuT
     user_authentication(tc,
                         0 /* don't close connection */,
                         1 /* allow pipelining during authn */,
-                        0 /* no authn header tweaks */);
+                        0 /* no custom scope */);
 }
 
 static void test_user_authentication_tweaked(CuTest *tc)
@@ -982,7 +977,7 @@ static void test_user_authentication_kee
     user_authentication(tc,
                         1 /* close connection */,
                         1 /* allow pipelining during authn */,
-                        0 /* no authn header tweaks */);
+                        0 /* no custom scope */);
 }
 
 static void test_user_authentication_pipelining_off(CuTest *tc)
@@ -990,7 +985,7 @@ static void test_user_authentication_pip
     user_authentication(tc,
                         0 /* don't close connection */,
                         0 /* don't allow pipelining during authn */,
-                        0 /* no authn header tweaks */);
+                        0 /* no custom scope */);
 }
 
 

Modified: serf/branches/user-defined-authn/test/test_internal.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/test_internal.c?rev=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/test_internal.c (original)
+++ serf/branches/user-defined-authn/test/test_internal.c Wed Jun 25 20:20:05 
2025
@@ -465,6 +465,88 @@ static void test_narrowing_conversions(C
     CuAssertIntEquals(tc, -1, val);
 }
 
+
+struct expected_attrs
+{
+    const char *token;
+    const char *value;
+};
+
+static void parse_parameters(CuTest *tc, const char *attrs,
+                             const struct expected_attrs expected[])
+{
+    test_baton_t *const tb = tc->testBaton;
+    apr_hash_t *const dict = serf__parse_authn_parameters(attrs, tb->pool);
+    const short attr_count = apr_hash_count(dict);
+    int i;
+
+    for (i = 0; expected[i].token; ++i)
+    {
+        const char *token = expected[i].token;
+        const char *value = apr_hash_get(dict, token, APR_HASH_KEY_STRING);
+        CuAssertStrEquals(tc, expected[i].value, value);
+    }
+    CuAssertIntEquals(tc, i, attr_count);
+}
+
+static void test_parse_parameters(CuTest *tc)
+{
+    static const struct expected_attrs expected[] = {
+        { "realm", "Wonderland" },
+        { "scope", "Alice" },
+        { "!#$%&'*+-.^_`|~", "(\"\\)"},
+        { NULL, NULL }
+    };
+
+    parse_parameters(tc,
+                     "Realm=\"Wonderland\","
+                     "ScOpE=Alice , "
+                     "!#$%&'*+-.^_`|~=\"(\\\"\\\\)\"",
+                     expected);
+}
+
+static void test_parse_bad_parameters(CuTest *tc)
+{
+    static const struct expected_attrs expected[] = {
+        { NULL, NULL }
+    };
+    static const struct expected_attrs unexpected[] = {
+        { "key", "value1" },
+        { NULL, NULL }
+    };
+
+    parse_parameters(tc, "", expected);
+    parse_parameters(tc, "\t", expected);
+    parse_parameters(tc, "(comm", expected);
+    parse_parameters(tc, "key", expected);
+    parse_parameters(tc, "key=\"value", expected);
+    parse_parameters(tc, "key = value", expected);
+    parse_parameters(tc, "key=\"value1\"key=value2", expected);
+    parse_parameters(tc, "key=value1 key=value2", unexpected);
+}
+
+static void test_parse_repeated_parameters(CuTest *tc)
+{
+    static const struct expected_attrs expected[] = {
+        { "key", "value2" },
+        { NULL, NULL }
+    };
+
+    parse_parameters(tc, "key=value1, key=value2", expected);
+}
+
+static void test_parameter_case_folding(CuTest *tc)
+{
+    static const struct expected_attrs expected[] = {
+        { "01234abcdefghijklmnopqrstuvwxyz56789", "Val" },
+        { NULL, NULL }
+    };
+
+    parse_parameters(tc, "01234abcdefghijklmnopqrstuvwxyz56789=Val", expected);
+    parse_parameters(tc, "01234ABCDEFGHIJKLMNOPQRSTUVWXYZ56789=Val", expected);
+}
+
+
 CuSuite *test_internal(void)
 {
     CuSuite *suite = CuSuiteNew();
@@ -479,6 +561,10 @@ CuSuite *test_internal(void)
     SUITE_ADD_TEST(suite, test_header_buckets_remove);
     SUITE_ADD_TEST(suite, test_runtime_versions);
     SUITE_ADD_TEST(suite, test_narrowing_conversions);
+    SUITE_ADD_TEST(suite, test_parse_parameters);
+    SUITE_ADD_TEST(suite, test_parse_bad_parameters);
+    SUITE_ADD_TEST(suite, test_parse_repeated_parameters);
+    SUITE_ADD_TEST(suite, test_parameter_case_folding);
 
     return suite;
 }

Modified: serf/branches/user-defined-authn/test/test_serf.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/test_serf.h?rev=1926725&r1=1926724&r2=1926725&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/test_serf.h (original)
+++ serf/branches/user-defined-authn/test/test_serf.h Wed Jun 25 20:20:05 2025
@@ -33,7 +33,7 @@
 
 /* Test logging facilities, set flag to 1 to enable console logging for
    the test suite. */
-#define TEST_VERBOSE 0
+#define TEST_VERBOSE 01
 
 /* Preferred proxy port */
 #define PROXY_PORT 23456



Reply via email to