On Wednesday, 14 December 2011 16:03:18 Jordan Michaels wrote:
> 1) Is there an ideal way to add POST data to a subrequest that I'm 
> missing here?

No, subrequests don't have a request body by definition. It's hard coded in 
httpd 2.2.x.

Enclosed you find a patch for httpd 2.2.21 that makes is possible to attach 
the request body of the main request to a subreq made by mod_includes <!--
#include virtual ... -->. The intent at the time of writing of the patch was 
to send a POST request via mod_proxy to a backend server passing the original 
req body where the subreq was initiated by the mod_include response handler. 
It turned out to be quite tricky.

Maybe it helps.

Torsten Förtsch

-- 
Need professional modperl support? Hire me! (http://foertsch.name)

Like fantasy? http://kabatinte.net
--- modules/filters/mod_include.c.orig	2011-10-07 14:49:47.461434968 +0200
+++ modules/filters/mod_include.c	2011-10-07 13:16:15.633377775 +0200
@@ -1654,6 +1654,7 @@
                                    apr_bucket_brigade *bb)
 {
     request_rec *r = f->r;
+    enum {METHOD_GET, METHOD_POST, METHOD_INHERIT} method;
 
     if (!ctx->argc) {
         ap_log_rerror(APLOG_MARK,
@@ -1672,6 +1673,8 @@
         return APR_SUCCESS;
     }
 
+    method=METHOD_GET;
+
     while (1) {
         char *tag     = NULL;
         char *tag_val = NULL;
@@ -1684,6 +1687,29 @@
             break;
         }
 
+        if (tag[0] == 'm' && !strcmp(tag, "method")) {
+            if ((tag_val[0] == 'g' || tag_val[0] == 'G')
+                && !strcasecmp(tag_val, "get")) {
+                method=METHOD_GET;
+            }
+            else if ((tag_val[0] == 'p' || tag_val[0] == 'P')
+                && !strcasecmp(tag_val, "post")) {
+                method=METHOD_POST;
+            }
+            else if ((tag_val[0] == 'i' || tag_val[0] == 'I')
+                && !strcasecmp(tag_val, "inherit")) {
+                method=METHOD_INHERIT;
+            }
+            else {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value "
+                              "\"%s\" to parameter \"method\" of tag "
+                              "include in %s", tag_val, r->filename);
+                SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+                break;
+            }
+            continue;
+        }
+
         if (strcmp(tag, "virtual") && strcmp(tag, "file")) {
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
                           "\"%s\" to tag include in %s", tag, r->filename);
@@ -1710,7 +1736,15 @@
             }
         }
         else {
-            rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
+            if (method == METHOD_GET
+                || (method == METHOD_INHERIT && strcmp(r->method, "POST"))) {
+                rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
+            }
+            else {              /* POST */
+                method = METHOD_POST;
+                apr_table_setn(r->notes, "subreq-pass-request-body", "1");
+                rr = ap_sub_req_method_uri("POST", parsed_string, r, f->next);
+            }
         }
 
         if (!error_fmt && rr->status != HTTP_OK) {
@@ -1732,10 +1766,22 @@
             ap_set_module_config(rr->request_config, &include_module, r);
         }
 
+        /* XXX: would be good to check for EOS on rr->input_filters
+         * if method==POST and issue a warning if so.
+         */
+
         if (!error_fmt && ap_run_sub_req(rr)) {
             error_fmt = "unable to include \"%s\" in parsed file %s";
         }
 
+        /* method="POST" must be specified *before* *each*
+         * virtual="..."
+         */
+        if (method != METHOD_GET) {
+            method = METHOD_GET;
+            apr_table_unset(r->notes, "subreq-pass-request-body");
+        }
+
         if (error_fmt) {
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val,
                           r->filename);
--- server/core.c.orig	2011-09-08 17:59:38.000000000 +0200
+++ server/core.c	2011-10-07 13:16:15.633377775 +0200
@@ -3692,17 +3692,11 @@
 
     ap_allow_standard_methods(r, MERGE_ALLOW, M_GET, M_OPTIONS, M_POST, -1);
 
