]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Expose "new" ap_parse_form_data() function instead of requiring
authorJim Jagielski <jim@apache.org>
Fri, 18 Feb 2011 18:40:31 +0000 (18:40 +0000)
committerJim Jagielski <jim@apache.org>
Fri, 18 Feb 2011 18:40:31 +0000 (18:40 +0000)
mod_request for any module that may want to parse form data...

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1072099 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
include/ap_mmn.h
include/httpd.h
include/mod_request.h
modules/aaa/mod_auth_form.c
modules/filters/mod_request.c
server/util.c

diff --git a/CHANGES b/CHANGES
index 6935ef16cb5d9a3850cdb8aa0b51fdf014507512..1661601777759c6cfdacfebe3116dc3f200df9a5 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,9 @@
 
 Changes with Apache 2.3.11
 
+  *) core: new util function: ap_parse_form_data(). Previously,
+     this capability was tucked away in mod_request. [Jim Jagielski]
+
   *) core: new hook: ap_run_pre_read_request. [Jim Jagielski]
 
   *) mod_cache: When a request other than GET or HEAD arrives, we must
index de64156afe0adc10dfd4ab72727610d51de6ed51..edaac77444c6c8f02606645f6ca70e6b6f482f9c 100644 (file)
  * 20110117.1 (2.3.11-dev) Add ap_pstr2_alnum() and ap_str2_alnum()
  * 20110203.0 (2.3.11-dev) Raise DYNAMIC_MODULE_LIMIT to 256
  * 20110203.1 (2.3.11-dev) Add ap_state_query()
- * 20110203.2 (2.3.11-dev) Add ap_run_pre_read_request() hook
+ * 20110203.2 (2.3.11-dev) Add ap_run_pre_read_request() hook and
+ *                         ap_parse_form_data() util
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
index 36faa745783c50911293041b33a9c66fd9f95e95..b2357b591853d6b6c8e74e2ff688d7f3429c2ddc 100644 (file)
@@ -1853,6 +1853,33 @@ AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
  */
 AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest);
 
+/**
+ * Structure to store the contents of an HTTP form of the type
+ * application/x-www-form-urlencoded.
+ * 
+ * Currently it contains the name as a char* of maximum length
+ * HUGE_STRING_LEN, and a value in the form of a bucket brigade
+ * of arbitrary length.
+ */
+typedef struct {
+    const char *name;
+    apr_bucket_brigade *value;
+} ap_form_pair_t;
+
+/**
+ * Read the body and parse any form found, which must be of the
+ * type application/x-www-form-urlencoded.
+ * @param r request containing POSTed form data
+ * @param f filter
+ * @param ptr returned array of ap_form_pair_t
+ * @param num max num of params or -1 for unlimited
+ * @param size max size allowed for parsed data
+ * @return OK or HTTP error
+ */
+AP_DECLARE(int) ap_parse_form_data(request_rec *r, struct ap_filter_t *f,
+                                  apr_array_header_t **ptr,
+                                   apr_size_t num, apr_size_t size);
+
 /* Misc system hackery */
 /**
  * Given the name of an object in the file system determine if it is a directory
index b65c4dcfef4126027155d42b41ffe0db69b5d6e0..ed28c05d20bbd8a4904bdae7df7c1e69851aa971 100644 (file)
@@ -52,56 +52,6 @@ typedef struct {
     int keep_body_set;
 } request_dir_conf;
 
-/**
- * Structure to store the contents of an HTTP form of the type
- * application/x-www-form-urlencoded.
- * 
- * Currently it contains the name as a char* of maximum length
- * HUGE_STRING_LEN, and a value in the form of a bucket brigade
- * of arbitrary length.
- */
-typedef struct {
-    const char *name;
-    apr_bucket_brigade *value;
-} ap_form_pair_t;
-
-/**
- * Read the body and parse any form found, which must be of the
- * type application/x-www-form-urlencoded.
- *
- * Name/value pairs are returned in an array, with the names as
- * strings with a maximum length of HUGE_STRING_LEN, and the
- * values as bucket brigades. This allows values to be arbitrarily
- * large.
- *
- * All url-encoding is removed from both the names and the values
- * on the fly. The names are interpreted as strings, while the
- * values are interpreted as blocks of binary data, that may
- * contain the 0 character.
- *
- * In order to ensure that resource limits are not exceeded, a
- * maximum size must be provided. If the sum of the lengths of
- * the names and the values exceed this size, this function
- * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
- *
- * An optional number of parameters can be provided, if the number
- * of parameters provided exceeds this amount, this function will
- * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
- * no limit is imposed, and the number of parameters is in turn
- * constrained by the size parameter above.
- * 
- * This function honours any kept_body configuration, and the
- * original raw request body will be saved to the kept_body brigade
- * if so configured, just as ap_discard_request_body does.
- * 
- * NOTE: File upload is not yet supported, but can be without change
- * to the function call.
- */
-
-APR_DECLARE_OPTIONAL_FN(int, ap_parse_request_form, (request_rec * r, ap_filter_t * f, 
-                                                     apr_array_header_t ** ptr,
-                                                     apr_size_t num, apr_size_t size));
-
 APR_DECLARE_OPTIONAL_FN(void, ap_request_insert_filter, (request_rec * r));
 
 APR_DECLARE_OPTIONAL_FN(void, ap_request_remove_filter, (request_rec * r));
