changeset 6495:92464ebace8e

HTTP/2: sending RST_STREAM with NO_ERROR to discard request body. RFC 7540 states that "A server can send a complete response prior to the client sending an entire request if the response does not depend on any portion of the request that has not been sent and received. When this is true, a server MAY request that the client abort transmission of a request without error by sending a RST_STREAM with an error code of NO_ERROR after sending a complete response (i.e., a frame with the END_STREAM flag)." This should prevent a client from blocking on the stream window, since it isn't maintained for closed streams. Currently, quite big initial stream windows are used, so such blocking is very unlikly, but that will be changed in the further patches.
author Valentin Bartenev <vbart@nginx.com>
date Fri, 01 Apr 2016 15:56:03 +0300
parents 5805301f990f
children 887cca40ba6a
files src/http/v2/ngx_http_v2.c src/http/v2/ngx_http_v2.h
diffstat 2 files changed, 25 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -3709,7 +3709,7 @@ ngx_http_v2_terminate_stream(ngx_http_v2
         return NGX_ERROR;
     }
 
-    stream->out_closed = 1;
+    stream->rst_sent = 1;
 
     fc = stream->request->connection;
     fc->error = 1;
@@ -3744,13 +3744,23 @@ ngx_http_v2_close_stream(ngx_http_v2_str
         return;
     }
 
-    if (!stream->out_closed) {
-        if (ngx_http_v2_send_rst_stream(h2c, node->id,
-                                     fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR
-                                                  : NGX_HTTP_V2_INTERNAL_ERROR)
-            != NGX_OK)
-        {
-            h2c->connection->error = 1;
+    if (!stream->rst_sent && !h2c->connection->error) {
+
+        if (!stream->out_closed) {
+            if (ngx_http_v2_send_rst_stream(h2c, node->id,
+                                      fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR
+                                                   : NGX_HTTP_V2_INTERNAL_ERROR)
+                != NGX_OK)
+            {
+                h2c->connection->error = 1;
+            }
+
+        } else if (!stream->in_closed) {
+            if (ngx_http_v2_send_rst_stream(h2c, node->id, NGX_HTTP_V2_NO_ERROR)
+                != NGX_OK)
+            {
+                h2c->connection->error = 1;
+            }
         }
     }
 
@@ -3942,23 +3952,23 @@ ngx_http_v2_finalize_connection(ngx_http
 
     c = h2c->connection;
 
-    if (h2c->state.stream) {
-        h2c->state.stream->out_closed = 1;
-        ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST);
-    }
-
     h2c->blocked = 1;
 
     if (!c->error && ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) {
         (void) ngx_http_v2_send_output_queue(h2c);
     }
 
+    c->error = 1;
+
+    if (h2c->state.stream) {
+        ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST);
+    }
+
     if (!h2c->processing) {
         ngx_http_close_connection(c);
         return;
     }
 
-    c->error = 1;
     c->read->handler = ngx_http_empty_handler;
     c->write->handler = ngx_http_empty_handler;
 
--- a/src/http/v2/ngx_http_v2.h
+++ b/src/http/v2/ngx_http_v2.h
@@ -194,6 +194,7 @@ struct ngx_http_v2_stream_s {
     unsigned                         exhausted:1;
     unsigned                         in_closed:1;
     unsigned                         out_closed:1;
+    unsigned                         rst_sent:1;
     unsigned                         skip_data:2;
 };