-    /* If filters intend to consume the request body, they must
-     * register an InputFilter to slurp the contents of the POST
-     * data from the POST input stream.  It no longer exists when
-     * the output filters are invoked by the default handler.
-     */
-    if ((errstatus = ap_discard_request_body(r)) != OK) {
-        return errstatus;
-    }
-
     if (r->method_number == M_GET || r->method_number == M_POST) {
         if (r->finfo.filetype == 0) {
+            if ((errstatus = ap_discard_request_body(r)) != OK) {
+                return errstatus;
+            }
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                           "File does not exist: %s", r->filename);
             return HTTP_NOT_FOUND;
@@ -3712,6 +3706,9 @@
          * raw I/O on a dir.
          */
         if (r->finfo.filetype == APR_DIR) {
+            if ((errstatus = ap_discard_request_body(r)) != OK) {
+                return errstatus;
+            }
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                           "Attempt to serve directory: %s", r->filename);
             return HTTP_NOT_FOUND;
@@ -3720,6 +3717,9 @@
         if ((r->used_path_info != AP_REQ_ACCEPT_PATH_INFO) &&
             r->path_info && *r->path_info)
         {
+            if ((errstatus = ap_discard_request_body(r)) != OK) {
+                return errstatus;
+            }
             /* default to reject */
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                           "File does not exist: %s",
@@ -3740,6 +3740,9 @@
 
             req_cfg = ap_get_module_config(r->request_config, &core_module);
             if (!req_cfg->deliver_script) {
+                if ((errstatus = ap_discard_request_body(r)) != OK) {
+                    return errstatus;
+                }
                 /* The flag hasn't been set for this request. Punt. */
                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                               "This resource does not accept the %s method.",
@@ -3755,6 +3758,9 @@
                                                 ? 0 : APR_SENDFILE_ENABLED)
 #endif
                                     , 0, r->pool)) != APR_SUCCESS) {
+            if ((errstatus = ap_discard_request_body(r)) != OK) {
+                return errstatus;
+            }
             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
                           "file permissions deny server access: %s", r->filename);
             return HTTP_FORBIDDEN;
@@ -3813,6 +3819,11 @@
         APR_BRIGADE_INSERT_TAIL(bb, e);
 
         status = ap_pass_brigade(r->output_filters, bb);
+
+        if ((errstatus = ap_discard_request_body(r)) != OK) {
+            return errstatus;
+        }
+
         if (status == APR_SUCCESS
             || r->status != HTTP_OK
             || c->aborted) {
@@ -3827,6 +3838,9 @@
         }
     }
     else {              /* unusual method (not GET or POST) */
+        if ((errstatus = ap_discard_request_body(r)) != OK) {
+            return errstatus;
+        }
         if (r->method_number == M_INVALID) {
             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                           "Invalid method in request %s", r->the_request);
--- modules/http/http_filters.c.orig	2011-05-06 15:14:27.000000000 +0200
+++ modules/http/http_filters.c	2011-10-07 14:21:50.247870961 +0200
@@ -321,8 +321,9 @@
         /* Since we're about to read data, send 100-Continue if needed.
          * Only valid on chunked and C-L bodies where the C-L is > 0. */
         if ((ctx->state == BODY_CHUNK ||
-            (ctx->state == BODY_LENGTH && ctx->remaining > 0)) &&
-            f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1) &&
+             (ctx->state == BODY_LENGTH && ctx->remaining > 0)) &&
+            f->r->expecting_100==1 &&
+            f->r->proto_num >= HTTP_VERSION(1,1) &&
             !(f->r->eos_sent || f->r->bytes_sent)) {
             if (!ap_is_HTTP_SUCCESS(f->r->status)) {
                 ctx->state = BODY_NONE;
@@ -331,6 +332,9 @@
                 char *tmp;
                 int len;
 
+                /* don't send it twice */
+                f->r->expecting_100++;
+
                 /* if we send an interim response, we're no longer
                  * in a state of expecting one.
                  */
@@ -550,6 +554,7 @@
     if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
         e = apr_bucket_eos_create(f->c->bucket_alloc);
         APR_BRIGADE_INSERT_TAIL(b, e);
+        ctx->eos_sent = 1;
     }
 
     /* We have a limit in effect. */
@@ -872,6 +877,78 @@
 
 }
 
