]> git.kaiwu.me - nginx.git/commitdiff
HTTP/3: bulk parse functions.
authorRoman Arutyunyan <arut@nginx.com>
Thu, 8 Jul 2021 18:52:47 +0000 (21:52 +0300)
committerRoman Arutyunyan <arut@nginx.com>
Thu, 8 Jul 2021 18:52:47 +0000 (21:52 +0300)
Previously HTTP/3 streams were parsed by one character.  Now all parse functions
receive buffers.  This should optimize parsing time and CPU load.

src/http/v3/ngx_http_v3_parse.c
src/http/v3/ngx_http_v3_parse.h
src/http/v3/ngx_http_v3_request.c
src/http/v3/ngx_http_v3_streams.c

index c5fc24482fb0ef6a4c002d981e7852806abb494d..3f676b4bb077698c5494d2b1cea4c3c2578049b9 100644 (file)
     ((type) == 0x02 || (type) == 0x06 || (type) == 0x08 || (type) == 0x09)
 
 
+static void ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc,
+    ngx_uint_t n);
+static void ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc,
+    ngx_uint_t *n);
+static ngx_int_t ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length);
+
 static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c,
-    ngx_http_v3_parse_varlen_int_t *st, u_char ch);
+    ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c,
-    ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch);
+    ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b);
 
 static ngx_int_t ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c,
-    ngx_http_v3_parse_field_section_prefix_t *st, u_char ch);
+    ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_rep(ngx_connection_t *c,
-    ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, u_char ch);
+    ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c,
-    ngx_http_v3_parse_literal_t *st, u_char ch);
+    ngx_http_v3_parse_literal_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_ri(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch);
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_lri(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch);
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_l(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch);
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_pbi(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch);
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_lpbi(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch);
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);
 
 static ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c,
-    ngx_http_v3_parse_control_t *st, u_char ch);
+    ngx_http_v3_parse_control_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c,
-    ngx_http_v3_parse_settings_t *st, u_char ch);
+    ngx_http_v3_parse_settings_t *st, ngx_buf_t *b);
 
 static ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c,
-    ngx_http_v3_parse_encoder_t *st, u_char ch);
+    ngx_http_v3_parse_encoder_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_inr(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch);
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);
 static ngx_int_t ngx_http_v3_parse_field_iln(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch);
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);
 
 static ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c,
-    ngx_http_v3_parse_decoder_t *st, u_char ch);
+    ngx_http_v3_parse_decoder_t *st, ngx_buf_t *b);
 
 static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c,
     ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value);
 
 
