I am currently working in frontporting some patches I use for a while
in the 2.2.x branch and I'd like to propose for trunk (at least, my
goal is to upgrade to 2.4 of course).
The reason I talk about this in this thread is that 2 of these patches
may collide with your current work/review on ap_http_filter() and
maybe it worth taking my suggestions into account. If I should open a
different thread let me know...
My 2 patches serve the following purposes :
1. make mod_proxy_http handle a truncated response body (closed
connection with remaining data) like any other streaming error,
2. make LimitRequestBody also apply to mod_proxy_http forwarded requests,
3. make mod_proxy_http respond the HTTP error mapping the
ap_http_filter() status.
1. When ap_http_filter() returns APR_EOF, currently mod_proxy simply
stops forwarding the response, no log, no error bucket, like a normal
end of sream.
For an output filter there is no way to be aware of the problem, and
mod_cache (for example) will cache an incomplete entity.
The following patch addresses this issue :
<patch1>
Index: modules/http/http_filters.c
===================================================================
--- modules/http/http_filters.c (revision 1483799)
+++ modules/http/http_filters.c (working copy)
@@ -520,8 +524,12 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bu
ctx->remaining -= totalread;
if (ctx->remaining > 0) {
e = APR_BRIGADE_LAST(b);
- if (APR_BUCKET_IS_EOS(e))
- return APR_EOF;
+ if (APR_BUCKET_IS_EOS(e)) {
+ apr_bucket_delete(e);
+ if (APR_BRIGADE_EMPTY(b)) {
+ return APR_EOF;
+ }
+ }
}
}
Index: modules/proxy/mod_proxy_http.c
===================================================================
--- modules/proxy/mod_proxy_http.c (revision 1483799)
+++ modules/proxy/mod_proxy_http.c (working copy)
@@ -1679,9 +1679,6 @@ int ap_proxy_http_process_response(apr_pool_t * p,
mode = APR_BLOCK_READ;
continue;
}
- else if (rv == APR_EOF) {
- break;
- }
else if (rv != APR_SUCCESS) {
if (rv == APR_ENOSPC) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv,
r, APLOGNO(02475)
@@ -1691,6 +1688,10 @@ int ap_proxy_http_process_response(apr_pool_t * p,
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv,
r, APLOGNO(02476)
"Response Transfer-Encoding
was not recognised");
}
+ else if (rv == APR_EOF) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv,
r, APLOGNO()
+ "Response content is incomplete");
+ }
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv,
r, APLOGNO(01110)
"Network error reading response");
</patch1>
In the 2.2 version of the patch I used APR_INCOMPLETE instead of
APR_EOF and change APR_EOF to APR_INCOMPLETE in ap_http_filter()
whenever the content is incomplete. I didn't want to change the
meaning of APR_EOF in mod_proxy, but now I think mod_proxy has to be
fixed and other modules should not be impacted.
Note the patch will not trigger APR_EOF on the first call if some data
is available with the EOS, IMHO anything received should be forwarded
to the client/output filters before closing the stream.
2. LimitRequestBody handling is disabled when mod_proxy is on stage,
not only for PROXYREQ_RESPONSE (although the associated comment says
"does not apply to proxied responses").
Maybe there should be a ProxyLimitRequestBody (and
ProxyLimitResponseBody?) for the mod_proxy case, not to break some
existing configurations (for example with a weird global
LimitRequestBody that currently only apply to non-proxy vhosts), but
that would duplicate the ap_http_filter() code somewhere else...
<patch3>
Index: modules/http/http_filters.c
===================================================================
--- modules/http/http_filters.c (revision 1483799)
+++ modules/http/http_filters.c (working copy)
@@ -223,7 +223,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bu
* Would adding a directive to limit the size of proxied
* responses be useful?
*/
- if (!f->r->proxyreq) {
+ if (f->r->proxyreq != PROXYREQ_RESPONSE) {
ctx->limit = ap_get_limit_req_body(f->r);
}
else {
@@ -387,6 +387,10 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bu
}
}
}
+ else if (ctx->limit && ctx->limit < ctx->limit_used) {
+ /* The limit is already reached, don't read any further. */
+ return APR_ENOSPC;
+ }
else {
bb = ctx->bb;
}
@@ -544,15 +548,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bu
"Read content-length of %" APR_OFF_T_FMT
" is larger than the configured limit"
" of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
- apr_brigade_cleanup(bb);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
+ return APR_ENOSPC;
}
}
</patch3>
3. For the ap_http_filter() status mapping to HTTP error you have done
all the work, and the patch is now as simple as :
<patch3>
Index: modules/http/http_filters.c
===================================================================
--- modules/http/http_filters.c (revision 1483799)
+++ modules/http/http_filters.c (working copy)
@@ -1399,7 +1399,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_heade
* Map specific APR codes returned by the filter stack to HTTP error
* codes, or the default status code provided. Use it as follows:
*
- * return ap_map_http_response(rv, HTTP_BAD_REQUEST);
+ * return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
*
* If the filter has already handled the error, AP_FILTER_ERROR will
* be returned, which is cleanly passed through.
Index: modules/proxy/mod_proxy_http.c
===================================================================
--- modules/proxy/mod_proxy_http.c (revision 1483799)
+++ modules/proxy/mod_proxy_http.c (working copy)
@@ -319,7 +319,7 @@ static int stream_reqbody_chunked(apr_pool_t *p,
HUGE_STRING_LEN);
if (status != APR_SUCCESS) {
- return HTTP_BAD_REQUEST;
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
}
}
@@ -464,7 +464,7 @@ static int stream_reqbody_cl(apr_pool_t *p,
HUGE_STRING_LEN);
if (status != APR_SUCCESS) {
- return HTTP_BAD_REQUEST;
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
}
}
@@ -607,7 +607,7 @@ static int spool_reqbody_cl(apr_pool_t *p,
HUGE_STRING_LEN);
if (status != APR_SUCCESS) {
- return HTTP_BAD_REQUEST;
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
}
}
@@ -791,7 +791,7 @@ int ap_proxy_http_request(apr_pool_t *p, request_r
" from %s (%s)",
p_conn->addr, p_conn->hostname ?
p_conn->hostname: "",
c->client_ip, c->remote_host ? c->remote_host: "");
- return HTTP_BAD_REQUEST;
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
}
apr_brigade_length(temp_brigade, 1, &bytes);
</patch3>
Regards,
Yann.