Browse Source

[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()
master
Glenn Strauss 4 months ago
parent
commit
0f90a9e320
  1. 50
      src/h2.c
  2. 10
      src/http_header.h
  3. 182
      src/request.c
  4. 2
      src/request.h

50
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;

10
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

182
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);
}
}

2
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;

Loading…
Cancel
Save