+static apr_status_t check_input_body_length(request_rec *r)
+{
+    const char *tenc;
+    const char *lenp;
+
+    /* avoid recursion */
+    if (r->status == HTTP_REQUEST_ENTITY_TOO_LARGE) {
+        return APR_SUCCESS;
+    }
+
+    tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
+    lenp = apr_table_get(r->headers_in, "Content-Length");
+
+    if( lenp && !tenc ) {
+        apr_off_t len=0, limit;
+        char *endstr;
+
+        if (apr_strtoff(&len, lenp, &endstr, 10)
+            || endstr == lenp
+            || *endstr
+            || len < 0) {
+            /* invalid CL */
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "Invalid Content-Length");
+            return HTTP_REQUEST_ENTITY_TOO_LARGE;
+        }
+
+        limit = ap_get_limit_req_body(r);
+        if (limit && limit < len) {
+            /* body too large */
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "Requested content-length of %" APR_OFF_T_FMT
+                          " is larger than the configured limit"
+                          " of %" APR_OFF_T_FMT, len, limit);
+            return HTTP_REQUEST_ENTITY_TOO_LARGE;
+        }
+    }
+
+    return APR_SUCCESS;
+ }
+
+static void http_send_continue_100(request_rec *r, apr_bucket_brigade *bb)
+{
+    const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
+    const char *lenp = apr_table_get(r->headers_in, "Content-Length");
+
+    if (r->expecting_100==1 &&  /* expected reply is not sent yet */
+        r->proto_num >= HTTP_VERSION(1,1) &&
+        ((tenc && !strcasecmp(tenc, "chunked")) || lenp)) {
+        
+        struct iovec vec[3];
+
+        vec[0].iov_base = (void *)(AP_SERVER_PROTOCOL " ");
+        vec[0].iov_len  = sizeof(AP_SERVER_PROTOCOL " ")-1;
+        vec[1].iov_base = (void *)(ap_get_status_line(100));
+        vec[1].iov_len  = strlen(vec[1].iov_base);
+        vec[2].iov_base = (void *)(CRLF CRLF);
+        vec[2].iov_len  = sizeof(CRLF CRLF) - 1;
+#if APR_CHARSET_EBCDIC
+        {
+            char *tmp;
+            apr_size_t len;
+            tmp = apr_pstrcatv(r->pool, vec, 3, &len);
+            ap_xlate_proto_to_ascii(tmp, len);
+            apr_brigade_write(bb, NULL, NULL, tmp, len);
+        }
+#else
+        apr_brigade_writev(bb, NULL, NULL, vec, 3);
+#endif
+    }
+}
+
 /* fill "bb" with a barebones/initial HTTP response header */
 static void basic_http_header(request_rec *r, apr_bucket_brigade *bb,
                               const char *protocol)
@@ -886,6 +963,9 @@
         return;
     }
 
+    /* this is the last chance to signal the client to send the req body */
+    http_send_continue_100(r, bb);
+
     /* Output the HTTP/1.x Status-Line and the Date and Server fields */
 
     vec[0].iov_base = (void *)protocol;
@@ -1125,6 +1205,7 @@
     header_filter_ctx *ctx = f->ctx;
     const char *ctype;
     ap_bucket_error *eb = NULL;
+    int status;
 
     AP_DEBUG_ASSERT(!r->main);
 
@@ -1156,12 +1237,17 @@
         }
     }
     if (eb) {
-        int status;
-
         status = eb->status;
         apr_brigade_cleanup(b);
         ap_die(status, r);
         return AP_FILTER_ERROR;
+    }
+
+    /* check request body in case the http input filter has not been called */
+    if ((status = check_input_body_length(r)) != APR_SUCCESS) {
+        apr_brigade_cleanup(b);
+        ap_die(status, r);
+        return AP_FILTER_ERROR;
     }
 
     if (r->assbackwards) {
--- modules/proxy/mod_proxy_http.c.orig	2011-02-14 09:04:57.000000000 +0100
+++ modules/proxy/mod_proxy_http.c	2011-10-07 13:16:15.641378735 +0200
@@ -935,8 +935,11 @@
      * input_brigade and jump past all of the request body logic...
      * Reading anything with ap_get_brigade is likely to consume the
      * main request's body or read beyond EOS - which would be unplesant.
+     * 
+     * An exception: if the main req is marked with the
+     * "subreq-pass-request-body" note the subreq may consume the body.
      */
-    if (r->main) {
+    if (r->main && !apr_table_get(r->main->notes, "subreq-pass-request-body")) {
         /* XXX: Why DON'T sub-requests use keepalives? */
         p_conn->close++;
         if (old_cl_val) {
--- server/protocol.c.orig	2011-05-07 13:39:29.000000000 +0200
+++ server/protocol.c	2011-10-07 13:16:15.641378735 +0200
@@ -1100,8 +1100,9 @@
     /* did the original request have a body?  (e.g. POST w/SSI tags)
      * if so, make sure the subrequest doesn't inherit body headers
      */
-    if (apr_table_get(r->headers_in, "Content-Length")
-        || apr_table_get(r->headers_in, "Transfer-Encoding")) {
+    if (!apr_table_get(r->notes, "subreq-pass-request-body")
+        && (apr_table_get(r->headers_in, "Content-Length")
+            || apr_table_get(r->headers_in, "Transfer-Encoding"))) {
         strip_headers_request_body(rnew);
     }
     rnew->subprocess_env  = apr_table_copy(rnew->pool, r->subprocess_env);

Reply via email to