aboutsummaryrefslogtreecommitdiffstats
path: root/test/rfc7540_SUITE.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-11-27 13:42:04 +0100
committerLoïc Hoguin <[email protected]>2017-11-27 13:42:04 +0100
commitbc82679330ea027e311474ddb3d28c8da50e5544 (patch)
treea1130d695f926892de47977b9d54021af65b502a /test/rfc7540_SUITE.erl
parent843b104fcb75bcd217359a8b02d0b1f263d42602 (diff)
downloadcowboy-bc82679330ea027e311474ddb3d28c8da50e5544.tar.gz
cowboy-bc82679330ea027e311474ddb3d28c8da50e5544.tar.bz2
cowboy-bc82679330ea027e311474ddb3d28c8da50e5544.zip
Fix a few rfc7540 tests
Cowboy takes a few shortcuts to avoid wasting resources when there is a protocol error. The RFC wants us to send a different error depending on the state of the stream at the time of the error, and for us to maintain the connection in cases where we would have to spend valuable resources to decode headers. In all these cases Cowboy will simply close the connection with an appropriate error.
Diffstat (limited to 'test/rfc7540_SUITE.erl')
-rw-r--r--test/rfc7540_SUITE.erl47
1 files changed, 31 insertions, 16 deletions
diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl
index 30140db..605db4a 100644
--- a/test/rfc7540_SUITE.erl
+++ b/test/rfc7540_SUITE.erl
@@ -1675,7 +1675,7 @@ continuation_wrong_stream_error(Config) ->
idle_stream_reject_data(Config) ->
doc("DATA frames received on an idle stream must be rejected "
- "with a PROTOCOL_ERROR connection error. (RFC7540 5.1)"),
+ "with a PROTOCOL_ERROR connection error. (RFC7540 5.1, RFC7540 6.1)"),
{ok, Socket} = do_handshake(Config),
%% Send a DATA frame on an idle stream.
ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)),
@@ -1779,7 +1779,7 @@ idle_stream_reject_window_update(Config) ->
half_closed_remote_reject_data(Config) ->
doc("DATA frames received on a half-closed (remote) stream must be rejected "
- "with a STREAM_CLOSED stream error. (RFC7540 5.1)"),
+ "with a STREAM_CLOSED stream error. (RFC7540 5.1, RFC7540 6.1)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame with the FIN flag set.
{HeadersBlock, _} = cow_hpack:encode([
@@ -1795,9 +1795,11 @@ half_closed_remote_reject_data(Config) ->
{ok, << _:24, 3:8, _:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 6000),
ok.
+%% We reject all invalid HEADERS with a connection error because
+%% we do not want to waste resources decoding them.
half_closed_remote_reject_headers(Config) ->
doc("HEADERS frames received on a half-closed (remote) stream must be rejected "
- "with a STREAM_CLOSED stream error. (RFC7540 5.1)"),
+ "with a STREAM_CLOSED connection error. (RFC7540 4.3, RFC7540 5.1)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame with the FIN flag set.
{HeadersBlock, _} = cow_hpack:encode([
@@ -1809,8 +1811,8 @@ half_closed_remote_reject_headers(Config) ->
ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
%% Send a HEADERS frame on that now half-closed (remote) stream.
ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
- %% Receive a STREAM_CLOSED stream error.
- {ok, << _:24, 3:8, _:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 6000),
+ %% Receive a STREAM_CLOSED connection error.
+ {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
ok.
half_closed_remote_accept_priority(Config) ->
@@ -1870,9 +1872,13 @@ half_closed_remote_accept_window_update(Config) ->
{ok, << _:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
ok.
+%% We reject DATA frames sent on closed streams with a STREAM_CLOSED
+%% connection error regardless of how the stream was closed to simplify
+%% the implementation. This excludes the few frames we ignore from
+%% lingering streams that we canceled.
rst_stream_closed_reject_data(Config) ->
doc("DATA frames received on a stream closed via RST_STREAM must be rejected "
- "with a STREAM_CLOSED stream error. (RFC7540 5.1)"),
+ "with a STREAM_CLOSED connection error. (RFC7540 5.1, RFC7540 6.1)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame.
{HeadersBlock, _} = cow_hpack:encode([
@@ -1886,13 +1892,15 @@ rst_stream_closed_reject_data(Config) ->
ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
%% Send a DATA frame on the now RST_STREAM closed stream.
ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)),
- %% Receive a STREAM_CLOSED stream error.
- {ok, << _:24, 3:8, _:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 6000),
+ %% Receive a STREAM_CLOSED connection error.
+ {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
ok.
+%% We reject all invalid HEADERS with a connection error because
+%% we do not want to waste resources decoding them.
rst_stream_closed_reject_headers(Config) ->
doc("HEADERS frames received on a stream closed via RST_STREAM must be rejected "
- "with a STREAM_CLOSED stream error. (RFC7540 5.1)"),
+ "with a STREAM_CLOSED connection error. (RFC7540 4.3, RFC7540 5.1)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame.
{HeadersBlock, _} = cow_hpack:encode([
@@ -1906,8 +1914,8 @@ rst_stream_closed_reject_headers(Config) ->
ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
%% Send a HEADERS frame on the now RST_STREAM closed stream.
ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
- %% Receive a STREAM_CLOSED stream error.
- {ok, << _:24, 3:8, _:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 6000),
+ %% Receive a STREAM_CLOSED connection error.
+ {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
ok.
rst_stream_closed_accept_priority(Config) ->
@@ -1978,7 +1986,7 @@ rst_stream_closed_reject_window_update(Config) ->
stream_closed_reject_data(Config) ->
doc("DATA frames received on a stream closed normally must be rejected "
- "with a STREAM_CLOSED connection error. (RFC7540 5.1)"),
+ "with a STREAM_CLOSED connection error. (RFC7540 5.1, RFC7540 6.1)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame.
{HeadersBlock, _} = cow_hpack:encode([
@@ -2204,9 +2212,16 @@ http_upgrade_reject_reuse_streamid_1(Config) ->
{ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
ok.
+%% The RFC gives us various error codes to return for this case,
+%% depending on whether the stream existed previously and how it
+%% ended up being (half-)closed. Cowboy rejects all these HEADERS
+%% frames the same way: with a STREAM_CLOSED connection error.
+%% Making it a connection error is particularly important in the
+%% cases where a stream error would be allowed because we avoid
+%% having to decode the headers and save up resources.
reject_streamid_lower(Config) ->
doc("HEADERS frames received with streamid lower than the previous stream "
- "must be rejected with a PROTOCOL_ERROR connection error. (RFC7540 5.1.1)"),
+ "must be rejected with a STREAM_CLOSED connection error. (RFC7540 5.1.1)"),
{ok, Socket} = do_handshake(Config),
%% Send a HEADERS frame with streamid 5.
{HeadersBlock, _} = cow_hpack:encode([
@@ -2223,12 +2238,12 @@ reject_streamid_lower(Config) ->
{ok, _} = gen_tcp:recv(Socket, Length2, 6000),
%% Send a HEADERS frame with streamid 3.
ok = gen_tcp:send(Socket, cow_http2:headers(3, fin, HeadersBlock)),
- %% Receive a PROTOCOL_ERROR connection error.
- {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
+ %% Receive a STREAM_CLOSED connection error.
+ {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
ok.
%% @todo We need an option to limit the number of streams one can open
-%% on a connection. And we need to enforce it.
+%% on a connection. And we need to enforce it. (RFC7540 5.1.1)
%
% Stream identifiers cannot be reused. Long-lived connections can
% result in an endpoint exhausting the available range of stream