https://issues.apache.org/bugzilla/show_bug.cgi?id=56707

            Bug ID: 56707
           Summary: Websocket response incorrectly includes transfer
                    encoding chunked header
           Product: Apache httpd-2
           Version: 2.4.9
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: critical
          Priority: P2
         Component: Core
          Assignee: [email protected]
          Reporter: [email protected]

When relaying a websocket response through Apache 2.4.9, it injects invalid (in
the context of websocket) Transfer-encoding: chunked header.

I've searched and found that Tomcat7 had similar problem but fixed (Bug#54067).

Here are the response headers before we relay them through Apache:

    0000: 48 54 54 50 2F 31 2E 31 20 31 30 31 20 53 77 69  HTTP/1.1 101 Swi
    0010: 74 63 68 69 6E 67 20 50 72 6F 74 6F 63 6F 6C 73  tching Protocols
    0020: 0D 0A 55 70 67 72 61 64 65 3A 20 57 65 62 73 6F  ..Upgrade: Webso
    0030: 63 6B 65 74 0D 0A 53 65 72 76 65 72 3A 20 4D 69  cket..Server: Mi
    0040: 63 72 6F 73 6F 66 74 2D 49 49 53 2F 38 2E 30 0D  crosoft-IIS/8.0.
    0050: 0A 53 65 63 2D 57 65 62 53 6F 63 6B 65 74 2D 41  .Sec-WebSocket-A
    0060: 63 63 65 70 74 3A 20 73 4F 79 70 36 53 78 73 33  ccept: sOyp6Sxs3
    0070: 5A 52 51 66 79 6D 66 2F 76 33 57 62 43 6F 58 41  ZRQfymf/v3WbCoXA
    0080: 5A 63 3D 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A  Zc=..Connection:
    0090: 20 55 70 67 72 61 64 65 0D 0A 44 61 74 65 3A 20   Upgrade..Date: 
    00a0: 57 65 64 2C 20 30 39 20 4A 75 6C 20 32 30 31 34  Wed, 09 Jul 2014
    00b0: 20 32 31 3A 34 37 3A 35 37 20 47 4D 54 0D 0A 0D   21:47:57 GMT...
    00c0: 0A                                               .               

Here are the response headers after Apache has written them to the client, note
the EXTRA transfer-encoding: chunked header which is invalid in the context of
a websocket response:

[Wed Jul 09 17:47:51.121491 2014] [dumpio:trace7] [pid 17506:tid 3074771856]
mod_dumpio.c(103): [client xxxxx:53506] mod_dumpio:  dumpio_out (data-HEAP):
HTTP/1.1 101 Switching Protocols\r\nDate: Wed, 09 Jul 2014 21:47:51
GMT\r\nServer: Apache/2.4.9 (Unix) OpenSSL/0.9.8b\r\nSet-Cookie:
ias-rs-sessionid="S0";\r\nUpgrade: Websocket\r\nSec-WebSocket-Accept:
sOyp6Sxs3ZRQfymf/v3WbCoXAZc=\r\nConnection: Upgrade\r\nTransfer-Encoding:
chunked\r\n\r\n  <<=============== invalid!

I've debugged this issue quiet a bit and found the chunked flag is getting set
in an "if" condition (that the documenter called "ungly side-effect") in
http_protocol.c:246.

Below is a snippet of the (long) "if" condition where chunked is set and also
its documentation. In addition, the stacktrace from GDB is also provided.

