#959 closed enhancement (fixed)
Permit post before acking settings
Reported by: | Owned by: | ||
---|---|---|---|
Priority: | minor | Milestone: | |
Component: | nginx-core | Version: | 1.9.x |
Keywords: | Cc: | ||
uname -a: | Darwin jwilson.local 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; | ||
nginx -V: | 1.9.15 |
Description
This report is motivated by a discussion on the HTTP-WG mailing list.
https://lists.w3.org/Archives/Public/ietf-http-wg/2016AprJun/0174.html
In OkHttp we’re investigating a bug where nginx v1.9.15 sends us
REFUSED_STREAM when we send our HEADERS and DATA frames before acking the
server’s SETTINGS.
https://github.com/square/okhttp/issues/2506
The spec says not waiting is okay:
To avoid unnecessary latency, clients are permitted to send additional
frames to the server immediately after sending the client connection
preface, without waiting to receive the server connection preface. It is
important to note, however, that the server connection preface SETTINGS
frame might include parameters that necessarily alter how a client is
expected to communicate with the server. Upon receiving the SETTINGS frame,
the client is expected to honor any parameters established.
Not acking settings should not be the basis for refusing streams. This may
add significant latency to the initial HTTP request.
Please change nginx to accept streams created prior to the SETTINGS ack.
Attachments (1)
Change History (28)
comment:1 by , 9 years ago
follow-up: 3 comment:2 by , 9 years ago
vbart, how would I cause a module to ask for the request body? If I send a POST request with HTTP/1.1 everything works fine, but if I send it with HTTP2, the initial window size is set to 0, but is never updated. I can't seem to get nginx to send a window update to allow the POST to occur.
follow-up: 4 comment:3 by , 9 years ago
Replying to dave-r12@…:
vbart, how would I cause a module to ask for the request body?
All modules that require a request body read it by calling the ngx_http_read_client_request_body()
function.
comment:4 by , 9 years ago
Replying to vbart:
Replying to dave-r12@…:
vbart, how would I cause a module to ask for the request body?
All modules that require a request body read it by calling the
ngx_http_read_client_request_body()
function.
Thanks, would you expect this configuration to call that function?
upstream myapp1 { server localhost:8080; } server { listen 443 ssl http2; server_name www.example.com; ssl on; ssl_certificate /root/ca.cert.pem; ssl_certificate_key /root/ca.key.pem; ssl_password_file /root/passfile; location / { proxy_pass http://myapp1; } }
comment:6 by , 9 years ago
I appreciate the help vbart. I was able to get it working.
In addition to what was mentioned above, from a user perspective, these constraints are going to add more latency to the requests. From what I understand, we will now have to block on receiving the settings frame, then ack it. This introduces latency because we cannot immediately begin sending data frames. After we ack, we have to transmit the headers frame, and wait for the windows update frame before sending the data frames. Again, this introduces more latency to the request. In this case, it feels like HTTP/1.1 would actually be more efficient than HTTP2 because at least with HTTP/1.1 we can send the entire request all at once.
comment:7 by , 9 years ago
Status: | new → accepted |
---|---|
Type: | defect → enhancement |
I agree, while it's not optimal from the latency point of view, such approach significantly simplifies the code. So it's a fair price, that was paid at the current stage of HTTP/2 implementation.
Introducing some kind of preread buffer will be the next step.
comment:8 by , 9 years ago
Awesome, this will be a win for the users, especially those on slow and flaky mobile networks.
comment:9 by , 9 years ago
I think NGINX is over-aggressive here (although, still within the spec).
Per RFC7540, section 6.9.2. Initial Flow-Control Window Size:
Prior to receiving a SETTINGS frame that sets a value for
SETTINGS_INITIAL_WINDOW_SIZE, an endpoint can only use the default
initial window size when sending flow-controlled frames. Similarly,
the connection flow-control window is set to the default initial
window size until a WINDOW_UPDATE frame is received.
i.e. NGINX should assume clients can send 64KB before receiving SETTINGS frame, otherwise we're either:
- always forcing retry if the first request contains DATA frames,
- requiring clients to add latency by not sending requests with DATA frames before receiving SETTINGS frame from the server.
comment:10 by , 9 years ago
This seems to impact all iOS application which try to negotiate HTTP2 on nginx. One of our applications fails directly while showing "No connectivity", the other one simply crashes. Disabling http2 on those server blocks eliminates the problem
comment:11 by , 9 years ago
we are having this problem with iOS Safari on ios9.x. it fails when the first thing in a connection is a POST request. is there anything we could configure in nginx to somehow workaround this? for now we had to go back to http1.1 unfortunately.
comment:12 by , 8 years ago
A minimum testcase is available on https://github.com/inventid/http2_nginx_bug-ios
If you have XCode, you can clone this repository. Open the project using the http2_nginx_bug.xcworkspace
file, then run the project on any iPhone. When you have the log open you will see the project either crashing or succeeding. (see the readme of the project for exact instructions)
comment:13 by , 8 years ago
Is there any expected timeline for nginx 1.10.1 release? I am hoping that this bug will be fixed in 1.10.1 release.
Also is there any other work around ?
by , 8 years ago
Attachment: | preread_buffer.patch added |
---|
comment:15 by , 8 years ago
Replying to vbart:
Please try the patch in attachment above.
I've tested with Ok Http and nghttp, both are working great! Thanks.
follow-up: 19 comment:16 by , 8 years ago
Also tested and seems to work.
Is a release to be scheduled for an nginx version 1.10.1 for this? It would be more difficult for many organizations to remain on 1.9.x (both due to the number of updates as well as the fact its not officially called "stable"). Due to the number of clients impacted by the bug, and the fact that http2 is not reliable possible using nginx for many untill 1.12 might warrant this..?
comment:17 by , 8 years ago
Replying to vbart:
Please try the patch in attachment above.
patch fixes my problem. (upload from ios9-web-browser)
tested on nginx checked out from todays's hg.
comment:18 by , 8 years ago
Owner: | set to |
---|---|
Resolution: | → fixed |
Status: | accepted → closed |
comment:19 by , 8 years ago
Replying to rogierslag@…:
Also tested and seems to work.
Is a release to be scheduled for an nginx version 1.10.1 for this? It would be more difficult for many organizations to remain on 1.9.x (both due to the number of updates as well as the fact its not officially called "stable"). Due to the number of clients impacted by the bug, and the fact that http2 is not reliable possible using nginx for many untill 1.12 might warrant this..?
I don't know about the plans for 1.10.1 yet.
But please note that this isn't an nginx bug and the affected clients should be fixed. For everybody who uses such innovative protocol like HTTP/2, it's a good idea to stay with the latest mainline version anyway.
follow-up: 21 comment:20 by , 8 years ago
Any plans to put this patch into 1.10.2? I see 1.10.1 is already released.
comment:21 by , 8 years ago
Replying to waqark3389@…:
Any plans to put this patch into 1.10.2? I see 1.10.1 is already released.
I would also like to see this integrated into 1.10 branch if possible. Thanks!
comment:23 by , 8 years ago
Replying to ksmithut@…:
Any updates on the ETA of the patched release?
The change in question is a part of nginx 1.11.0, released more than two months ago.
comment:25 by , 8 years ago
Replying to alaz@…:
Please backport the fix into stable 1.10.x branch.
We don't backport features to the stable branch (that's what we call stable, no enhancements). It receives only critical bug fixes. If you use such new, very complicated and actively developing protocol as HTTP/2 then it's naturally that you have to stay with the mainline branch.
See also: https://www.nginx.com/blog/nginx-1-10-1-11-released/
comment:27 by , 8 years ago
For the record of anyone reading these comments, it seems that he went back on his decision, and this has in fact been included in 1.10.2 despite explicitly stated otherwise.
First of all, nginx doesn't reject streams prior receiving SETTINGS ack, but it rejects them without END_STREAM flag (i.e. with following DATA). This allows for a client to repeat its request without violating flow control.
Until the client has received the SETTINGS frame, it has a wrong idea about initial window size, that nginx has to change to zero.
The request body cannot be read and processed by nginx until some module asks for it. To prevent the client from sending the request body that hasn't been asked yet, the stream's window is kept zero.