diff options
author | Glenn Strauss <gstrauss@gluelogic.com> | 2021-06-13 23:11:18 -0400 |
---|---|---|
committer | Glenn Strauss <gstrauss@gluelogic.com> | 2021-08-27 02:16:53 -0400 |
commit | 0f90a9e3204fa563873abbc26784f689566b02e8 (patch) | |
tree | da6b3af452322bed4a5398d138e01720b5f93c8b /src | |
parent | 5beee8b2d4d1a84ce0d53c0ea787d2714693aec1 (diff) | |
download | lighttpd-git-0f90a9e3204fa563873abbc26784f689566b02e8.tar.gz |
[core] reduce memcmp in http_request_parse_header
extend http_header_parse_ctx to support enum http_header_h2_e
HTTP/2 pseudo-headers in hpctx->id before using memcmp()
Diffstat (limited to 'src')
-rw-r--r-- | src/h2.c | 50 | ||||
-rw-r--r-- | src/http_header.h | 10 | ||||
-rw-r--r-- | src/request.c | 182 | ||||
-rw-r--r-- | src/request.h | 2 |
4 files changed, 132 insertions, 112 deletions
@@ -161,22 +161,22 @@ static const uint8_t http_header_lshpack_idx[] = { /* Note: must be kept in sync with ls-hpack/lshpack.h:lshpack_static_hdr_idx[]*/ -static const uint8_t lshpack_idx_http_header[] = { - [LSHPACK_HDR_UNKNOWN] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_AUTHORITY] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_METHOD_GET] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_METHOD_POST] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_PATH] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_PATH_INDEX_HTML] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_SCHEME_HTTP] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_SCHEME_HTTPS] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_STATUS_200] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_STATUS_204] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_STATUS_206] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_STATUS_304] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_STATUS_400] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_STATUS_404] = HTTP_HEADER_OTHER - ,[LSHPACK_HDR_STATUS_500] = HTTP_HEADER_OTHER +static const int8_t lshpack_idx_http_header[] = { + [LSHPACK_HDR_UNKNOWN] = HTTP_HEADER_H2_UNKNOWN + ,[LSHPACK_HDR_AUTHORITY] = HTTP_HEADER_H2_AUTHORITY + ,[LSHPACK_HDR_METHOD_GET] = HTTP_HEADER_H2_METHOD_GET + ,[LSHPACK_HDR_METHOD_POST] = HTTP_HEADER_H2_METHOD_POST + ,[LSHPACK_HDR_PATH] = HTTP_HEADER_H2_PATH + ,[LSHPACK_HDR_PATH_INDEX_HTML] = HTTP_HEADER_H2_PATH_INDEX_HTML + ,[LSHPACK_HDR_SCHEME_HTTP] = HTTP_HEADER_H2_SCHEME_HTTP + ,[LSHPACK_HDR_SCHEME_HTTPS] = HTTP_HEADER_H2_SCHEME_HTTPS + ,[LSHPACK_HDR_STATUS_200] = HTTP_HEADER_H2_UNKNOWN + ,[LSHPACK_HDR_STATUS_204] = HTTP_HEADER_H2_UNKNOWN + ,[LSHPACK_HDR_STATUS_206] = HTTP_HEADER_H2_UNKNOWN + ,[LSHPACK_HDR_STATUS_304] = HTTP_HEADER_H2_UNKNOWN + ,[LSHPACK_HDR_STATUS_400] = HTTP_HEADER_H2_UNKNOWN + ,[LSHPACK_HDR_STATUS_404] = HTTP_HEADER_H2_UNKNOWN + ,[LSHPACK_HDR_STATUS_500] = HTTP_HEADER_H2_UNKNOWN ,[LSHPACK_HDR_ACCEPT_CHARSET] = HTTP_HEADER_OTHER ,[LSHPACK_HDR_ACCEPT_ENCODING] = HTTP_HEADER_ACCEPT_ENCODING ,[LSHPACK_HDR_ACCEPT_LANGUAGE] = HTTP_HEADER_ACCEPT_LANGUAGE @@ -1224,24 +1224,6 @@ h2_parse_headers_frame (request_st * const restrict r, const unsigned char *psrc "fd:%d id:%u rqst: %.*s: %.*s", r->con->fd, r->h2id, (int)hpctx.klen, hpctx.k, (int)hpctx.vlen, hpctx.v); - #if 0 - /* might not be worth special-casing pseudo headers here and - * repeating the code in http_request_parse_header() to avoid - * memcmp() for pseudo headers (request.c does not know ls-hpack)*/ - if (hpctx.pseudo && lsx.hpack_index - && lsx.hpack_index < LSHPACK_HDR_STATUS_200 && !trailers) { - /* lsx.hpack_index: - * LSHPACK_HDR_AUTHORITY - * LSHPACK_HDR_METHOD_GET - * LSHPACK_HDR_METHOD_POST - * LSHPACK_HDR_PATH - * LSHPACK_HDR_PATH_INDEX_HTML - * LSHPACK_HDR_SCHEME_HTTP - * LSHPACK_HDR_SCHEME_HTTPS - */ - } - #endif - r->http_status = http_request_parse_header(r, &hpctx); if (0 != r->http_status) break; diff --git a/src/http_header.h b/src/http_header.h index 13731468..2f8d9343 100644 --- a/src/http_header.h +++ b/src/http_header.h @@ -25,6 +25,16 @@ * list may be revisitied and reviewed, and less frequent headers removed * or replaced. */ +enum http_header_h2_e { /* pseudo-headers */ + HTTP_HEADER_H2_UNKNOWN = -1 + ,HTTP_HEADER_H2_AUTHORITY = -2 + ,HTTP_HEADER_H2_METHOD_GET = -3 + ,HTTP_HEADER_H2_METHOD_POST = -4 + ,HTTP_HEADER_H2_PATH = -5 + ,HTTP_HEADER_H2_PATH_INDEX_HTML = -6 + ,HTTP_HEADER_H2_SCHEME_HTTP = -7 + ,HTTP_HEADER_H2_SCHEME_HTTPS = -8 +}; enum http_header_e { HTTP_HEADER_OTHER = 0 ,HTTP_HEADER_ACCEPT diff --git a/src/request.c b/src/request.c index 2b7bd281..520f2070 100644 --- a/src/request.c +++ b/src/request.c @@ -577,11 +577,6 @@ http_request_parse_header (request_st * const restrict r, http_header_parse_ctx #endif } - if (__builtin_expect( (2 == klen), 0) && k[0] == 't' && k[1] == 'e' - && !buffer_eq_icase_ss(v, vlen, CONST_STR_LEN("trailers"))) - return http_request_header_line_invalid(r, 400, - "invalid TE header value with HTTP/2 -> 400"); - if (!hpctx->trailers) { if (*k == ':') { /* HTTP/2 request pseudo-header fields */ @@ -591,69 +586,93 @@ http_request_parse_header (request_st * const restrict r, http_header_parse_ctx if (0 == vlen) return http_request_header_line_invalid(r, 400, "invalid header value -> 400"); - switch (klen-1) { - case 4: - if (0 == memcmp(k+1, "path", 4)) { - if (!buffer_is_blank(&r->target)) - return http_request_header_line_invalid(r, 400, - "repeated pseudo-header -> 400"); - buffer_copy_string_len(&r->target, v, vlen); - return 0; - } - break; - case 6: - if (0 == memcmp(k+1, "method", 6)) { - if (HTTP_METHOD_UNSET != r->http_method) - return http_request_header_line_invalid(r, 400, - "repeated pseudo-header -> 400"); - r->http_method = get_http_method_key(v, vlen); - if (HTTP_METHOD_UNSET >= r->http_method) - return http_request_header_line_invalid(r, 501, - "unknown http-method -> 501"); - return 0; + + /* (note: relies on implementation details using ls-hpack in h2.c) + * (hpctx->id mapped from lsxpack_header_t hpack_index, which only + * matches key, not also value, if lsxpack_header_t flags does not + * have LSXPACK_HPACK_VAL_MATCHED set, so HTTP_HEADER_H2_METHOD_GET + * below indicates any method, not only "GET") */ + if (__builtin_expect( (hpctx->id == HTTP_HEADER_H2_UNKNOWN), 0)) { + switch (klen-1) { + case 4: + if (0 == memcmp(k+1, "path", 4)) + hpctx->id = HTTP_HEADER_H2_PATH; + break; + case 6: + if (0 == memcmp(k+1, "method", 6)) + hpctx->id = HTTP_HEADER_H2_METHOD_GET; + else if (0 == memcmp(k+1, "scheme", 6)) + hpctx->id = HTTP_HEADER_H2_SCHEME_HTTP; + break; + case 9: + if (0 == memcmp(k+1, "authority", 9)) + hpctx->id = HTTP_HEADER_H2_AUTHORITY; + break; + default: + break; } - else if (0 == memcmp(k+1, "scheme", 6)) { - if (hpctx->scheme) - return http_request_header_line_invalid(r, 400, - "repeated pseudo-header -> 400"); - switch (vlen) {/*(validated, but then ignored)*/ - case 5: /* "https" */ - if (v[4]!='s') break; - __attribute_fallthrough__ - case 4: /* "http" */ - if (v[0]=='h' && v[1]=='t' && v[2]=='t' && v[3]=='p') { - hpctx->scheme = 1; - return 0; - } - break; - default: - break; - } + if (hpctx->id >= HTTP_HEADER_H2_UNKNOWN) return http_request_header_line_invalid(r, 400, - "unknown pseudo-header scheme -> 400"); - } - break; - case 9: - if (0 == memcmp(k+1, "authority", 9)) { - if (r->http_host) - return http_request_header_line_invalid(r, 400, - "repeated pseudo-header -> 400"); - if (vlen >= 1024) /*(expecting < 256)*/ - return http_request_header_line_invalid(r, 400, - "invalid pseudo-header authority too long -> 400"); - /* insert as host header */ - r->http_host = - http_header_request_set_ptr(r, HTTP_HEADER_HOST, - CONST_STR_LEN("Host")); - buffer_copy_string_len(r->http_host, v, vlen); - return 0; + "invalid pseudo-header -> 400"); + } + + switch (hpctx->id) { + case HTTP_HEADER_H2_AUTHORITY: + if (__builtin_expect( (r->http_host != NULL), 0)) + break; + if (vlen >= 1024) /*(expecting < 256)*/ + return http_request_header_line_invalid(r, 400, + "invalid pseudo-header authority too long -> 400"); + /* insert as host header */ + r->http_host = + http_header_request_set_ptr(r, HTTP_HEADER_HOST, + CONST_STR_LEN("Host")); + buffer_copy_string_len(r->http_host, v, vlen); + return 0; + case HTTP_HEADER_H2_METHOD_GET: /*(any method, not only "GET")*/ + case HTTP_HEADER_H2_METHOD_POST: + if (__builtin_expect( (HTTP_METHOD_UNSET != r->http_method), 0)) + break; + r->http_method = get_http_method_key(v, vlen); + if (HTTP_METHOD_UNSET >= r->http_method) + return http_request_header_line_invalid(r, 501, + "unknown http-method -> 501"); + return 0; + case HTTP_HEADER_H2_PATH: /*(any path, not only "/")*/ + case HTTP_HEADER_H2_PATH_INDEX_HTML: + if (__builtin_expect( (!buffer_is_blank(&r->target)), 0)) + break; + buffer_copy_string_len(&r->target, v, vlen); + return 0; + case HTTP_HEADER_H2_SCHEME_HTTP: /*(any scheme, not only "http")*/ + case HTTP_HEADER_H2_SCHEME_HTTPS: + if (__builtin_expect( (hpctx->scheme), 0)) + break; + hpctx->scheme = 1; /*(marked present, but otherwise ignored)*/ + return 0; + #if 0 + switch (vlen) {/*(validated, but then ignored)*/ + case 5: /* "https" */ + if (v[4]!='s') break; + __attribute_fallthrough__ + case 4: /* "http" */ + if (v[0]=='h' && v[1]=='t' && v[2]=='t' && v[3]=='p') { + hpctx->scheme = 1; + return 0; + } + break; + default: + break; } - break; + return http_request_header_line_invalid(r, 400, + "unknown pseudo-header scheme -> 400"); + #endif default: - break; + return http_request_header_line_invalid(r, 400, + "invalid pseudo-header -> 400"); } return http_request_header_line_invalid(r, 400, - "invalid pseudo-header -> 400"); + "repeated pseudo-header -> 400"); } else { /*(non-pseudo headers)*/ if (hpctx->pseudo) { /*(transition to non-pseudo headers)*/ @@ -666,23 +685,11 @@ http_request_parse_header (request_st * const restrict r, http_header_parse_ctx if (0 == vlen) return 0; - uint32_t j = 0; - while (j < klen && (light_islower(k[j]) || k[j] == '-')) - ++j; - const unsigned int http_header_strict = (hpctx->http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); - if (__builtin_expect( (j != klen), 0)) { - if (light_isupper(k[j])) - return 400; - if (0 != http_request_parse_header_other(r, k+j, klen-j, - http_header_strict)) - return 400; - } - if (http_header_strict) { - for (j = 0; j < vlen; ++j) { + for (uint32_t j = 0; j < vlen; ++j) { if ((((uint8_t *)v)[j] < 32 && v[j] != '\t') || v[j]==127) return http_request_header_char_invalid(r, v[j], "invalid character in header -> 400"); @@ -694,8 +701,29 @@ http_request_parse_header (request_st * const restrict r, http_header_parse_ctx "invalid character in header -> 400"); } - const enum http_header_e id = - hpctx->id ? hpctx->id : http_header_hkey_get_lc(k, klen); + if (__builtin_expect( (hpctx->id == HTTP_HEADER_H2_UNKNOWN), 0)) { + uint32_t j = 0; + while (j < klen && (light_islower(k[j]) || k[j] == '-')) + ++j; + + if (__builtin_expect( (j != klen), 0)) { + if (light_isupper(k[j])) + return 400; + if (0 != http_request_parse_header_other(r, k+j, klen-j, + http_header_strict)) + return 400; + } + + hpctx->id = http_header_hkey_get_lc(k, klen); + } + + const enum http_header_e id = (enum http_header_e)hpctx->id; + + if (__builtin_expect( (id == HTTP_HEADER_TE), 0) + && !buffer_eq_icase_ss(v, vlen, CONST_STR_LEN("trailers"))) + return http_request_header_line_invalid(r, 400, + "invalid TE header value with HTTP/2 -> 400"); + return http_request_parse_single_header(r, id, k, klen, v, vlen); } } diff --git a/src/request.h b/src/request.h index 457927bb..0f6793a0 100644 --- a/src/request.h +++ b/src/request.h @@ -206,7 +206,7 @@ typedef struct http_header_parse_ctx { uint8_t pseudo; uint8_t scheme; uint8_t trailers; - uint8_t id; + int8_t id; uint32_t max_request_field_size; unsigned int http_parseopts; } http_header_parse_ctx; |