index 05ac6c26cb89266f5277f67c75d476fa3ab6bdd9..41e1f2fe369418c45a2519a117a230fcb0ed1bcc 100644 (file)
@@ -46,9 +46,6 @@ static void (*ap_session_get_fn) (request_rec * r, session_rec * z,
                                   const char *key, const char **value) = NULL;
 static void (*ap_session_set_fn) (request_rec * r, session_rec * z,
                                   const char *key, const char *value) = NULL;
-static int (*ap_parse_request_form_fn) (request_rec * r, ap_filter_t *f,
-                                        apr_array_header_t ** ptr,
-                                        apr_size_t num, apr_size_t size) = NULL;
 static void (*ap_request_insert_filter_fn) (request_rec * r) = NULL;
 static void (*ap_request_remove_filter_fn) (request_rec * r) = NULL;
 
@@ -187,11 +184,10 @@ static const char *add_authn_provider(cmd_parms * cmd, void *config,
         }
     }
 
-    if (!ap_parse_request_form_fn || !ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
-        ap_parse_request_form_fn = APR_RETRIEVE_OPTIONAL_FN(ap_parse_request_form);
+    if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
         ap_request_insert_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_insert_filter);
         ap_request_remove_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_remove_filter);
-        if (!ap_parse_request_form_fn || !ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
+        if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
             return "You must load mod_request to enable the mod_auth_form "
                    "functions";
         }
@@ -607,7 +603,7 @@ static int get_form_auth(request_rec * r,
         return OK;
     }
 
-    res = ap_parse_request_form_fn(r, NULL, &pairs, -1, conf->form_size);
+    res = ap_parse_form_data(r, NULL, &pairs, -1, conf->form_size);
     if (res != OK) {
         return res;
     }
index 3693db8d25b727e164c797bccabaefc37705652d..cebe539a19fc30d9bc82301059d9386313ed3626 100644 (file)
@@ -267,221 +267,6 @@ static apr_status_t kept_body_filter(ap_filter_t *f, apr_bucket_brigade *b,
 
 }
 
