aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2018-04-27 17:58:11 +0200
committerLoïc Hoguin <[email protected]>2018-04-27 17:58:37 +0200
commit9a29aea148c55d12ac9141bc8c60b2456b4ef42b (patch)
tree41c8dbaca0b470b78fa9264b717793eb88b1d365 /test
parentd38d86c4a93340b1dd2633e1649c257e3f160d63 (diff)
downloadcowboy-9a29aea148c55d12ac9141bc8c60b2456b4ef42b.tar.gz
cowboy-9a29aea148c55d12ac9141bc8c60b2456b4ef42b.tar.bz2
cowboy-9a29aea148c55d12ac9141bc8c60b2456b4ef42b.zip
Add options controlling maximum h2 frame sizes
Diffstat (limited to 'test')
-rw-r--r--test/http2_SUITE.erl40
-rw-r--r--test/rfc7540_SUITE.erl169
2 files changed, 143 insertions, 66 deletions
diff --git a/test/http2_SUITE.erl b/test/http2_SUITE.erl
index 5b9ca95..3849303 100644
--- a/test/http2_SUITE.erl
+++ b/test/http2_SUITE.erl
@@ -28,15 +28,19 @@ groups() -> [{clear, [parallel], ct_helper:all(?MODULE)}].
init_routes(_) -> [
{"localhost", [
{"/", hello_h, []},
+ {"/echo/:key", echo_h, []},
{"/resp_iolist_body", resp_iolist_body_h, []}
]}
].
-%% Do a prior knowledge handshake (function copied from rfc7540_SUITE).
+%% Do a prior knowledge handshake (function originally copied from rfc7540_SUITE).
do_handshake(Config) ->
+ do_handshake(#{}, Config).
+
+do_handshake(Settings, Config) ->
{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
%% Send a valid preface.
- ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
+ ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(Settings)]),
%% Receive the server preface.
{ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
{ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
@@ -81,6 +85,38 @@ initial_connection_window_size(Config) ->
ConfiguredSize = Size + 65535,
ok.
+max_frame_size_sent(Config) ->
+ doc("Confirm that frames sent by Cowboy are limited in size "
+ "by the max_frame_size_sent configuration value."),
+ MaxFrameSize = 20000,
+ ProtoOpts = #{
+ env => #{dispatch => cowboy_router:compile(init_routes(Config))},
+ max_frame_size_sent => MaxFrameSize
+ },
+ {ok, _} = cowboy:start_clear(name(), [{port, 0}], ProtoOpts),
+ Port = ranch:get_port(name()),
+ {ok, Socket} = do_handshake(#{max_frame_size => MaxFrameSize + 10000}, [{port, Port}|Config]),
+ %% Send a request with a 30000 bytes body.
+ {HeadersBlock, _} = cow_hpack:encode([
+ {<<":method">>, <<"POST">>},
+ {<<":scheme">>, <<"http">>},
+ {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+ {<<":path">>, <<"/echo/read_body">>}
+ ]),
+ ok = gen_tcp:send(Socket, [
+ cow_http2:headers(1, nofin, HeadersBlock),
+ cow_http2:data(1, nofin, <<0:16384/unit:8>>),
+ cow_http2:data(1, fin, <<0:13616/unit:8>>)
+ ]),
+ %% Receive a HEADERS frame as a response.
+ {ok, <<Len:24, 1:8, _:40>>} = gen_tcp:recv(Socket, 9, 6000),
+ {ok, _} = gen_tcp:recv(Socket, Len, 6000),
+ %% The DATA frames following must have lengths of 20000
+ %% and then 10000 due to the limit.
+ {ok, <<20000:24, 0:8, _:40, _:20000/unit:8>>} = gen_tcp:recv(Socket, 20009, 6000),
+ {ok, <<10000:24, 0:8, _:40, _:10000/unit:8>>} = gen_tcp:recv(Socket, 10009, 6000),
+ ok.
+
preface_timeout_infinity(Config) ->
doc("Ensure infinity for preface_timeout is accepted."),
ProtoOpts = #{
diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl
index d4d5f30..ca483c2 100644
--- a/test/rfc7540_SUITE.erl
+++ b/test/rfc7540_SUITE.erl
@@ -1277,65 +1277,63 @@ max_frame_size_reject_larger_than_default(Config) ->
{ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
ok.
-%% @todo We need configurable SETTINGS in Cowboy for these tests.
-%% max_frame_size_config_reject_too_small(Config) ->
-%% doc("SETTINGS_MAX_FRAME_SIZE configuration values smaller than "
-%% "16384 must be rejected. (RFC7540 6.5.2)"),
-%% %% @todo This requires us to have a configurable SETTINGS in Cowboy.
-%% todo.
-%%
-%% max_frame_size_config_reject_too_large(Config) ->
-%% doc("SETTINGS_MAX_FRAME_SIZE configuration values larger than "
-%% "16777215 must be rejected. (RFC7540 6.5.2)"),
-%% %% @todo This requires us to have a configurable SETTINGS in Cowboy.
-%% todo.
-%%
-%% max_frame_size_allow_exactly_custom(Config) ->
-%% doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must allow frames "
-%% "of up to that size. (RFC7540 4.2, RFC7540 6.5.2)"),
-%% %% @todo This requires us to have a configurable SETTINGS in Cowboy.
-%% todo.
-%%
-%% max_frame_size_reject_larger_than_custom(Config) ->
-%% doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must reject frames "
-%% "of up to that size with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.5.2)"),
-%% %% @todo This requires us to have a configurable SETTINGS in Cowboy.
-%% todo.
-
-%% @todo How do I test this?
-%%
-%% max_frame_size_client_default_respect_limits(Config) ->
-%% doc("The server must not send frame sizes of more "
-%% "than 16384 by default. (RFC7540 4.1, RFC7540 4.2)"),
-
-%% This is about the client sending a SETTINGS frame.
-max_frame_size_client_override_reject_too_small(Config) ->
- doc("A SETTINGS_MAX_FRAME_SIZE smaller than 16384 must be rejected "
- "with a PROTOCOL_ERROR connection error. (RFC7540 6.5.2)"),
+max_frame_size_allow_exactly_custom(Config0) ->
+ doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must allow frames "
+ "of up to that size. (RFC7540 4.2, RFC7540 6.5.2)"),
+ %% Create a new listener that sets the maximum frame size to 30000.
+ Config = cowboy_test:init_http(name(), #{
+ env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
+ max_frame_size_received => 30000
+ }, Config0),
+ %% Do the handshake.
{ok, Socket} = do_handshake(Config),
- %% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE lower than 16384.
- ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16383:32 >>),
- %% Receive a PROTOCOL_ERROR connection error.
- {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
+ %% Send a HEADERS frame initiating a stream followed by
+ %% a single 30000 bytes DATA frame.
+ Headers = [
+ {<<":method">>, <<"POST">>},
+ {<<":scheme">>, <<"http">>},
+ {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+ {<<":path">>, <<"/long_polling">>}
+ ],
+ {HeadersBlock, _} = cow_hpack:encode(Headers),
+ ok = gen_tcp:send(Socket, [
+ cow_http2:headers(1, nofin, HeadersBlock),
+ cow_http2:data(1, fin, <<0:30000/unit:8>>)
+ ]),
+ %% Receive a proper response.
+ {ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
+ {ok, _} = gen_tcp:recv(Socket, Len2, 6000),
+ %% No errors follow due to our sending of a 25000 bytes frame.
+ {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
ok.
-%% This is about the client sending a SETTINGS frame.
-max_frame_size_client_override_reject_too_large(Config) ->
- doc("A SETTINGS_MAX_FRAME_SIZE larger than 16777215 must be rejected "
- "with a PROTOCOL_ERROR connection error. (RFC7540 6.5.2)"),
+max_frame_size_reject_larger_than_custom(Config0) ->
+ doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must reject frames "
+ "of up to that size with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.5.2)"),
+ %% Create a new listener that sets the maximum frame size to 30000.
+ Config = cowboy_test:init_http(name(), #{
+ env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
+ max_frame_size_received => 30000
+ }, Config0),
+ %% Do the handshake.
{ok, Socket} = do_handshake(Config),
- %% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE larger than 16777215.
- ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16777216:32 >>),
- %% Receive a PROTOCOL_ERROR connection error.
- {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
+ %% Send a HEADERS frame initiating a stream followed by
+ %% a single DATA frame larger than 30000 bytes.
+ Headers = [
+ {<<":method">>, <<"POST">>},
+ {<<":scheme">>, <<"http">>},
+ {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+ {<<":path">>, <<"/long_polling">>}
+ ],
+ {HeadersBlock, _} = cow_hpack:encode(Headers),
+ ok = gen_tcp:send(Socket, [
+ cow_http2:headers(1, nofin, HeadersBlock),
+ cow_http2:data(1, fin, <<0:30001/unit:8>>)
+ ]),
+ %% Receive a FRAME_SIZE_ERROR connection error.
+ {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
ok.
-%% @todo How do I test this?
-%%
-%% max_frame_size_client_custom_respect_limits(Config) ->
-%% doc("The server must not send frame sizes of more than "
-%% "client's advertised limits. (RFC7540 4.1, RFC7540 4.2)"),
-
%% I am using FRAME_SIZE_ERROR here because the information in the
%% frame header tells us this frame is at least 1 byte long, while
%% the given length is smaller; i.e. it is too small to contain
@@ -2422,7 +2420,7 @@ continuation_with_extension_frame_interleaved_error(Config) ->
% incomplete SETTINGS frame MUST be treated as a connection error
% (Section 5.4.1) of type PROTOCOL_ERROR.
-%% (RFC7540 6.5.2)
+%% Settings.
settings_header_table_size_client(Config) ->
doc("The SETTINGS_HEADER_TABLE_SIZE setting can be used to "
@@ -2581,7 +2579,7 @@ settings_max_concurrent_streams_0(Config0) ->
settings_initial_window_size(Config0) ->
doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
"change the initial window size of streams. (RFC7540 6.5.2)"),
- %% Create a new listener that allows only a single concurrent stream.
+ %% Create a new listener that sets initial window sizes to 100000.
Config = cowboy_test:init_http(name(), #{
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
initial_connection_window_size => 100000,
@@ -2630,7 +2628,7 @@ settings_initial_window_size_after_ack(Config0) ->
doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
"change the initial window size of streams. It is applied "
"to all existing streams upon receipt of the SETTINGS ack. (RFC7540 6.5.2)"),
- %% Create a new listener that allows only a single concurrent stream.
+ %% Create a new listener that sets the initial stream window sizes to 0.
Config = cowboy_test:init_http(name(), #{
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
initial_stream_window_size => 0
@@ -2670,7 +2668,7 @@ settings_initial_window_size_before_ack(Config0) ->
doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
"change the initial window size of streams. It is only "
"applied upon receipt of the SETTINGS ack. (RFC7540 6.5.2)"),
- %% Create a new listener that allows only a single concurrent stream.
+ %% Create a new listener that sets the initial stream window sizes to 0.
Config = cowboy_test:init_http(name(), #{
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
initial_stream_window_size => 0
@@ -2711,13 +2709,56 @@ settings_initial_window_size_before_ack(Config0) ->
{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
ok.
-% SETTINGS_MAX_FRAME_SIZE (0x5):
-% The initial value is 2^14 (16,384) octets. The value advertised
-% by an endpoint MUST be between this initial value and the maximum
-% allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
-% Values outside this range MUST be treated as a connection error
-% (Section 5.4.1) of type PROTOCOL_ERROR.
-%
+settings_max_frame_size(Config0) ->
+ doc("The SETTINGS_MAX_FRAME_SIZE setting can be used to "
+ "change the maximum frame size allowed. (RFC7540 6.5.2)"),
+ %% Create a new listener that sets the maximum frame size to 30000.
+ Config = cowboy_test:init_http(name(), #{
+ env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
+ max_frame_size_received => 30000
+ }, Config0),
+ %% Do the handshake.
+ {ok, Socket} = do_handshake(Config),
+ %% Send a HEADERS frame initiating a stream followed by
+ %% a single 25000 bytes DATA frame.
+ Headers = [
+ {<<":method">>, <<"POST">>},
+ {<<":scheme">>, <<"http">>},
+ {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+ {<<":path">>, <<"/long_polling">>}
+ ],
+ {HeadersBlock, _} = cow_hpack:encode(Headers),
+ ok = gen_tcp:send(Socket, [
+ cow_http2:headers(1, nofin, HeadersBlock),
+ cow_http2:data(1, fin, <<0:25000/unit:8>>)
+ ]),
+ %% Receive a proper response.
+ {ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
+ {ok, _} = gen_tcp:recv(Socket, Len2, 6000),
+ %% No errors follow due to our sending of a 25000 bytes frame.
+ {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
+ ok.
+
+settings_max_frame_size_reject_too_small(Config) ->
+ doc("A SETTINGS_MAX_FRAME_SIZE smaller than 16384 must be rejected "
+ "with a PROTOCOL_ERROR connection error. (RFC7540 6.5.2)"),
+ {ok, Socket} = do_handshake(Config),
+ %% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE lower than 16384.
+ ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16383:32 >>),
+ %% Receive a PROTOCOL_ERROR connection error.
+ {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
+ ok.
+
+settings_max_frame_size_reject_too_large(Config) ->
+ doc("A SETTINGS_MAX_FRAME_SIZE larger than 16777215 must be rejected "
+ "with a PROTOCOL_ERROR connection error. (RFC7540 6.5.2)"),
+ {ok, Socket} = do_handshake(Config),
+ %% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE larger than 16777215.
+ ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16777216:32 >>),
+ %% Receive a PROTOCOL_ERROR connection error.
+ {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
+ ok.
+
% SETTINGS_MAX_HEADER_LIST_SIZE (0x6): This advisory setting informs a
% peer of the maximum size of header list that the sender is
% prepared to accept, in octets. The value is based on the