wandenberg / nginx-push-stream-module

A pure stream http push technology for your Nginx setup. Comet made easy and really scalable.
Other
2.22k stars 295 forks source link

Mess with HTTP1.0/1.1 and chunked encoding #106

Closed AlexeyKupershtokh closed 10 years ago

AlexeyKupershtokh commented 10 years ago

I have a few strange cases with my nginx 1.2.6 + I think nginx-push-stream-module e2700bef08fd7ab0ced7fd82299090280940e465

wicked@f2p-dev:~$ nginx -V
nginx version: nginx/1.2.6
built by gcc 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) 
TLS SNI support enabled
configure arguments: --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --http-scgi-temp-path=/var/lib/nginx/scgi --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module --with-file-aio --add-module=../nginx-push-stream-module

Current related configuration is:

server  {
    listen      ***;
    server_name ***;
    charset     utf-8;
    access_log off;
    chunked_transfer_encoding off;

    location /pub {
        push_stream_publisher                   admin;
        push_stream_store_messages              on;
        set $push_stream_channel_id             $arg_id;
    }

    location ~ /sub-p/(.*) {
        chunked_transfer_encoding               off;
        push_stream_subscriber                  polling;
        set $push_stream_channels_path          $1;
    }
}

Note chunked_transfer_encoding off; everywhere.

Issue 1 [Resolved: another nginx was proxying on the way to nginx_push_stream_module]:

1) I post two messages into pub channel. 2) I send an http 1.0 request and get http 1.1 response, Transfer-Encoding: chunked and not chunked body:

curl -v -s http://***/sub-p/channel1/channel2 -0
* About to connect() to *** port 80 (#0)
*   Trying 208.76.170.197... connected
> GET /sub-p/channel1/channel2 HTTP/1.0
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: ***
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.0.11
< Date: Thu, 31 Oct 2013 01:06:04 GMT
< Content-Type: text/plain
< Connection: close
< Last-Modified: Thu, 31 Oct 2013 00:46:03 GMT
< Expires: Thu, 01 Jan 1970 00:00:01 GMT
< Cache-Control: no-cache, no-store, must-revalidate
< Etag: 0
< Transfer-Encoding: chunked
< 
{Notification:{2}}
{Notification:{2}}
* Closing connection #0

What I would expect is an http 1.0 response. I would also expect it to be without the Transfer-Encoding: chunked - because of chunked_transfer_encoding off; and because of using http 1.0.

Issue 2 [Resolved: another nginx was proxying on the way to nginx_push_stream_module]:

1) I post two messages into pub channel. 2) I send an http 1.1 request and get http 1.1 response, Transfer-Encoding: chunked and really chunked body:

