Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
Tatsuhiro Tsujikawa | e6381b2b65 | |
Tatsuhiro Tsujikawa | b6930e3d80 | |
Tatsuhiro Tsujikawa | 19d28365e9 | |
Tatsuhiro Tsujikawa | a26eb08a89 | |
Tatsuhiro Tsujikawa | 8625a93bb0 | |
Tatsuhiro Tsujikawa | 51e4ca9f7e |
|
@ -9,6 +9,7 @@ HEADERS = [
|
||||||
':scheme',
|
':scheme',
|
||||||
':status',
|
':status',
|
||||||
':host', # for spdy
|
':host', # for spdy
|
||||||
|
':protocol',
|
||||||
'expect',
|
'expect',
|
||||||
'host',
|
'host',
|
||||||
'if-modified-since',
|
'if-modified-since',
|
||||||
|
@ -31,6 +32,7 @@ HEADERS = [
|
||||||
"user-agent",
|
"user-agent",
|
||||||
"date",
|
"date",
|
||||||
"content-type",
|
"content-type",
|
||||||
|
"sec-websocket-accept",
|
||||||
# disallowed h1 headers
|
# disallowed h1 headers
|
||||||
'connection',
|
'connection',
|
||||||
'keep-alive',
|
'keep-alive',
|
||||||
|
|
|
@ -67,6 +67,7 @@ HEADERS = [
|
||||||
('keep-alive',None),
|
('keep-alive',None),
|
||||||
('proxy-connection', None),
|
('proxy-connection', None),
|
||||||
('upgrade', None),
|
('upgrade', None),
|
||||||
|
(':protocol', None),
|
||||||
]
|
]
|
||||||
|
|
||||||
def to_enum_hd(k):
|
def to_enum_hd(k):
|
||||||
|
|
|
@ -680,7 +680,12 @@ typedef enum {
|
||||||
/**
|
/**
|
||||||
* SETTINGS_MAX_HEADER_LIST_SIZE
|
* SETTINGS_MAX_HEADER_LIST_SIZE
|
||||||
*/
|
*/
|
||||||
NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06
|
NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06,
|
||||||
|
/**
|
||||||
|
* SETTINGS_ENABLE_CONNECT_PROTOCOL
|
||||||
|
* (https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-00)
|
||||||
|
*/
|
||||||
|
NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08
|
||||||
} nghttp2_settings_id;
|
} nghttp2_settings_id;
|
||||||
/* Note: If we add SETTINGS, update the capacity of
|
/* Note: If we add SETTINGS, update the capacity of
|
||||||
NGHTTP2_INBOUND_NUM_IV as well */
|
NGHTTP2_INBOUND_NUM_IV as well */
|
||||||
|
|
|
@ -1050,6 +1050,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||||
|
if (iv[i].value != 0 && iv[i].value != 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -271,6 +271,15 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 9:
|
||||||
|
switch (name[8]) {
|
||||||
|
case 'l':
|
||||||
|
if (memeq(":protoco", name, 8)) {
|
||||||
|
return NGHTTP2_TOKEN__PROTOCOL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
switch (name[9]) {
|
switch (name[9]) {
|
||||||
case 'e':
|
case 'e':
|
||||||
|
|
|
@ -111,6 +111,7 @@ typedef enum {
|
||||||
NGHTTP2_TOKEN_KEEP_ALIVE,
|
NGHTTP2_TOKEN_KEEP_ALIVE,
|
||||||
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
NGHTTP2_TOKEN_PROXY_CONNECTION,
|
||||||
NGHTTP2_TOKEN_UPGRADE,
|
NGHTTP2_TOKEN_UPGRADE,
|
||||||
|
NGHTTP2_TOKEN__PROTOCOL,
|
||||||
} nghttp2_token;
|
} nghttp2_token;
|
||||||
|
|
||||||
struct nghttp2_hd_entry;
|
struct nghttp2_hd_entry;
|
||||||
|
|
|
@ -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:
|
||||||
|
@ -4361,6 +4365,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||||
session->local_settings.max_header_list_size = iv[i].value;
|
session->local_settings.max_header_list_size = iv[i].value;
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||||
|
session->local_settings.enable_connect_protocol = iv[i].value;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4499,6 +4506,26 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||||
|
|
||||||
session->remote_settings.max_header_list_size = entry->value;
|
session->remote_settings.max_header_list_size = entry->value;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||||
|
|
||||||
|
if (entry->value != 0 && entry->value != 1) {
|
||||||
|
return session_handle_invalid_connection(
|
||||||
|
session, frame, NGHTTP2_ERR_PROTO,
|
||||||
|
"SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session->server &&
|
||||||
|
session->remote_settings.enable_connect_protocol &&
|
||||||
|
entry->value == 0) {
|
||||||
|
return session_handle_invalid_connection(
|
||||||
|
session, frame, NGHTTP2_ERR_PROTO,
|
||||||
|
"SETTINGS: server attempted to disable "
|
||||||
|
"SETTINGS_ENABLE_CONNECT_PROTOCOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
session->remote_settings.enable_connect_protocol = entry->value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5250,6 +5277,7 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
|
||||||
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||||
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
||||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||||
|
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
|
DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
|
||||||
|
@ -7330,6 +7358,8 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
|
||||||
return session->remote_settings.max_frame_size;
|
return session->remote_settings.max_frame_size;
|
||||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||||
return session->remote_settings.max_header_list_size;
|
return session->remote_settings.max_header_list_size;
|
||||||
|
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||||
|
return session->remote_settings.enable_connect_protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(0);
|
assert(0);
|
||||||
|
@ -7351,6 +7381,8 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
|
||||||
return session->local_settings.max_frame_size;
|
return session->local_settings.max_frame_size;
|
||||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||||
return session->local_settings.max_header_list_size;
|
return session->local_settings.max_header_list_size;
|
||||||
|
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
|
||||||
|
return session->local_settings.enable_connect_protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
|
@ -164,6 +164,7 @@ typedef struct {
|
||||||
uint32_t initial_window_size;
|
uint32_t initial_window_size;
|
||||||
uint32_t max_frame_size;
|
uint32_t max_frame_size;
|
||||||
uint32_t max_header_list_size;
|
uint32_t max_header_list_size;
|
||||||
|
uint32_t enable_connect_protocol;
|
||||||
} nghttp2_settings_storage;
|
} nghttp2_settings_storage;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
22
src/http2.cc
22
src/http2.cc
|
@ -438,6 +438,11 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
|
||||||
kv = &(*it_via);
|
kv = &(*it_via);
|
||||||
it_via = it;
|
it_via = it;
|
||||||
break;
|
break;
|
||||||
|
case HD_SEC_WEBSOCKET_ACCEPT:
|
||||||
|
if (flags & HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
nva.push_back(
|
nva.push_back(
|
||||||
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
|
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
|
||||||
|
@ -821,6 +826,11 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
||||||
return HD_FORWARDED;
|
return HD_FORWARDED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (util::streq_l(":protoco", name, 8)) {
|
||||||
|
return HD__PROTOCOL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
|
@ -926,6 +936,15 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 20:
|
||||||
|
switch (name[19]) {
|
||||||
|
case 't':
|
||||||
|
if (util::streq_l("sec-websocket-accep", name, 19)) {
|
||||||
|
return HD_SEC_WEBSOCKET_ACCEPT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1313,7 +1332,8 @@ std::string path_join(const StringRef &base_path, const StringRef &base_query,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool expect_response_body(int status_code) {
|
bool expect_response_body(int status_code) {
|
||||||
return status_code / 100 != 1 && status_code != 304 && status_code != 204;
|
return status_code == 101 ||
|
||||||
|
(status_code / 100 != 1 && status_code != 304 && status_code != 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool expect_response_body(const std::string &method, int status_code) {
|
bool expect_response_body(const std::string &method, int status_code) {
|
||||||
|
|
|
@ -206,6 +206,8 @@ enum HeaderBuildOp {
|
||||||
// Strip above all header fields.
|
// Strip above all header fields.
|
||||||
HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
|
HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
|
||||||
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA,
|
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA,
|
||||||
|
// Sec-WebSocket-Accept header field must be stripped.
|
||||||
|
HDOP_STRIP_SEC_WEBSOCKET_ACCEPT = 1 << 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
||||||
|
@ -293,6 +295,7 @@ enum {
|
||||||
HD__HOST,
|
HD__HOST,
|
||||||
HD__METHOD,
|
HD__METHOD,
|
||||||
HD__PATH,
|
HD__PATH,
|
||||||
|
HD__PROTOCOL,
|
||||||
HD__SCHEME,
|
HD__SCHEME,
|
||||||
HD__STATUS,
|
HD__STATUS,
|
||||||
HD_ACCEPT_ENCODING,
|
HD_ACCEPT_ENCODING,
|
||||||
|
@ -313,6 +316,7 @@ enum {
|
||||||
HD_LINK,
|
HD_LINK,
|
||||||
HD_LOCATION,
|
HD_LOCATION,
|
||||||
HD_PROXY_CONNECTION,
|
HD_PROXY_CONNECTION,
|
||||||
|
HD_SEC_WEBSOCKET_ACCEPT,
|
||||||
HD_SERVER,
|
HD_SERVER,
|
||||||
HD_TE,
|
HD_TE,
|
||||||
HD_TRAILER,
|
HD_TRAILER,
|
||||||
|
|
|
@ -978,7 +978,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||||
StringRef path;
|
StringRef path;
|
||||||
// CONNECT method does not have path. But we requires path in
|
// CONNECT method does not have path. But we requires path in
|
||||||
// host-path mapping. As workaround, we assume that path is "/".
|
// host-path mapping. As workaround, we assume that path is "/".
|
||||||
if (req.method != HTTP_CONNECT) {
|
if (!req.regular_connect_method()) {
|
||||||
path = req.path;
|
path = req.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -722,7 +722,12 @@ bool Downstream::validate_response_recv_body_length() const {
|
||||||
|
|
||||||
void Downstream::check_upgrade_fulfilled() {
|
void Downstream::check_upgrade_fulfilled() {
|
||||||
if (req_.method == HTTP_CONNECT) {
|
if (req_.method == HTTP_CONNECT) {
|
||||||
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
|
if (req_.connect_proto) {
|
||||||
|
// TODO For websocket, check Sec-WebSocket-Accept header field.
|
||||||
|
upgraded_ = resp_.http_status == 101;
|
||||||
|
} else {
|
||||||
|
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,12 @@ private:
|
||||||
bool trailer_key_prev_;
|
bool trailer_key_prev_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Protocols allowed in HTTP/2 :protocol header field.
|
||||||
|
enum shrpx_connect_proto {
|
||||||
|
CONNECT_PROTO_NONE,
|
||||||
|
CONNECT_PROTO_WEBSOCKET,
|
||||||
|
};
|
||||||
|
|
||||||
struct Request {
|
struct Request {
|
||||||
Request(BlockAllocator &balloc)
|
Request(BlockAllocator &balloc)
|
||||||
: fs(balloc, 16),
|
: fs(balloc, 16),
|
||||||
|
@ -142,6 +148,7 @@ struct Request {
|
||||||
method(-1),
|
method(-1),
|
||||||
http_major(1),
|
http_major(1),
|
||||||
http_minor(1),
|
http_minor(1),
|
||||||
|
connect_proto(CONNECT_PROTO_NONE),
|
||||||
upgrade_request(false),
|
upgrade_request(false),
|
||||||
http2_upgrade_seen(false),
|
http2_upgrade_seen(false),
|
||||||
connection_close(false),
|
connection_close(false),
|
||||||
|
@ -153,6 +160,14 @@ struct Request {
|
||||||
unconsumed_body_length -= len;
|
unconsumed_body_length -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool regular_connect_method() const {
|
||||||
|
return method == HTTP_CONNECT && !connect_proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool extended_connect_method() const {
|
||||||
|
return method == HTTP_CONNECT && connect_proto;
|
||||||
|
}
|
||||||
|
|
||||||
FieldStore fs;
|
FieldStore fs;
|
||||||
// Timestamp when all request header fields are received.
|
// Timestamp when all request header fields are received.
|
||||||
std::shared_ptr<Timestamp> tstamp;
|
std::shared_ptr<Timestamp> tstamp;
|
||||||
|
@ -176,6 +191,9 @@ struct Request {
|
||||||
int method;
|
int method;
|
||||||
// HTTP major and minor version
|
// HTTP major and minor version
|
||||||
int http_major, http_minor;
|
int http_major, http_minor;
|
||||||
|
// connect_protocol specified in HTTP/2 :protocol pseudo header
|
||||||
|
// field which enables extended CONNECT method.
|
||||||
|
int connect_proto;
|
||||||
// Returns true if the request is HTTP upgrade (HTTP Upgrade or
|
// Returns true if the request is HTTP upgrade (HTTP Upgrade or
|
||||||
// CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2
|
// CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2
|
||||||
// Upgrade, check get_http2_upgrade_request().
|
// Upgrade, check get_http2_upgrade_request().
|
||||||
|
|
|
@ -250,7 +250,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
auto &http2conf = config->http2;
|
auto &http2conf = config->http2;
|
||||||
|
|
||||||
auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy ||
|
auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy ||
|
||||||
req.method == HTTP_CONNECT;
|
req.regular_connect_method();
|
||||||
|
|
||||||
// http2session_ has already in CONNECTED state, so we can get
|
// http2session_ has already in CONNECTED state, so we can get
|
||||||
// addr_idx here.
|
// addr_idx here.
|
||||||
|
@ -288,7 +288,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
nva.push_back(
|
nva.push_back(
|
||||||
http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
|
http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
|
||||||
|
|
||||||
if (req.method != HTTP_CONNECT) {
|
if (!req.regular_connect_method()) {
|
||||||
assert(!req.scheme.empty());
|
assert(!req.scheme.empty());
|
||||||
|
|
||||||
auto addr = http2session_->get_addr();
|
auto addr = http2session_->get_addr();
|
||||||
|
@ -339,7 +339,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
if (fwdconf.params) {
|
if (fwdconf.params) {
|
||||||
auto params = fwdconf.params;
|
auto params = fwdconf.params;
|
||||||
|
|
||||||
if (config->http2_proxy || req.method == HTTP_CONNECT) {
|
if (config->http2_proxy || req.regular_connect_method()) {
|
||||||
params &= ~FORWARDED_PROTO;
|
params &= ~FORWARDED_PROTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
||||||
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff->value));
|
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff->value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->http2_proxy && req.method != HTTP_CONNECT) {
|
if (!config->http2_proxy && !req.regular_connect_method()) {
|
||||||
auto xfp = xfpconf.strip_incoming
|
auto xfp = xfpconf.strip_incoming
|
||||||
? nullptr
|
? nullptr
|
||||||
: req.fs.header(http2::HD_X_FORWARDED_PROTO);
|
: req.fs.header(http2::HD_X_FORWARDED_PROTO);
|
||||||
|
|
|
@ -391,6 +391,17 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto connect_proto = req.fs.header(http2::HD__PROTOCOL);
|
||||||
|
if (connect_proto) {
|
||||||
|
if (connect_proto->value != "websocket") {
|
||||||
|
if (error_reply(downstream, 400) != 0) {
|
||||||
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
req.connect_proto = CONNECT_PROTO_WEBSOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
||||||
req.http2_expect_body = true;
|
req.http2_expect_body = true;
|
||||||
} else if (req.fs.content_length == -1) {
|
} else if (req.fs.content_length == -1) {
|
||||||
|
@ -1001,7 +1012,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
|
||||||
flow_control_ = true;
|
flow_control_ = true;
|
||||||
|
|
||||||
// TODO Maybe call from outside?
|
// TODO Maybe call from outside?
|
||||||
std::array<nghttp2_settings_entry, 3> entry;
|
std::array<nghttp2_settings_entry, 4> entry;
|
||||||
size_t nentry = 2;
|
size_t nentry = 2;
|
||||||
|
|
||||||
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||||
|
@ -1014,6 +1025,12 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
|
||||||
entry[1].value = http2conf.upstream.window_size;
|
entry[1].value = http2conf.upstream.window_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!config->http2_proxy) {
|
||||||
|
entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
|
||||||
|
entry[nentry].value = 1;
|
||||||
|
++nentry;
|
||||||
|
}
|
||||||
|
|
||||||
if (http2conf.upstream.decoder_dynamic_table_size !=
|
if (http2conf.upstream.decoder_dynamic_table_size !=
|
||||||
NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
|
NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
|
||||||
entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||||
|
@ -1662,11 +1679,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
nva.reserve(resp.fs.headers().size() + 5 +
|
nva.reserve(resp.fs.headers().size() + 5 +
|
||||||
httpconf.add_response_headers.size());
|
httpconf.add_response_headers.size());
|
||||||
|
|
||||||
auto response_status = http2::stringify_status(balloc, resp.http_status);
|
|
||||||
|
|
||||||
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
|
|
||||||
|
|
||||||
if (downstream->get_non_final_response()) {
|
if (downstream->get_non_final_response()) {
|
||||||
|
auto response_status = http2::stringify_status(balloc, resp.http_status);
|
||||||
|
|
||||||
|
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
|
||||||
|
|
||||||
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(),
|
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(),
|
||||||
http2::HDOP_STRIP_ALL);
|
http2::HDOP_STRIP_ALL);
|
||||||
|
|
||||||
|
@ -1688,8 +1705,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
http2::copy_headers_to_nva_nocopy(
|
auto striphd_flags = http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA;
|
||||||
nva, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
|
StringRef response_status;
|
||||||
|
|
||||||
|
if (req.connect_proto && resp.http_status == 101) {
|
||||||
|
response_status = http2::stringify_status(balloc, 200);
|
||||||
|
striphd_flags |= http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT;
|
||||||
|
} else {
|
||||||
|
response_status = http2::stringify_status(balloc, resp.http_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
|
||||||
|
|
||||||
|
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), striphd_flags);
|
||||||
|
|
||||||
if (!config->http2_proxy && !httpconf.no_server_rewrite) {
|
if (!config->http2_proxy && !httpconf.no_server_rewrite) {
|
||||||
nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
|
nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
|
||||||
|
@ -1700,7 +1728,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
|
if (!req.regular_connect_method() || !downstream->get_upgraded()) {
|
||||||
auto affinity_cookie = downstream->get_affinity_cookie_to_send();
|
auto affinity_cookie = downstream->get_affinity_cookie_to_send();
|
||||||
if (affinity_cookie) {
|
if (affinity_cookie) {
|
||||||
auto dconn = downstream->get_downstream_connection();
|
auto dconn = downstream->get_downstream_connection();
|
||||||
|
@ -1874,7 +1902,7 @@ int Http2Upstream::on_downstream_abort_request_with_https_redirect(
|
||||||
|
|
||||||
int Http2Upstream::redirect_to_https(Downstream *downstream) {
|
int Http2Upstream::redirect_to_https(Downstream *downstream) {
|
||||||
auto &req = downstream->request();
|
auto &req = downstream->request();
|
||||||
if (req.method == HTTP_CONNECT || req.scheme != "http") {
|
if (req.regular_connect_method() || req.scheme != "http") {
|
||||||
return error_reply(downstream, 400);
|
return error_reply(downstream, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -484,7 +484,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||||
|
|
||||||
auto &balloc = downstream_->get_block_allocator();
|
auto &balloc = downstream_->get_block_allocator();
|
||||||
|
|
||||||
auto connect_method = req.method == HTTP_CONNECT;
|
auto connect_method = req.regular_connect_method();
|
||||||
|
|
||||||
auto config = get_config();
|
auto config = get_config();
|
||||||
auto &httpconf = config->http;
|
auto &httpconf = config->http;
|
||||||
|
@ -508,7 +508,8 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||||
auto buf = downstream_->get_request_buf();
|
auto buf = downstream_->get_request_buf();
|
||||||
|
|
||||||
// Assume that method and request path do not contain \r\n.
|
// Assume that method and request path do not contain \r\n.
|
||||||
auto meth = http2::to_method_string(req.method);
|
auto meth = http2::to_method_string(
|
||||||
|
req.connect_proto == CONNECT_PROTO_WEBSOCKET ? HTTP_GET : req.method);
|
||||||
buf->append(meth);
|
buf->append(meth);
|
||||||
buf->append(' ');
|
buf->append(' ');
|
||||||
|
|
||||||
|
@ -552,7 +553,8 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||||
|
|
||||||
// set transfer-encoding only when content-length is unknown and
|
// set transfer-encoding only when content-length is unknown and
|
||||||
// request body is expected.
|
// request body is expected.
|
||||||
if (!connect_method && req.http2_expect_body && req.fs.content_length == -1) {
|
if (req.method != HTTP_CONNECT && req.http2_expect_body &&
|
||||||
|
req.fs.content_length == -1) {
|
||||||
downstream_->set_chunked_request(true);
|
downstream_->set_chunked_request(true);
|
||||||
buf->append("Transfer-Encoding: chunked\r\n");
|
buf->append("Transfer-Encoding: chunked\r\n");
|
||||||
}
|
}
|
||||||
|
@ -561,7 +563,11 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||||
buf->append("Connection: close\r\n");
|
buf->append("Connection: close\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connect_method && req.upgrade_request) {
|
if (req.connect_proto == CONNECT_PROTO_WEBSOCKET) {
|
||||||
|
// TODO Generate Sec-WebSocket-Key
|
||||||
|
buf->append("Upgrade: websocket\r\nConnection: "
|
||||||
|
"Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n");
|
||||||
|
} else if (!connect_method && req.upgrade_request) {
|
||||||
auto connection = req.fs.header(http2::HD_CONNECTION);
|
auto connection = req.fs.header(http2::HD_CONNECTION);
|
||||||
if (connection) {
|
if (connection) {
|
||||||
buf->append("Connection: ");
|
buf->append("Connection: ");
|
||||||
|
@ -693,7 +699,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||||
// Don't call signal_write() if we anticipate request body. We call
|
// Don't call signal_write() if we anticipate request body. We call
|
||||||
// signal_write() when we received request body chunk, and it
|
// signal_write() when we received request body chunk, and it
|
||||||
// enables us to send headers and data in one writev system call.
|
// enables us to send headers and data in one writev system call.
|
||||||
if (connect_method ||
|
if (connect_method || req.connect_proto ||
|
||||||
(!req.http2_expect_body && req.fs.content_length == 0)) {
|
(!req.http2_expect_body && req.fs.content_length == 0)) {
|
||||||
signal_write();
|
signal_write();
|
||||||
}
|
}
|
||||||
|
@ -901,7 +907,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (resp.http_status / 100 == 1 ||
|
} else if (resp.http_status / 100 == 1 ||
|
||||||
(resp.http_status == 200 && req.method == HTTP_CONNECT)) {
|
(resp.http_status == 200 && req.regular_connect_method())) {
|
||||||
if (resp.fs.header(http2::HD_CONTENT_LENGTH) ||
|
if (resp.fs.header(http2::HD_CONTENT_LENGTH) ||
|
||||||
resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
|
resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -3480,6 +3480,29 @@ void test_nghttp2_session_on_settings_received(void) {
|
||||||
CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream1->state);
|
CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream1->state);
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* It is invalid that peer disables ENABLE_CONNECT_PROTOCOL once it
|
||||||
|
has been enabled. */
|
||||||
|
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||||
|
|
||||||
|
session->remote_settings.enable_connect_protocol = 1;
|
||||||
|
|
||||||
|
iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
|
||||||
|
iv[0].value = 0;
|
||||||
|
|
||||||
|
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1),
|
||||||
|
1);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
|
||||||
|
|
||||||
|
nghttp2_frame_settings_free(&frame.settings, mem);
|
||||||
|
|
||||||
|
item = nghttp2_session_get_next_ob_item(session);
|
||||||
|
|
||||||
|
CU_ASSERT(NULL != item);
|
||||||
|
CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_on_push_promise_received(void) {
|
void test_nghttp2_session_on_push_promise_received(void) {
|
||||||
|
@ -10851,6 +10874,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();
|
||||||
|
|
||||||
|
@ -10998,6 +11032,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