[nginx] gRPC: RST_STREAM(NO_ERROR) handling (ticket #1792).

Ruslan Ermilov ru at nginx.com
Thu Apr 23 12:11:02 UTC 2020


details:   https://hg.nginx.org/nginx/rev/2096b21fcd10
branches:  
changeset: 7646:2096b21fcd10
user:      Ruslan Ermilov <ru at nginx.com>
date:      Thu Apr 23 15:10:24 2020 +0300
description:
gRPC: RST_STREAM(NO_ERROR) handling (ticket #1792).

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

diffstat:

 src/http/modules/ngx_http_grpc_module.c |  26 ++++++++++++++++++++------
 1 files changed, 20 insertions(+), 6 deletions(-)

diffs (57 lines):

diff -r ed3a10cf88e8 -r 2096b21fcd10 src/http/modules/ngx_http_grpc_module.c
--- a/src/http/modules/ngx_http_grpc_module.c	Thu Apr 23 15:10:21 2020 +0300
+++ b/src/http/modules/ngx_http_grpc_module.c	Thu Apr 23 15:10:24 2020 +0300
@@ -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_re
     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
                 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
                 return NGX_ERROR;
             }
 
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "upstream rejected request with error %ui",
-                          ctx->error);
-
-            return NGX_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;
+            }
+
+            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) {


More information about the nginx-devel mailing list