Hi, There:
I run into a bug which I believe it is about
ngx_http_discard_request_body()
(discard_body() for short). This bug is reproducible using the 1.9.7
release.
The discard_body() discards request body by reading it. However,
the if the body is not ready yet (i.e.
ngx_http_read_discarded_request_body()
returns NGX_AGAIN), it still return NGX_OK.
ngx_http_special_response_handler() (special_handler() for short)
calls
discard_body(). If the discard_body() returns NGX_OK, it does NOT
disable
keepalive connection, in the meantime, it sends out the special response
before the request body is completely discarded. This cause the problem!
This is my setup to expose the bug:
My OS is Ubuntu 14.
a). the “backend” server have two locations, one return 200; the other
one return 403
b). the reverse proxy hook to the backend with:
b1) keepalive
b2) uploading is unbuffered;
so we can send incomplete request to the backend, which is to
mimic the situation
when the body is not sent at all of somehow dealayed.
b3) send incomplete POST request R1 to the ‘/bad’ relocation
via "cat post_bad.txt | nc -q -1 127.0.0.1 8080’
b4) (much be quick) send another request R2 via “curl -X GET
http://localhost:8080/good”
you may end up see 400 reponse.
Observeration/Analysis:
If you strace the backend server, you will see it first send
403-response to
the proxy, then call recvfrom() trying to get the body the R1.
The recvfrom() does not get the body of the R1, instead it gets the
leading
part of the R2, and discard it.
The subsequent call to recvfrom() get the trailing part of the R2.
Nginx thought
it is starting part of R2, and gives 400 response.
Proposed Fix:
-
Make sure discard_body() completely discard body before it sends
the headerThe disadvantage is that it may waste lots of resource in
read-then-discard
process. -
If discard_body() does not complete, just return NGX_AGAIN instead
of
NGX_OK, whichyby special handler disable keepalive, making sure
the boundary of requests are clean.The disadvantage is that it compromise the performance the
keepalive
connections between backend and proxy.
Please shed some light, lots thanks in advance!
Shuxin
====================================================
Following is my setup:
-
proxy conf;
upstream backend {
server 127.0.0.1:8081;
keepalive 32;
}server {
listen 8080;
proxy_request_buffering off;
location / {
proxy_http_version 1.1;
proxy_set_header Connection “”;
proxy_pass http://backend;
}
}
o. backend:
server {
listen 8081;
proxy_request_buffering off; # dose not matter
location /good {
content_by_lua 'ngx.say("lol")';
}
location /bad1 {
return 403;
}
}
o. incomplete request to the bad request
cat post_bad1.txt | nc -q -1 127.0.0.1 8080
The post_bad1.txt is attached to this mail.