+static void
+ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t n)
+{
+    *loc = *b;
+
+    if ((size_t) (loc->last - loc->pos) > n) {
+        loc->last = loc->pos + n;
+    }
+}
+
+
+static void
+ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t *pn)
+{
+    *pn -= loc->pos - b->pos;
+    b->pos = loc->pos;
+}
+
+
+static ngx_int_t
+ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length)
+{
+    if ((size_t) (b->last - b->pos) < *length) {
+        *length -= b->last - b->pos;
+        b->pos = b->last;
+        return NGX_AGAIN;
+    }
+
+    b->pos += *length;
+    return NGX_DONE;
+}
+
+
 static ngx_int_t
 ngx_http_v3_parse_varlen_int(ngx_connection_t *c,
-    ngx_http_v3_parse_varlen_int_t *st, u_char ch)
+    ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b)
 {
+    u_char  ch;
     enum {
         sw_start = 0,
         sw_length_2,
@@ -70,57 +110,64 @@ ngx_http_v3_parse_varlen_int(ngx_connection_t *c,
         sw_length_8
     };
 
-    switch (st->state) {
-
-    case sw_start:
+    for ( ;; ) {
 
-        st->value = ch;
-        if (st->value & 0xc0) {
-            st->state = sw_length_2;
-            break;
+        if (b->pos == b->last) {
+            return NGX_AGAIN;
         }
 
-        goto done;
+        ch = *b->pos++;
+
+        switch (st->state) {
+
+        case sw_start:
 
-    case sw_length_2:
+            st->value = ch;
+            if (st->value & 0xc0) {
+                st->state = sw_length_2;
+                break;
+            }
 
-        st->value = (st->value << 8) + ch;
-        if ((st->value & 0xc000) == 0x4000) {
-            st->value &= 0x3fff;
             goto done;
-        }
 
-        st->state = sw_length_3;
-        break;
+        case sw_length_2:
 
-    case sw_length_4:
+            st->value = (st->value << 8) + ch;
+            if ((st->value & 0xc000) == 0x4000) {
+                st->value &= 0x3fff;
+                goto done;
+            }
 
-        st->value = (st->value << 8) + ch;
-        if ((st->value & 0xc0000000) == 0x80000000) {
-            st->value &= 0x3fffffff;
-            goto done;
-        }
+            st->state = sw_length_3;
+            break;
 
-        st->state = sw_length_5;
-        break;
+        case sw_length_4:
 
-    case sw_length_3:
-    case sw_length_5:
-    case sw_length_6:
-    case sw_length_7:
+            st->value = (st->value << 8) + ch;
+            if ((st->value & 0xc0000000) == 0x80000000) {
+                st->value &= 0x3fffffff;
+                goto done;
+            }
 
-        st->value = (st->value << 8) + ch;
-        st->state++;
-        break;
+            st->state = sw_length_5;
+            break;
 
-    case sw_length_8:
+        case sw_length_3:
+        case sw_length_5:
+        case sw_length_6:
+        case sw_length_7:
 
-        st->value = (st->value << 8) + ch;
-        st->value &= 0x3fffffffffffffff;
-        goto done;
-    }
+            st->value = (st->value << 8) + ch;
+            st->state++;
+            break;
+
+        case sw_length_8:
 
-    return NGX_AGAIN;
+            st->value = (st->value << 8) + ch;
+            st->value &= 0x3fffffffffffffff;
+            goto done;
+        }
+    }
 
 done:
 
@@ -134,50 +181,58 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_prefix_int(ngx_connection_t *c,
-    ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, u_char ch)
+    ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b)
 {
+    u_char      ch;
     ngx_uint_t  mask;
     enum {
         sw_start = 0,
         sw_value
     };
 
-    switch (st->state) {
-
-    case sw_start:
-
-        mask = (1 << prefix) - 1;
-        st->value = ch & mask;
+    for ( ;; ) {
 
-        if (st->value != mask) {
-            goto done;
+        if (b->pos == b->last) {
+            return NGX_AGAIN;
         }
 
-        st->shift = 0;
-        st->state = sw_value;
-        break;
+        ch = *b->pos++;
 
-    case sw_value:
+        switch (st->state) {
 
-        st->value += (uint64_t) (ch & 0x7f) << st->shift;
+        case sw_start:
 
-        if (st->shift == 56
-            && ((ch & 0x80) || (st->value & 0xc000000000000000)))
-        {
-            ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                          "client exceeded integer size limit");
-            return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD;
-        }
+            mask = (1 << prefix) - 1;
+            st->value = ch & mask;
 
-        if (ch & 0x80) {
-            st->shift += 7;
+            if (st->value != mask) {
+                goto done;
+            }
+
+            st->shift = 0;
+            st->state = sw_value;
             break;
-        }
 
-        goto done;
-    }
+        case sw_value:
+
+            st->value += (uint64_t) (ch & 0x7f) << st->shift;
+
+            if (st->shift == 56
+                && ((ch & 0x80) || (st->value & 0xc000000000000000)))
+            {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client exceeded integer size limit");
+                return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD;
+            }
 
-    return NGX_AGAIN;
+            if (ch & 0x80) {
+                st->shift += 7;
+                break;
+            }
+
+            goto done;
+        }
+    }
 
 done:
 
@@ -191,8 +246,9 @@ done:
 
 ngx_int_t
 ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
+    ngx_buf_t  loc;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -205,118 +261,131 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st,
         sw_done
     };
 
-    switch (st->state) {
+    for ( ;; ) {
+
+        switch (st->state) {
+
+        case sw_start:
 
-    case sw_start:
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse headers");
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers");
+            st->state = sw_type;
 
-        st->state = sw_type;
+            /* fall through */
 
-        /* fall through */
+        case sw_type:
 
-    case sw_type:
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->type = st->vlint.value;
 
-        st->type = st->vlint.value;
-
-        if (ngx_http_v3_is_v2_frame(st->type)
-            || st->type == NGX_HTTP_V3_FRAME_DATA
-            || st->type == NGX_HTTP_V3_FRAME_GOAWAY
-            || st->type == NGX_HTTP_V3_FRAME_SETTINGS
-            || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID
-            || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH
-            || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)
-        {
-            return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
-        }
+            if (ngx_http_v3_is_v2_frame(st->type)
+                || st->type == NGX_HTTP_V3_FRAME_DATA
+                || st->type == NGX_HTTP_V3_FRAME_GOAWAY
+                || st->type == NGX_HTTP_V3_FRAME_SETTINGS
+                || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID
+                || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH
+                || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)
+            {
+                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
+            }
 
-        st->state = sw_length;
-        break;
+            st->state = sw_length;
+            break;
 
-    case sw_length:
+        case sw_length:
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        st->length = st->vlint.value;
+            st->length = st->vlint.value;
 
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse headers type:%ui, len:%ui",
-                       st->type, 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;
-        }
+            if (st->type != NGX_HTTP_V3_FRAME_HEADERS) {
+                st->state = st->length > 0 ? sw_skip : sw_type;
+                break;
+            }
 
-        if (st->length == 0) {
-            return NGX_HTTP_V3_ERR_FRAME_ERROR;
-        }
+            if (st->length == 0) {
+                return NGX_HTTP_V3_ERR_FRAME_ERROR;
+            }
 
-        st->state = sw_prefix;
-        break;
+            st->state = sw_prefix;
+            break;
 
-    case sw_skip:
+        case sw_skip:
+
+            rc = ngx_http_v3_parse_skip(b, &st->length);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        if (--st->length == 0) {
             st->state = sw_type;
-        }
+            break;
 
-        break;
+        case sw_prefix:
 
-    case sw_prefix:
+            ngx_http_v3_parse_start_local(b, &loc, st->length);
 
-        if (--st->length == 0) {
-            return NGX_HTTP_V3_ERR_FRAME_ERROR;
-        }
+            rc = ngx_http_v3_parse_field_section_prefix(c, &st->prefix, &loc);
 
-        rc = ngx_http_v3_parse_field_section_prefix(c, &st->prefix, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            ngx_http_v3_parse_end_local(b, &loc, &st->length);
 
-        st->state = sw_verify;
-        break;
+            if (st->length == 0 && rc == NGX_AGAIN) {
+                return NGX_HTTP_V3_ERR_FRAME_ERROR;
+            }
 
-    case sw_verify:
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            st->state = sw_verify;
+            break;
 
-        st->state = sw_field_rep;
+        case sw_verify:
 
-        /* fall through */
+            rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-    case sw_field_rep:
+            st->state = sw_field_rep;
 
-        rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base,
-                                         ch);
+            /* fall through */
 
-        if (--st->length == 0 && rc == NGX_AGAIN) {
-            return NGX_HTTP_V3_ERR_FRAME_ERROR;
-        }
+        case sw_field_rep:
 
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            ngx_http_v3_parse_start_local(b, &loc, st->length);
 
-        if (st->length == 0) {
-            goto done;
-        }
+            rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base,
+                                             &loc);
 
-        return NGX_OK;
-    }
+            ngx_http_v3_parse_end_local(b, &loc, &st->length);
+
+            if (st->length == 0 && rc == NGX_AGAIN) {
+                return NGX_HTTP_V3_ERR_FRAME_ERROR;
+            }
+
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            if (st->length == 0) {
+                goto done;
+            }
 
-    return NGX_AGAIN;
+            return NGX_OK;
+        }
+    }
 
 done:
 
@@ -335,8 +404,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c,
-    ngx_http_v3_parse_field_section_prefix_t *st, u_char ch)
+    ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -345,47 +415,54 @@ ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c,
         sw_read_delta_base
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse field section prefix");
+        case sw_start:
+
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse field section prefix");
 
-        st->state = sw_req_insert_count;
+            st->state = sw_req_insert_count;
 
-        /* fall through */
+            /* fall through */
 
-    case sw_req_insert_count:
+        case sw_req_insert_count:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 8, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 8, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        st->insert_count = st->pint.value;
-        st->state = sw_delta_base;
-        break;
+            st->insert_count = st->pint.value;
+            st->state = sw_delta_base;
+            break;
 
-    case sw_delta_base:
+        case sw_delta_base:
 
-        st->sign = (ch & 0x80) ? 1 : 0;
-        st->state = sw_read_delta_base;
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-        /* fall through */
+            ch = *b->pos;
 
