]> git.kaiwu.me - nginx.git/commitdiff
gRPC: RST_STREAM(NO_ERROR) handling (ticket #1792).
authorRuslan Ermilov <ru@nginx.com>
Thu, 23 Apr 2020 12:10:24 +0000 (15:10 +0300)
committerRuslan Ermilov <ru@nginx.com>
Thu, 23 Apr 2020 12:10:24 +0000 (15:10 +0300)
As per https://tools.ietf.org/html/rfc7540#section-8.1,

: 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).  Clients MUST NOT discard responses as a
: result of receiving such a RST_STREAM, though clients can
: always discard responses at their discretion for other
: reasons.

Previously, RST_STREAM(NO_ERROR) received from upstream after
a frame with the END_STREAM flag was incorrectly treated as an
error.  Now, a single RST_STREAM(NO_ERROR) is properly handled.

This fixes problems observed with modern grpc-c [1], as well
as with the Go gRPC module.

[1] https://github.com/grpc/grpc/pull/1661

src/http/modules/ngx_http_grpc_module.c

index d4af66dbf2cd15a712781a1990fa0867e7e33f12..9e62d8e2a7b450abd0648d2e932edc58f63c4ff2 100644 (file)
@@ -120,6 +120,7 @@ typedef struct {
     unsigned                   end_stream:1;
     unsigned                   done:1;
     unsigned                   status:1;
+    unsigned                   rst:1;
 
     ngx_http_request_t        *request;
 
@@ -1205,6 +1206,7 @@ ngx_http_grpc_reinit_request(ngx_http_request_t *r)
     ctx->end_stream = 0;
     ctx->done = 0;
     ctx->status = 0;
+    ctx->rst = 0;
     ctx->connection = NULL;
 
     return NGX_OK;
@@ -2088,7 +2090,9 @@ ngx_http_grpc_filter(void *data, ssize_t bytes)
                 return NGX_ERROR;
             }
 
-            if (ctx->stream_id && ctx->done) {
+            if (ctx->stream_id && ctx->done
+                && ctx->type != NGX_HTTP_V2_RST_STREAM_FRAME)
+            {
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                               "upstream sent frame for closed stream %ui",
                               ctx->stream_id);
@@ -2131,11 +2135,21 @@ ngx_http_grpc_filter(void *data, ssize_t bytes)
                 return NGX_ERROR;
             }
 
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "upstream rejected request with error %ui",
-                          ctx->error);
+            if (ctx->error || !ctx->done) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream rejected request with error %ui",
+                              ctx->error);
+                return NGX_ERROR;
+            }
 
-            return NGX_ERROR;
+            if (ctx->rst) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "upstream sent frame for closed stream %ui",
+                              ctx->stream_id);
+                return NGX_ERROR;
+            }
+
+            ctx->rst = 1;
         }
 
         if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {