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);