-/* form parsing stuff */
-typedef enum {
-    FORM_NORMAL,
-    FORM_AMP,
-    FORM_NAME,
-    FORM_VALUE,
-    FORM_PERCENTA,
-    FORM_PERCENTB,
-    FORM_ABORT
-} ap_form_type_t;
-
-/**
- * Read the body and parse any form found, which must be of the
- * type application/x-www-form-urlencoded.
- *
- * Name/value pairs are returned in an array, with the names as
- * strings with a maximum length of HUGE_STRING_LEN, and the
- * values as bucket brigades. This allows values to be arbitrarily
- * large.
- *
- * All url-encoding is removed from both the names and the values
- * on the fly. The names are interpreted as strings, while the
- * values are interpreted as blocks of binary data, that may
- * contain the 0 character.
- *
- * In order to ensure that resource limits are not exceeded, a
- * maximum size must be provided. If the sum of the lengths of
- * the names and the values exceed this size, this function
- * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
- *
- * An optional number of parameters can be provided, if the number
- * of parameters provided exceeds this amount, this function will
- * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
- * no limit is imposed, and the number of parameters is in turn
- * constrained by the size parameter above.
- *
- * This function honours any kept_body configuration, and the
- * original raw request body will be saved to the kept_body brigade
- * if so configured, just as ap_discard_request_body does.
- *
- * NOTE: File upload is not yet supported, but can be without change
- * to the function call.
- */
-static int ap_parse_request_form(request_rec * r, ap_filter_t * f,
-                                 apr_array_header_t ** ptr,
-                                 apr_size_t num, apr_size_t usize)
-{
-    apr_bucket_brigade *bb = NULL;
-    int seen_eos = 0;
-    char buffer[HUGE_STRING_LEN + 1];
-    const char *ct;
-    apr_size_t offset = 0;
-    apr_ssize_t size;
-    ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
-    ap_form_pair_t *pair = NULL;
-    apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
-
-    char hi = 0;
-    char low = 0;
-
-    *ptr = pairs;
-
-    /* sanity check - we only support forms for now */
-    ct = apr_table_get(r->headers_in, "Content-Type");
-    if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
-        return ap_discard_request_body(r);
-    }
-
-    if (usize > APR_SIZE_MAX >> 1)
-        size = APR_SIZE_MAX >> 1;
-    else
-        size = usize;
-
-    if (!f) {
-        f = r->input_filters;
-    }
-
-    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
-    do {
-        apr_bucket *bucket = NULL, *last = NULL;
-
-        int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
-                                APR_BLOCK_READ, HUGE_STRING_LEN);
-        if (rv != APR_SUCCESS) {
-            apr_brigade_destroy(bb);
-            return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
-        }
-
-        for (bucket = APR_BRIGADE_FIRST(bb);
-             bucket != APR_BRIGADE_SENTINEL(bb);
-             last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
-            const char *data;
-            apr_size_t len, slide;
-
-            if (last) {
-                apr_bucket_delete(last);
-            }
-            if (APR_BUCKET_IS_EOS(bucket)) {
-                seen_eos = 1;
-                break;
-            }
-            if (bucket->length == 0) {
-                continue;
-            }
-
-            rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
-            if (rv != APR_SUCCESS) {
-                apr_brigade_destroy(bb);
-                return HTTP_BAD_REQUEST;
-            }
-
-            slide = len;
-            while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
-                char c = *data++;
-                if ('+' == c) {
-                    c = ' ';
-                }
-                else if ('&' == c) {
-                    state = FORM_AMP;
-                }
-                if ('%' == c) {
-                    percent = FORM_PERCENTA;
-                    continue;
-                }
-                if (FORM_PERCENTA == percent) {
-                    if (c >= 'a') {
-                        hi = c - 'a' + 10;
-                    }
-                    else if (c >= 'A') {
-                        hi = c - 'A' + 10;
-                    }
-                    else if (c >= '0') {
-                        hi = c - '0';
-                    }
-                    hi = hi << 4;
-                    percent = FORM_PERCENTB;
-                    continue;
-                }
-                if (FORM_PERCENTB == percent) {
-                    if (c >= 'a') {
-                        low = c - 'a' + 10;
-                    }
-                    else if (c >= 'A') {
-                        low = c - 'A' + 10;
-                    }
-                    else if (c >= '0') {
-                        low = c - '0';
-                    }
-                    c = low | hi;
-                    percent = FORM_NORMAL;
-                }
-                switch (state) {
-                case FORM_AMP:
-                    if (pair) {
-                        const char *tmp = apr_pmemdup(r->pool, buffer, offset);
-                        apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
-                        APR_BRIGADE_INSERT_TAIL(pair->value, b);
-                    }
-                    state = FORM_NAME;
-                    pair = NULL;
-                    offset = 0;
-                    num--;
-                    break;
-                case FORM_NAME:
-                    if (offset < HUGE_STRING_LEN) {
-                        if ('=' == c) {
-                            buffer[offset] = 0;
-                            offset = 0;
-                            pair = (ap_form_pair_t *) apr_array_push(pairs);
-                            pair->name = apr_pstrdup(r->pool, buffer);
-                            pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
-                            state = FORM_VALUE;
-                        }
-                        else {
-                            buffer[offset++] = c;
-                            size--;
-                        }
-                    }
-                    else {
-                        state = FORM_ABORT;
-                    }
-                    break;
-                case FORM_VALUE:
-                    if (offset >= HUGE_STRING_LEN) {
-                        const char *tmp = apr_pmemdup(r->pool, buffer, offset);
-                        apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
-                        APR_BRIGADE_INSERT_TAIL(pair->value, b);
-                        offset = 0;
-                    }
-                    buffer[offset++] = c;
-                    size--;
-                    break;
-                default:
-                    break;
-                }
-            }
-
-        }
-
-        apr_brigade_cleanup(bb);
-    } while (!seen_eos);
-
-    if (FORM_ABORT == state || size < 0 || num == 0) {
-        return HTTP_REQUEST_ENTITY_TOO_LARGE;
-    }
-    else if (FORM_VALUE == state && pair && offset > 0) {
-        const char *tmp = apr_pmemdup(r->pool, buffer, offset);
-        apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
-        APR_BRIGADE_INSERT_TAIL(pair->value, b);
-    }
-
-    return OK;
-
-}
-
 /**
  * Check whether this filter is not already present.
  */
