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; 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, uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value,
ngx_uint_t prefix); 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_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic,
ngx_uint_t index, ngx_str_t *value); ngx_uint_t index, ngx_str_t *value);
ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, 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> #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, static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r,
ngx_str_t *name, ngx_str_t *value); 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; u_char *p;
size_t len, n; size_t len, n;
ngx_buf_t *b; ngx_buf_t *b;
ngx_uint_t i, j; ngx_uint_t i;
ngx_chain_t *hl, *cl, *bl; ngx_chain_t *hl, *cl, *bl;
ngx_list_part_t *part; ngx_list_part_t *part;
ngx_table_elt_t *header; 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"); 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) { 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 { } else {
len += 3 + ngx_http_v3_encode_prefix_int(NULL, 25, 4) len += ngx_http_v3_encode_header_lri(NULL, 0,
+ ngx_http_v3_encode_prefix_int(NULL, 3, 7); NGX_HTTP_V3_HEADER_STATUS_200,
NULL, 3);
} }
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 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; n = sizeof("nginx") - 1;
} }
len += ngx_http_v3_encode_prefix_int(NULL, 92, 4) len += ngx_http_v3_encode_header_lri(NULL, 0,
+ ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; NGX_HTTP_V3_HEADER_SERVER,
NULL, n);
} }
if (r->headers_out.date == NULL) { if (r->headers_out.date == NULL) {
len += ngx_http_v3_encode_prefix_int(NULL, 6, 4) len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE,
+ ngx_http_v3_encode_prefix_int(NULL, ngx_cached_http_time.len, NULL, ngx_cached_http_time.len);
7)
+ ngx_cached_http_time.len;
} }
if (r->headers_out.content_type.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; n += sizeof("; charset=") - 1 + r->headers_out.charset.len;
} }
len += ngx_http_v3_encode_prefix_int(NULL, 53, 4) len += ngx_http_v3_encode_header_lri(NULL, 0,
+ ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN,
NULL, n);
} }
if (r->headers_out.content_length_n > 0) { if (r->headers_out.content_length == NULL) {
len += ngx_http_v3_encode_prefix_int(NULL, 4, 4) + 1 + NGX_OFF_T_LEN; 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) { } else if (r->headers_out.content_length_n == 0) {
len += ngx_http_v3_encode_prefix_int(NULL, 4, 6); len += ngx_http_v3_encode_header_ri(NULL, 0,
NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO);
}
} }
if (r->headers_out.last_modified == NULL if (r->headers_out.last_modified == NULL
&& r->headers_out.last_modified_time != -1) && r->headers_out.last_modified_time != -1)
{ {
len += ngx_http_v3_encode_prefix_int(NULL, 10, 4) + 1 len += ngx_http_v3_encode_header_lri(NULL, 0,
+ sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT"); NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL,
sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
} }
/* XXX location */ /* XXX location */
@ -493,8 +511,8 @@ ngx_http_v3_create_header(ngx_http_request_t *r)
#if (NGX_HTTP_GZIP) #if (NGX_HTTP_GZIP)
if (r->gzip_vary) { if (r->gzip_vary) {
if (clcf->gzip_vary) { if (clcf->gzip_vary) {
/* Vary: Accept-Encoding */ len += ngx_http_v3_encode_header_ri(NULL, 0,
len += ngx_http_v3_encode_prefix_int(NULL, 59, 6); NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING);
} else { } else {
r->gzip_vary = 0; r->gzip_vary = 0;
@ -521,10 +539,8 @@ ngx_http_v3_create_header(ngx_http_request_t *r)
continue; continue;
} }
len += ngx_http_v3_encode_prefix_int(NULL, header[i].key.len, 3) len += ngx_http_v3_encode_header_l(NULL, &header[i].key,
+ header[i].key.len &header[i].value);
+ ngx_http_v3_encode_prefix_int(NULL, header[i].value.len, 7 )
+ header[i].value.len;
} }
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); 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; return NULL;
} }
*b->last++ = 0; b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last,
*b->last++ = 0; 0, 0, 0);
if (r->headers_out.status == NGX_HTTP_OK) { if (r->headers_out.status == NGX_HTTP_OK) {
/* :status: 200 */ b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0,
*b->last = 0xc0; NGX_HTTP_V3_HEADER_STATUS_200);
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 6);
} else { } else {
/* :status: 200 */ b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
*b->last = 0x70; NGX_HTTP_V3_HEADER_STATUS_200,
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 4); NULL, 3);
*b->last = 0;
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 3, 7);
b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); 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; n = sizeof("nginx") - 1;
} }
/* server */ b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
*b->last = 0x70; NGX_HTTP_V3_HEADER_SERVER,
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 92, 4); p, n);
*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);
} }
if (r->headers_out.date == NULL) { if (r->headers_out.date == NULL) {
/* date */ b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
*b->last = 0x70; NGX_HTTP_V3_HEADER_DATE,
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 6, 4); ngx_cached_http_time.data,
*b->last = 0; ngx_cached_http_time.len);
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);
} }
if (r->headers_out.content_type.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; n += sizeof("; charset=") - 1 + r->headers_out.charset.len;
} }
/* content-type: text/plain */ b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
*b->last = 0x70; NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN,
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 53, 4); NULL, n);
*b->last = 0;
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7);
p = b->last; p = b->last;
b->last = ngx_copy(b->last, r->headers_out.content_type.data, b->last = ngx_cpymem(b->last, r->headers_out.content_type.data,
r->headers_out.content_type.len); r->headers_out.content_type.len);
if (r->headers_out.content_type_len == r->headers_out.content_type.len if (r->headers_out.content_type_len == r->headers_out.content_type.len
&& r->headers_out.charset.len) && r->headers_out.charset.len)
{ {
b->last = ngx_cpymem(b->last, "; charset=", b->last = ngx_cpymem(b->last, "; charset=",
sizeof("; charset=") - 1); sizeof("; charset=") - 1);
b->last = ngx_copy(b->last, r->headers_out.charset.data, b->last = ngx_cpymem(b->last, r->headers_out.charset.data,
r->headers_out.charset.len); r->headers_out.charset.len);
/* update r->headers_out.content_type for possible logging */ /* 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) { if (r->headers_out.content_length == NULL) {
/* content-length: 0 */ if (r->headers_out.content_length_n > 0) {
*b->last = 0x70; p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n);
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 4); n = p - b->last;
p = b->last++;
b->last = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n);
*p = b->last - p - 1;
} else if (r->headers_out.content_length_n == 0) { b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
/* content-length: 0 */ NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO,
*b->last = 0xc0; NULL, n);
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 6);
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 if (r->headers_out.last_modified == NULL
&& r->headers_out.last_modified_time != -1) && r->headers_out.last_modified_time != -1)
{ {
/* last-modified */ b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0,
*b->last = 0x70; NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL,
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 10, 4); sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
p = b->last++;
b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);
*p = b->last - p - 1;
} }
#if (NGX_HTTP_GZIP) #if (NGX_HTTP_GZIP)
if (r->gzip_vary) { if (r->gzip_vary) {
/* vary: accept-encoding */ b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0,
*b->last = 0xc0; NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING);
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 59, 6);
} }
#endif #endif
@ -670,19 +676,9 @@ ngx_http_v3_create_header(ngx_http_request_t *r)
continue; continue;
} }
*b->last = 0x30; b->last = (u_char *) ngx_http_v3_encode_header_l(b->last,
b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, &header[i].key,
header[i].key.len, &header[i].value);
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);
} }
if (r->header_only) { if (r->header_only) {