wicked@f2p-dev:~$ curl -v -s http://***/sub-p/channel1/channel2
* About to connect() to *** port 80 (#0)
*   Trying 208.76.170.197... connected
> GET /sub-p/channel1/channel2 HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: ***
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.0.11
< Date: Thu, 31 Oct 2013 01:08:13 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
< Last-Modified: Thu, 31 Oct 2013 00:46:03 GMT
< Expires: Thu, 01 Jan 1970 00:00:01 GMT
< Cache-Control: no-cache, no-store, must-revalidate
< Etag: 0
< Transfer-Encoding: chunked
< 
14
{Notification:{2}}

14
{Notification:{2}}

0

* Connection #0 to host *** left intact
* Closing connection #0

I would except an http 1.1 response but also without the Transfer-Encoding: chunked - because of chunked_transfer_encoding off;.

Am I missing some logic of how nginx-push-stream-module works?

wandenberg commented 10 years ago

Hi,

until the current tag 0.3.5 the module was forcing the response to be chunked and also do not use the nginx chunked filter. Please, repeat your tests with the code on 0.4.x branch. But, the module behavior without the chunked filter is unpredictable and was not tested. To keep a streaming open a chunked response is needed. Why do you want to turn it off?

AlexeyKupershtokh commented 10 years ago

Thanks for your blazing fast replies!

Actually I don't want to turn it off, just experimenting. I want to use nginx + this module with various mobile clients. Also I'll need to collect requirements for http libraries on ios/android/etc. and some integration guide so they would not have issues with communications. I mean something like this:

For polling you will need a regular http library with ability to send http headers. You will also need a chunked body parser and json parser. For long-polling you will need an http library that can do long requests without blocking the entire applications. For event-source ... In order to not get repeating messages you will need to pass If-None-Match and If-Modified-Since in http request. Values for them you'll need get from E-Tag and Last-Modified response headers. It's also a good practice to store their last values persistently to cover application restart. Etc.

So do I understand you correctly that chunked encoding is normal and also I should use http 1.1?

Also does each chunk always contain a complete single message so it could be called framing? Otherwise I should first concat all chunks into a non-chunked body and only then split them line by line (?) like I would do for the response in the "Issue 2" I posted here.

Could you clarify what stream types (stream, stream+eventsource, polling, long polling, ...) tend to use chunked encoding?

AlexeyKupershtokh commented 10 years ago

It seems that we have nginx to nging proxying in my company. Have just noticed < Server: nginx/1.0.11 in the responses.

AlexeyKupershtokh commented 10 years ago

Yeah, these issues (1 and 2) came from proxying via another nginx. But when I started using nginx directly, another one appeared:

Issue 3:

nginx_push_stream_module 0.4.x-RC3 sends Transfer-Encoding: chunked but the response body is not actually chunked

wicked@f2p-dev:~$ curl -s -v "http://push-stream.f2p-dev/sub-lp/ch1" -H 'If-Modified-Since: Fri, 01 Nov 2013 00:43:50 GMT'
* About to connect() to push-stream.f2p-dev port 80 (#0)
*   Trying 127.0.0.1... connected
> GET /sub-lp/ch1 HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: push-stream.f2p-dev
> Accept: */*
> If-Modified-Since: Fri, 01 Nov 2013 00:43:50 GMT
> 
< HTTP/1.1 200 OK
< Server: nginx/1.2.6
< Date: Fri, 01 Nov 2013 00:44:19 GMT
< Content-Type: application/octet-stream
< Last-Modified: Fri, 01 Nov 2013 00:43:51 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< Expires: Thu, 01 Jan 1970 00:00:01 GMT
< Cache-Control: no-cache, no-store, must-revalidate
< Etag: 1
< 
* Connection #0 to host push-stream.f2p-dev left intact
* Closing connection #0
{"json":true}

with a --trace - option it shows

0000: 45 74 61 67 3a 20 31 0d 0a                      Etag: 1..
<= Recv header, 2 bytes (0x2)
0000: 0d 0a                                           ..
<= Recv data, 23 bytes (0x17)
0000: 64 0d 0a 7b 22 6a 73 6f 6e 22 3a 74 72 75 65 7d d..{"json":true}
0010: 0d 0a 30 0d 0a 0d 0a                            ..0....
{"json":true}== Info: Connection #0 to host push-stream.f2p-dev left intact
== Info: Closing connection #0

Do you see it's missing hex-encoded chunk length?

wandenberg commented 10 years ago

Hi, the curl command parse the received response and only show what is intended to be the received data. If you do a telnet to this server/port and send the http headers

GET /sub-lp/ch1 HTTP/1.1 Host: push-stream.f2p-dev If-Modified-Since: Fri, 01 Nov 2013 00:43:50 GMT

you will see the hex-encoded chunk length with no delay, because the curl also buffer the response.

AlexeyKupershtokh commented 10 years ago

Yeah. You're right. Though I've used curl --raw for checking.

--raw When used, it disables all internal HTTP decoding of content or transfer encodings and instead makes them passed on unaltered, raw. (Added in 7.16.2)