AP_DECLARE(int) ap_set_keepalive(request_rec *r)
{
    int ka_sent = 0;
    int left = r->server->keep_alive_max - r->connection->keepalives;
    int wimpy = ap_find_token(r->pool,
                              apr_table_get(r->headers_out, "Connection"),
                              "close");
    const char *conn = apr_table_get(r->headers_in, "Connection");

    /* The following convoluted conditional determines whether or not
     * the current connection should remain persistent after this response
     * (a.k.a. HTTP Keep-Alive) and whether or not the output message
     * body should use the HTTP/1.1 chunked transfer-coding.  In English,
     *
     *   IF  we have not marked this connection as errored;
     *   and the client isn't expecting 100-continue (PR47087 - more
     *       input here could be the client continuing when we're
     *       closing the request).
     *   and the response body has a defined length due to the status code
     *       being 304 or 204, the request method being HEAD, already
     *       having defined Content-Length or Transfer-Encoding: chunked, or
     *       the request version being HTTP/1.1 and thus capable of being set
     *       as chunked [we know the (r->chunked = 1) side-effect is ugly];
     *   and the server configuration enables keep-alive;
     *   and the server configuration has a reasonable inter-request timeout;
     *   and there is no maximum # requests or the max hasn't been reached;
     *   and the response status does not require a close;
     *   and the response generator has not already indicated close;
     *   and the client did not request non-persistence (Connection: close);
     *   and    we haven't been configured to ignore the buggy twit
     *       or they're a buggy twit coming through a HTTP/1.1 proxy
     *   and    the client is requesting an HTTP/1.0-style keep-alive
     *       or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
     *   and this MPM process is not already exiting
     *   THEN we can be persistent, which requires more headers be output.
     *
     * Note that the condition evaluation order is extremely important.
     */
    if ((r->connection->keepalive != AP_CONN_CLOSE)
        && !r->expecting_100
        && ((r->status == HTTP_NOT_MODIFIED)
            || (r->status == HTTP_NO_CONTENT)
            || r->header_only
            || apr_table_get(r->headers_out, "Content-Length")
            || ap_find_last_token(r->pool,
                                  apr_table_get(r->headers_out,
                                                "Transfer-Encoding"),
                                 "chunked")
            || ((r->proto_num >= HTTP_VERSION(1,1))
                && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
<<========= Here is where chunked is set! 
        && r->server->keep_alive
        && (r->server->keep_alive_timeout > 0)
        && ((r->server->keep_alive_max == 0)
            || (left > 0))
        && !ap_status_drops_connection(r->status)
        && !wimpy
        && !ap_find_token(r->pool, conn, "close")
        && (!apr_table_get(r->subprocess_env, "nokeepalive")
            || apr_table_get(r->headers_in, "Via"))
        && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
            || (r->proto_num >= HTTP_VERSION(1,1)))
        && is_mpm_running()) {

        r->connection->keepalive = AP_CONN_KEEPALIVE;
        r->connection->keepalives++;
...


GDB Stacktrace:
---------------

(gdb) p _r->chunked
$4 = 0
(gdb) p &(_r->chunked)
$5 = (int *) 0x9ad4f50
(gdb) watch *((int*)0x9ad4f50)
Hardware watchpoint 2: *(int *) 162352976
(gdb) c
Continuing.
Hardware watchpoint 2: *(int *) 162352976

Old value = 0
New value = 1
ap_set_keepalive (r=0x9ad4ed0) at http_protocol.c:247
247             && r->server->keep_alive
Current language:  auto; currently c
(gdb) where
#0  ap_set_keepalive (r=0x9ad4ed0) at http_protocol.c:247
#1  0x080aa84b in ap_http_header_filter (f=0x9ad5ad8, b=0x9ad67d0) at
http_filters.c:1262
#2  0x0807374f in ap_pass_brigade (next=0x9ad5ad8, bb=0x9ad67d0) at
util_filter.c:590
#3  0x08078132 in ap_content_length_filter (f=0x9ad5ac0, b=0x9ad67d0) at
protocol.c:1421
#4  0x0807374f in ap_pass_brigade (next=0x9ad5ac0, bb=0x9ad67d0) at
util_filter.c:590
#5  0x080ac963 in ap_byterange_filter (f=0x9ad5aa8, bb=0x9ad67d0) at
byterange_filter.c:483
#6  0x0807374f in ap_pass_brigade (next=0x9ad5aa8, bb=0x9ad67d0) at
util_filter.c:590
#7  0x08078368 in ap_old_write_filter (f=0x9ad67f0, bb=0x9ad67d0) at
protocol.c:1490
#8  0x0807374f in ap_pass_brigade (next=0x9ad67f0, bb=0x9ad67d0) at
util_filter.c:590
#9  0x08078873 in ap_rflush (r=0x9ad4ed0) at protocol.c:1699
...
#19 0x0808ddfe in ap_run_handler (r=0x9ad4ed0) at config.c:170
#20 0x0808e651 in ap_invoke_handler (r=0x9ad4ed0) at config.c:439
#21 0x080a7042 in ap_process_async_request (r=0x9ad4ed0) at http_request.c:317
#22 0x080a3556 in ap_process_http_async_connection (c=0x9ad10b0) at
http_core.c:143
#23 0x080a3726 in ap_process_http_connection (c=0x9ad10b0) at http_core.c:228
#24 0x0809990f in ap_run_process_connection (c=0x9ad10b0) at connection.c:41
#25 0x080ae81e in process_socket (thd=0x9a40958, p=0x9ad0e80, sock=0x9ad0ec0,
cs=0x9ad1068,
    my_child_num=1, my_thread_num=0) at event.c:970
#26 0x080b096b in worker_thread (thd=0x9a40958, dummy=0x9a976d0) at
event.c:1815
#27 0x006d0f7b in dummy_worker (opaque=0x9a40958) at
threadproc/unix/thread.c:142
#28 0x00ad345b in start_thread () from /lib/libpthread.so.0
#29 0x00a2b24e in clone () from /lib/libc.so.6
(gdb)

-- 
You are receiving this mail because:
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to