mirror of https://github.com/nginx/nginx
HTTP: Use common validation for :method pseudo-headers
The code was duplicated between HTTP/2 and HTTP/3. No functional change intended.
This commit is contained in:
parent
3f28843859
commit
b75ebbaacc
|
|
@ -188,6 +188,11 @@ size_t ngx_http_huff_encode(u_char *src, size_t len, u_char *dst,
|
||||||
*/
|
*/
|
||||||
ngx_int_t ngx_http_v23_fixup_header(ngx_http_request_t *r,
|
ngx_int_t ngx_http_v23_fixup_header(ngx_http_request_t *r,
|
||||||
ngx_str_t *name, ngx_str_t *value);
|
ngx_str_t *name, ngx_str_t *value);
|
||||||
|
/*
|
||||||
|
* Parse an HTTP/2 and/or HTTP/3 method.
|
||||||
|
*/
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_v23_parse_method(ngx_http_request_t *r, ngx_str_t *value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1137,6 +1137,101 @@ header_done:
|
||||||
|
|
||||||
#if (NGX_HTTP_V2 || NGX_HTTP_V3)
|
#if (NGX_HTTP_V2 || NGX_HTTP_V3)
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_http_v23_parse_method(ngx_http_request_t *r, ngx_str_t *value)
|
||||||
|
{
|
||||||
|
size_t k, len;
|
||||||
|
ngx_uint_t n;
|
||||||
|
const u_char *p, *m;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This array takes less than 256 sequential bytes,
|
||||||
|
* and if typical CPU cache line size is 64 bytes,
|
||||||
|
* it is prefetched for 4 load operations.
|
||||||
|
*/
|
||||||
|
static const struct {
|
||||||
|
u_char len;
|
||||||
|
const u_char method[11];
|
||||||
|
uint32_t value;
|
||||||
|
} tests[] = {
|
||||||
|
{ 3, "GET", NGX_HTTP_GET },
|
||||||
|
{ 4, "POST", NGX_HTTP_POST },
|
||||||
|
{ 4, "HEAD", NGX_HTTP_HEAD },
|
||||||
|
{ 7, "OPTIONS", NGX_HTTP_OPTIONS },
|
||||||
|
{ 8, "PROPFIND", NGX_HTTP_PROPFIND },
|
||||||
|
{ 3, "PUT", NGX_HTTP_PUT },
|
||||||
|
{ 5, "MKCOL", NGX_HTTP_MKCOL },
|
||||||
|
{ 6, "DELETE", NGX_HTTP_DELETE },
|
||||||
|
{ 4, "COPY", NGX_HTTP_COPY },
|
||||||
|
{ 4, "MOVE", NGX_HTTP_MOVE },
|
||||||
|
{ 9, "PROPPATCH", NGX_HTTP_PROPPATCH },
|
||||||
|
{ 4, "LOCK", NGX_HTTP_LOCK },
|
||||||
|
{ 6, "UNLOCK", NGX_HTTP_UNLOCK },
|
||||||
|
{ 5, "PATCH", NGX_HTTP_PATCH },
|
||||||
|
{ 5, "TRACE", NGX_HTTP_TRACE },
|
||||||
|
{ 7, "CONNECT", NGX_HTTP_CONNECT }
|
||||||
|
}, *test;
|
||||||
|
|
||||||
|
if (r->method_name.len) {
|
||||||
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||||
|
"client sent duplicate :method header");
|
||||||
|
|
||||||
|
return NGX_DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value->len == 0) {
|
||||||
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||||
|
"client sent empty :method header");
|
||||||
|
|
||||||
|
return NGX_DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->method_name.len = value->len;
|
||||||
|
r->method_name.data = value->data;
|
||||||
|
|
||||||
|
len = r->method_name.len;
|
||||||
|
n = sizeof(tests) / sizeof(tests[0]);
|
||||||
|
test = tests;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (len == test->len) {
|
||||||
|
p = r->method_name.data;
|
||||||
|
m = test->method;
|
||||||
|
k = len;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (*p++ != *m++) {
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
} while (--k);
|
||||||
|
|
||||||
|
r->method = test->value;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
test++;
|
||||||
|
|
||||||
|
} while (--n);
|
||||||
|
|
||||||
|
p = r->method_name.data;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {
|
||||||
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||||
|
"client sent invalid method: \"%V\"",
|
||||||
|
&r->method_name);
|
||||||
|
|
||||||
|
return NGX_DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
p++;
|
||||||
|
|
||||||
|
} while (--len);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline ngx_int_t
|
static inline ngx_int_t
|
||||||
ngx_isspace(u_char ch)
|
ngx_isspace(u_char ch)
|
||||||
|
|
|
||||||
|
|
@ -146,8 +146,6 @@ static ngx_int_t ngx_http_v2_pseudo_header(ngx_http_request_t *r,
|
||||||
ngx_http_v2_header_t *header);
|
ngx_http_v2_header_t *header);
|
||||||
static ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r,
|
||||||
ngx_str_t *value);
|
ngx_str_t *value);
|
||||||
static ngx_int_t ngx_http_v2_parse_method(ngx_http_request_t *r,
|
|
||||||
ngx_str_t *value);
|
|
||||||
static ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r,
|
||||||
ngx_str_t *value);
|
ngx_str_t *value);
|
||||||
static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r,
|
static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r,
|
||||||
|
|
@ -3251,7 +3249,7 @@ ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
|
||||||
if (ngx_memcmp(header->name.data, "method", sizeof("method") - 1)
|
if (ngx_memcmp(header->name.data, "method", sizeof("method") - 1)
|
||||||
== 0)
|
== 0)
|
||||||
{
|
{
|
||||||
return ngx_http_v2_parse_method(r, &header->value);
|
return ngx_http_v23_parse_method(r, &header->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ngx_memcmp(header->name.data, "scheme", sizeof("scheme") - 1)
|
if (ngx_memcmp(header->name.data, "scheme", sizeof("scheme") - 1)
|
||||||
|
|
@ -3319,102 +3317,6 @@ ngx_http_v2_parse_path(ngx_http_request_t *r, ngx_str_t *value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
|
||||||
ngx_http_v2_parse_method(ngx_http_request_t *r, ngx_str_t *value)
|
|
||||||
{
|
|
||||||
size_t k, len;
|
|
||||||
ngx_uint_t n;
|
|
||||||
const u_char *p, *m;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This array takes less than 256 sequential bytes,
|
|
||||||
* and if typical CPU cache line size is 64 bytes,
|
|
||||||
* it is prefetched for 4 load operations.
|
|
||||||
*/
|
|
||||||
static const struct {
|
|
||||||
u_char len;
|
|
||||||
const u_char method[11];
|
|
||||||
uint32_t value;
|
|
||||||
} tests[] = {
|
|
||||||
{ 3, "GET", NGX_HTTP_GET },
|
|
||||||
{ 4, "POST", NGX_HTTP_POST },
|
|
||||||
{ 4, "HEAD", NGX_HTTP_HEAD },
|
|
||||||
{ 7, "OPTIONS", NGX_HTTP_OPTIONS },
|
|
||||||
{ 8, "PROPFIND", NGX_HTTP_PROPFIND },
|
|
||||||
{ 3, "PUT", NGX_HTTP_PUT },
|
|
||||||
{ 5, "MKCOL", NGX_HTTP_MKCOL },
|
|
||||||
{ 6, "DELETE", NGX_HTTP_DELETE },
|
|
||||||
{ 4, "COPY", NGX_HTTP_COPY },
|
|
||||||
{ 4, "MOVE", NGX_HTTP_MOVE },
|
|
||||||
{ 9, "PROPPATCH", NGX_HTTP_PROPPATCH },
|
|
||||||
{ 4, "LOCK", NGX_HTTP_LOCK },
|
|
||||||
{ 6, "UNLOCK", NGX_HTTP_UNLOCK },
|
|
||||||
{ 5, "PATCH", NGX_HTTP_PATCH },
|
|
||||||
{ 5, "TRACE", NGX_HTTP_TRACE },
|
|
||||||
{ 7, "CONNECT", NGX_HTTP_CONNECT }
|
|
||||||
}, *test;
|
|
||||||
|
|
||||||
if (r->method_name.len) {
|
|
||||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
||||||
"client sent duplicate :method header");
|
|
||||||
|
|
||||||
return NGX_DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value->len == 0) {
|
|
||||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
||||||
"client sent empty :method header");
|
|
||||||
|
|
||||||
return NGX_DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->method_name.len = value->len;
|
|
||||||
r->method_name.data = value->data;
|
|
||||||
|
|
||||||
len = r->method_name.len;
|
|
||||||
n = sizeof(tests) / sizeof(tests[0]);
|
|
||||||
test = tests;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (len == test->len) {
|
|
||||||
p = r->method_name.data;
|
|
||||||
m = test->method;
|
|
||||||
k = len;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (*p++ != *m++) {
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
} while (--k);
|
|
||||||
|
|
||||||
r->method = test->value;
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
next:
|
|
||||||
test++;
|
|
||||||
|
|
||||||
} while (--n);
|
|
||||||
|
|
||||||
p = r->method_name.data;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {
|
|
||||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
||||||
"client sent invalid method: \"%V\"",
|
|
||||||
&r->method_name);
|
|
||||||
|
|
||||||
return NGX_DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
p++;
|
|
||||||
|
|
||||||
} while (--len);
|
|
||||||
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
|
ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,30 +29,6 @@ static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r,
|
||||||
ngx_chain_t *in);
|
ngx_chain_t *in);
|
||||||
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
ngx_str_t name;
|
|
||||||
ngx_uint_t method;
|
|
||||||
} ngx_http_v3_methods[] = {
|
|
||||||
|
|
||||||
{ ngx_string("GET"), NGX_HTTP_GET },
|
|
||||||
{ ngx_string("POST"), NGX_HTTP_POST },
|
|
||||||
{ ngx_string("HEAD"), NGX_HTTP_HEAD },
|
|
||||||
{ ngx_string("OPTIONS"), NGX_HTTP_OPTIONS },
|
|
||||||
{ ngx_string("PROPFIND"), NGX_HTTP_PROPFIND },
|
|
||||||
{ ngx_string("PUT"), NGX_HTTP_PUT },
|
|
||||||
{ ngx_string("MKCOL"), NGX_HTTP_MKCOL },
|
|
||||||
{ ngx_string("DELETE"), NGX_HTTP_DELETE },
|
|
||||||
{ ngx_string("COPY"), NGX_HTTP_COPY },
|
|
||||||
{ ngx_string("MOVE"), NGX_HTTP_MOVE },
|
|
||||||
{ ngx_string("PROPPATCH"), NGX_HTTP_PROPPATCH },
|
|
||||||
{ ngx_string("LOCK"), NGX_HTTP_LOCK },
|
|
||||||
{ ngx_string("UNLOCK"), NGX_HTTP_UNLOCK },
|
|
||||||
{ ngx_string("PATCH"), NGX_HTTP_PATCH },
|
|
||||||
{ ngx_string("TRACE"), NGX_HTTP_TRACE },
|
|
||||||
{ ngx_string("CONNECT"), NGX_HTTP_CONNECT }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ngx_http_v3_init_stream(ngx_connection_t *c)
|
ngx_http_v3_init_stream(ngx_connection_t *c)
|
||||||
{
|
{
|
||||||
|
|
@ -704,44 +680,10 @@ ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) {
|
if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) {
|
||||||
|
if (ngx_http_v23_parse_method(r, value) != NGX_OK) {
|
||||||
if (r->method_name.len) {
|
|
||||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
||||||
"client sent duplicate \":method\" header");
|
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value->len == 0) {
|
|
||||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
||||||
"client sent empty \":method\" header");
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->method_name = *value;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(ngx_http_v3_methods)
|
|
||||||
/ sizeof(ngx_http_v3_methods[0]); i++)
|
|
||||||
{
|
|
||||||
if (value->len == ngx_http_v3_methods[i].name.len
|
|
||||||
&& ngx_strncmp(value->data,
|
|
||||||
ngx_http_v3_methods[i].name.data, value->len)
|
|
||||||
== 0)
|
|
||||||
{
|
|
||||||
r->method = ngx_http_v3_methods[i].method;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < value->len; i++) {
|
|
||||||
ch = value->data[i];
|
|
||||||
|
|
||||||
if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
|
|
||||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
||||||
"client sent invalid method: \"%V\"", value);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||||
"http3 method \"%V\" %ui", value, r->method);
|
"http3 method \"%V\" %ui", value, r->method);
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue