changeset 8549:d70a38acaea0 quic

HTTP/3: skip unknown frames on request stream. As per HTTP/3 draft 29, section 4.1: Frames of unknown types (Section 9), including reserved frames (Section 7.2.8) MAY be sent on a request or push stream before, after, or interleaved with other frames described in this section. Also, trailers frame is now used as an indication of the request body end.
author Roman Arutyunyan <arut@nginx.com>
date Mon, 24 Aug 2020 09:56:36 +0300
parents 9ffef6054abf
children 234e9d89ff7f
files src/http/v3/ngx_http_v3_parse.c src/http/v3/ngx_http_v3_parse.h src/http/v3/ngx_http_v3_request.c
diffstat 3 files changed, 60 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/v3/ngx_http_v3_parse.c
+++ b/src/http/v3/ngx_http_v3_parse.c
@@ -155,7 +155,9 @@ ngx_http_v3_parse_headers(ngx_connection
     ngx_int_t  rc;
     enum {
         sw_start = 0,
+        sw_type,
         sw_length,
+        sw_skip,
         sw_prefix,
         sw_verify,
         sw_header_rep,
@@ -168,10 +170,18 @@ ngx_http_v3_parse_headers(ngx_connection
 
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers");
 
-        if (ch != NGX_HTTP_V3_FRAME_HEADERS) {
-            return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
+        st->state = sw_type;
+
+        /* fall through */
+
+    case sw_type:
+
+        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
+        if (rc != NGX_DONE) {
+            return rc;
         }
 
+        st->type = st->vlint.value;
         st->state = sw_length;
         break;
 
@@ -184,12 +194,26 @@ ngx_http_v3_parse_headers(ngx_connection
 
         st->length = st->vlint.value;
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse headers len:%ui", st->length);
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "http3 parse headers type:%ui, len:%ui",
+                       st->type, st->length);
+
+        if (st->type != NGX_HTTP_V3_FRAME_HEADERS) {
+            st->state = st->length > 0 ? sw_skip : sw_type;
+            break;
+        }
 
         st->state = sw_prefix;
         break;
 
+    case sw_skip:
+
+        if (--st->length == 0) {
+            st->state = sw_type;
+        }
+
+        break;
+
     case sw_prefix:
 
         if (st->length-- == 0) {
@@ -1529,7 +1553,8 @@ ngx_http_v3_parse_data(ngx_connection_t 
     enum {
         sw_start = 0,
         sw_type,
-        sw_length
+        sw_length,
+        sw_skip
     };
 
     switch (st->state) {
@@ -1549,8 +1574,11 @@ ngx_http_v3_parse_data(ngx_connection_t 
             return rc;
         }
 
-        if (st->vlint.value != NGX_HTTP_V3_FRAME_DATA) {
-            return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
+        st->type = st->vlint.value;
+
+        if (st->type == NGX_HTTP_V3_FRAME_HEADERS) {
+            /* trailers */
+            goto done;
         }
 
         st->state = sw_length;
@@ -1565,10 +1593,25 @@ ngx_http_v3_parse_data(ngx_connection_t 
 
         st->length = st->vlint.value;
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse data frame len:%ui", st->length);
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "http3 parse data type:%ui, len:%ui",
+                       st->type, st->length);
+
+        if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) {
+            st->state = sw_skip;
+            break;
+        }
 
-        goto done;
+        st->state = sw_type;
+        return NGX_OK;
+
+    case sw_skip:
+
+        if (--st->length == 0) {
+            st->state = sw_type;
+        }
+
+        break;
     }
 
     return NGX_AGAIN;
--- a/src/http/v3/ngx_http_v3_parse.h
+++ b/src/http/v3/ngx_http_v3_parse.h
@@ -76,6 +76,7 @@ typedef struct {
 
 typedef struct {
     ngx_uint_t                      state;
+    ngx_uint_t                      type;
     ngx_uint_t                      length;
     ngx_http_v3_parse_varlen_int_t  vlint;
     ngx_http_v3_parse_header_block_prefix_t  prefix;
@@ -107,6 +108,7 @@ typedef struct {
 
 typedef struct {
     ngx_uint_t                      state;
+    ngx_uint_t                      type;
     ngx_uint_t                      length;
     ngx_http_v3_parse_varlen_int_t  vlint;
 } ngx_http_v3_parse_data_t;
--- a/src/http/v3/ngx_http_v3_request.c
+++ b/src/http/v3/ngx_http_v3_request.c
@@ -418,7 +418,11 @@ ngx_http_v3_parse_request_body(ngx_http_
             continue;
         }
 
-        /* rc == NGX_DONE */
+        if (rc == NGX_DONE) {
+            return NGX_DONE;
+        }
+
+        /* rc == NGX_OK */
 
         ctx->size = st->length;
         ctx->state = sw_start;