HTTP/3: header encoding functions.

This commit is contained in:
Roman Arutyunyan 2020-07-13 16:00:00 +03:00
parent 548d515fba
commit 04b2a169a4
3 changed files with 226 additions and 90 deletions

View File

@ -97,3 +97,131 @@ ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix)
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;
}

View File

@ -132,6 +132,18 @@ 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,
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,

View File

@ -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_request_t *r)
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_request_t *r)
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_request_t *r)
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_request_t *r)
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_request_t *r)
#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_request_t *r)
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_request_t *r)
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_request_t *r)
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_request_t *r)
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_request_t *r)
}
}
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_request_t *r)
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) {