Closing upstream keepalive connections in an invalid state
Maxim Dounin
mdounin at mdounin.ru
Mon Nov 30 19:10:06 UTC 2015
Hello!
On Mon, Nov 30, 2015 at 09:09:46PM +0300, Maxim Dounin wrote:
> Hello!
>
> On Mon, Nov 30, 2015 at 03:20:17PM +0000, Chris Branch wrote:
>
> > There was a thread on the nginx mailing list last week,
> > regarding upstream keepalive connections being placed in an
> > invalid state due to a partially-transmitted request body. With
> > regard to that discussion, I’m submitting two patches for your
> > review.
> >
> > The first adds a test case to nginx-tests demonstrating the
> > problem as of nginx 1.9.7. Most of the change involves extending
> > the mock origin to consume a request body, and verify the method
> > transmitted. Currently, nginx will reuse the upstream connection
> > for a subsequent request and (from the point of view of an
> > upstream client) insert some or all of a request line and
> > headers into the previous request's body. The result is
> > typically a 400 Bad Request error due to a malformed request.
>
> A test case for this was already committed by Sergey Kandaurov a
> couple of days ago:
>
> http://hg.nginx.org/nginx-tests/rev/2f292082c8a0
>
> > The second patch fixes this bug using the method suggested by
> > Maxim, i.e. close the upstream connection when a response is
> > received before the request body is completely sent. This is the
> > behaviour suggested in RFC 2616 section 8.2.2. The relevant Trac
> > issue is #669.
>
> The patch looks incomplete for me. It doesn't seem to handle the
> "next upstream" case. And the condition used looks wrong, too, as
> it doesn't take into account what nginx actually tried to send.
>
> (And please also take a look at this article:
> http://nginx.org/en/docs/contributing_changes.html)
>
> Quick and dirty patch to address this is as follows. Though I
> can't say I like it.
>
> diff --git a/src/http/modules/ngx_http_upstream_keepalive_module.c b/src/http/modules/ngx_http_upstream_keepalive_module.c
> --- a/src/http/modules/ngx_http_upstream_keepalive_module.c
> +++ b/src/http/modules/ngx_http_upstream_keepalive_module.c
> @@ -302,6 +302,10 @@ ngx_http_upstream_free_keepalive_peer(ng
> goto invalid;
> }
>
> + if (!u->request_body_sent) {
> + goto invalid;
> + }
> +
> if (ngx_terminate || ngx_exiting) {
> goto invalid;
> }
> diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
> --- a/src/http/ngx_http_upstream.c
> +++ b/src/http/ngx_http_upstream.c
> @@ -1818,6 +1818,8 @@ ngx_http_upstream_send_request(ngx_http_
>
> /* rc == NGX_OK */
>
> + u->request_body_sent = 1;
> +
> if (c->write->timer_set) {
> ngx_del_timer(c->write);
> }
> diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
> --- a/src/http/ngx_http_upstream.h
> +++ b/src/http/ngx_http_upstream.h
> @@ -370,6 +370,7 @@ struct ngx_http_upstream_s {
> unsigned upgrade:1;
>
> unsigned request_sent:1;
> + unsigned request_body_sent:1;
> unsigned header_sent:1;
> };
>
And an additional part to properly reset the flag on next
upstream:
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -1434,6 +1434,7 @@ ngx_http_upstream_connect(ngx_http_reque
}
u->request_sent = 0;
+ u->request_body_sent = 0;
if (rc == NGX_AGAIN) {
ngx_add_timer(c->write, u->conf->connect_timeout);
--
Maxim Dounin
http://nginx.org/
More information about the nginx-devel
mailing list