changeset 8292:46e3542d51b3 quic

Chunked response body in HTTP/3.
author Roman Arutyunyan <arut@nginx.com>
date Fri, 27 Mar 2020 19:46:54 +0300
parents 4feae8bc0ca9
children 1ec905f4d851
files src/http/modules/ngx_http_chunked_filter_module.c src/http/v3/ngx_http_v3.h src/http/v3/ngx_http_v3_request.c
diffstat 3 files changed, 76 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -18,7 +18,7 @@ typedef struct {
 
 static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);
 static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r,
-    ngx_http_chunked_filter_ctx_t *ctx);
+    ngx_http_chunked_filter_ctx_t *ctx, size_t size);
 
 
 static ngx_http_module_t  ngx_http_chunked_filter_module_ctx = {
@@ -106,6 +106,7 @@ ngx_http_chunked_body_filter(ngx_http_re
 {
     u_char                         *chunk;
     off_t                           size;
+    size_t                          n;
     ngx_int_t                       rc;
     ngx_buf_t                      *b;
     ngx_chain_t                    *out, *cl, *tl, **ll;
@@ -161,29 +162,50 @@ ngx_http_chunked_body_filter(ngx_http_re
         chunk = b->start;
 
         if (chunk == NULL) {
-            /* the "0000000000000000" is 64-bit hexadecimal string */
+
+#if (NGX_HTTP_V3)
+            if (r->http_version == NGX_HTTP_VERSION_30) {
+                n = NGX_HTTP_V3_VARLEN_INT_LEN * 2;
 
-            chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
+            } else
+#endif
+            {
+                /* the "0000000000000000" is 64-bit hexadecimal string */
+                n = sizeof("0000000000000000" CRLF) - 1;
+            }
+
+            chunk = ngx_palloc(r->pool, n);
             if (chunk == NULL) {
                 return NGX_ERROR;
             }
 
             b->start = chunk;
-            b->end = chunk + sizeof("0000000000000000" CRLF) - 1;
+            b->end = chunk + n;
         }
 
         b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
         b->memory = 0;
         b->temporary = 1;
         b->pos = chunk;
-        b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
+
+#if (NGX_HTTP_V3)
+        if (r->http_version == NGX_HTTP_VERSION_30) {
+            b->last = (u_char *) ngx_http_v3_encode_varlen_int(chunk,
+                                                       NGX_HTTP_V3_FRAME_DATA);
+            b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size);
+
+        } else
+#endif
+        {
+            b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
+        }
 
         tl->next = out;
         out = tl;
     }
 
     if (cl->buf->last_buf) {
-        tl = ngx_http_chunked_create_trailers(r, ctx);
+        tl = ngx_http_chunked_create_trailers(r, ctx, size);
         if (tl == NULL) {
             return NGX_ERROR;
         }
@@ -192,11 +214,12 @@ ngx_http_chunked_body_filter(ngx_http_re
 
         *ll = tl;
 
-        if (size == 0) {
-            tl->buf->pos += 2;
-        }
-
-    } else if (size > 0) {
+    } else if (size > 0
+#if (NGX_HTTP_V3)
+               && r->http_version != NGX_HTTP_VERSION_30
+#endif
+               )
+    {
         tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
         if (tl == NULL) {
             return NGX_ERROR;
@@ -227,7 +250,7 @@ ngx_http_chunked_body_filter(ngx_http_re
 
 static ngx_chain_t *
 ngx_http_chunked_create_trailers(ngx_http_request_t *r,
-    ngx_http_chunked_filter_ctx_t *ctx)
+    ngx_http_chunked_filter_ctx_t *ctx, size_t size)
 {
     size_t            len;
     ngx_buf_t        *b;
@@ -236,6 +259,12 @@ ngx_http_chunked_create_trailers(ngx_htt
     ngx_list_part_t  *part;
     ngx_table_elt_t  *header;
 
+#if (NGX_HTTP_V3)
+    if (r->http_version == NGX_HTTP_VERSION_30) {
+        return ngx_http_v3_create_trailers(r);
+    }
+#endif
+
     len = 0;
 
     part = &r->headers_out.trailers.part;
@@ -288,7 +317,10 @@ ngx_http_chunked_create_trailers(ngx_htt
 
     b->last = b->pos;
 
-    *b->last++ = CR; *b->last++ = LF;
+    if (size > 0) {
+        *b->last++ = CR; *b->last++ = LF;
+    }
+
     *b->last++ = '0';
     *b->last++ = CR; *b->last++ = LF;
 
--- a/src/http/v3/ngx_http_v3.h
+++ b/src/http/v3/ngx_http_v3.h
@@ -69,6 +69,7 @@ typedef struct {
 
 ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b);
 ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r);
+ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r);
 
 uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value);
 uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value,
--- a/src/http/v3/ngx_http_v3_request.c
+++ b/src/http/v3/ngx_http_v3_request.c
@@ -258,11 +258,6 @@ ngx_http_v3_create_header(ngx_http_reque
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header");
 
-    /* XXX support chunked body in the chunked filter */
-    if (!r->header_only && r->headers_out.content_length_n == -1) {
-        return NULL;
-    }
-
     len = 2;
 
     if (r->headers_out.status == NGX_HTTP_OK) {
@@ -578,3 +573,33 @@ ngx_http_v3_create_header(ngx_http_reque
 
     return hl;
 }
+
+
+ngx_chain_t *
+ngx_http_v3_create_trailers(ngx_http_request_t *r)
+{
+    ngx_buf_t    *b;
+    ngx_chain_t  *cl;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http3 create trailers");
+
+    /* XXX */
+
+    b = ngx_calloc_buf(r->pool);
+    if (b == NULL) {
+        return NULL;
+    }
+
+    b->last_buf = 1;
+
+    cl = ngx_alloc_chain_link(r->pool);
+    if (cl == NULL) {
+        return NULL;
+    }
+
+    cl->buf = b;
+    cl->next = NULL;
+
+    return cl;
+}