changeset 7646:2096b21fcd10

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
author Ruslan Ermilov <ru@nginx.com>
date Thu, 23 Apr 2020 15:10:24 +0300
parents ed3a10cf88e8
children 716eddd74bc2
files src/http/modules/ngx_http_grpc_module.c
diffstat 1 files changed, 20 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -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) {