summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2021-06-13 23:11:18 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2021-08-27 02:16:53 -0400
commit0f90a9e3204fa563873abbc26784f689566b02e8 (patch)
treeda6b3af452322bed4a5398d138e01720b5f93c8b
parent5beee8b2d4d1a84ce0d53c0ea787d2714693aec1 (diff)
downloadlighttpd-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()
-rw-r--r--src/h2.c50
-rw-r--r--src/http_header.h10
-rw-r--r--src/request.c182
-rw-r--r--src/request.h2
4 files changed, 132 insertions, 112 deletions
diff --git a/src/h2.c b/src/h2.c
index aff64807..35e21bc9 100644
--- a/src/h2.c
+++ b/src/h2.c
@@ -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;