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.
This commit is contained in:
parent
dfbbb08124
commit
5e7e479c6c
|
@ -381,7 +381,8 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
|
||||||
|
|
||||||
if (!expect_response_body(stream)) {
|
if (!expect_response_body(stream)) {
|
||||||
stream->content_length = 0;
|
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;
|
stream->content_length = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6559,6 +6559,16 @@ int nghttp2_session_upgrade(nghttp2_session *session,
|
||||||
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
|
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
|
||||||
session->next_stream_id += 2;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,19 +116,21 @@ typedef enum {
|
||||||
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
|
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
|
||||||
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
|
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
|
||||||
NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9,
|
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_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT |
|
||||||
NGHTTP2_HTTP_FLAG_METH_HEAD |
|
NGHTTP2_HTTP_FLAG_METH_HEAD |
|
||||||
NGHTTP2_HTTP_FLAG_METH_OPTIONS,
|
NGHTTP2_HTTP_FLAG_METH_OPTIONS |
|
||||||
|
NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND,
|
||||||
/* :path category */
|
/* :path category */
|
||||||
/* path starts with "/" */
|
/* path starts with "/" */
|
||||||
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10,
|
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11,
|
||||||
/* path "*" */
|
/* path "*" */
|
||||||
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11,
|
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12,
|
||||||
/* scheme */
|
/* scheme */
|
||||||
/* "http" or "https" 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 */
|
/* 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;
|
} nghttp2_http_flag;
|
||||||
|
|
||||||
struct nghttp2_stream {
|
struct nghttp2_stream {
|
||||||
|
|
|
@ -298,6 +298,8 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_http_record_request_method) ||
|
test_nghttp2_http_record_request_method) ||
|
||||||
!CU_add_test(pSuite, "http_push_promise",
|
!CU_add_test(pSuite, "http_push_promise",
|
||||||
test_nghttp2_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",
|
!CU_add_test(pSuite, "frame_pack_headers",
|
||||||
test_nghttp2_frame_pack_headers) ||
|
test_nghttp2_frame_pack_headers) ||
|
||||||
!CU_add_test(pSuite, "frame_pack_headers_frame_too_large",
|
!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_session_del(session);
|
||||||
nghttp2_bufs_free(&bufs);
|
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_ignore_content_length(void);
|
||||||
void test_nghttp2_http_record_request_method(void);
|
void test_nghttp2_http_record_request_method(void);
|
||||||
void test_nghttp2_http_push_promise(void);
|
void test_nghttp2_http_push_promise(void);
|
||||||
|
void test_nghttp2_http_head_method_upgrade_workaround(void);
|
||||||
|
|
||||||
#endif /* NGHTTP2_SESSION_TEST_H */
|
#endif /* NGHTTP2_SESSION_TEST_H */
|
||||||
|
|
Loading…
Reference in New Issue