-    case sw_read_delta_base:
+            st->sign = (ch & 0x80) ? 1 : 0;
+            st->state = sw_read_delta_base;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            /* fall through */
 
-        st->delta_base = st->pint.value;
-        goto done;
-    }
+        case sw_read_delta_base:
 
-    return NGX_AGAIN;
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->delta_base = st->pint.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -412,8 +489,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_rep(ngx_connection_t *c,
-    ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, u_char ch)
+    ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -429,6 +507,12 @@ ngx_http_v3_parse_field_rep(ngx_connection_t *c,
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http3 parse field representation");
 
+        if (b->pos == b->last) {
+            return NGX_AGAIN;
+        }
+
+        ch = *b->pos;
+
         ngx_memzero(&st->field, sizeof(ngx_http_v3_parse_field_t));
 
         st->field.base = base;
@@ -463,23 +547,23 @@ ngx_http_v3_parse_field_rep(ngx_connection_t *c,
     switch (st->state) {
 
     case sw_field_ri:
-        rc = ngx_http_v3_parse_field_ri(c, &st->field, ch);
+        rc = ngx_http_v3_parse_field_ri(c, &st->field, b);
         break;
 
     case sw_field_lri:
-        rc = ngx_http_v3_parse_field_lri(c, &st->field, ch);
+        rc = ngx_http_v3_parse_field_lri(c, &st->field, b);
         break;
 
     case sw_field_l:
-        rc = ngx_http_v3_parse_field_l(c, &st->field, ch);
+        rc = ngx_http_v3_parse_field_l(c, &st->field, b);
         break;
 
     case sw_field_pbi:
-        rc = ngx_http_v3_parse_field_pbi(c, &st->field, ch);
+        rc = ngx_http_v3_parse_field_pbi(c, &st->field, b);
         break;
 
     case sw_field_lpbi:
-        rc = ngx_http_v3_parse_field_lpbi(c, &st->field, ch);
+        rc = ngx_http_v3_parse_field_lpbi(c, &st->field, b);
         break;
 
     default:
@@ -500,8 +584,9 @@ ngx_http_v3_parse_field_rep(ngx_connection_t *c,
 
 static ngx_int_t
 ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
+    u_char                     ch;
     ngx_uint_t                 n;
     ngx_http_core_srv_conf_t  *cscf;
     enum {
@@ -509,63 +594,70 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st,
         sw_value
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse literal huff:%ui, len:%ui",
-                       st->huffman, st->length);
+        case sw_start:
 
-        n = st->length;
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse literal huff:%ui, len:%ui",
+                           st->huffman, st->length);
 
-        cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module);
+            n = st->length;
 
-        if (n > cscf->large_client_header_buffers.size) {
-            ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                          "client sent too large field line");
-            return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD;
-        }
+            cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module);
 
-        if (st->huffman) {
-            n = n * 8 / 5;
-            st->huffstate = 0;
-        }
+            if (n > cscf->large_client_header_buffers.size) {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client sent too large field line");
+                return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD;
+            }
 
-        st->last = ngx_pnalloc(c->pool, n + 1);
-        if (st->last == NULL) {
-            return NGX_ERROR;
-        }
+            if (st->huffman) {
+                n = n * 8 / 5;
+                st->huffstate = 0;
+            }
+
+            st->last = ngx_pnalloc(c->pool, n + 1);
+            if (st->last == NULL) {
+                return NGX_ERROR;
+            }
 
-        st->value.data = st->last;
-        st->state = sw_value;
+            st->value.data = st->last;
+            st->state = sw_value;
 
-        /* fall through */
+            /* fall through */
 
-    case sw_value:
+        case sw_value:
 
-        if (st->huffman) {
-            if (ngx_http_v2_huff_decode(&st->huffstate, &ch, 1, &st->last,
-                                        st->length == 1, c->log)
-                != NGX_OK)
-            {
-                return NGX_ERROR;
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
             }
 
-        } else {
-            *st->last++ = ch;
-        }
+            ch = *b->pos++;
 
-        if (--st->length) {
-            break;
-        }
+            if (st->huffman) {
+                if (ngx_http_v2_huff_decode(&st->huffstate, &ch, 1, &st->last,
+                                            st->length == 1, c->log)
+                    != NGX_OK)
+                {
+                    return NGX_ERROR;
+                }
 
-        st->value.len = st->last - st->value.data;
-        *st->last = '\0';
-        goto done;
-    }
+            } else {
+                *st->last++ = ch;
+            }
+
+            if (--st->length) {
+                break;
+            }
 
-    return NGX_AGAIN;
+            st->value.len = st->last - st->value.data;
+            *st->last = '\0';
+            goto done;
+        }
+    }
 
 done:
 
@@ -579,37 +671,46 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_ri(ngx_connection_t *c, ngx_http_v3_parse_field_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
         sw_index
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field ri");
+        case sw_start:
 
-        st->dynamic = (ch & 0x40) ? 0 : 1;
-        st->state = sw_index;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse field ri");
 
-        /* fall through */
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-    case sw_index:
+            ch = *b->pos;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->dynamic = (ch & 0x40) ? 0 : 1;
+            st->state = sw_index;
 
-        st->index = st->pint.value;
-        goto done;
-    }
+            /* fall through */
 
