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