@@ -596,7 +381,6 @@ static void register_hooks(apr_pool_t *p)
         ap_register_input_filter(KEPT_BODY_FILTER, kept_body_filter,
                                  kept_body_filter_init, AP_FTYPE_RESOURCE);
     ap_hook_insert_filter(ap_request_insert_filter, NULL, NULL, APR_HOOK_LAST);
-    APR_REGISTER_OPTIONAL_FN(ap_parse_request_form);
     APR_REGISTER_OPTIONAL_FN(ap_request_insert_filter);
     APR_REGISTER_OPTIONAL_FN(ap_request_remove_filter);
 }
index b61aa8eb8040d16d96d470cc6fc44cce8804bedc..c443c942a3d37fb37ab09e83b3262c6d1a31a3d5 100644 (file)
@@ -2147,7 +2147,7 @@ AP_DECLARE(int) ap_request_has_body(request_rec *r)
     char *estr;
     const char *cls;
     int has_body;
-            
+
     has_body = (!r->header_only
                 && (r->kept_body
                     || apr_table_get(r->headers_in, "Transfer-Encoding")
@@ -2168,7 +2168,7 @@ AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
 }
 
 AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
-    
+
     for ( ; *src; src++, dest++)
     {
         if (!apr_isprint(*src))
@@ -2180,7 +2180,7 @@ AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
     }
     *dest = '\0';
     return APR_SUCCESS;
-    
+
 }
 
 AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
@@ -2192,3 +2192,219 @@ AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
     *dest = new;
     return ap_str2_alnum(src, new);
 }
