Commit 5e7e479c authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Workaround HTTP upgrade with HEAD request

By default, we check the length of response body matches
content-length.  For HEAD request, this is not necessarily true, so we
sniff request method, and if it is HEAD, make sure that response body
length is 0.  But this does not work for HTTP Upgrade, since
nghttp2_session_upgrade() has no parameter to tell the request method
was HEAD.  This commit disables this response body length validation
for the stream upgraded by HTTP Upgrade.  We will add new version of
nghttp2_session_upgrade with the parameter to pass the request method
information so that we can handle this situation properly.
parent dfbbb081
......@@ -381,7 +381,8 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
if (!expect_response_body(stream)) {
stream->content_length = 0;
} else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
} else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT |
NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) {
stream->content_length = -1;
}
......
......@@ -6559,6 +6559,16 @@ int nghttp2_session_upgrade(nghttp2_session *session,
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
session->next_stream_id += 2;
}
/* We have no information about request header fields when Upgrade
was happened. So we don't know the request method here. If
request method is HEAD, we have a trouble because we may have
nonzero content-length header field in response headers, and we
will going to check it against the actual DATA frames, but we may
get mismatch because HEAD response body must be empty. We will
add new version of nghttp2_session_upgrade with the parameter to
pass the request method information so that we can handle this
situation properly. */
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND;
return 0;
}
......
......@@ -116,19 +116,21 @@ typedef enum {
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9,
NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10,
NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT |
NGHTTP2_HTTP_FLAG_METH_HEAD |
NGHTTP2_HTTP_FLAG_METH_OPTIONS,
NGHTTP2_HTTP_FLAG_METH_OPTIONS |
NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND,
/* :path category */
/* path starts with "/" */
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10,
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11,
/* path "*" */
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11,
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12,
/* scheme */
/* "http" or "https" scheme */
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
/* set if final response is expected */
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14
} nghttp2_http_flag;
struct nghttp2_stream {
......
......@@ -298,6 +298,8 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_http_record_request_method) ||
!CU_add_test(pSuite, "http_push_promise",
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, "frame_pack_headers",
test_nghttp2_frame_pack_headers) ||
!CU_add_test(pSuite, "frame_pack_headers_frame_too_large",
......
......@@ -9246,3 +9246,45 @@ void test_nghttp2_http_push_promise(void) {
nghttp2_session_del(session);
nghttp2_bufs_free(&bufs);
}
void test_nghttp2_http_head_method_upgrade_workaround(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
const nghttp2_nv resnv[] = {MAKE_NV(":status", "200"),
MAKE_NV("content-length", "1000000007")};
nghttp2_bufs bufs;
nghttp2_hd_deflater deflater;
nghttp2_mem *mem;
ssize_t rv;
nghttp2_stream *stream;
mem = nghttp2_mem_default();
frame_pack_bufs_init(&bufs);
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
nghttp2_session_client_new(&session, &callbacks, NULL);
nghttp2_hd_deflate_init(&deflater, mem);
nghttp2_session_upgrade(session, NULL, 0, NULL);
rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, resnv,
ARRLEN(resnv), 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);
stream = nghttp2_session_get_stream(session, 1);
CU_ASSERT(-1 == stream->content_length);
nghttp2_hd_deflate_free(&deflater);
nghttp2_session_del(session);
nghttp2_bufs_free(&bufs);
}
......@@ -141,5 +141,6 @@ void test_nghttp2_http_ignore_regular_header(void);
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);
#endif /* NGHTTP2_SESSION_TEST_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment