0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Reject HTTP/2 requests with a body size different than content-length

This commit is contained in:
Loïc Hoguin 2018-04-30 13:47:33 +02:00
parent 17349fafc2
commit 8d1f468ac0
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
2 changed files with 155 additions and 25 deletions

View file

@ -3778,14 +3778,96 @@ reject_many_pseudo_header_path(Config) ->
% before being passed into a non-HTTP/2 context, such as an HTTP/1.1
% connection, or a generic HTTP server application.
%% (RFC7540 8.1.2.6)
% A request or response that includes a payload body can include a
% content-length header field. A request or response is also malformed
% if the value of a content-length header field does not equal the sum
% of the DATA frame payload lengths that form the body. A response
% that is defined to have no payload, as described in [RFC7230],
% Section 3.3.2, can have a non-zero content-length header field, even
% though no content is included in DATA frames.
reject_data_size_smaller_than_content_length(Config) ->
doc("Requests that have a content-length header whose value does not "
"match the total length of the DATA frames must be rejected with "
"a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.6)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame with a content-length header different
%% than the sum of the DATA frame sizes.
{HeadersBlock, _} = cow_hpack:encode([
{<<":method">>, <<"POST">>},
{<<":scheme">>, <<"http">>},
{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
{<<":path">>, <<"/long_polling">>},
{<<"content-length">>, <<"12">>}
]),
ok = gen_tcp:send(Socket, [
cow_http2:headers(1, nofin, HeadersBlock),
cow_http2:data(1, fin, <<"Hello!">>)
]),
%% Receive a PROTOCOL_ERROR stream error.
{ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
ok.
reject_data_size_larger_than_content_length(Config) ->
doc("Requests that have a content-length header whose value does not "
"match the total length of the DATA frames must be rejected with "
"a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.6)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame with a content-length header different
%% than the sum of the DATA frame sizes.
{HeadersBlock, _} = cow_hpack:encode([
{<<":method">>, <<"POST">>},
{<<":scheme">>, <<"http">>},
{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
{<<":path">>, <<"/long_polling">>},
{<<"content-length">>, <<"12">>}
]),
ok = gen_tcp:send(Socket, [
cow_http2:headers(1, nofin, HeadersBlock),
cow_http2:data(1, nofin, <<"Hello! World! Universe!">>),
cow_http2:data(1, fin, <<"Multiverse!">>)
]),
%% Receive a PROTOCOL_ERROR stream error.
{ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
ok.
reject_content_length_without_data(Config) ->
doc("Requests that have a content-length header whose value does not "
"match the total length of the DATA frames must be rejected with "
"a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.6)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame with a content-length header different
%% than the sum of the DATA frame sizes.
{HeadersBlock, _} = cow_hpack:encode([
{<<":method">>, <<"POST">>},
{<<":scheme">>, <<"http">>},
{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
{<<":path">>, <<"/long_polling">>},
{<<"content-length">>, <<"12">>}
]),
ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
%% Receive a PROTOCOL_ERROR stream error.
{ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
ok.
reject_data_size_different_than_content_length_with_trailers(Config) ->
doc("Requests that have a content-length header whose value does not "
"match the total length of the DATA frames must be rejected with "
"a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.6)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame with a content-length header different
%% than the sum of the DATA frame sizes.
{HeadersBlock, EncodeState} = cow_hpack:encode([
{<<":method">>, <<"POST">>},
{<<":scheme">>, <<"http">>},
{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
{<<":path">>, <<"/long_polling">>},
{<<"content-length">>, <<"12">>},
{<<"trailer">>, <<"x-checksum">>}
]),
{TrailersBlock, _} = cow_hpack:encode([
{<<"x-checksum">>, <<"md5:4cc909a007407f3706399b6496babec3">>}
], EncodeState),
ok = gen_tcp:send(Socket, [
cow_http2:headers(1, nofin, HeadersBlock),
cow_http2:data(1, nofin, <<"Hello!">>),
cow_http2:headers(1, fin, TrailersBlock)
]),
%% Receive a PROTOCOL_ERROR stream error.
{ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
ok.
reject_duplicate_content_length_header(Config) ->
doc("A request with duplicate content-length headers must be rejected "