mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Add options controlling maximum h2 frame sizes
This commit is contained in:
parent
d38d86c4a9
commit
9a29aea148
4 changed files with 174 additions and 73 deletions
|
@ -26,6 +26,8 @@ opts() :: #{
|
||||||
max_concurrent_streams => non_neg_integer() | infinity,
|
max_concurrent_streams => non_neg_integer() | infinity,
|
||||||
max_decode_table_size => non_neg_integer(),
|
max_decode_table_size => non_neg_integer(),
|
||||||
max_encode_table_size => non_neg_integer(),
|
max_encode_table_size => non_neg_integer(),
|
||||||
|
max_frame_size_received => 16384..16777215,
|
||||||
|
max_frame_size_sent => 16384..16777215 | infinity,
|
||||||
middlewares => [module()],
|
middlewares => [module()],
|
||||||
preface_timeout => timeout(),
|
preface_timeout => timeout(),
|
||||||
shutdown_timeout => timeout(),
|
shutdown_timeout => timeout(),
|
||||||
|
@ -84,6 +86,19 @@ max_encode_table_size (4096)::
|
||||||
value to what the client advertises and choose the smallest one as the
|
value to what the client advertises and choose the smallest one as the
|
||||||
encoder's header table size.
|
encoder's header table size.
|
||||||
|
|
||||||
|
max_frame_size_received (16384)::
|
||||||
|
Maximum size of the frames received by the server. This value is
|
||||||
|
advertised to the remote endpoint which can then decide to use
|
||||||
|
any value lower or equal for its frame sizes.
|
||||||
|
|
||||||
|
max_frame_size_sent (infinity)::
|
||||||
|
Maximum size of the frames sent by the server. This option allows
|
||||||
|
setting an upper limit to the frame sizes instead of blindly
|
||||||
|
following the client's advertised maximum.
|
||||||
|
+
|
||||||
|
Note that actual frame sizes may be lower than the limit when
|
||||||
|
there is not enough space left in the flow control window.
|
||||||
|
|
||||||
middlewares ([cowboy_router, cowboy_handler])::
|
middlewares ([cowboy_router, cowboy_handler])::
|
||||||
Middlewares to run for every request.
|
Middlewares to run for every request.
|
||||||
|
|
||||||
|
@ -100,7 +115,8 @@ stream_handlers ([cowboy_stream_h])::
|
||||||
|
|
||||||
* *2.4*: Add the options `initial_connection_window_size`,
|
* *2.4*: Add the options `initial_connection_window_size`,
|
||||||
`initial_stream_window_size`, `max_concurrent_streams`,
|
`initial_stream_window_size`, `max_concurrent_streams`,
|
||||||
`max_decode_table_size` and `max_encode_table_size`
|
`max_decode_table_size`, `max_encode_table_size`,
|
||||||
|
`max_frame_size_received` and `max_frame_size_sent`
|
||||||
to configure HTTP/2 SETTINGS.
|
to configure HTTP/2 SETTINGS.
|
||||||
* *2.4*: Add the experimental option `enable_connect_protocol`.
|
* *2.4*: Add the experimental option `enable_connect_protocol`.
|
||||||
* *2.0*: Protocol introduced.
|
* *2.0*: Protocol introduced.
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
max_concurrent_streams => non_neg_integer() | infinity,
|
max_concurrent_streams => non_neg_integer() | infinity,
|
||||||
max_decode_table_size => non_neg_integer(),
|
max_decode_table_size => non_neg_integer(),
|
||||||
max_encode_table_size => non_neg_integer(),
|
max_encode_table_size => non_neg_integer(),
|
||||||
|
max_frame_size_received => 16384..16777215,
|
||||||
|
max_frame_size_sent => 16384..16777215 | infinity,
|
||||||
middlewares => [module()],
|
middlewares => [module()],
|
||||||
preface_timeout => timeout(),
|
preface_timeout => timeout(),
|
||||||
shutdown_timeout => timeout(),
|
shutdown_timeout => timeout(),
|
||||||
|
@ -89,7 +91,7 @@
|
||||||
%% the final settings handling will be very different.
|
%% the final settings handling will be very different.
|
||||||
local_settings = #{
|
local_settings = #{
|
||||||
% header_table_size => 4096,
|
% header_table_size => 4096,
|
||||||
% enable_push => false, %% We are the server. Push is never enabled.
|
% enable_push => false, %% We are the server. Push is never enabled for clients.
|
||||||
% max_concurrent_streams => infinity,
|
% max_concurrent_streams => infinity,
|
||||||
initial_window_size => 65535,
|
initial_window_size => 65535,
|
||||||
max_frame_size => 16384
|
max_frame_size => 16384
|
||||||
|
@ -215,9 +217,10 @@ settings_init(State, Opts) ->
|
||||||
max_concurrent_streams, infinity),
|
max_concurrent_streams, infinity),
|
||||||
S2 = setting_from_opt(S1, Opts, initial_stream_window_size,
|
S2 = setting_from_opt(S1, Opts, initial_stream_window_size,
|
||||||
initial_window_size, 65535),
|
initial_window_size, 65535),
|
||||||
%% @todo max_frame_size
|
S3 = setting_from_opt(S2, Opts, max_frame_size_received,
|
||||||
|
max_frame_size, 16384),
|
||||||
%% @todo max_header_list_size
|
%% @todo max_header_list_size
|
||||||
Settings = setting_from_opt(S2, Opts, enable_connect_protocol,
|
Settings = setting_from_opt(S3, Opts, enable_connect_protocol,
|
||||||
enable_connect_protocol, false),
|
enable_connect_protocol, false),
|
||||||
State#state{next_settings=Settings}.
|
State#state{next_settings=Settings}.
|
||||||
|
|
||||||
|
@ -810,10 +813,15 @@ send_data(State=#state{local_window=ConnWindow},
|
||||||
Stream=#stream{local_window=StreamWindow}, IsFin, Data, In)
|
Stream=#stream{local_window=StreamWindow}, IsFin, Data, In)
|
||||||
when ConnWindow =< 0; StreamWindow =< 0 ->
|
when ConnWindow =< 0; StreamWindow =< 0 ->
|
||||||
{State, queue_data(Stream, IsFin, Data, In)};
|
{State, queue_data(Stream, IsFin, Data, In)};
|
||||||
send_data(State=#state{socket=Socket, transport=Transport, local_window=ConnWindow},
|
send_data(State=#state{socket=Socket, transport=Transport, opts=Opts,
|
||||||
|
remote_settings=RemoteSettings, local_window=ConnWindow},
|
||||||
Stream=#stream{id=StreamID, local_window=StreamWindow}, IsFin, Data, In) ->
|
Stream=#stream{id=StreamID, local_window=StreamWindow}, IsFin, Data, In) ->
|
||||||
MaxFrameSize = 16384, %% @todo Use the real SETTINGS_MAX_FRAME_SIZE set by the client.
|
RemoteMaxFrameSize = maps:get(max_frame_size, RemoteSettings, 16384),
|
||||||
MaxSendSize = min(min(ConnWindow, StreamWindow), MaxFrameSize),
|
ConfiguredMaxFrameSize = maps:get(max_frame_size_sent, Opts, infinity),
|
||||||
|
MaxSendSize = min(
|
||||||
|
min(ConnWindow, StreamWindow),
|
||||||
|
min(RemoteMaxFrameSize, ConfiguredMaxFrameSize)
|
||||||
|
),
|
||||||
case Data of
|
case Data of
|
||||||
{sendfile, Offset, Bytes, Path} when Bytes =< MaxSendSize ->
|
{sendfile, Offset, Bytes, Path} when Bytes =< MaxSendSize ->
|
||||||
Transport:send(Socket, cow_http2:data_header(StreamID, IsFin, Bytes)),
|
Transport:send(Socket, cow_http2:data_header(StreamID, IsFin, Bytes)),
|
||||||
|
|
|
@ -28,15 +28,19 @@ groups() -> [{clear, [parallel], ct_helper:all(?MODULE)}].
|
||||||
init_routes(_) -> [
|
init_routes(_) -> [
|
||||||
{"localhost", [
|
{"localhost", [
|
||||||
{"/", hello_h, []},
|
{"/", hello_h, []},
|
||||||
|
{"/echo/:key", echo_h, []},
|
||||||
{"/resp_iolist_body", resp_iolist_body_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(#{}, Config).
|
||||||
|
|
||||||
|
do_handshake(Settings, Config) ->
|
||||||
{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
|
{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
|
||||||
%% Send a valid preface.
|
%% 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.
|
%% Receive the server preface.
|
||||||
{ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
|
{ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
|
||||||
{ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 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,
|
ConfiguredSize = Size + 65535,
|
||||||
ok.
|
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) ->
|
preface_timeout_infinity(Config) ->
|
||||||
doc("Ensure infinity for preface_timeout is accepted."),
|
doc("Ensure infinity for preface_timeout is accepted."),
|
||||||
ProtoOpts = #{
|
ProtoOpts = #{
|
||||||
|
|
|
@ -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, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% @todo We need configurable SETTINGS in Cowboy for these tests.
|
max_frame_size_allow_exactly_custom(Config0) ->
|
||||||
%% max_frame_size_config_reject_too_small(Config) ->
|
doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must allow frames "
|
||||||
%% doc("SETTINGS_MAX_FRAME_SIZE configuration values smaller than "
|
"of up to that size. (RFC7540 4.2, RFC7540 6.5.2)"),
|
||||||
%% "16384 must be rejected. (RFC7540 6.5.2)"),
|
%% Create a new listener that sets the maximum frame size to 30000.
|
||||||
%% %% @todo This requires us to have a configurable SETTINGS in Cowboy.
|
Config = cowboy_test:init_http(name(), #{
|
||||||
%% todo.
|
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
|
||||||
%%
|
max_frame_size_received => 30000
|
||||||
%% max_frame_size_config_reject_too_large(Config) ->
|
}, Config0),
|
||||||
%% doc("SETTINGS_MAX_FRAME_SIZE configuration values larger than "
|
%% Do the handshake.
|
||||||
%% "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)"),
|
|
||||||
{ok, Socket} = do_handshake(Config),
|
{ok, Socket} = do_handshake(Config),
|
||||||
%% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE lower than 16384.
|
%% Send a HEADERS frame initiating a stream followed by
|
||||||
ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16383:32 >>),
|
%% a single 30000 bytes DATA frame.
|
||||||
%% Receive a PROTOCOL_ERROR connection error.
|
Headers = [
|
||||||
{ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
|
{<<":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.
|
ok.
|
||||||
|
|
||||||
%% This is about the client sending a SETTINGS frame.
|
max_frame_size_reject_larger_than_custom(Config0) ->
|
||||||
max_frame_size_client_override_reject_too_large(Config) ->
|
doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must reject frames "
|
||||||
doc("A SETTINGS_MAX_FRAME_SIZE larger than 16777215 must be rejected "
|
"of up to that size with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.5.2)"),
|
||||||
"with a PROTOCOL_ERROR connection error. (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),
|
{ok, Socket} = do_handshake(Config),
|
||||||
%% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE larger than 16777215.
|
%% Send a HEADERS frame initiating a stream followed by
|
||||||
ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16777216:32 >>),
|
%% a single DATA frame larger than 30000 bytes.
|
||||||
%% Receive a PROTOCOL_ERROR connection error.
|
Headers = [
|
||||||
{ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
|
{<<":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.
|
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
|
%% 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
|
%% 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
|
%% 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
|
% incomplete SETTINGS frame MUST be treated as a connection error
|
||||||
% (Section 5.4.1) of type PROTOCOL_ERROR.
|
% (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||||
|
|
||||||
%% (RFC7540 6.5.2)
|
%% Settings.
|
||||||
|
|
||||||
settings_header_table_size_client(Config) ->
|
settings_header_table_size_client(Config) ->
|
||||||
doc("The SETTINGS_HEADER_TABLE_SIZE setting can be used to "
|
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) ->
|
settings_initial_window_size(Config0) ->
|
||||||
doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
|
doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
|
||||||
"change the initial window size of streams. (RFC7540 6.5.2)"),
|
"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(), #{
|
Config = cowboy_test:init_http(name(), #{
|
||||||
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
|
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
|
||||||
initial_connection_window_size => 100000,
|
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 "
|
doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
|
||||||
"change the initial window size of streams. It is applied "
|
"change the initial window size of streams. It is applied "
|
||||||
"to all existing streams upon receipt of the SETTINGS ack. (RFC7540 6.5.2)"),
|
"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(), #{
|
Config = cowboy_test:init_http(name(), #{
|
||||||
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
|
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
|
||||||
initial_stream_window_size => 0
|
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 "
|
doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
|
||||||
"change the initial window size of streams. It is only "
|
"change the initial window size of streams. It is only "
|
||||||
"applied upon receipt of the SETTINGS ack. (RFC7540 6.5.2)"),
|
"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(), #{
|
Config = cowboy_test:init_http(name(), #{
|
||||||
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
|
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
|
||||||
initial_stream_window_size => 0
|
initial_stream_window_size => 0
|
||||||
|
@ -2711,13 +2709,56 @@ settings_initial_window_size_before_ack(Config0) ->
|
||||||
{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
|
{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
% SETTINGS_MAX_FRAME_SIZE (0x5):
|
settings_max_frame_size(Config0) ->
|
||||||
% The initial value is 2^14 (16,384) octets. The value advertised
|
doc("The SETTINGS_MAX_FRAME_SIZE setting can be used to "
|
||||||
% by an endpoint MUST be between this initial value and the maximum
|
"change the maximum frame size allowed. (RFC7540 6.5.2)"),
|
||||||
% allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
|
%% Create a new listener that sets the maximum frame size to 30000.
|
||||||
% Values outside this range MUST be treated as a connection error
|
Config = cowboy_test:init_http(name(), #{
|
||||||
% (Section 5.4.1) of type PROTOCOL_ERROR.
|
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
|
% SETTINGS_MAX_HEADER_LIST_SIZE (0x6): This advisory setting informs a
|
||||||
% peer of the maximum size of header list that the sender is
|
% peer of the maximum size of header list that the sender is
|
||||||
% prepared to accept, in octets. The value is based on the
|
% prepared to accept, in octets. The value is based on the
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue