changeset 8488:79125ef2e39f quic

HTTP/3: header encoding functions.
author Roman Arutyunyan <arut@nginx.com>
date Mon, 13 Jul 2020 16:00:00 +0300
parents 6e84524886d4
children 5d2e285677a7
files src/http/v3/ngx_http_v3.c src/http/v3/ngx_http_v3.h src/http/v3/ngx_http_v3_request.c
diffstat 3 files changed, 226 insertions(+), 90 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/v3/ngx_http_v3.c
+++ b/src/http/v3/ngx_http_v3.c
@@ -97,3 +97,131 @@ ngx_http_v3_encode_prefix_int(u_char *p,
 
     return (uintptr_t) p;
 }
+
+
+uintptr_t
+ngx_http_v3_encode_header_block_prefix(u_char *p, ngx_uint_t insert_count,
+    ngx_uint_t sign, ngx_uint_t delta_base)
+{
+    if (p == NULL) {
+        return ngx_http_v3_encode_prefix_int(NULL, insert_count, 8)
+               + ngx_http_v3_encode_prefix_int(NULL, delta_base, 7);
+    }
+
+    *p = 0;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, insert_count, 8);
+
+    *p = sign ? 0x80 : 0;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, delta_base, 7);
+
+    return (uintptr_t) p;
+}
+
+
+uintptr_t
+ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index)
+{
+    /* Indexed Header Field */
+
+    if (p == NULL) {
+        return ngx_http_v3_encode_prefix_int(NULL, index, 6);
+    }
+
+    *p = dynamic ? 0x80 : 0xc0;
+
+    return ngx_http_v3_encode_prefix_int(p, index, 6);
+}
+
+
+uintptr_t
+ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index,
+    u_char *data, size_t len)
+{
+    /* Literal Header Field With Name Reference */
+
+    if (p == NULL) {
+        return ngx_http_v3_encode_prefix_int(NULL, index, 4)
+               + ngx_http_v3_encode_prefix_int(NULL, len, 7)
+               + len;
+    }
+
+    *p = dynamic ? 0x60 : 0x70;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4);
+
+    *p = 0;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7);
+
+    if (data) {
+        p = ngx_cpymem(p, data, len);
+    }
+
+    return (uintptr_t) p;
+}
+
+
+uintptr_t
+ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name, ngx_str_t *value)
+{
+    /* Literal Header Field Without Name Reference */
+
+    if (p == NULL) {
+        return ngx_http_v3_encode_prefix_int(NULL, name->len, 3)
+               + name->len
+               + ngx_http_v3_encode_prefix_int(NULL, value->len, 7)
+               + value->len;
+    }
+
+    *p = 0x30;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3);
+
+    ngx_strlow(p, name->data, name->len);
+    p += name->len;
+
+    *p = 0;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7);
+
+    p = ngx_cpymem(p, value->data, value->len);
+
+    return (uintptr_t) p;
+}
+
+
+uintptr_t
+ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index)
+{
+    /* Indexed Header Field With Post-Base Index */
+
+    if (p == NULL) {
+        return ngx_http_v3_encode_prefix_int(NULL, index, 4);
+    }
+
+    *p = 0x10;
+
+    return ngx_http_v3_encode_prefix_int(p, index, 4);
+}
+
+
+uintptr_t
+ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index, u_char *data,
+    size_t len)
+{
+    /* Literal Header Field With Post-Base Name Reference */
+
+    if (p == NULL) {
+        return ngx_http_v3_encode_prefix_int(NULL, index, 3)
+               + ngx_http_v3_encode_prefix_int(NULL, len, 7)
+               + len;
+    }
+
+    *p = 0x08;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3);
+
+    *p = 0;
+    p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7);
+
+    if (data) {
+        p = ngx_cpymem(p, data, len);
+    }
+
+    return (uintptr_t) p;
+}
--- a/src/http/v3/ngx_http_v3.h
+++ b/src/http/v3/ngx_http_v3.h
@@ -132,6 +132,18 @@ uintptr_t ngx_http_v3_encode_varlen_int(
 uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value,
     ngx_uint_t prefix);
 
+uintptr_t ngx_http_v3_encode_header_block_prefix(u_char *p,
+    ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base);
+uintptr_t ngx_http_v3_encode_header_ri(u_char *p, ngx_uint_t dynamic,
+    ngx_uint_t index);
+uintptr_t ngx_http_v3_encode_header_lri(u_char *p, ngx_uint_t dynamic,
+    ngx_uint_t index, u_char *data, size_t len);
+uintptr_t ngx_http_v3_encode_header_l(u_char *p, ngx_str_t *name,
+    ngx_str_t *value);
+uintptr_t ngx_http_v3_encode_header_pbi(u_char *p, ngx_uint_t index);
+uintptr_t ngx_http_v3_encode_header_lpbi(u_char *p, ngx_uint_t index,
+    u_char *data, size_t len);
+
 ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic,
     ngx_uint_t index, ngx_str_t *value);
 ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name,
--- a/src/http/v3/ngx_http_v3_request.c
+++ b/src/http/v3/ngx_http_v3_request.c
@@ -10,6 +10,16 @@
 #include <ngx_http.h>
 
 