-    return NGX_AGAIN;
+        case sw_index:
+
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->index = st->pint.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -634,8 +735,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_lri(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch)
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -645,62 +747,76 @@ ngx_http_v3_parse_field_lri(ngx_connection_t *c,
         sw_value
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field lri");
+        case sw_start:
 
-        st->dynamic = (ch & 0x10) ? 0 : 1;
-        st->state = sw_index;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse field lri");
 
-        /* fall through */
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-    case sw_index:
+            ch = *b->pos;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->dynamic = (ch & 0x10) ? 0 : 1;
+            st->state = sw_index;
 
-        st->index = st->pint.value;
-        st->state = sw_value_len;
-        break;
+            /* fall through */
 
-    case sw_value_len:
+        case sw_index:
 
-        st->literal.huffman = (ch & 0x80) ? 1 : 0;
-        st->state = sw_read_value_len;
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        /* fall through */
+            st->index = st->pint.value;
+            st->state = sw_value_len;
+            break;
 
-    case sw_read_value_len:
+        case sw_value_len:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-        st->literal.length = st->pint.value;
-        if (st->literal.length == 0) {
-            goto done;
-        }
+            ch = *b->pos;
 
-        st->state = sw_value;
-        break;
+            st->literal.huffman = (ch & 0x80) ? 1 : 0;
+            st->state = sw_read_value_len;
 
-    case sw_value:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_literal(c, &st->literal, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_read_value_len:
 
-        st->value = st->literal.value;
-        goto done;
-    }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->literal.length = st->pint.value;
+            if (st->literal.length == 0) {
+                goto done;
+            }
+
+            st->state = sw_value;
+            break;
+
+        case sw_value:
+
+            rc = ngx_http_v3_parse_literal(c, &st->literal, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    return NGX_AGAIN;
+            st->value = st->literal.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -725,8 +841,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_l(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch)
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -737,77 +854,90 @@ ngx_http_v3_parse_field_l(ngx_connection_t *c,
         sw_value
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field l");
+        case sw_start:
 
-        st->literal.huffman = (ch & 0x08) ? 1 : 0;
-        st->state = sw_name_len;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field l");
 
-        /* fall through */
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-    case sw_name_len:
+            ch = *b->pos;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->literal.huffman = (ch & 0x08) ? 1 : 0;
+            st->state = sw_name_len;
 
-        st->literal.length = st->pint.value;
-        if (st->literal.length == 0) {
-            return NGX_ERROR;
-        }
+            /* fall through */
 
-        st->state = sw_name;
-        break;
+        case sw_name_len:
 
-    case sw_name:
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_parse_literal(c, &st->literal, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->literal.length = st->pint.value;
+            if (st->literal.length == 0) {
+                return NGX_ERROR;
+            }
 
-        st->name = st->literal.value;
-        st->state = sw_value_len;
-        break;
+            st->state = sw_name;
+            break;
 
-    case sw_value_len:
+        case sw_name:
 
-        st->literal.huffman = (ch & 0x80) ? 1 : 0;
-        st->state = sw_read_value_len;
+            rc = ngx_http_v3_parse_literal(c, &st->literal, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        /* fall through */
+            st->name = st->literal.value;
+            st->state = sw_value_len;
+            break;
 
-    case sw_read_value_len:
+        case sw_value_len:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-        st->literal.length = st->pint.value;
-        if (st->literal.length == 0) {
-            goto done;
-        }
+            ch = *b->pos;
 
-        st->state = sw_value;
-        break;
+            st->literal.huffman = (ch & 0x80) ? 1 : 0;
+            st->state = sw_read_value_len;
 
-    case sw_value:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_literal(c, &st->literal, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_read_value_len:
 
-        st->value = st->literal.value;
-        goto done;
-    }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->literal.length = st->pint.value;
+            if (st->literal.length == 0) {
+                goto done;
+            }
+
+            st->state = sw_value;
+            break;
+
+        case sw_value:
+
+            rc = ngx_http_v3_parse_literal(c, &st->literal, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    return NGX_AGAIN;
+            st->value = st->literal.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -822,7 +952,7 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_pbi(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch)
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)
 {
     ngx_int_t  rc;
     enum {
@@ -830,28 +960,30 @@ ngx_http_v3_parse_field_pbi(ngx_connection_t *c,
         sw_index
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field pbi");
+        case sw_start:
 
-        st->state = sw_index;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse field pbi");
 
-        /* fall through */
+            st->state = sw_index;
 
-    case sw_index:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_index:
 
-        st->index = st->pint.value;
-        goto done;
-    }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    return NGX_AGAIN;
+            st->index = st->pint.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -871,8 +1003,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_lpbi(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch)
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -882,62 +1015,69 @@ ngx_http_v3_parse_field_lpbi(ngx_connection_t *c,
         sw_value
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse field lpbi");
+        case sw_start:
 
-        st->state = sw_index;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse field lpbi");
 
-        /* fall through */
+            st->state = sw_index;
 
-    case sw_index:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_index:
 
-        st->index = st->pint.value;
-        st->state = sw_value_len;
-        break;
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    case sw_value_len:
+            st->index = st->pint.value;
+            st->state = sw_value_len;
+            break;
 
-        st->literal.huffman = (ch & 0x80) ? 1 : 0;
-        st->state = sw_read_value_len;
+        case sw_value_len:
 
-        /* fall through */
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-    case sw_read_value_len:
+            ch = *b->pos;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->literal.huffman = (ch & 0x80) ? 1 : 0;
+            st->state = sw_read_value_len;
 
-        st->literal.length = st->pint.value;
-        if (st->literal.length == 0) {
-            goto done;
-        }
+            /* fall through */
 
-        st->state = sw_value;
-        break;
+        case sw_read_value_len:
+
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    case sw_value:
+            st->literal.length = st->pint.value;
+            if (st->literal.length == 0) {
+                goto done;
+            }
 
-        rc = ngx_http_v3_parse_literal(c, &st->literal, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->state = sw_value;
+            break;
 
-        st->value = st->literal.value;
-        goto done;
-    }
+        case sw_value:
+
+            rc = ngx_http_v3_parse_literal(c, &st->literal, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    return NGX_AGAIN;
+            st->value = st->literal.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -1001,8 +1141,9 @@ ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic,
 
 static ngx_int_t
 ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
+    ngx_buf_t  loc;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -1016,188 +1157,208 @@ ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st,
         sw_skip
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse control");
+        case sw_start:
 
-        st->state = sw_first_type;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse control");
 
-        /* fall through */
+            st->state = sw_first_type;
 
-    case sw_first_type:
-    case sw_type:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_first_type:
+        case sw_type:
 
-        st->type = st->vlint.value;
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse frame type:%ui", st->type);
+            st->type = st->vlint.value;
 
-        if (st->state == sw_first_type
-            && st->type != NGX_HTTP_V3_FRAME_SETTINGS)
-        {
-            return NGX_HTTP_V3_ERR_MISSING_SETTINGS;
-        }
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse frame type:%ui", st->type);
 
-        if (st->state != sw_first_type
-            && st->type == NGX_HTTP_V3_FRAME_SETTINGS)
-        {
-            return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
-        }
+            if (st->state == sw_first_type
+                && st->type != NGX_HTTP_V3_FRAME_SETTINGS)
+            {
+                return NGX_HTTP_V3_ERR_MISSING_SETTINGS;
+            }
 
-        if (ngx_http_v3_is_v2_frame(st->type)
-            || st->type == NGX_HTTP_V3_FRAME_DATA
-            || st->type == NGX_HTTP_V3_FRAME_HEADERS
-            || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)
-        {
-            return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
-        }
+            if (st->state != sw_first_type
+                && st->type == NGX_HTTP_V3_FRAME_SETTINGS)
+            {
+                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
+            }
 
-        st->state = sw_length;
-        break;
+            if (ngx_http_v3_is_v2_frame(st->type)
+                || st->type == NGX_HTTP_V3_FRAME_DATA
+                || st->type == NGX_HTTP_V3_FRAME_HEADERS
+                || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)
+            {
+                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
+            }
 
-    case sw_length:
+            st->state = sw_length;
+            break;
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_length:
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse frame len:%uL", st->vlint.value);
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        st->length = st->vlint.value;
-        if (st->length == 0) {
-            st->state = sw_type;
-            break;
-        }
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse frame len:%uL", st->vlint.value);
 
-        switch (st->type) {
+            st->length = st->vlint.value;
+            if (st->length == 0) {
+                st->state = sw_type;
+                break;
+            }
 
-        case NGX_HTTP_V3_FRAME_CANCEL_PUSH:
-            st->state = sw_cancel_push;
-            break;
+            switch (st->type) {
 
-        case NGX_HTTP_V3_FRAME_SETTINGS:
-            st->state = sw_settings;
-            break;
+            case NGX_HTTP_V3_FRAME_CANCEL_PUSH:
+                st->state = sw_cancel_push;
+                break;
 
-        case NGX_HTTP_V3_FRAME_MAX_PUSH_ID:
-            st->state = sw_max_push_id;
-            break;
+            case NGX_HTTP_V3_FRAME_SETTINGS:
+                st->state = sw_settings;
+                break;
+
+            case NGX_HTTP_V3_FRAME_MAX_PUSH_ID:
+                st->state = sw_max_push_id;
+                break;
+
+            case NGX_HTTP_V3_FRAME_GOAWAY:
+                st->state = sw_goaway;
+                break;
+
+            default:
+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                               "http3 parse skip unknown frame");
+                st->state = sw_skip;
+            }
 
-        case NGX_HTTP_V3_FRAME_GOAWAY:
-            st->state = sw_goaway;
             break;
 
-        default:
-            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                           "http3 parse skip unknown frame");
-            st->state = sw_skip;
-        }
+        case sw_cancel_push:
 
-        break;
+            ngx_http_v3_parse_start_local(b, &loc, st->length);
 
-    case sw_cancel_push:
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc);
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
+            ngx_http_v3_parse_end_local(b, &loc, &st->length);
 
-        if (--st->length == 0 && rc == NGX_AGAIN) {
-            return NGX_HTTP_V3_ERR_FRAME_ERROR;
-        }
+            if (st->length == 0 && rc == NGX_AGAIN) {
+                return NGX_HTTP_V3_ERR_FRAME_ERROR;
+            }
 
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_cancel_push(c, st->vlint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            rc = ngx_http_v3_cancel_push(c, st->vlint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-        st->state = sw_type;
-        break;
+            st->state = sw_type;
+            break;
 
-    case sw_settings:
+        case sw_settings:
 
-        rc = ngx_http_v3_parse_settings(c, &st->settings, ch);
+            ngx_http_v3_parse_start_local(b, &loc, st->length);
 
-        if (--st->length == 0 && rc == NGX_AGAIN) {
-            return NGX_HTTP_V3_ERR_SETTINGS_ERROR;
-        }
+            rc = ngx_http_v3_parse_settings(c, &st->settings, &loc);
 
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            ngx_http_v3_parse_end_local(b, &loc, &st->length);
 
-        if (st->length == 0) {
-            st->state = sw_type;
-        }
+            if (st->length == 0 && rc == NGX_AGAIN) {
+                return NGX_HTTP_V3_ERR_SETTINGS_ERROR;
+            }
 
-        break;
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    case sw_max_push_id:
+            if (st->length == 0) {
+                st->state = sw_type;
+            }
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
+            break;
 
-        if (--st->length == 0 && rc == NGX_AGAIN) {
-            return NGX_HTTP_V3_ERR_FRAME_ERROR;
-        }
+        case sw_max_push_id:
 
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            ngx_http_v3_parse_start_local(b, &loc, st->length);
 
-        rc = ngx_http_v3_set_max_push_id(c, st->vlint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc);
 
-        st->state = sw_type;
-        break;
+            ngx_http_v3_parse_end_local(b, &loc, &st->length);
+
+            if (st->length == 0 && rc == NGX_AGAIN) {
+                return NGX_HTTP_V3_ERR_FRAME_ERROR;
+            }
 
-    case sw_goaway:
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
+            rc = ngx_http_v3_set_max_push_id(c, st->vlint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-        if (--st->length == 0 && rc == NGX_AGAIN) {
-            return NGX_HTTP_V3_ERR_FRAME_ERROR;
-        }
+            st->state = sw_type;
+            break;
 
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_goaway:
 
-        rc = ngx_http_v3_goaway(c, st->vlint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            ngx_http_v3_parse_start_local(b, &loc, st->length);
 
-        st->state = sw_type;
-        break;
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc);
 
-    case sw_skip:
+            ngx_http_v3_parse_end_local(b, &loc, &st->length);
+
+            if (st->length == 0 && rc == NGX_AGAIN) {
+                return NGX_HTTP_V3_ERR_FRAME_ERROR;
+            }
+
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            rc = ngx_http_v3_goaway(c, st->vlint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-        if (--st->length == 0) {
             st->state = sw_type;
-        }
+            break;
 
-        break;
-    }
+        case sw_skip:
 
-    return NGX_AGAIN;
+            rc = ngx_http_v3_parse_skip(b, &st->length);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->state = sw_type;
+            break;
+        }
+    }
 }
 
 
 static ngx_int_t
 ngx_http_v3_parse_settings(ngx_connection_t *c,
-    ngx_http_v3_parse_settings_t *st, u_char ch)
+    ngx_http_v3_parse_settings_t *st, ngx_buf_t *b)
 {
     ngx_int_t  rc;
     enum {
@@ -1206,42 +1367,44 @@ ngx_http_v3_parse_settings(ngx_connection_t *c,
         sw_value
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse settings");
+        case sw_start:
 
-        st->state = sw_id;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse settings");
 
-        /* fall through */
+            st->state = sw_id;
 
-    case sw_id:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_id:
 
-        st->id = st->vlint.value;
-        st->state = sw_value;
-        break;
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    case sw_value:
+            st->id = st->vlint.value;
+            st->state = sw_value;
+            break;
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_value:
 
-        if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) {
-            return NGX_HTTP_V3_ERR_SETTINGS_ERROR;
-        }
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        goto done;
-    }
+            if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) {
+                return NGX_HTTP_V3_ERR_SETTINGS_ERROR;
+            }
 
-    return NGX_AGAIN;
+            goto done;
+        }
+    }
 
 done:
 
@@ -1254,8 +1417,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -1265,98 +1429,102 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st,
         sw_duplicate
     };
 
-    if (st->state == sw_start) {
+    for ( ;; ) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse encoder instruction");
+        if (st->state == sw_start) {
 
-        if (ch & 0x80) {
-            /* Insert With Name Reference */
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse encoder instruction");
 
-            st->state = sw_inr;
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-        } else if (ch & 0x40) {
-            /* Insert With Literal Name */
+            ch = *b->pos;
 
-            st->state = sw_iln;
+            if (ch & 0x80) {
+                /* Insert With Name Reference */
 
-        } else if (ch & 0x20) {
-            /* Set Dynamic Table Capacity */
+                st->state = sw_inr;
 
-            st->state = sw_capacity;
+            } else if (ch & 0x40) {
+                /* Insert With Literal Name */
 
-        } else {
-            /* Duplicate */
+                st->state = sw_iln;
 
-            st->state = sw_duplicate;
-        }
-    }
+            } else if (ch & 0x20) {
+                /* Set Dynamic Table Capacity */
 
-    switch (st->state) {
+                st->state = sw_capacity;
 
-    case sw_inr:
+            } else {
+                /* Duplicate */
 
-        rc = ngx_http_v3_parse_field_inr(c, &st->field, ch);
-        if (rc != NGX_DONE) {
-            return rc;
+                st->state = sw_duplicate;
+            }
         }
 
-        goto done;
-
-    case sw_iln:
+        switch (st->state) {
 
-        rc = ngx_http_v3_parse_field_iln(c, &st->field, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_inr:
 
-        goto done;
+            rc = ngx_http_v3_parse_field_inr(c, &st->field, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    case sw_capacity:
+            st->state = sw_start;
+            break;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_iln:
 
-        rc = ngx_http_v3_set_capacity(c, st->pint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            rc = ngx_http_v3_parse_field_iln(c, &st->field, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        goto done;
+            st->state = sw_start;
+            break;
 
-    case sw_duplicate:
+        case sw_capacity:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_duplicate(c, st->pint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            rc = ngx_http_v3_set_capacity(c, st->pint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-        goto done;
-    }
+            st->state = sw_start;
+            break;
 
-    return NGX_AGAIN;
+        default: /* sw_duplicate */
 
-done:
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                   "http3 parse encoder instruction done");
+            rc = ngx_http_v3_duplicate(c, st->pint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-    st->state = sw_start;
-    return NGX_AGAIN;
+            st->state = sw_start;
+            break;
+        }
+    }
 }
 
 
 static ngx_int_t
 ngx_http_v3_parse_field_inr(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch)
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -1366,63 +1534,77 @@ ngx_http_v3_parse_field_inr(ngx_connection_t *c,
         sw_value
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse field inr");
+        case sw_start:
 
-        st->dynamic = (ch & 0x40) ? 0 : 1;
-        st->state = sw_name_index;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse field inr");
 
-        /* fall through */
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-    case sw_name_index:
+            ch = *b->pos;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->dynamic = (ch & 0x40) ? 0 : 1;
+            st->state = sw_name_index;
 
-        st->index = st->pint.value;
-        st->state = sw_value_len;
-        break;
+            /* fall through */
 
-    case sw_value_len:
+        case sw_name_index:
 
-        st->literal.huffman = (ch & 0x80) ? 1 : 0;
-        st->state = sw_read_value_len;
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        /* fall through */
+            st->index = st->pint.value;
+            st->state = sw_value_len;
+            break;
 
-    case sw_read_value_len:
+        case sw_value_len:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-        st->literal.length = st->pint.value;
-        if (st->literal.length == 0) {
-            st->value.len = 0;
-            goto done;
-        }
+            ch = *b->pos;
 
-        st->state = sw_value;
-        break;
+            st->literal.huffman = (ch & 0x80) ? 1 : 0;
+            st->state = sw_read_value_len;
 
-    case sw_value:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_literal(c, &st->literal, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_read_value_len:
 
-        st->value = st->literal.value;
-        goto done;
-    }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->literal.length = st->pint.value;
+            if (st->literal.length == 0) {
+                st->value.len = 0;
+                goto done;
+            }
 
-    return NGX_AGAIN;
+            st->state = sw_value;
+            break;
+
+        case sw_value:
+
+            rc = ngx_http_v3_parse_literal(c, &st->literal, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->value = st->literal.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -1443,8 +1625,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_field_iln(ngx_connection_t *c,
-    ngx_http_v3_parse_field_t *st, u_char ch)
+    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -1455,79 +1638,92 @@ ngx_http_v3_parse_field_iln(ngx_connection_t *c,
         sw_value
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse field iln");
+        case sw_start:
 
-        st->literal.huffman = (ch & 0x20) ? 1 : 0;
-        st->state = sw_name_len;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse field iln");
 
-        /* fall through */
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-    case sw_name_len:
+            ch = *b->pos;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->literal.huffman = (ch & 0x20) ? 1 : 0;
+            st->state = sw_name_len;
 
-        st->literal.length = st->pint.value;
-        if (st->literal.length == 0) {
-            return NGX_ERROR;
-        }
+            /* fall through */
 
-        st->state = sw_name;
-        break;
+        case sw_name_len:
 
-    case sw_name:
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_parse_literal(c, &st->literal, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            st->literal.length = st->pint.value;
+            if (st->literal.length == 0) {
+                return NGX_ERROR;
+            }
 
-        st->name = st->literal.value;
-        st->state = sw_value_len;
-        break;
+            st->state = sw_name;
+            break;
 
-    case sw_value_len:
+        case sw_name:
 
-        st->literal.huffman = (ch & 0x80) ? 1 : 0;
-        st->state = sw_read_value_len;
+            rc = ngx_http_v3_parse_literal(c, &st->literal, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        /* fall through */
+            st->name = st->literal.value;
+            st->state = sw_value_len;
+            break;
 
-    case sw_read_value_len:
+        case sw_value_len:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-        st->literal.length = st->pint.value;
-        if (st->literal.length == 0) {
-            st->value.len = 0;
-            goto done;
-        }
+            ch = *b->pos;
 
-        st->state = sw_value;
-        break;
+            st->literal.huffman = (ch & 0x80) ? 1 : 0;
+            st->state = sw_read_value_len;
 
-    case sw_value:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_literal(c, &st->literal, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_read_value_len:
 
-        st->value = st->literal.value;
-        goto done;
-    }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->literal.length = st->pint.value;
+            if (st->literal.length == 0) {
+                st->value.len = 0;
+                goto done;
+            }
+
+            st->state = sw_value;
+            break;
+
+        case sw_value:
 
-    return NGX_AGAIN;
+            rc = ngx_http_v3_parse_literal(c, &st->literal, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
+
+            st->value = st->literal.value;
+            goto done;
+        }
+    }
 
 done:
 
@@ -1547,8 +1743,9 @@ done:
 
 static ngx_int_t
 ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
+    u_char     ch;
     ngx_int_t  rc;
     enum {
         sw_start = 0,
@@ -1557,88 +1754,90 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st,
         sw_inc_insert_count
     };
 
-    if (st->state == sw_start) {
-
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse decoder instruction");
+    for ( ;; ) {
 
-        if (ch & 0x80) {
-            /* Section Acknowledgment */
+        if (st->state == sw_start) {
 
-            st->state = sw_ack_section;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse decoder instruction");
 
-        } else if (ch & 0x40) {
-            /*  Stream Cancellation */
+            if (b->pos == b->last) {
+                return NGX_AGAIN;
+            }
 
-            st->state = sw_cancel_stream;
+            ch = *b->pos;
 
-        }  else {
-            /*  Insert Count Increment */
+            if (ch & 0x80) {
+                /* Section Acknowledgment */
 
-            st->state = sw_inc_insert_count;
-        }
-    }
+                st->state = sw_ack_section;
 
-    switch (st->state) {
+            } else if (ch & 0x40) {
+                /*  Stream Cancellation */
 
-    case sw_ack_section:
+                st->state = sw_cancel_stream;
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            }  else {
+                /*  Insert Count Increment */
 
-        rc = ngx_http_v3_ack_section(c, st->pint.value);
-        if (rc != NGX_OK) {
-            return rc;
+                st->state = sw_inc_insert_count;
+            }
         }
 
-        goto done;
+        switch (st->state) {
 
-    case sw_cancel_stream:
+        case sw_ack_section:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_cancel_stream(c, st->pint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            rc = ngx_http_v3_ack_section(c, st->pint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-        goto done;
+            st->state = sw_start;
+            break;
 
-    case sw_inc_insert_count:
+        case sw_cancel_stream:
 
-        rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        rc = ngx_http_v3_inc_insert_count(c, st->pint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            rc = ngx_http_v3_cancel_stream(c, st->pint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-        goto done;
-    }
+            st->state = sw_start;
+            break;
 
-    return NGX_AGAIN;
+        case sw_inc_insert_count:
 
-done:
+            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                   "http3 parse decoder instruction done");
+            rc = ngx_http_v3_inc_insert_count(c, st->pint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-    st->state = sw_start;
-    return NGX_AGAIN;
+            st->state = sw_start;
+            break;
+        }
+    }
 }
 
 
 ngx_int_t
 ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
     ngx_int_t  rc;
     enum {
@@ -1648,75 +1847,78 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st,
         sw_skip
     };
 
-    switch (st->state) {
+    for ( ;; ) {
 
-    case sw_start:
+        switch (st->state) {
 
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data");
+        case sw_start:
 
-        st->state = sw_type;
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data");
 
-        /* fall through */
+            st->state = sw_type;
 
-    case sw_type:
+            /* fall through */
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_type:
 
-        st->type = st->vlint.value;
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        if (st->type == NGX_HTTP_V3_FRAME_HEADERS) {
-            /* trailers */
-            goto done;
-        }
+            st->type = st->vlint.value;
 
-        if (ngx_http_v3_is_v2_frame(st->type)
-            || st->type == NGX_HTTP_V3_FRAME_GOAWAY
-            || st->type == NGX_HTTP_V3_FRAME_SETTINGS
-            || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID
-            || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH
-            || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)
-        {
-            return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
-        }
+            if (st->type == NGX_HTTP_V3_FRAME_HEADERS) {
+                /* trailers */
+                goto done;
+            }
 
-        st->state = sw_length;
-        break;
+            if (ngx_http_v3_is_v2_frame(st->type)
+                || st->type == NGX_HTTP_V3_FRAME_GOAWAY
+                || st->type == NGX_HTTP_V3_FRAME_SETTINGS
+                || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID
+                || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH
+                || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)
+            {
+                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;
+            }
 
-    case sw_length:
+            st->state = sw_length;
+            break;
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+        case sw_length:
 
-        st->length = st->vlint.value;
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http3 parse data type:%ui, len:%ui",
-                       st->type, st->length);
+            st->length = st->vlint.value;
 
-        if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) {
-            st->state = sw_skip;
-            break;
-        }
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 parse data type:%ui, len:%ui",
+                           st->type, st->length);
 
-        st->state = sw_type;
-        return NGX_OK;
+            if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) {
+                st->state = sw_skip;
+                break;
+            }
+
+            st->state = sw_type;
+            return NGX_OK;
+
+        case sw_skip:
 
-    case sw_skip:
+            rc = ngx_http_v3_parse_skip(b, &st->length);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        if (--st->length == 0) {
             st->state = sw_type;
+            break;
         }
-
-        break;
     }
 
-    return NGX_AGAIN;
-
 done:
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data done");
@@ -1728,7 +1930,7 @@ done:
 
 ngx_int_t
 ngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st,
-    u_char ch)
+    ngx_buf_t *b)
 {
     ngx_int_t  rc;
     enum {
@@ -1740,76 +1942,64 @@ ngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st,
         sw_unknown
     };
 
-    switch (st->state) {
-    case sw_start:
-
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni");
+    for ( ;; ) {
 
-        st->state = sw_type;
+        switch (st->state) {
+        case sw_start:
 
-        /* fall through */
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni");
 
-    case sw_type:
+            st->state = sw_type;
 
-        rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, ch);
-        if (rc != NGX_DONE) {
-            return rc;
-        }
+            /* fall through */
 
-        rc = ngx_http_v3_register_uni_stream(c, st->vlint.value);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+        case sw_type:
 
-        switch (st->vlint.value) {
-        case NGX_HTTP_V3_STREAM_CONTROL:
-            st->state = sw_control;
-            break;
+            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);
+            if (rc != NGX_DONE) {
+                return rc;
+            }
 
-        case NGX_HTTP_V3_STREAM_ENCODER:
-            st->state = sw_encoder;
-            break;
+            rc = ngx_http_v3_register_uni_stream(c, st->vlint.value);
+            if (rc != NGX_OK) {
+                return rc;
+            }
 
-        case NGX_HTTP_V3_STREAM_DECODER:
-            st->state = sw_decoder;
-            break;
+            switch (st->vlint.value) {
+            case NGX_HTTP_V3_STREAM_CONTROL:
+                st->state = sw_control;
+                break;
 
-        default:
-            st->state = sw_unknown;
-        }
+            case NGX_HTTP_V3_STREAM_ENCODER:
+                st->state = sw_encoder;
+                break;
 
-        break;
+            case NGX_HTTP_V3_STREAM_DECODER:
+                st->state = sw_decoder;
+                break;
 
-    case sw_control:
+            default:
+                st->state = sw_unknown;
+            }
 
-        rc = ngx_http_v3_parse_control(c, &st->u.control, ch);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            break;
 
-        break;
+        case sw_control:
 
-    case sw_encoder:
+            return ngx_http_v3_parse_control(c, &st->u.control, b);
 
-        rc = ngx_http_v3_parse_encoder(c, &st->u.encoder, ch);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+        case sw_encoder:
 
-        break;
+            return ngx_http_v3_parse_encoder(c, &st->u.encoder, b);
 
-    case sw_decoder:
+        case sw_decoder:
 
-        rc = ngx_http_v3_parse_decoder(c, &st->u.decoder, ch);
-        if (rc != NGX_OK) {
-            return rc;
-        }
+            return ngx_http_v3_parse_decoder(c, &st->u.decoder, b);
 
-        break;
+        case sw_unknown:
 
-    case sw_unknown:
-        break;
+            b->pos = b->last;
+            return NGX_AGAIN;
+        }
     }
-
-    return NGX_AGAIN;
 }
index 5ca3a410f1fd5d7da1244e2bbe7275fe15fe4e4f..ba004db5df4305d2ec8e2cdd45b31066497d4970 100644 (file)
@@ -136,11 +136,11 @@ typedef struct {
  */
 
 ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c,
-    ngx_http_v3_parse_headers_t *st, u_char ch);
+    ngx_http_v3_parse_headers_t *st, ngx_buf_t *b);
 ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c,
-    ngx_http_v3_parse_data_t *st, u_char ch);
+    ngx_http_v3_parse_data_t *st, ngx_buf_t *b);
 ngx_int_t ngx_http_v3_parse_uni(ngx_connection_t *c,
-    ngx_http_v3_parse_uni_t *st, u_char ch);
+    ngx_http_v3_parse_uni_t *st, ngx_buf_t *b);
 
 
 #endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */
index 0bd585317c1f0e3dbdd2434469832d2cd6823733..fb9ea8ff1aee2adb390a8fa7538aa653bd110c6e 100644 (file)
@@ -207,6 +207,7 @@ ngx_http_v3_cleanup_request(void *data)
 static void
 ngx_http_v3_process_request(ngx_event_t *rev)
 {
+    u_char                       *p;
     ssize_t                       n;
     ngx_buf_t                    *b;
     ngx_int_t                     rc;
@@ -273,7 +274,9 @@ ngx_http_v3_process_request(ngx_event_t *rev)
             b->last = b->start + n;
         }
 
-        rc = ngx_http_v3_parse_headers(c, st, *b->pos);
+        p = b->pos;
+
+        rc = ngx_http_v3_parse_headers(c, st, b);
 
         if (rc > 0) {
             ngx_http_v3_finalize_connection(c, rc,
@@ -302,8 +305,7 @@ ngx_http_v3_process_request(ngx_event_t *rev)
             break;
         }
 
-        b->pos++;
-        r->request_length++;
+        r->request_length += b->pos - p;
 
         if (rc == NGX_AGAIN) {
             continue;
@@ -1024,6 +1026,7 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
     off_t                      max;
     size_t                     size;
+    u_char                    *p;
     ngx_int_t                  rc;
     ngx_buf_t                 *b;
     ngx_uint_t                 last;
@@ -1078,9 +1081,11 @@ ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
         while (cl->buf->pos < cl->buf->last) {
 
             if (st->length == 0) {
-                r->request_length++;
+                p = cl->buf->pos;
+
+                rc = ngx_http_v3_parse_data(r->connection, st, cl->buf);
 
-                rc = ngx_http_v3_parse_data(r->connection, st, *cl->buf->pos++);
+                r->request_length += cl->buf->pos - p;
 
                 if (rc == NGX_AGAIN) {
                     continue;
index 1b0f9145400b6e83b77187d60ba891a34398a64a..1cff2931908ada7eb9d114e3d8b6bc08a8675280 100644 (file)
@@ -168,7 +168,8 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev)
 {
     u_char                     buf[128];
     ssize_t                    n;
-    ngx_int_t                  rc, i;
+    ngx_buf_t                  b;
+    ngx_int_t                  rc;
     ngx_connection_t          *c;
     ngx_http_v3_uni_stream_t  *us;
 
@@ -177,6 +178,8 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev)
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler");
 
+    ngx_memzero(&b, sizeof(ngx_buf_t));
+
     while (rev->ready) {
 
         n = c->recv(c, buf, sizeof(buf));
@@ -201,25 +204,25 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev)
             break;
         }
 
-        for (i = 0; i < n; i++) {
+        b.pos = buf;
+        b.last = buf + n;
 
-            rc = ngx_http_v3_parse_uni(c, &us->parse, buf[i]);
+        rc = ngx_http_v3_parse_uni(c, &us->parse, &b);
 
-            if (rc == NGX_DONE) {
-                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                               "http3 read done");
-                ngx_http_v3_close_uni_stream(c);
-                return;
-            }
+        if (rc == NGX_DONE) {
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http3 read done");
+            ngx_http_v3_close_uni_stream(c);
+            return;
+        }
 
-            if (rc > 0) {
-                goto failed;
-            }
+        if (rc > 0) {
+            goto failed;
+        }
 
-            if (rc != NGX_AGAIN) {
-                rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR;
-                goto failed;
-            }
+        if (rc != NGX_AGAIN) {
+            rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR;
+            goto failed;
         }
     }