bug of discarding request body

Shuxin Yang shuxinyang.oss at gmail.com
Mon Nov 23 00:21:36 UTC 2015


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:
============
   1. Make sure discard_body() completely discard body before it sends
      the header

      The disadvantage is that it may waste lots of resource in 
read-then-discard
      process.

   2. 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:

1. 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.


















-------------- next part --------------
POST /bad1 HTTP/1.1
User-Agent: curl/7.35.0
Host: localhost:8088
Accept: */*
Content-Length: 8
Content-Type: application/x-www-form-urlencoded



More information about the nginx mailing list