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.