mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Add test for lingering_data handling
This commit is contained in:
parent
ca32a22a55
commit
5ebc9aa219
4 changed files with 77 additions and 2 deletions
2
Makefile
2
Makefile
|
@ -15,7 +15,7 @@ CT_OPTS += -ct_hooks cowboy_ct_hook [] # -boot start_sasl
|
||||||
LOCAL_DEPS = crypto
|
LOCAL_DEPS = crypto
|
||||||
|
|
||||||
DEPS = cowlib ranch
|
DEPS = cowlib ranch
|
||||||
dep_cowlib = git https://github.com/ninenines/cowlib 2.7.3
|
dep_cowlib = git https://github.com/elixir-grpc/cowlib.git fix-lingering-data
|
||||||
dep_ranch = git https://github.com/ninenines/ranch 1.7.1
|
dep_ranch = git https://github.com/ninenines/ranch 1.7.1
|
||||||
|
|
||||||
DOC_DEPS = asciideck
|
DOC_DEPS = asciideck
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{deps, [
|
{deps, [
|
||||||
{cowlib,".*",{git,"https://github.com/ninenines/cowlib","2.7.3"}},{ranch,".*",{git,"https://github.com/ninenines/ranch","1.7.1"}}
|
{cowlib,".*",{git,"https://github.com/elixir-grpc/cowlib.git","fix-lingering-data"}},{ranch,".*",{git,"https://github.com/ninenines/ranch","1.7.1"}}
|
||||||
]}.
|
]}.
|
||||||
{erl_opts, [debug_info,warn_export_vars,warn_shadow_vars,warn_obsolete_guard,warn_missing_spec,warn_untyped_record]}.
|
{erl_opts, [debug_info,warn_export_vars,warn_shadow_vars,warn_obsolete_guard,warn_missing_spec,warn_untyped_record]}.
|
||||||
|
|
21
test/handlers/loop_handler_abort_h.erl
Normal file
21
test/handlers/loop_handler_abort_h.erl
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
%% This module implements a loop handler that reads
|
||||||
|
%% 1000 bytes request body after sending itself a message,
|
||||||
|
%% then finish 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.
|
|
@ -49,6 +49,7 @@ init_routes(_) -> [
|
||||||
{"/", hello_h, []},
|
{"/", hello_h, []},
|
||||||
{"/echo/:key", echo_h, []},
|
{"/echo/:key", echo_h, []},
|
||||||
{"/long_polling", long_polling_h, []},
|
{"/long_polling", long_polling_h, []},
|
||||||
|
{"/loop_handler_abort", loop_handler_abort_h, []},
|
||||||
{"/resp/:key[/:arg]", resp_h, []}
|
{"/resp/:key[/:arg]", resp_h, []}
|
||||||
]}
|
]}
|
||||||
].
|
].
|
||||||
|
@ -3116,6 +3117,59 @@ data_reject_overflow_stream(Config0) ->
|
||||||
cowboy:stop_listener(?FUNCTION_NAME)
|
cowboy:stop_listener(?FUNCTION_NAME)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
lingering_data_account_for_connection_window_when(Config0) ->
|
||||||
|
doc("A receiver that receives a flow-controlled frame MUST "
|
||||||
|
"always account for its contribution against the connection "
|
||||||
|
"flow-control window"),
|
||||||
|
%% Create a new listener that allows only a single concurrent stream.
|
||||||
|
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),
|
||||||
|
% @todo: A WINDOW_UPDATE frame is expected to be received, but cowboy_stream_h
|
||||||
|
% doesn't send it when only first 1000 bytes of body is read
|
||||||
|
% Skip RST_STREAM
|
||||||
|
{ok, << 4:24, 3:8, 1:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
|
||||||
|
% Received a WINDOW_UPDATE frame after 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)
|
%% (RFC7540 6.9.1)
|
||||||
% Frames with zero length with the END_STREAM flag set (that
|
% 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
|
% is, an empty DATA frame) MAY be sent if there is no available space
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue