changeset 8817:8e8cdb7bfb17 quic

HTTP/3: response trailers support.
author Roman Arutyunyan <arut@nginx.com>
date Tue, 13 Jul 2021 22:44:03 +0300
parents 7f29db5294bd
children 07f90cb79fec
files src/http/v3/ngx_http_v3_filter_module.c
diffstat 1 files changed, 120 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/v3/ngx_http_v3_filter_module.c
+++ b/src/http/v3/ngx_http_v3_filter_module.c
@@ -49,7 +49,8 @@ static ngx_chain_t *ngx_http_v3_create_p
     ngx_str_t *path, uint64_t push_id);
 static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r,
     ngx_chain_t *in);
-static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r);
+static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r,
+    ngx_http_v3_filter_ctx_t *ctx);
 static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf);
 
 
@@ -515,7 +516,9 @@ ngx_http_v3_header_filter(ngx_http_reque
     *ll = hl;
     ll = &cl->next;
 
-    if (r->headers_out.content_length_n >= 0 && !r->header_only) {
+    if (r->headers_out.content_length_n >= 0
+        && !r->header_only && !r->expect_trailers)
+    {
         len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA)
               + ngx_http_v3_encode_varlen_int(NULL,
                                               r->headers_out.content_length_n);
@@ -1303,7 +1306,7 @@ ngx_http_v3_body_filter(ngx_http_request
     }
 
     if (cl->buf->last_buf) {
-        tl = ngx_http_v3_create_trailers(r);
+        tl = ngx_http_v3_create_trailers(r, ctx);
         if (tl == NULL) {
             return NGX_ERROR;
         }
@@ -1326,32 +1329,131 @@ ngx_http_v3_body_filter(ngx_http_request
 
 
 static ngx_chain_t *
-ngx_http_v3_create_trailers(ngx_http_request_t *r)
+ngx_http_v3_create_trailers(ngx_http_request_t *r,
+    ngx_http_v3_filter_ctx_t *ctx)
 {
-    ngx_buf_t    *b;
-    ngx_chain_t  *cl;
+    size_t            len, n;
+    u_char           *p;
+    ngx_buf_t        *b;
+    ngx_uint_t        i;
+    ngx_chain_t      *cl, *hl;
+    ngx_list_part_t  *part;
+    ngx_table_elt_t  *header;
+
+    len = 0;
+
+    part = &r->headers_out.trailers.part;
+    header = part->elts;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http3 create trailers");
+    for (i = 0; /* void */; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
 
-    /* XXX */
+            part = part->next;
+            header = part->elts;
+            i = 0;
+        }
 
-    b = ngx_calloc_buf(r->pool);
-    if (b == NULL) {
-        return NULL;
+        if (header[i].hash == 0) {
+            continue;
+        }
+
+        len += ngx_http_v3_encode_field_l(NULL, &header[i].key,
+                                          &header[i].value);
     }
 
-    b->last_buf = 1;
-
-    cl = ngx_alloc_chain_link(r->pool);
+    cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
     if (cl == NULL) {
         return NULL;
     }
 
-    cl->buf = b;
-    cl->next = NULL;
+    b = cl->buf;
+
+    b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module;
+    b->memory = 0;
+    b->last_buf = 1;
+
+    if (len == 0) {
+        b->temporary = 0;
+        b->pos = b->last = NULL;
+        return cl;
+    }
+
+    b->temporary = 1;
+
+    len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0);
+
+    b->pos = ngx_palloc(r->pool, len);
+    if (b->pos == NULL) {
+        return NULL;
+    }
+
+    b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->pos,
+                                                                 0, 0, 0);
+
+    part = &r->headers_out.trailers.part;
+    header = part->elts;
+
+    for (i = 0; /* void */; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            header = part->elts;
+            i = 0;
+        }
 
-    return cl;
+        if (header[i].hash == 0) {
+            continue;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http3 trailer: \"%V: %V\"",
+                       &header[i].key, &header[i].value);
+
+        b->last = (u_char *) ngx_http_v3_encode_field_l(b->last,
+                                                        &header[i].key,
+                                                        &header[i].value);
+    }
+
+    n = b->last - b->pos;
+
+    hl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+    if (hl == NULL) {
+        return NULL;
+    }
+
+    b = hl->buf;
+    p = b->start;
+
+    if (p == NULL) {
+        p = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2);
+        if (p == NULL) {
+            return NULL;
+        }
+
+        b->start = p;
+        b->end = p + NGX_HTTP_V3_VARLEN_INT_LEN * 2;
+    }
+
+    b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module;
+    b->memory = 0;
+    b->temporary = 1;
+    b->pos = p;
+
+    b->last = (u_char *) ngx_http_v3_encode_varlen_int(p,
+                                                    NGX_HTTP_V3_FRAME_HEADERS);
+    b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n);
+
+    hl->next = cl;
+
+    return hl;
 }