Add nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation
This commit is contained in:
parent
a94d2de89a
commit
eb06e33e38
|
@ -69,6 +69,7 @@ APIDOCS= \
|
|||
nghttp2_option_set_no_closed_streams.rst \
|
||||
nghttp2_option_set_no_http_messaging.rst \
|
||||
nghttp2_option_set_no_recv_client_magic.rst \
|
||||
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation.rst \
|
||||
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
||||
nghttp2_option_set_server_fallback_rfc7540_priorities.rst \
|
||||
nghttp2_option_set_user_recv_extension_type.rst \
|
||||
|
|
|
@ -2750,6 +2750,18 @@ NGHTTP2_EXTERN void
|
|||
nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option,
|
||||
int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* This option, if set to nonzero, turns off RFC 9113 leading and
|
||||
* trailing white spaces validation against HTTP field value. Some
|
||||
* important fields, such as HTTP/2 pseudo header fields, are
|
||||
* validated more strictly and this option does not apply to them.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(
|
||||
nghttp2_option *option, int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
|
|
@ -388,6 +388,10 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
|||
case NGHTTP2_TOKEN_HOST:
|
||||
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||
rv = nghttp2_check_authority(nv->value->base, nv->value->len);
|
||||
} else if (
|
||||
stream->flags &
|
||||
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
|
||||
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
|
||||
} else {
|
||||
rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
|
||||
}
|
||||
|
@ -399,13 +403,20 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
|||
/* Check the value consists of just white spaces, which was done
|
||||
in check_pseudo_header before
|
||||
nghttp2_check_header_value_rfc9113 has been introduced. */
|
||||
if (lws(nv->value->base, nv->value->len)) {
|
||||
if ((stream->flags &
|
||||
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
|
||||
lws(nv->value->base, nv->value->len)) {
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
|
||||
if (stream->flags &
|
||||
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
|
||||
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
|
||||
} else {
|
||||
rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
|
|
|
@ -136,3 +136,10 @@ void nghttp2_option_set_server_fallback_rfc7540_priorities(
|
|||
option->opt_set_mask |= NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES;
|
||||
option->server_fallback_rfc7540_priorities = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(
|
||||
nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |=
|
||||
NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
|
||||
option->no_rfc9113_leading_and_trailing_ws_validation = val;
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ typedef enum {
|
|||
NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11,
|
||||
NGHTTP2_OPT_MAX_SETTINGS = 1 << 12,
|
||||
NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 13,
|
||||
NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14,
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
|
@ -132,6 +133,10 @@ struct nghttp2_option {
|
|||
* NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES
|
||||
*/
|
||||
int server_fallback_rfc7540_priorities;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION
|
||||
*/
|
||||
int no_rfc9113_leading_and_trailing_ws_validation;
|
||||
/**
|
||||
* NGHTTP2_OPT_USER_RECV_EXT_TYPES
|
||||
*/
|
||||
|
|
|
@ -566,6 +566,13 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
(*session_ptr)->opt_flags |=
|
||||
NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES;
|
||||
}
|
||||
|
||||
if ((option->opt_set_mask &
|
||||
NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
|
||||
option->no_rfc9113_leading_and_trailing_ws_validation) {
|
||||
(*session_ptr)->opt_flags |=
|
||||
NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
|
||||
}
|
||||
}
|
||||
|
||||
rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
|
||||
|
@ -1296,6 +1303,11 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
|
|||
mem = &session->mem;
|
||||
stream = nghttp2_session_get_stream_raw(session, stream_id);
|
||||
|
||||
if (session->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
|
||||
flags |= NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
|
||||
}
|
||||
|
||||
if (stream) {
|
||||
assert(stream->state == NGHTTP2_STREAM_IDLE);
|
||||
assert((stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
|
||||
|
|
|
@ -53,7 +53,8 @@ typedef enum {
|
|||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
|
||||
NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3,
|
||||
NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4,
|
||||
NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 5
|
||||
NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 5,
|
||||
NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 6,
|
||||
} nghttp2_optmask;
|
||||
|
||||
/*
|
||||
|
|
|
@ -96,6 +96,9 @@ typedef enum {
|
|||
NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10,
|
||||
/* Ignore client RFC 9218 priority signal. */
|
||||
NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20,
|
||||
/* Indicates that RFC 9113 leading and trailing white spaces
|
||||
validation against a field value is not performed. */
|
||||
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 0x40,
|
||||
} nghttp2_stream_flag;
|
||||
|
||||
/* HTTP related flags to enforce HTTP semantics */
|
||||
|
|
|
@ -363,6 +363,9 @@ int main(void) {
|
|||
test_nghttp2_http_push_promise) ||
|
||||
!CU_add_test(pSuite, "http_head_method_upgrade_workaround",
|
||||
test_nghttp2_http_head_method_upgrade_workaround) ||
|
||||
!CU_add_test(
|
||||
pSuite, "http_no_rfc9113_leading_and_trailing_ws_validation",
|
||||
test_nghttp2_http_no_rfc9113_leading_and_trailing_ws_validation) ||
|
||||
!CU_add_test(pSuite, "frame_pack_headers",
|
||||
test_nghttp2_frame_pack_headers) ||
|
||||
!CU_add_test(pSuite, "frame_pack_headers_frame_too_large",
|
||||
|
|
|
@ -13224,3 +13224,83 @@ void test_nghttp2_http_head_method_upgrade_workaround(void) {
|
|||
nghttp2_session_del(session);
|
||||
nghttp2_bufs_free(&bufs);
|
||||
}
|
||||
|
||||
void test_nghttp2_http_no_rfc9113_leading_and_trailing_ws_validation(void) {
|
||||
nghttp2_session *session;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
nghttp2_hd_deflater deflater;
|
||||
nghttp2_mem *mem;
|
||||
nghttp2_bufs bufs;
|
||||
ssize_t rv;
|
||||
const nghttp2_nv ws_reqnv[] = {
|
||||
MAKE_NV(":path", "/"),
|
||||
MAKE_NV(":method", "GET"),
|
||||
MAKE_NV(":authority", "localhost"),
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV("foo", "bar "),
|
||||
};
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_option *option;
|
||||
|
||||
mem = nghttp2_mem_default();
|
||||
frame_pack_bufs_init(&bufs);
|
||||
|
||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks.send_callback = null_send_callback;
|
||||
|
||||
/* By default, the leading and trailing white spaces validation is
|
||||
enabled as per RFC 9113. */
|
||||
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||
|
||||
nghttp2_hd_deflate_init(&deflater, mem);
|
||||
|
||||
rv = pack_headers(&bufs, &deflater, 1,
|
||||
NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
|
||||
ws_reqnv, ARRLEN(ws_reqnv), mem);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
|
||||
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||
nghttp2_buf_len(&bufs.head->buf));
|
||||
|
||||
CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* Turn off the validation */
|
||||
nghttp2_option_new(&option);
|
||||
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(option, 1);
|
||||
|
||||
nghttp2_session_server_new2(&session, &callbacks, NULL, option);
|
||||
|
||||
nghttp2_hd_deflate_init(&deflater, mem);
|
||||
|
||||
rv = pack_headers(&bufs, &deflater, 1,
|
||||
NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM,
|
||||
ws_reqnv, ARRLEN(ws_reqnv), mem);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
|
||||
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||
nghttp2_buf_len(&bufs.head->buf));
|
||||
|
||||
CU_ASSERT((ssize_t)nghttp2_buf_len(&bufs.head->buf) == rv);
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(NULL == item);
|
||||
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
nghttp2_session_del(session);
|
||||
nghttp2_option_del(option);
|
||||
|
||||
nghttp2_bufs_free(&bufs);
|
||||
}
|
||||
|
|
|
@ -178,5 +178,6 @@ void test_nghttp2_http_ignore_content_length(void);
|
|||
void test_nghttp2_http_record_request_method(void);
|
||||
void test_nghttp2_http_push_promise(void);
|
||||
void test_nghttp2_http_head_method_upgrade_workaround(void);
|
||||
void test_nghttp2_http_no_rfc9113_leading_and_trailing_ws_validation(void);
|
||||
|
||||
#endif /* NGHTTP2_SESSION_TEST_H */
|
||||
|
|
Loading…
Reference in New Issue