+/* static table indices */
+#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO       4
+#define NGX_HTTP_V3_HEADER_DATE                      6
+#define NGX_HTTP_V3_HEADER_LAST_MODIFIED             10
+#define NGX_HTTP_V3_HEADER_STATUS_200                25
+#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN   53
+#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING      59
+#define NGX_HTTP_V3_HEADER_SERVER                    92
+
+
 static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r,
     ngx_str_t *name, ngx_str_t *value);
 
@@ -416,7 +426,7 @@ ngx_http_v3_create_header(ngx_http_reque
     u_char                    *p;
     size_t                     len, n;
     ngx_buf_t                 *b;
-    ngx_uint_t                 i, j;
+    ngx_uint_t                 i;
     ngx_chain_t               *hl, *cl, *bl;
     ngx_list_part_t           *part;
     ngx_table_elt_t           *header;
@@ -427,14 +437,16 @@ ngx_http_v3_create_header(ngx_http_reque
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header");
 
-    len = 2;
+    len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0);
 
     if (r->headers_out.status == NGX_HTTP_OK) {
-        len += ngx_http_v3_encode_prefix_int(NULL, 25, 6);
+        len += ngx_http_v3_encode_header_ri(NULL, 0,
+                                            NGX_HTTP_V3_HEADER_STATUS_200);
 
     } else {
-        len += 3 + ngx_http_v3_encode_prefix_int(NULL, 25, 4)
-                 + ngx_http_v3_encode_prefix_int(NULL, 3, 7);
+        len += ngx_http_v3_encode_header_lri(NULL, 0,
+                                             NGX_HTTP_V3_HEADER_STATUS_200,
+                                             NULL, 3);
     }
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
@@ -450,15 +462,14 @@ ngx_http_v3_create_header(ngx_http_reque
             n = sizeof("nginx") - 1;
         }
 
-        len += ngx_http_v3_encode_prefix_int(NULL, 92, 4)
-               + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n;
+        len += ngx_http_v3_encode_header_lri(NULL, 0,
+                                             NGX_HTTP_V3_HEADER_SERVER,
+                                             NULL, n);
     }
 
     if (r->headers_out.date == NULL) {
-        len += ngx_http_v3_encode_prefix_int(NULL, 6, 4)
-               + ngx_http_v3_encode_prefix_int(NULL, ngx_cached_http_time.len,
-                                               7)
-               + ngx_cached_http_time.len;
+        len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE,
+                                             NULL, ngx_cached_http_time.len);
     }
 
     if (r->headers_out.content_type.len) {
@@ -470,22 +481,29 @@ ngx_http_v3_create_header(ngx_http_reque
             n += sizeof("; charset=") - 1 + r->headers_out.charset.len;
         }
 
-        len += ngx_http_v3_encode_prefix_int(NULL, 53, 4)
-               + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n;
+        len += ngx_http_v3_encode_header_lri(NULL, 0,
+                                    NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN,
+                                    NULL, n);
     }
 
-    if (r->headers_out.content_length_n > 0) {
-        len += ngx_http_v3_encode_prefix_int(NULL, 4, 4) + 1 + NGX_OFF_T_LEN;
+    if (r->headers_out.content_length == NULL) {
+        if (r->headers_out.content_length_n > 0) {
+            len += ngx_http_v3_encode_header_lri(NULL, 0,
+                                        NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO,
+                                        NULL, NGX_OFF_T_LEN);
 
-    } else if (r->headers_out.content_length_n == 0) {
-        len += ngx_http_v3_encode_prefix_int(NULL, 4, 6);
+        } else if (r->headers_out.content_length_n == 0) {
+            len += ngx_http_v3_encode_header_ri(NULL, 0,
+                                       NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO);
+        }
     }
 
     if (r->headers_out.last_modified == NULL
         && r->headers_out.last_modified_time != -1)
     {
-        len += ngx_http_v3_encode_prefix_int(NULL, 10, 4) + 1
-               + sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT");
+        len += ngx_http_v3_encode_header_lri(NULL, 0,
+                                  NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL,
+                                  sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
     }
 
     /* XXX location */
@@ -493,8 +511,8 @@ ngx_http_v3_create_header(ngx_http_reque
 #if (NGX_HTTP_GZIP)
     if (r->gzip_vary) {
         if (clcf->gzip_vary) {
-            /* Vary: Accept-Encoding */
-            len += ngx_http_v3_encode_prefix_int(NULL, 59, 6);
+            len += ngx_http_v3_encode_header_ri(NULL, 0,
+                                      NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING);
 
         } else {
             r->gzip_vary = 0;
@@ -521,10 +539,8 @@ ngx_http_v3_create_header(ngx_http_reque
             continue;
         }
 
-        len += ngx_http_v3_encode_prefix_int(NULL, header[i].key.len, 3)
-               + header[i].key.len
-               + ngx_http_v3_encode_prefix_int(NULL, header[i].value.len, 7 )
-               + header[i].value.len;
+        len += ngx_http_v3_encode_header_l(NULL, &header[i].key,
+                                           &header[i].value);
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len);
@@ -534,20 +550,17 @@ ngx_http_v3_create_header(ngx_http_reque
         return NULL;
     }
 
-    *b->last++ = 0;
-    *b->last++ = 0;
+    b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last,
+                                                                0, 0, 0);
 
     if (r->headers_out.status == NGX_HTTP_OK) {
-        /* :status: 200 */
-        *b->last = 0xc0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 6);
+        b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0,
+                                                NGX_HTTP_V3_HEADER_STATUS_200);
 
     } else {
-        /* :status: 200 */
-        *b->last = 0x70;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 4);
-        *b->last = 0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 3, 7);
+        b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
+                                                 NGX_HTTP_V3_HEADER_STATUS_200,
+                                                 NULL, 3);
         b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status);
     }
 
