Deal with :protocol pseudo header
This commit is contained in:
parent
33f6e90a56
commit
a19d8f5d31
|
@ -113,7 +113,7 @@ static int check_path(nghttp2_stream *stream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||||
int trailer) {
|
int trailer, int connect_protocol) {
|
||||||
if (nv->name->base[0] == ':') {
|
if (nv->name->base[0] == ':') {
|
||||||
if (trailer ||
|
if (trailer ||
|
||||||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
|
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
|
||||||
|
@ -146,10 +146,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
}
|
}
|
||||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
||||||
if (stream->http_flags &
|
|
||||||
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
|
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
|
@ -162,9 +158,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_TOKEN__PATH:
|
case NGHTTP2_TOKEN__PATH:
|
||||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
|
||||||
}
|
|
||||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
}
|
}
|
||||||
|
@ -175,9 +168,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_TOKEN__SCHEME:
|
case NGHTTP2_TOKEN__SCHEME:
|
||||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
|
||||||
}
|
|
||||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
}
|
}
|
||||||
|
@ -186,6 +176,15 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
|
||||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_TOKEN__PROTOCOL:
|
||||||
|
if (!connect_protocol) {
|
||||||
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
|
||||||
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case NGHTTP2_TOKEN_HOST:
|
case NGHTTP2_TOKEN_HOST:
|
||||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
|
@ -458,16 +457,22 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||||
return http_request_on_header(stream, nv, trailer);
|
return http_request_on_header(
|
||||||
|
stream, nv, trailer,
|
||||||
|
session->server && session->local_settings.enable_connect_protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
return http_response_on_header(stream, nv, trailer);
|
return http_response_on_header(stream, nv, trailer);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||||
nghttp2_frame *frame) {
|
nghttp2_frame *frame,
|
||||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
int connect_protocol) {
|
||||||
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
|
if (!connect_protocol &&
|
||||||
|
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
|
||||||
|
if ((stream->http_flags &
|
||||||
|
(NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
|
||||||
|
(stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
stream->content_length = -1;
|
stream->content_length = -1;
|
||||||
|
@ -478,6 +483,11 @@ int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||||
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
|
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
|
||||||
|
(!connect_protocol ||
|
||||||
|
(stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (!check_path(stream)) {
|
if (!check_path(stream)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,13 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
|
||||||
int trailer);
|
int trailer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is called when request header is received. This
|
* This function is called when request header is received.
|
||||||
|
* |connect_protocol| is nonzero if SETTINGS_ENABLE_CONNECT_PROTOCOL
|
||||||
|
* is enabled by the local endpoint (which must be server). This
|
||||||
* function performs validation and returns 0 if it succeeds, or -1.
|
* function performs validation and returns 0 if it succeeds, or -1.
|
||||||
*/
|
*/
|
||||||
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
||||||
nghttp2_frame *frame);
|
nghttp2_frame *frame, int connect_protocol);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is called when response header is received. This
|
* This function is called when response header is received. This
|
||||||
|
|
|
@ -3746,13 +3746,17 @@ static int session_after_header_block_received(nghttp2_session *session) {
|
||||||
subject_stream = nghttp2_session_get_stream(
|
subject_stream = nghttp2_session_get_stream(
|
||||||
session, frame->push_promise.promised_stream_id);
|
session, frame->push_promise.promised_stream_id);
|
||||||
if (subject_stream) {
|
if (subject_stream) {
|
||||||
rv = nghttp2_http_on_request_headers(subject_stream, frame);
|
rv = nghttp2_http_on_request_headers(
|
||||||
|
subject_stream, frame,
|
||||||
|
session->server && session->local_settings.enable_connect_protocol);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(frame->hd.type == NGHTTP2_HEADERS);
|
assert(frame->hd.type == NGHTTP2_HEADERS);
|
||||||
switch (frame->headers.cat) {
|
switch (frame->headers.cat) {
|
||||||
case NGHTTP2_HCAT_REQUEST:
|
case NGHTTP2_HCAT_REQUEST:
|
||||||
rv = nghttp2_http_on_request_headers(stream, frame);
|
rv = nghttp2_http_on_request_headers(
|
||||||
|
stream, frame,
|
||||||
|
session->server && session->local_settings.enable_connect_protocol);
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_HCAT_RESPONSE:
|
case NGHTTP2_HCAT_RESPONSE:
|
||||||
case NGHTTP2_HCAT_PUSH_RESPONSE:
|
case NGHTTP2_HCAT_PUSH_RESPONSE:
|
||||||
|
|
|
@ -130,7 +130,8 @@ typedef enum {
|
||||||
/* "http" or "https" scheme */
|
/* "http" or "https" scheme */
|
||||||
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
|
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
|
||||||
/* set if final response is expected */
|
/* set if final response is expected */
|
||||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14
|
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14,
|
||||||
|
NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15,
|
||||||
} nghttp2_http_flag;
|
} nghttp2_http_flag;
|
||||||
|
|
||||||
struct nghttp2_stream {
|
struct nghttp2_stream {
|
||||||
|
|
|
@ -10907,6 +10907,17 @@ void test_nghttp2_http_mandatory_headers(void) {
|
||||||
const nghttp2_nv asteriskoptions2_reqnv[] = {
|
const nghttp2_nv asteriskoptions2_reqnv[] = {
|
||||||
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
|
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
|
||||||
MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")};
|
MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")};
|
||||||
|
const nghttp2_nv connectproto_reqnv[] = {
|
||||||
|
MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
|
||||||
|
MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost"),
|
||||||
|
MAKE_NV(":protocol", "websocket")};
|
||||||
|
const nghttp2_nv connectprotoget_reqnv[] = {
|
||||||
|
MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
|
||||||
|
MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"),
|
||||||
|
MAKE_NV(":protocol", "websocket")};
|
||||||
|
const nghttp2_nv connectprotonopath_reqnv[] = {
|
||||||
|
MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
|
||||||
|
MAKE_NV(":authority", "localhost"), MAKE_NV(":protocol", "websocket")};
|
||||||
|
|
||||||
mem = nghttp2_mem_default();
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
@ -11054,6 +11065,39 @@ void test_nghttp2_http_mandatory_headers(void) {
|
||||||
asteriskoptions2_reqnv,
|
asteriskoptions2_reqnv,
|
||||||
ARRLEN(asteriskoptions2_reqnv));
|
ARRLEN(asteriskoptions2_reqnv));
|
||||||
|
|
||||||
|
/* :protocol is not allowed unless it is enabled by the local
|
||||||
|
endpoint. */
|
||||||
|
check_nghttp2_http_recv_headers_fail(session, &deflater, 27, -1,
|
||||||
|
connectproto_reqnv,
|
||||||
|
ARRLEN(connectproto_reqnv));
|
||||||
|
|
||||||
|
nghttp2_hd_deflate_free(&deflater);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* enable SETTINGS_CONNECT_PROTOCOL */
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||||
|
|
||||||
|
session->local_settings.enable_connect_protocol = 1;
|
||||||
|
|
||||||
|
nghttp2_hd_deflate_init(&deflater, mem);
|
||||||
|
|
||||||
|
/* :protocol is allowed if SETTINGS_CONNECT_PROTOCOL is enabled by
|
||||||
|
the local endpoint. */
|
||||||
|
check_nghttp2_http_recv_headers_ok(session, &deflater, 1, -1,
|
||||||
|
connectproto_reqnv,
|
||||||
|
ARRLEN(connectproto_reqnv));
|
||||||
|
|
||||||
|
/* :protocol is only allowed with CONNECT method. */
|
||||||
|
check_nghttp2_http_recv_headers_fail(session, &deflater, 3, -1,
|
||||||
|
connectprotoget_reqnv,
|
||||||
|
ARRLEN(connectprotoget_reqnv));
|
||||||
|
|
||||||
|
/* CONNECT method with :protocol requires :path. */
|
||||||
|
check_nghttp2_http_recv_headers_fail(session, &deflater, 5, -1,
|
||||||
|
connectprotonopath_reqnv,
|
||||||
|
ARRLEN(connectprotonopath_reqnv));
|
||||||
|
|
||||||
nghttp2_hd_deflate_free(&deflater);
|
nghttp2_hd_deflate_free(&deflater);
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
Loading…
Reference in New Issue