httpd-44311
Version:
2.2.8
Bug Link:
https://issues.apache.org/bugzilla/show_bug.cgi?id=44311
Symptom(Failure):
Apache proxy returned blank page result.
How it is diagnosed:
We can not reproduce the failure, only analyzed the source code, discussions, and patches.
Root Cause:
The backend server (www.hotels.com) returned a “Transfer-Encoding” header in its response. This causes httpd to remove Transfer-Encoding too early during its internal processing and thus disables it from processing the chunked response.
apr_status_t ap_proxy_http_process_response(... ...) {
… ...
+ /*
+ * Save a possible Transfer-Encoding header as we need it later for
+ * ap_http_filter to know where to end.
+ */
+ te = apr_table_get(r->headers_out, "Transfer-Encoding");
/* strip connection listed hop-by-hop headers from response */
backend->close += ap_proxy_liststr(apr_table_get(r->headers_out,
"Connection"),
…. ….
/* We need to copy the output headers and treat them as input
* headers as well. BUT, we need to do this before we remove
* TE, so that they are preserved accordingly for
* ap_http_filter to know where to end.
*/ <-- their original comments. They should ensure that
headers_in still include Transfer-Encoding... Here they
can add checking and logging
rp->headers_in = apr_table_copy(r->pool, r->headers_out);
+ /*
+ * Restore Transfer-Encoding header from response if we saved
+ * one before and there is none left. We need it for the
+ * ap_http_filter. See below.
+ */
+ if (te && !apr_table_get(rp->headers_in, "Transfer-Encoding")) {
+ apr_table_add(rp->headers_in, "Transfer-Encoding", te);
+ }
apr_table_unset(r->headers_out,"Transfer-Encoding");
if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) {
do {
rv = ap_get_brigade(rp->input_filters, bb,
AP_MODE_READBYTES, mode,
conf->io_buffer_size);
// This will call rp->input_filters, which is ‘ap_http_filter’
if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS
|| c->aborted) {
/* Ack! Phbtt! Die! User aborted! */
backend->close = 1; /* this causes socket close below */
finish = TRUE;
}
}
}
}
/* This is the HTTP_INPUT filter for HTTP requests and responses from
* proxied servers (mod_proxy). It handles chunked and content-length
* bodies. This can only be inserted/used after the headers
* are successfully parsed.
*/
apr_status_t ap_http_filter(... ...) {
ctx->state = BODY_NONE;
… …
tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding");
// In buggy version, since the Transfer-Encoding is removed, so this will be NULL.
lenp = apr_table_get(f->r->headers_in, "Content-Length");
if (tenc) {
if (!strcasecmp(tenc, "chunked"))
ctx->state = BODY_CHUNK;
else if (!lenp) {
… return …
}
}
if (lenp && !tenc) {
/* In the buggy execution, ctx->state is set to BODY_LENGTH*/
ctx->state = BODY_LENGTH;
if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10))
… …
}
if (ctx->state == BODY_NONE && f->r->proxyreq != PROXYREQ_RESPONSE)
return APR_SUCCESS;
if (ctx->state == BODY_CHUNK || ctx->state == BODY_LENGTH)
… ...
switch (ctx->state) {
case BODY_NONE:
break;
case BODY_LENGTH:
e = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(b, e);
ctx->eos_sent = 1;
return APR_SUCCESS;
case BODY_CHUNK:
case BODY_CHUNK_PART:
….
} // switch
if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK)
… ...
if (ctx->state != BODY_NONE)
… …
if (ctx->state == BODY_LENGTH && ctx->remaining == 0)
… …
}
Is there any log message?:
No.
The bug reporters posted some messages of [debug] or [info] level. But actually they were not related to the actual failure execution. In addition, they won’t be printed in default log level [warn].
Can Errlog print any log message?:
No....
It is hard to anticipate an error message. For Apache, it thinks nothing is wrong, just the response did not contain a “Transfer-Encoding” header, so it did not process the chunked result and returned blank page.