@@ -565,23 +578,16 @@ ngx_http_v3_create_header(ngx_http_reque
             n = sizeof("nginx") - 1;
         }
 
-        /* server */
-        *b->last = 0x70;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 92, 4);
-        *b->last = 0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7);
-        b->last = ngx_cpymem(b->last, p, n);
+        b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
+                                                     NGX_HTTP_V3_HEADER_SERVER,
+                                                     p, n);
     }
 
     if (r->headers_out.date == NULL) {
-        /* date */
-        *b->last = 0x70;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 6, 4);
-        *b->last = 0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last,
-                                                 ngx_cached_http_time.len, 7);
-        b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,
-                             ngx_cached_http_time.len);
+        b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
+                                                     NGX_HTTP_V3_HEADER_DATE,
+                                                     ngx_cached_http_time.data,
+                                                     ngx_cached_http_time.len);
     }
 
     if (r->headers_out.content_type.len) {
@@ -593,23 +599,21 @@ ngx_http_v3_create_header(ngx_http_reque
             n += sizeof("; charset=") - 1 + r->headers_out.charset.len;
         }
 
-        /* content-type: text/plain */
-        *b->last = 0x70;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 53, 4);
-        *b->last = 0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7);
+        b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
+                                    NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN,
+                                    NULL, n);
 
         p = b->last;
-        b->last = ngx_copy(b->last, r->headers_out.content_type.data,
-                           r->headers_out.content_type.len);
+        b->last = ngx_cpymem(b->last, r->headers_out.content_type.data,
+                             r->headers_out.content_type.len);
 
         if (r->headers_out.content_type_len == r->headers_out.content_type.len
             && r->headers_out.charset.len)
         {
             b->last = ngx_cpymem(b->last, "; charset=",
                                  sizeof("; charset=") - 1);
-            b->last = ngx_copy(b->last, r->headers_out.charset.data,
-                               r->headers_out.charset.len);
+            b->last = ngx_cpymem(b->last, r->headers_out.charset.data,
+                                 r->headers_out.charset.len);
 
             /* update r->headers_out.content_type for possible logging */
 
@@ -618,36 +622,38 @@ ngx_http_v3_create_header(ngx_http_reque
         }
     }
 
-    if (r->headers_out.content_length_n > 0) {
-        /* content-length: 0 */
-        *b->last = 0x70;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 4);
-        p = b->last++;
-        b->last = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n);
-        *p = b->last - p - 1;
+    if (r->headers_out.content_length == NULL) {
+        if (r->headers_out.content_length_n > 0) {
+            p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n);
+            n = p - b->last;
 
-    } else if (r->headers_out.content_length_n == 0) {
-        /* content-length: 0 */
-        *b->last = 0xc0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 6);
+            b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
+                                        NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO,
+                                        NULL, n);
+
+            b->last = ngx_sprintf(b->last, "%O",
+                                  r->headers_out.content_length_n);
+
+        } else if (r->headers_out.content_length_n == 0) {
+            b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0,
+                                       NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO);
+        }
     }
 
     if (r->headers_out.last_modified == NULL
         && r->headers_out.last_modified_time != -1)
     {
-        /* last-modified */
-        *b->last = 0x70;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 10, 4);
-        p = b->last++;
+        b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
+                                  NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL,
+                                  sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
+
         b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);
-        *p = b->last - p - 1;
     }
 
 #if (NGX_HTTP_GZIP)
     if (r->gzip_vary) {
-        /* vary: accept-encoding */
-        *b->last = 0xc0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 59, 6);
+        b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0,
+                                      NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING);
     }
 #endif
 
@@ -670,19 +676,9 @@ ngx_http_v3_create_header(ngx_http_reque
             continue;
         }
 
-        *b->last = 0x30;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last,
-                                                           header[i].key.len,
-                                                           3);
-        for (j = 0; j < header[i].key.len; j++) {
-            *b->last++ = ngx_tolower(header[i].key.data[j]);
-        }
-
-        *b->last = 0;
-        b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last,
-                                                           header[i].value.len,
-                                                           7);
-        b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
+        b->last = (u_char *) ngx_http_v3_encode_header_l(b->last,
+                                                         &header[i].key,
+                                                         &header[i].value);
     }
 
     if (r->header_only) {