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

Data received after RST_STREAM counts toward window

This commit is contained in:
Tony Han 2019-04-28 10:46:17 +08:00 committed by Loïc Hoguin
parent 504c7c55f7
commit 7708fc77cd
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
3 changed files with 79 additions and 0 deletions

View file

@ -0,0 +1,21 @@
%% This module implements a loop handler that reads
%% 1000 bytes of the request body after sending itself
%% a message, then terminates the stream.
-module(loop_handler_abort_h).
-export([init/2]).
-export([info/3]).
-export([terminate/3]).
init(Req, _) ->
self() ! timeout,
{cowboy_loop, Req, undefined, hibernate}.
info(timeout, Req0, State) ->
{_Status, Body, Req} = cowboy_req:read_body(Req0, #{length => 1000}),
1000 = byte_size(Body),
{stop, cowboy_req:reply(200, Req), State}.
terminate(stop, _, _) ->
ok.

View file

@ -49,6 +49,7 @@ init_routes(_) -> [
{"/", hello_h, []},
{"/echo/:key", echo_h, []},
{"/long_polling", long_polling_h, []},
{"/loop_handler_abort", loop_handler_abort_h, []},
{"/resp/:key[/:arg]", resp_h, []}
]}
].
@ -3116,6 +3117,55 @@ data_reject_overflow_stream(Config0) ->
cowboy:stop_listener(?FUNCTION_NAME)
end.
lingering_data_counts_toward_connection_window(Config0) ->
doc("DATA frames received after sending RST_STREAM must be counted "
"toward the connection flow-control window. (RFC7540 5.1)"),
Config = cowboy_test:init_http(?FUNCTION_NAME, #{
env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
initial_connection_window_size => 100000
}, Config0),
try
%% We need to do the handshake manually because a WINDOW_UPDATE
%% frame will be sent to update the connection window.
{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, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
%% Send the SETTINGS ack.
ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
%% Receive the WINDOW_UPDATE for the connection.
{ok, << 4:24, 8:8, 0:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
%% Receive the SETTINGS ack.
{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
Headers = [
{<<":method">>, <<"POST">>},
{<<":scheme">>, <<"http">>},
{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
{<<":path">>, <<"/loop_handler_abort">>}
],
{HeadersBlock, _} = cow_hpack:encode(Headers),
ok = gen_tcp:send(Socket, [
cow_http2:headers(1, nofin, HeadersBlock),
cow_http2:data(1, nofin, <<0:1000/unit:8>>)
]),
% Make sure server send RST_STREAM.
timer:sleep(100),
ok = gen_tcp:send(Socket, [
cow_http2:data(1, fin, <<0:1000/unit:8>>)
]),
{ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
% Skip the header.
{ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
% Skip RST_STREAM.
{ok, << 4:24, 3:8, 1:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
% Received a WINDOW_UPDATE frame after we got RST_STREAM.
{ok, << 4:24, 8:8, 0:40, 1000:32 >>} = gen_tcp:recv(Socket, 13, 1000)
after
cowboy:stop_listener(?FUNCTION_NAME)
end.
%% (RFC7540 6.9.1)
% Frames with zero length with the END_STREAM flag set (that
% is, an empty DATA frame) MAY be sent if there is no available space