summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorStefan Eissing <icing@apache.org>2022-10-22 11:41:55 +0000
committerStefan Eissing <icing@apache.org>2022-10-22 11:41:55 +0000
commit8b68438b2e0b38909b5b2f76e22ceccd2fa48278 (patch)
tree860b86bba90ea742c402269304529678d644644e /modules
parentd7ec293abc89a0dde9cc730acfa7ba838601a197 (diff)
downloadhttpd-8b68438b2e0b38909b5b2f76e22ceccd2fa48278.tar.gz
*) mod_http2: field values (headers and trailers) are stripped of
leading/trailing whitespace (space +htab) before being processed or send in a response. This is compatible behaviour to HTTP/1.1 parsers that strip incoming headers of such characters. [Stefan Eissing] - removed intermittent "H2HeaderStrictness" directive again. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1904777 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'modules')
-rw-r--r--modules/http2/h2_c2_filter.c2
-rw-r--r--modules/http2/h2_config.c29
-rw-r--r--modules/http2/h2_config.h1
-rw-r--r--modules/http2/h2_session.c10
-rw-r--r--modules/http2/h2_stream.c2
-rw-r--r--modules/http2/h2_util.c123
-rw-r--r--modules/http2/h2_util.h25
-rw-r--r--modules/http2/h2_version.h4
8 files changed, 95 insertions, 101 deletions
diff --git a/modules/http2/h2_c2_filter.c b/modules/http2/h2_c2_filter.c
index e1d7cb72bf..f537a19f07 100644
--- a/modules/http2/h2_c2_filter.c
+++ b/modules/http2/h2_c2_filter.c
@@ -495,7 +495,7 @@ static apr_table_t *make_table(h2_response_parser *parser)
++sep;
}
- if (!h2_util_ignore_header(hline)) {
+ if (!h2_util_ignore_resp_header(hline)) {
apr_table_merge(headers, hline, sep);
}
}
diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c
index 40ef8b4ceb..eea4be2c59 100644
--- a/modules/http2/h2_config.c
+++ b/modules/http2/h2_config.c
@@ -75,7 +75,6 @@ typedef struct h2_config {
int padding_always;
int output_buffered;
apr_interval_time_t stream_timeout;/* beam timeout */
- int header_strictness; /* which rfc to follow when verifying header */
} h2_config;
typedef struct h2_dir_config {
@@ -111,7 +110,6 @@ static h2_config defconf = {
1, /* padding always */
1, /* stream output buffered */
-1, /* beam timeout */
- 7540, /* header strictness */
};
static h2_dir_config defdconf = {
@@ -155,7 +153,6 @@ void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
conf->padding_always = DEF_VAL;
conf->output_buffered = DEF_VAL;
conf->stream_timeout = DEF_VAL;
- conf->header_strictness = DEF_VAL;
return conf;
}
@@ -198,7 +195,6 @@ static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
n->padding_bits = H2_CONFIG_GET(add, base, padding_bits);
n->padding_always = H2_CONFIG_GET(add, base, padding_always);
n->stream_timeout = H2_CONFIG_GET(add, base, stream_timeout);
- n->header_strictness = H2_CONFIG_GET(add, base, header_strictness);
return n;
}
@@ -282,8 +278,6 @@ static apr_int64_t h2_srv_config_geti64(const h2_config *conf, h2_config_var_t v
return H2_CONFIG_GET(conf, &defconf, output_buffered);
case H2_CONF_STREAM_TIMEOUT:
return H2_CONFIG_GET(conf, &defconf, stream_timeout);
- case H2_CONF_HEADER_STRICTNESS:
- return H2_CONFIG_GET(conf, &defconf, header_strictness);
default:
return DEF_VAL;
}
@@ -343,9 +337,6 @@ static void h2_srv_config_seti(h2_config *conf, h2_config_var_t var, int val)
case H2_CONF_OUTPUT_BUFFER:
H2_CONFIG_SET(conf, output_buffered, val);
break;
- case H2_CONF_HEADER_STRICTNESS:
- H2_CONFIG_SET(conf, header_strictness, val);
- break;
default:
break;
}
@@ -712,24 +703,6 @@ static const char *h2_conf_set_modern_tls_only(cmd_parms *cmd,
return "value must be On or Off";
}
-static const char *h2_conf_set_header_strictness(
- cmd_parms *cmd, void *dirconf, const char *value)
-{
- if (!strcasecmp(value, "highest")) {
- CONFIG_CMD_SET(cmd, dirconf, H2_CONF_HEADER_STRICTNESS, 1000000);
- return NULL;
- }
- else if (!strcasecmp(value, "rfc7540")) {
- CONFIG_CMD_SET(cmd, dirconf, H2_CONF_HEADER_STRICTNESS, 7540);
- return NULL;
- }
- else if (!strcasecmp(value, "rfc9113")) {
- CONFIG_CMD_SET(cmd, dirconf, H2_CONF_HEADER_STRICTNESS, 9113);
- return NULL;
- }
- return "value must be one of highest|rfc7540|rfc9113";
-}
-
static const char *h2_conf_set_upgrade(cmd_parms *cmd,
void *dirconf, const char *value)
{
@@ -964,8 +937,6 @@ const command_rec h2_cmds[] = {
RSRC_CONF, "set stream output buffer on/off"),
AP_INIT_TAKE1("H2StreamTimeout", h2_conf_set_stream_timeout, NULL,
RSRC_CONF, "set stream timeout"),
- AP_INIT_TAKE1("H2HeaderStrictness", h2_conf_set_header_strictness, NULL,
- RSRC_CONF, "set strictness of header value checks"),
AP_END_CMD
};
diff --git a/modules/http2/h2_config.h b/modules/http2/h2_config.h
index d3d47386a8..6d2e65f926 100644
--- a/modules/http2/h2_config.h
+++ b/modules/http2/h2_config.h
@@ -43,7 +43,6 @@ typedef enum {
H2_CONF_PADDING_ALWAYS,
H2_CONF_OUTPUT_BUFFER,
H2_CONF_STREAM_TIMEOUT,
- H2_CONF_HEADER_STRICTNESS
} h2_config_var_t;
struct apr_hash_t;
diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
index d0b3a47598..7ba49cf8d5 100644
--- a/modules/http2/h2_session.c
+++ b/modules/http2/h2_session.c
@@ -957,12 +957,10 @@ apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *
#endif
#ifdef H2_NG2_RFC9113_STRICTNESS
/* nghttp2 v1.50.0 introduces the strictness checks on leading/trailing
- * whitespace of RFC 9113. */
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
- "nghttp2_session_server_new: header strictness is %d",
- h2_config_sgeti(s, H2_CONF_HEADER_STRICTNESS));
- nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(options,
- h2_config_sgeti(s, H2_CONF_HEADER_STRICTNESS) < 9113);
+ * whitespace of RFC 9113 for fields. But, by default, it RST streams
+ * carrying such. We do not want that. We want to strip the ws and
+ * handle them, just like the HTTP/1.1 parser does. */
+ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(options, 1);
#endif
rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
session, options);
diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c
index 606e2762d0..cf6f79897d 100644
--- a/modules/http2/h2_stream.c
+++ b/modules/http2/h2_stream.c
@@ -654,7 +654,7 @@ static apr_status_t add_trailer(h2_stream *stream,
"pseudo header in trailer"));
return APR_EINVAL;
}
- if (h2_req_ignore_trailer(name, nlen)) {
+ if (h2_ignore_req_trailer(name, nlen)) {
return APR_SUCCESS;
}
if (!stream->trailers_in) {
diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c
index f40bce1b87..728cee95aa 100644
--- a/modules/http2/h2_util.c
+++ b/modules/http2/h2_util.c
@@ -1390,19 +1390,9 @@ apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb)
* h2_ngheader
******************************************************************************/
-int h2_util_ignore_header(const char *name)
-{
- /* never forward, ch. 8.1.2.2 */
- return (H2_HD_MATCH_LIT_CS("connection", name)
- || H2_HD_MATCH_LIT_CS("proxy-connection", name)
- || H2_HD_MATCH_LIT_CS("upgrade", name)
- || H2_HD_MATCH_LIT_CS("keep-alive", name)
- || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
-}
-
static int count_header(void *ctx, const char *key, const char *value)
{
- if (!h2_util_ignore_header(key)) {
+ if (!h2_util_ignore_resp_header(key)) {
(*((size_t*)ctx))++;
}
return 1;
@@ -1423,6 +1413,17 @@ static const char *inv_field_value_chr(const char *token)
return (p && *p)? p : NULL;
}
+static void strip_field_value_ws(nghttp2_nv *nv)
+{
+ while(nv->valuelen && (nv->value[0] == ' ' || nv->value[0] == '\t')) {
+ nv->value++; nv->valuelen--;
+ }
+ while(nv->valuelen && (nv->value[nv->valuelen-1] == ' '
+ || nv->value[nv->valuelen-1] == '\t')) {
+ nv->valuelen--;
+ }
+}
+
typedef struct ngh_ctx {
apr_pool_t *p;
int unsafe;
@@ -1455,13 +1456,14 @@ static int add_header(ngh_ctx *ctx, const char *key, const char *value)
nv->namelen = strlen(key);
nv->value = (uint8_t*)value;
nv->valuelen = strlen(value);
+ strip_field_value_ws(nv);
return 1;
}
static int add_table_header(void *ctx, const char *key, const char *value)
{
- if (!h2_util_ignore_header(key)) {
+ if (!h2_util_ignore_resp_header(key)) {
add_header(ctx, key, value);
}
return 1;
@@ -1620,6 +1622,12 @@ static literal IgnoredRequestTrailers[] = { /* Ignore, see rfc7230, ch. 4.1.2 */
H2_DEF_LITERAL("content-length"),
H2_DEF_LITERAL("proxy-authorization"),
};
+static literal IgnoredResponseHeaders[] = {
+ H2_DEF_LITERAL("upgrade"),
+ H2_DEF_LITERAL("connection"),
+ H2_DEF_LITERAL("keep-alive"),
+ H2_DEF_LITERAL("transfer-encoding"),
+};
static literal IgnoredResponseTrailers[] = {
H2_DEF_LITERAL("age"),
H2_DEF_LITERAL("date"),
@@ -1634,89 +1642,126 @@ static literal IgnoredResponseTrailers[] = {
H2_DEF_LITERAL("proxy-authenticate"),
};
-static int ignore_header(const literal *lits, size_t llen,
- const char *name, size_t nlen)
+static int contains_name(const literal *lits, size_t llen, nghttp2_nv *nv)
{
const literal *lit;
size_t i;
for (i = 0; i < llen; ++i) {
lit = &lits[i];
- if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
+ if (lit->len == nv->namelen
+ && !apr_strnatcasecmp(lit->name, (const char *)nv->name)) {
return 1;
}
}
return 0;
}
-int h2_req_ignore_header(const char *name, size_t len)
+int h2_util_ignore_resp_header(const char *name)
{
- return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len);
+ nghttp2_nv nv;
+
+ nv.name = (uint8_t*)name;
+ nv.namelen = strlen(name);
+ return contains_name(H2_LIT_ARGS(IgnoredResponseHeaders), &nv);
}
-int h2_req_ignore_trailer(const char *name, size_t len)
+
+static int h2_req_ignore_header(nghttp2_nv *nv)
{
- return (h2_req_ignore_header(name, len)
- || ignore_header(H2_LIT_ARGS(IgnoredRequestTrailers), name, len));
+ return contains_name(H2_LIT_ARGS(IgnoredRequestHeaders), nv);
}
-int h2_res_ignore_trailer(const char *name, size_t len)
+int h2_ignore_req_trailer(const char *name, size_t len)
{
- return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
+ nghttp2_nv nv;
+
+ nv.name = (uint8_t*)name;
+ nv.namelen = strlen(name);
+ return (h2_req_ignore_header(&nv)
+ || contains_name(H2_LIT_ARGS(IgnoredRequestTrailers), &nv));
}
-apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool,
- const char *name, size_t nlen,
- const char *value, size_t vlen,
- size_t max_field_len, int *pwas_added)
+int h2_ignore_resp_trailer(const char *name, size_t len)
+{
+ nghttp2_nv nv;
+
+ nv.name = (uint8_t*)name;
+ nv.namelen = strlen(name);
+ return (contains_name(H2_LIT_ARGS(IgnoredResponseHeaders), &nv)
+ || contains_name(H2_LIT_ARGS(IgnoredResponseTrailers), &nv));
+}
+
+static apr_status_t req_add_header(apr_table_t *headers, apr_pool_t *pool,
+ nghttp2_nv *nv, size_t max_field_len,
+ int *pwas_added)
{
char *hname, *hvalue;
const char *existing;
*pwas_added = 0;
- if (h2_req_ignore_header(name, nlen)) {
+ strip_field_value_ws(nv);
+
+ if (h2_req_ignore_header(nv)) {
return APR_SUCCESS;
}
- else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
+ else if (nv->namelen == sizeof("cookie")-1
+ && !apr_strnatcasecmp("cookie", (const char *)nv->name)) {
existing = apr_table_get(headers, "cookie");
if (existing) {
- char *nval;
-
/* Cookie header come separately in HTTP/2, but need
* to be merged by "; " (instead of default ", ")
*/
- if (max_field_len && strlen(existing) + vlen + nlen + 4 > max_field_len) {
+ if (max_field_len
+ && strlen(existing) + nv->valuelen + nv->namelen + 4
+ > max_field_len) {
/* "key: oldval, nval" is too long */
return APR_EINVAL;
}
- hvalue = apr_pstrndup(pool, value, vlen);
- nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
- apr_table_setn(headers, "Cookie", nval);
+ hvalue = apr_pstrndup(pool, (const char*)nv->value, nv->valuelen);
+ apr_table_setn(headers, "Cookie",
+ apr_psprintf(pool, "%s; %s", existing, hvalue));
return APR_SUCCESS;
}
}
- else if (H2_HD_MATCH_LIT("host", name, nlen)) {
+ else if (nv->namelen == sizeof("host")-1
+ && !apr_strnatcasecmp("host", (const char *)nv->name)) {
if (apr_table_get(headers, "Host")) {
return APR_SUCCESS; /* ignore duplicate */
}
}
- hname = apr_pstrndup(pool, name, nlen);
- h2_util_camel_case_header(hname, nlen);
+ hname = apr_pstrndup(pool, (const char*)nv->name, nv->namelen);
+ h2_util_camel_case_header(hname, nv->namelen);
existing = apr_table_get(headers, hname);
if (max_field_len) {
- if ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len) {
+ if ((existing? strlen(existing)+2 : 0) + nv->valuelen + nv->namelen + 2
+ > max_field_len) {
/* "key: (oldval, )?nval" is too long */
return APR_EINVAL;
}
}
if (!existing) *pwas_added = 1;
- hvalue = apr_pstrndup(pool, value, vlen);
+ hvalue = apr_pstrndup(pool, (const char*)nv->value, nv->valuelen);
apr_table_mergen(headers, hname, hvalue);
return APR_SUCCESS;
}
+apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen,
+ size_t max_field_len, int *pwas_added)
+{
+ nghttp2_nv nv;
+
+ nv.name = (uint8_t*)name;
+ nv.namelen = nlen;
+ nv.value = (uint8_t*)value;
+ nv.valuelen = vlen;
+ return req_add_header(headers, pool, &nv, max_field_len, pwas_added);
+}
+
/*******************************************************************************
* frame logging
******************************************************************************/
diff --git a/modules/http2/h2_util.h b/modules/http2/h2_util.h
index f6bd44bba6..d2e6548ba8 100644
--- a/modules/http2/h2_util.h
+++ b/modules/http2/h2_util.h
@@ -342,9 +342,8 @@ apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra);
/*******************************************************************************
* HTTP/2 header helpers
******************************************************************************/
-int h2_req_ignore_header(const char *name, size_t len);
-int h2_req_ignore_trailer(const char *name, size_t len);
-int h2_res_ignore_trailer(const char *name, size_t len);
+int h2_ignore_req_trailer(const char *name, size_t len);
+int h2_ignore_resp_trailer(const char *name, size_t len);
/**
* Set the push policy for the given request. Takes request headers into
@@ -375,25 +374,7 @@ const char *h2_util_base64url_encode(const char *data,
* nghttp2 helpers
******************************************************************************/
-#define H2_HD_MATCH_LIT_CS(l, name) \
- ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
-
-#define H2_CREATE_NV_LIT_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME; \
- nv->namelen = sizeof(NAME) - 1; \
- nv->value = (uint8_t *)VALUE; \
- nv->valuelen = strlen(VALUE)
-
-#define H2_CREATE_NV_CS_LIT(nv, NAME, VALUE) nv->name = (uint8_t *)NAME; \
- nv->namelen = strlen(NAME); \
- nv->value = (uint8_t *)VALUE; \
- nv->valuelen = sizeof(VALUE) - 1
-
-#define H2_CREATE_NV_CS_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME; \
- nv->namelen = strlen(NAME); \
- nv->value = (uint8_t *)VALUE; \
- nv->valuelen = strlen(VALUE)
-
-int h2_util_ignore_header(const char *name);
+int h2_util_ignore_resp_header(const char *name);
typedef struct h2_ngheader {
nghttp2_nv *nv;
diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h
index c939b8c8af..c961089901 100644
--- a/modules/http2/h2_version.h
+++ b/modules/http2/h2_version.h
@@ -27,7 +27,7 @@
* @macro
* Version number of the http2 module as c string
*/
-#define MOD_HTTP2_VERSION "2.0.10"
+#define MOD_HTTP2_VERSION "2.0.11"
/**
* @macro
@@ -35,7 +35,7 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
-#define MOD_HTTP2_VERSION_NUM 0x02000a
+#define MOD_HTTP2_VERSION_NUM 0x02000b
#endif /* mod_h2_h2_version_h */