+
+/**
+ * Read the body and parse any form found, which must be of the
+ * type application/x-www-form-urlencoded.
+ *
+ * Name/value pairs are returned in an array, with the names as
+ * strings with a maximum length of HUGE_STRING_LEN, and the
+ * values as bucket brigades. This allows values to be arbitrarily
+ * large.
+ *
+ * All url-encoding is removed from both the names and the values
+ * on the fly. The names are interpreted as strings, while the
+ * values are interpreted as blocks of binary data, that may
+ * contain the 0 character.
+ *
+ * In order to ensure that resource limits are not exceeded, a
+ * maximum size must be provided. If the sum of the lengths of
+ * the names and the values exceed this size, this function
+ * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
+ *
+ * An optional number of parameters can be provided, if the number
+ * of parameters provided exceeds this amount, this function will
+ * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
+ * no limit is imposed, and the number of parameters is in turn
+ * constrained by the size parameter above.
+ *
+ * This function honours any kept_body configuration, and the
+ * original raw request body will be saved to the kept_body brigade
+ * if so configured, just as ap_discard_request_body does.
+ *
+ * NOTE: File upload is not yet supported, but can be without change
+ * to the function call.
+ */
+
+/* form parsing stuff */
+typedef enum {
+    FORM_NORMAL,
+    FORM_AMP,
+    FORM_NAME,
+    FORM_VALUE,
+    FORM_PERCENTA,
+    FORM_PERCENTB,
+    FORM_ABORT
+} ap_form_type_t;
+
+AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
+                                   apr_array_header_t **ptr,
+                                   apr_size_t num, apr_size_t usize)
+{
+    apr_bucket_brigade *bb = NULL;
+    int seen_eos = 0;
+    char buffer[HUGE_STRING_LEN + 1];
+    const char *ct;
+    apr_size_t offset = 0;
+    apr_ssize_t size;
+    ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
+    ap_form_pair_t *pair = NULL;
+    apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
+
+    char hi = 0;
+    char low = 0;
+
+    *ptr = pairs;
+
+    /* sanity check - we only support forms for now */
+    ct = apr_table_get(r->headers_in, "Content-Type");
+    if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
+        return ap_discard_request_body(r);
+    }
+
+    if (usize > APR_SIZE_MAX >> 1)
+        size = APR_SIZE_MAX >> 1;
+    else
+        size = usize;
+
+    if (!f) {
+        f = r->input_filters;
+    }
+
+    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    do {
+        apr_bucket *bucket = NULL, *last = NULL;
+
+        int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
+                                APR_BLOCK_READ, HUGE_STRING_LEN);
+        if (rv != APR_SUCCESS) {
+            apr_brigade_destroy(bb);
+            return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
+        }
+
+        for (bucket = APR_BRIGADE_FIRST(bb);
+             bucket != APR_BRIGADE_SENTINEL(bb);
+             last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
+            const char *data;
+            apr_size_t len, slide;
+
+            if (last) {
+                apr_bucket_delete(last);
+            }
+            if (APR_BUCKET_IS_EOS(bucket)) {
+                seen_eos = 1;
+                break;
+            }
+            if (bucket->length == 0) {
+                continue;
+            }
+
+            rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+            if (rv != APR_SUCCESS) {
+                apr_brigade_destroy(bb);
+                return HTTP_BAD_REQUEST;
+            }
+
+            slide = len;
+            while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
+                char c = *data++;
+                if ('+' == c) {
+                    c = ' ';
+                }
+                else if ('&' == c) {
+                    state = FORM_AMP;
+                }
+                if ('%' == c) {
+                    percent = FORM_PERCENTA;
+                    continue;
+                }
+                if (FORM_PERCENTA == percent) {
+                    if (c >= 'a') {
+                        hi = c - 'a' + 10;
+                    }
+                    else if (c >= 'A') {
+                        hi = c - 'A' + 10;
+                    }
+                    else if (c >= '0') {
+                        hi = c - '0';
+                    }
+                    hi = hi << 4;
+                    percent = FORM_PERCENTB;
+                    continue;
+                }
+                if (FORM_PERCENTB == percent) {
+                    if (c >= 'a') {
+                        low = c - 'a' + 10;
+                    }
+                    else if (c >= 'A') {
+                        low = c - 'A' + 10;
+                    }
+                    else if (c >= '0') {
+                        low = c - '0';
+                    }
+                    c = low | hi;
+                    percent = FORM_NORMAL;
+                }
+                switch (state) {
+                    case FORM_AMP:
+                        if (pair) {
+                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
+                        }
+                        state = FORM_NAME;
+                        pair = NULL;
+                        offset = 0;
+                        num--;
+                        break;
+                    case FORM_NAME:
+                        if (offset < HUGE_STRING_LEN) {
+                            if ('=' == c) {
+                                buffer[offset] = 0;
+                                offset = 0;
+                                pair = (ap_form_pair_t *) apr_array_push(pairs);
+                                pair->name = apr_pstrdup(r->pool, buffer);
+                                pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+                                state = FORM_VALUE;
+                            }
+                            else {
+                                buffer[offset++] = c;
+                                size--;
+                            }
+                        }
+                        else {
+                            state = FORM_ABORT;
+                        }
+                        break;
+                    case FORM_VALUE:
+                        if (offset >= HUGE_STRING_LEN) {
+                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
+                            offset = 0;
+                        }
+                        buffer[offset++] = c;
+                        size--;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+        }
+
+        apr_brigade_cleanup(bb);
+    } while (!seen_eos);
+
+    if (FORM_ABORT == state || size < 0 || num == 0) {
+        return HTTP_REQUEST_ENTITY_TOO_LARGE;
+    }
+    else if (FORM_VALUE == state && pair && offset > 0) {
+        const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+        apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(pair->value, b);
+    }
+
+    return OK;
+
+}