HTTP/3 $request_line variable.

This commit is contained in:
Roman Arutyunyan 2020-03-18 20:22:16 +03:00
parent 5aa8e519c9
commit e63accd7bd
2 changed files with 37 additions and 540 deletions

View File

@ -1177,7 +1177,7 @@ ngx_http_process_request_line(ngx_event_t *rev)
r->request_line.len = r->request_end - r->request_start;
r->request_line.data = r->request_start;
r->request_length = r->header_in->pos - r->request_start;
r->request_length = r->header_in->pos - r->request_start; /* XXX */
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http request line: \"%V\"", &r->request_line);
@ -1593,7 +1593,7 @@ ngx_http_process_request_headers(ngx_event_t *rev)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header done");
r->request_length += r->header_in->pos - r->header_name_start;
r->request_length += r->header_in->pos - r->header_name_start; /* XXX */
r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;

View File

@ -40,6 +40,8 @@ struct {
ngx_int_t
ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b)
{
size_t n;
u_char *p;
ngx_int_t rc;
ngx_str_t *name, *value;
ngx_connection_t *c;
@ -97,23 +99,45 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b)
name = &st->header_rep.header.name;
value = &st->header_rep.header.value;
if (r->state == sw_start
&& ngx_http_v3_process_pseudo_header(r, name, value) != NGX_OK)
{
if (rc == NGX_DONE) {
r->state = sw_last;
} else {
if (r->state == sw_start) {
if (ngx_http_v3_process_pseudo_header(r, name, value) == NGX_OK) {
if (rc == NGX_OK) {
continue;
}
r->state = sw_done;
} else if (rc == NGX_OK) {
r->state = sw_prev;
} else {
r->state = sw_last;
}
n = (r->method_end - r->method_start) + 1
+ (r->uri_end - r->uri_start) + 1
+ sizeof("HTTP/3") - 1;
p = ngx_pnalloc(c->pool, n);
if (p == NULL) {
goto failed;
}
r->request_start = p;
p = ngx_cpymem(p, r->method_start, r->method_end - r->method_start);
*p++ = ' ';
p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start);
*p++ = ' ';
p = ngx_cpymem(p, "HTTP/3", sizeof("HTTP/3") - 1);
r->request_end = p;
} else if (rc == NGX_DONE) {
r->state = sw_done;
}
if (r->state == sw_start) {
continue;
}
r->header_name_start = name->data;
r->header_name_end = name->data + name->len;
r->header_start = value->data;
@ -139,533 +163,6 @@ done:
return NGX_HTTP_PARSE_HEADER_DONE;
}
#if 0
ngx_int_t
ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t pseudo)
{
u_char *p, ch;
ngx_str_t name, value;
ngx_int_t rc;
ngx_uint_t length, index, insert_count, sign, base, delta_base,
huffman, dynamic, offset;
ngx_connection_t *c;
ngx_http_v3_header_t *h;
enum {
sw_start = 0,
sw_length,
sw_length_1,
sw_length_2,
sw_length_3,
sw_header_block,
sw_req_insert_count,
sw_delta_base,
sw_read_delta_base,
sw_header,
sw_old_header,
sw_header_ri,
sw_header_pbi,
sw_header_lri,
sw_header_lpbi,
sw_header_l_name_len,
sw_header_l_name,
sw_header_value_len,
sw_header_read_value_len,
sw_header_value
} state;
c = r->connection;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http3 parse header, pseudo:%ui", pseudo);
if (r->state == sw_old_header) {
r->state = sw_header;
return NGX_OK;
}
length = r->h3_length;
index = r->h3_index;
insert_count = r->h3_insert_count;
sign = r->h3_sign;
delta_base = r->h3_delta_base;
huffman = r->h3_huffman;
dynamic = r->h3_dynamic;
offset = r->h3_offset;
name.data = r->header_name_start;
name.len = r->header_name_end - r->header_name_start;
value.data = r->header_start;
value.len = r->header_end - r->header_start;
if (r->state == sw_start) {
length = 1;
}
again:
state = r->state;
if (state == sw_header && length == 0) {
r->state = sw_start;
return NGX_HTTP_PARSE_HEADER_DONE;
}
for (p = b->pos; p < b->last; p++) {
if (state >= sw_header_block && length-- == 0) {
goto failed;
}
ch = *p;
switch (state) {
case sw_start:
if (ch != NGX_HTTP_V3_FRAME_HEADERS) {
goto failed;
}
r->request_start = p;
state = sw_length;
break;
case sw_length:
length = ch;
if (length & 0xc0) {
state = sw_length_1;
break;
}
state = sw_header_block;
break;
case sw_length_1:
length = (length << 8) + ch;
if ((length & 0xc000) != 0x4000) {
state = sw_length_2;
break;
}
length &= 0x3fff;
state = sw_header_block;
break;
case sw_length_2:
length = (length << 8) + ch;
if ((length & 0xc00000) != 0x800000) {
state = sw_length_3;
break;
}
length &= 0x3fffff;
state = sw_header_block;
break;
case sw_length_3:
length = (length << 8) + ch;
length &= 0x3fffffff;
state = sw_header_block;
break;
case sw_header_block:
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http3 header block length:%ui", length);
if (ch != 0xff) {
insert_count = ch;
state = sw_delta_base;
break;
}
insert_count = 0;
state = sw_req_insert_count;
break;
case sw_req_insert_count:
insert_count = (insert_count << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
insert_count += 0xff;
state = sw_delta_base;
break;
case sw_delta_base:
sign = (ch & 0x80) ? 1 : 0;
delta_base = ch & 0x7f;
if (delta_base != 0x7f) {
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http3 header block "
"insert_count:%ui, sign:%ui, delta_base:%ui",
insert_count, sign, delta_base);
goto done;
}
delta_base = 0;
state = sw_read_delta_base;
break;
case sw_read_delta_base:
delta_base = (delta_base << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
delta_base += 0x7f;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http3 header block "
"insert_count:%ui, sign:%ui, delta_base:%ui",
insert_count, sign, delta_base);
goto done;
case sw_header:
index = 0;
huffman = 0;
ngx_str_null(&name);
ngx_str_null(&value);
if (ch & 0x80) {
/* Indexed Header Field */
dynamic = (ch & 0x40) ? 0 : 1;
index = ch & 0x3f;
if (index != 0x3f) {
goto done;
}
index = 0;
state = sw_header_ri;
break;
}
if (ch & 0x40) {
/* Literal Header Field With Name Reference */
dynamic = (ch & 0x10) ? 0 : 1;
index = ch & 0x0f;
if (index != 0x0f) {
state = sw_header_value_len;
break;
}
index = 0;
state = sw_header_lri;
break;
}
if (ch & 0x20) {
/* Literal Header Field Without Name Reference */
huffman = (ch & 0x08) ? 1 : 0;
name.len = ch & 0x07;
if (name.len == 0) {
goto failed;
}
if (name.len != 0x07) {
offset = 0;
state = sw_header_l_name;
break;
}
name.len = 0;
state = sw_header_l_name_len;
break;
}
if (ch & 10) {
/* Indexed Header Field With Post-Base Index */
dynamic = 2;
index = ch & 0x0f;
if (index != 0x0f) {
goto done;
}
index = 0;
state = sw_header_pbi;
break;
}
/* Literal Header Field With Post-Base Name Reference */
dynamic = 2;
index = ch & 0x07;
if (index != 0x07) {
state = sw_header_value_len;
break;
}
index = 0;
state = sw_header_lpbi;
break;
case sw_header_ri:
index = (index << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
index += 0x3f;
goto done;
case sw_header_pbi:
index = (index << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
index += 0x0f;
goto done;
case sw_header_lri:
index = (index << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
index += 0x0f;
state = sw_header_value_len;
break;
case sw_header_lpbi:
index = (index << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
index += 0x07;
state = sw_header_value_len;
break;
case sw_header_l_name_len:
name.len = (name.len << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
name.len += 0x07;
offset = 0;
state = sw_header_l_name;
break;
case sw_header_l_name:
if (offset++ == 0) {
name.data = p;
}
if (offset != name.len) {
break;
}
if (huffman) {
if (ngx_http_v3_decode_huffman(c, &name) != NGX_OK) {
goto failed;
}
}
state = sw_header_value_len;
break;
case sw_header_value_len:
huffman = (ch & 0x80) ? 1 : 0;
value.len = ch & 0x7f;
if (value.len == 0) {
value.data = p;
goto done;
}
if (value.len != 0x7f) {
offset = 0;
state = sw_header_value;
break;
}
value.len = 0;
state = sw_header_read_value_len;
break;
case sw_header_read_value_len:
value.len = (value.len << 7) + (ch & 0x7f);
if (ch & 0x80) {
break;
}
value.len += 0x7f;
offset = 0;
state = sw_header_value;
break;
case sw_header_value:
if (offset++ == 0) {
value.data = p;
}
if (offset != value.len) {
break;
}
if (huffman) {
if (ngx_http_v3_decode_huffman(c, &value) != NGX_OK) {
goto failed;
}
}
goto done;
case sw_old_header:
break;
}
}
b->pos = p;
r->state = state;
r->h3_length = length;
r->h3_index = index;
r->h3_insert_count = insert_count;
r->h3_sign = sign;
r->h3_delta_base = delta_base;
r->h3_huffman = huffman;
r->h3_dynamic = dynamic;
r->h3_offset = offset;
/* XXX fix large reallocations */
r->header_name_start = name.data;
r->header_name_end = name.data + name.len;
r->header_start = value.data;
r->header_end = value.data + value.len;
/* XXX r->lowcase_index = i; */
return NGX_AGAIN;
done:
b->pos = p + 1;
r->state = sw_header;
r->h3_length = length;
r->h3_insert_count = insert_count;
r->h3_sign = sign;
r->h3_delta_base = delta_base;
if (state < sw_header) {
if (ngx_http_v3_check_insert_count(c, insert_count) != NGX_OK) {
return NGX_DONE;
}
goto again;
}
if (sign == 0) {
base = insert_count + delta_base;
} else {
base = insert_count - delta_base - 1;
}
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http3 header %s[%ui], base:%ui, \"%V\":\"%V\"",
dynamic ? "dynamic" : "static", index, base, &name, &value);
if (name.data == NULL) {
if (dynamic == 2) {
index = base - index - 1;
} else if (dynamic == 1) {
index += base;
}
h = ngx_http_v3_lookup_table(c, dynamic, index);
if (h == NULL) {
goto failed;
}
name = h->name;
if (value.data == NULL) {
value = h->value;
}
}
/* XXX ugly reallocation for the trailing '\0' */
p = ngx_pnalloc(c->pool, name.len + value.len + 2);
if (p == NULL) {
return NGX_ERROR;
}
ngx_memcpy(p, name.data, name.len);
name.data = p;
ngx_memcpy(p + name.len + 1, value.data, value.len);
value.data = p + name.len + 1;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http3 header \"%V\":\"%V\"", &name, &value);
if (pseudo) {
rc = ngx_http_v3_process_pseudo_header(r, &name, &value);
if (rc == NGX_ERROR) {
goto failed;
}
if (rc == NGX_OK) {
r->request_end = p + 1;
goto again;
}
/* rc == NGX_DONE */
r->state = sw_old_header;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http3 header left:%ui", length);
r->header_name_start = name.data;
r->header_name_end = name.data + name.len;
r->header_start = value.data;
r->header_end = value.data + value.len;
r->header_hash = ngx_hash_key(name.data, name.len); /* XXX */
/* XXX r->lowcase_index = i; */
return NGX_OK;
failed:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
#endif
static ngx_int_t
ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name,
@ -675,7 +172,7 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name,
ngx_connection_t *c;
if (name->len == 0 || name->data[0] != ':') {
return NGX_DECLINED;
return NGX_DONE;
}
c = r->connection;