diff options
author | Loïc Hoguin <[email protected]> | 2017-02-25 20:05:31 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2017-02-25 20:05:31 +0100 |
commit | a3636adcac15f0eea4151dca8a28a915eadaa1ed (patch) | |
tree | 4cfa365b489b70ead9f033fd9cc8dda82a864668 /test/rfc7540_SUITE.erl | |
parent | 2af0506d51768b0c187f1ad4498ac9b815ce4114 (diff) | |
download | cowboy-a3636adcac15f0eea4151dca8a28a915eadaa1ed.tar.gz cowboy-a3636adcac15f0eea4151dca8a28a915eadaa1ed.tar.bz2 cowboy-a3636adcac15f0eea4151dca8a28a915eadaa1ed.zip |
Add many test cases covering RFC7540 4.2
These tests cover frame sizes. It's mostly edge cases for sure
(ie misbehaving clients and us having to reject them properly).
I had these almost ready for a long time, so I'm glad I can
push them out.
This requires updating Cowlib too (we currently track master).
Diffstat (limited to 'test/rfc7540_SUITE.erl')
-rw-r--r-- | test/rfc7540_SUITE.erl | 427 |
1 files changed, 422 insertions, 5 deletions
diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl index e9e7911..d99e9f2 100644 --- a/test/rfc7540_SUITE.erl +++ b/test/rfc7540_SUITE.erl @@ -44,7 +44,8 @@ end_per_group(Name, _) -> init_routes(_) -> [ {"localhost", [ - {"/", hello_h, []} + {"/", hello_h, []}, + {"/echo/:key", echo_h, []} ]} ]. @@ -773,12 +774,10 @@ prior_knowledge_client_preface_settings_ack_timeout(Config) -> {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000), ok. -prior_knowledge(Config) -> - doc("Streams can be initiated after a successful HTTP/2 connection " - "with prior knowledge of server capabilities. (RFC7540 3.4)"), +%% Do a prior knowledge handshake. +do_handshake(Config) -> {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]), %% Send a valid preface. - %% @todo Use non-empty SETTINGS here. Just because. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]), %% Receive the server preface. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000), @@ -787,6 +786,13 @@ prior_knowledge(Config) -> ok = gen_tcp:send(Socket, cow_http2:settings_ack()), %% Receive the SETTINGS ack. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000), + {ok, Socket}. + +prior_knowledge(Config) -> + doc("Streams can be initiated after a successful HTTP/2 connection " + "with prior knowledge of server capabilities. (RFC7540 3.4)"), + %% @todo Use non-empty SETTINGS here. Just because. + {ok, Socket} = do_handshake(Config), %% Wait until after the SETTINGS ack timeout was supposed to trigger. receive after 6000 -> ok end, %% Send a PING. @@ -802,3 +808,414 @@ prior_knowledge(Config) -> %% without waiting for the 101 response (3.2, 3.5) %% * Prior knowledge handshake fails (3.4) %% * ALPN selects HTTP/1.1 (3.3) + +%% Frame size. + +max_frame_size_allow_exactly_default(Config) -> + doc("All implementations must allow frame sizes of at least 16384. (RFC7540 4.1, RFC7540 4.2)"), + {ok, Socket} = do_handshake(Config), + %% Send a POST request with a DATA frame of exactly 16384 bytes. + {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, fin, << 0:16384/unit:8 >>) + ]), + %% Receive a response with the same DATA frame. + {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000), + {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000), + {ok, << 16384:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000), + {ok, << 0:16384/unit:8 >>} = gen_tcp:recv(Socket, 16384, 1000), + ok. + +max_frame_size_reject_larger_than_default(Config) -> + doc("A FRAME_SIZE_ERROR connection error must be sent when receiving " + "frames larger than the default 16384 length. (RFC7540 4.1, RFC7540 4.2)"), + {ok, Socket} = do_handshake(Config), + %% Send a POST request with a DATA frame larger than 16384 bytes. + {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, fin, << 0:16385/unit:8 >>) + ]), + %% Receive a FRAME_SIZE_ERROR connection error. + {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)"), + {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. + +%% 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)"), + {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. + +%% @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 +%% mandatory frame data (the pad length). + +data_reject_frame_size_0_padded_flag(Config) -> + doc("DATA frames of size 0 with the PADDED flag set must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.1)"), + {ok, Socket} = do_handshake(Config), + %% Send a POST request with an incorrect padded DATA frame size. + {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), + << 0:24, 0:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31 >> + ]), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +%% This case on the other hand is noted specifically in the RFC +%% as being a PROTOCOL_ERROR. It can be thought of as the Pad Length +%% being incorrect, rather than the frame size. + +data_reject_frame_size_too_small_padded_flag(Config) -> + doc("DATA frames with Pad Length >= Length must be rejected " + "with a PROTOCOL_ERROR connection error. (RFC7540 6.1)"), + {ok, Socket} = do_handshake(Config), + %% Send a POST request with an incorrect padded DATA frame size. + {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), + << 10:24, 0:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31, 10:8, 0:80 >> + ]), + %% Receive a PROTOCOL_ERROR connection error. + {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +headers_reject_frame_size_0_padded_flag(Config) -> + doc("HEADERS frames of size 0 with the PADDED flag set must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"), + {ok, Socket} = do_handshake(Config), + %% Send a padded HEADERS frame with an incorrect size. + ok = gen_tcp:send(Socket, << 0:24, 1:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +headers_reject_frame_size_too_small_padded_flag(Config) -> + doc("HEADERS frames with no priority flag and Pad Length >= Length " + "must be rejected with a PROTOCOL_ERROR connection error. (RFC7540 6.2)"), + {ok, Socket} = do_handshake(Config), + %% Send a padded HEADERS frame with an incorrect size. + ok = gen_tcp:send(Socket, << 10:24, 1:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31, 10:8, 0:80 >>), + %% Receive a PROTOCOL_ERROR connection error. + {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +headers_reject_frame_size_too_small_priority_flag(Config) -> + doc("HEADERS frames of size smaller than 5 with the PRIORITY flag set must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"), + {ok, Socket} = do_handshake(Config), + %% Send a HEADERS frame with priority set and an incorrect size. + ok = gen_tcp:send(Socket, << 4:24, 1:8, + 0:2, 1:1, 0:4, 1:1, 0:1, 1:31, 0:1, 3:31, 0:8 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +headers_reject_frame_size_5_padded_and_priority_flags(Config) -> + doc("HEADERS frames of size smaller than 6 with the PADDED " + "and PRIORITY flags set must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"), + {ok, Socket} = do_handshake(Config), + %% Send a padded HEADERS frame with an incorrect size. + ok = gen_tcp:send(Socket, << 5:24, 1:8, + 0:2, 1:1, 0:1, 1:1, 0:2, 1:1, 0:1, 1:31, 0:8, 0:1, 3:31, 0:8 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +headers_reject_frame_size_too_small_padded_and_priority_flags(Config) -> + doc("HEADERS frames of size smaller than Length+6 with the PADDED and PRIORITY flags set " + "must be rejected with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"), + {ok, Socket} = do_handshake(Config), + %% Send a padded HEADERS frame with an incorrect size. + ok = gen_tcp:send(Socket, << 15:24, 1:8, + 0:2, 1:1, 0:1, 1:1, 0:2, 1:1, 0:1, 1:31, 10:8, 0:1, 3:31, 0:8, 0:80 >>), + %% Receive a PROTOCOL_ERROR connection error. + {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +priority_reject_frame_size_too_small(Config) -> + doc("PRIORITY frames of size smaller than 5 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.3)"), + {ok, Socket} = do_handshake(Config), + %% Send a PRIORITY frame with an incorrect size. + ok = gen_tcp:send(Socket, << 4:24, 2:8, 0:9, 1:31, 0:1, 3:31, 0:8 >>), + %% Receive a FRAME_SIZE_ERROR stream error. + {ok, << _:24, 3:8, _:40, 6:32 >>} = gen_tcp:recv(Socket, 13, 6000), + ok. + +priority_reject_frame_size_too_large(Config) -> + doc("PRIORITY frames of size larger than 5 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.3)"), + {ok, Socket} = do_handshake(Config), + %% Send a PRIORITY frame with an incorrect size. + ok = gen_tcp:send(Socket, << 6:24, 2:8, 0:9, 1:31, 0:1, 3:31, 0:16 >>), + %% Receive a FRAME_SIZE_ERROR stream error. + {ok, << _:24, 3:8, _:40, 6:32 >>} = gen_tcp:recv(Socket, 13, 6000), + ok. + +rst_stream_reject_frame_size_too_small(Config) -> + doc("RST_STREAM frames of size smaller than 4 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.4)"), + {ok, Socket} = do_handshake(Config), + %% Send a request and reset it immediately. + {HeadersBlock, _} = cow_hpack:encode([ + {<<":method">>, <<"GET">>}, + {<<":scheme">>, <<"http">>}, + {<<":authority">>, <<"localhost">>}, %% @todo Correct port number. + {<<":path">>, <<"/">>} + ]), + ok = gen_tcp:send(Socket, [ + cow_http2:headers(1, fin, HeadersBlock), + << 3:24, 3:8, 0:9, 1:31, 8:32 >> + ]), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +rst_stream_reject_frame_size_too_large(Config) -> + doc("RST_STREAM frames of size larger than 4 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.4)"), + {ok, Socket} = do_handshake(Config), + %% Send a request and reset it immediately. + {HeadersBlock, _} = cow_hpack:encode([ + {<<":method">>, <<"GET">>}, + {<<":scheme">>, <<"http">>}, + {<<":authority">>, <<"localhost">>}, %% @todo Correct port number. + {<<":path">>, <<"/">>} + ]), + ok = gen_tcp:send(Socket, [ + cow_http2:headers(1, fin, HeadersBlock), + << 5:24, 3:8, 0:9, 1:31, 8:32 >> + ]), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +settings_reject_bad_frame_size(Config) -> + doc("SETTINGS frames must have a size multiple of 6 or be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.5)"), + {ok, Socket} = do_handshake(Config), + %% Send a SETTINGS frame with an incorrect size. + ok = gen_tcp:send(Socket, << 5:24, 4:8, 0:40, 1:16, 4096:32 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +settings_ack_reject_non_empty_frame_size(Config) -> + doc("SETTINGS frames with the ACK flag set and a non-empty payload " + "must be rejected with a FRAME_SIZE_ERROR connection error (RFC7540 4.2, RFC7540 6.5)"), + {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(#{})]), + %% 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), + %% Send a SETTINGS ack with a payload. + ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:7, 1:1, 0:32, 1:16, 4096:32 >>), + %% Receive the SETTINGS ack. + {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +%% Note that clients are not supposed to send PUSH_PROMISE frames. +%% However when they do, we need to be able to parse it in order +%% to reject it, and so these errors may still occur. + +push_promise_reject_frame_size_too_small(Config) -> + doc("PUSH_PROMISE frames of size smaller than 4 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.6)"), + {ok, Socket} = do_handshake(Config), + %% Send a PUSH_PROMISE frame with an incorrect size. + {HeadersBlock, _} = cow_hpack:encode([ + {<<":method">>, <<"GET">>}, + {<<":scheme">>, <<"http">>}, + {<<":authority">>, <<"localhost">>}, %% @todo Correct port number. + {<<":path">>, <<"/">>} + ]), + ok = gen_tcp:send(Socket, [ + << 3:24, 5:8, 0:5, 1:1, 0:3, 1:31, 0:1, 3:31 >>, + HeadersBlock + ]), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +push_promise_reject_frame_size_4_padded_flag(Config) -> + doc("PUSH_PROMISE frames of size smaller than 5 with the PADDED flag set must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.6)"), + {ok, Socket} = do_handshake(Config), + %% Send a PUSH_PROMISE frame with an incorrect size. + {HeadersBlock, _} = cow_hpack:encode([ + {<<":method">>, <<"GET">>}, + {<<":scheme">>, <<"http">>}, + {<<":authority">>, <<"localhost">>}, %% @todo Correct port number. + {<<":path">>, <<"/">>} + ]), + ok = gen_tcp:send(Socket, [ + << 4:24, 5:8, 0:4, 1:1, 1:1, 0:3, 1:31, 0:1, 0:8, 3:31 >>, + HeadersBlock + ]), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +push_promise_reject_frame_size_too_small_padded_flag(Config) -> + doc("PUSH_PROMISE frames of size smaller than Length+5 with the PADDED flag set " + "must be rejected with a PROTOCOL_ERROR connection error. (RFC7540 4.2, RFC7540 6.6)"), + {ok, Socket} = do_handshake(Config), + %% Send a PUSH_PROMISE frame with an incorrect size. + {HeadersBlock, _} = cow_hpack:encode([ + {<<":method">>, <<"GET">>}, + {<<":scheme">>, <<"http">>}, + {<<":authority">>, <<"localhost">>}, %% @todo Correct port number. + {<<":path">>, <<"/">>} + ]), + ok = gen_tcp:send(Socket, [ + << 14:24, 5:8, 0:4, 1:1, 1:1, 0:3, 1:31, 10:8, 0:1, 3:31 >>, + HeadersBlock, + << 0:80 >> + ]), + %% Receive a PROTOCOL_ERROR connection error. + {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +ping_reject_frame_size_too_small(Config) -> + doc("PING frames of size smaller than 8 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.7)"), + {ok, Socket} = do_handshake(Config), + %% Send a PING frame with an incorrect size. + ok = gen_tcp:send(Socket, << 7:24, 6:8, 0:40, 0:56 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +ping_reject_frame_size_too_large(Config) -> + doc("PING frames of size larger than 8 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.7)"), + {ok, Socket} = do_handshake(Config), + %% Send a PING frame with an incorrect size. + ok = gen_tcp:send(Socket, << 9:24, 6:8, 0:40, 0:72 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +goaway_reject_frame_size_too_small(Config) -> + doc("GOAWAY frames of size smaller than 8 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.8)"), + {ok, Socket} = do_handshake(Config), + %% Send a GOAWAY frame with an incorrect size. + ok = gen_tcp:send(Socket, << 7:24, 7:8, 0:40, 0:56 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +goaway_allow_frame_size_too_large(Config) -> + doc("GOAWAY frames of size larger than 8 must be allowed. (RFC7540 6.8)"), + {ok, Socket} = do_handshake(Config), + %% Send a GOAWAY frame with debug data. + ok = gen_tcp:send(Socket, << 12:24, 7:8, 0:40, 0:64, 99999:32 >>), + %% Receive a GOAWAY frame back. + {ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +window_update_reject_frame_size_too_small(Config) -> + doc("WINDOW_UPDATE frames of size smaller than 4 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.9)"), + {ok, Socket} = do_handshake(Config), + %% Send a WINDOW_UPDATE frame with an incorrect size. + ok = gen_tcp:send(Socket, << 3:24, 8:8, 0:40, 1000:24 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +window_update_reject_frame_size_too_large(Config) -> + doc("WINDOW_UPDATE frames of size larger than 4 must be rejected " + "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.9)"), + {ok, Socket} = do_handshake(Config), + %% Send a WINDOW_UPDATE frame with an incorrect size. + ok = gen_tcp:send(Socket, << 5:24, 8:8, 0:40, 1000:40 >>), + %% Receive a FRAME_SIZE_ERROR connection error. + {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000), + ok. + +%% Note: There is no particular limits on the size of CONTINUATION frames, +%% they can go from 0 to SETTINGS_MAX_FRAME_SIZE. |