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]