140 lines
3.9 KiB
Erlang
140 lines
3.9 KiB
Erlang
%% Copyright (c) 2020-2023, Björn Svensson <bjorn.a.svensson@est.tech>
|
|
%%
|
|
%% Permission to use, copy, modify, and/or distribute this software for any
|
|
%% purpose with or without fee is hereby granted, provided that the above
|
|
%% copyright notice and this permission notice appear in all copies.
|
|
%%
|
|
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-module(send_errors_SUITE).
|
|
-compile(export_all).
|
|
-compile(nowarn_export_all).
|
|
|
|
-import(ct_helper, [doc/1]).
|
|
-import(gun_test, [http2_handshake/2]).
|
|
|
|
suite() ->
|
|
[{timetrap, 180000}].
|
|
|
|
all() ->
|
|
[{group, gun}].
|
|
|
|
groups() ->
|
|
[{gun, [parallel], ct_helper:all(?MODULE)}].
|
|
|
|
init_per_suite(Config) ->
|
|
case os:type() of
|
|
{_, linux} -> Config;
|
|
_ -> {skip, "This test suite is Linux-only due to socket juggling."}
|
|
end.
|
|
|
|
end_per_suite(_) -> ok.
|
|
|
|
%% Tests.
|
|
|
|
http2_send_request_fail(_) ->
|
|
doc("Handle send failures of requests in HTTP/2."),
|
|
{ok, ListenSocket} = gen_tcp:listen(0, [binary, {active, false}]),
|
|
{ok, {_, Port}} = inet:sockname(ListenSocket),
|
|
%% Socket buffers needs to be smaller than local_window/ConnWindow
|
|
{ok, Pid} = gun:open("localhost", Port, #{
|
|
protocols => [http2],
|
|
tcp_opts => [
|
|
{send_timeout, 250},
|
|
{send_timeout_close, true},
|
|
{sndbuf, 2048},
|
|
{nodelay, true}
|
|
]
|
|
}),
|
|
{ok, ClientSocket} = gen_tcp:accept(ListenSocket, 5000),
|
|
inet:setopts(ClientSocket, [{recbuf, 512}]),
|
|
http2_handshake(ClientSocket, gen_tcp),
|
|
{ok, http2} = gun:await_up(Pid),
|
|
post_loop(Pid, 1000), %% Fill buffer
|
|
receive
|
|
{gun_error, Pid, _, {closed, {error, _}}} ->
|
|
gun:close(Pid);
|
|
Msg ->
|
|
error({fail, Msg})
|
|
after 5000 ->
|
|
error(timeout)
|
|
end.
|
|
|
|
http2_send_ping_fail(_) ->
|
|
doc("Handle send failures of ping in HTTP/2."),
|
|
{ok, ListenSocket} = gen_tcp:listen(0, [binary, {active, false}]),
|
|
{ok, {_, Port}} = inet:sockname(ListenSocket),
|
|
{ok, Pid} = gun:open("localhost", Port, #{
|
|
protocols => [http2],
|
|
http2_opts => #{keepalive => 1},
|
|
tcp_opts => [
|
|
{send_timeout, 250},
|
|
{send_timeout_close, true},
|
|
{sndbuf, 256},
|
|
{nodelay, true}
|
|
]
|
|
}),
|
|
{ok, ClientSocket} = gen_tcp:accept(ListenSocket, 5000),
|
|
inet:setopts(ClientSocket, [{recbuf, 256}]),
|
|
http2_handshake(ClientSocket, gen_tcp),
|
|
{ok, http2} = gun:await_up(Pid),
|
|
receive
|
|
{gun_down, Pid, http2, {error, _}, []} ->
|
|
gun:close(Pid);
|
|
Msg ->
|
|
error({fail, Msg})
|
|
after 5000 ->
|
|
error(timeout)
|
|
end.
|
|
|
|
http2_send_ping_ack_fail(_) ->
|
|
doc("Handle send failures of ping ack in HTTP/2."),
|
|
{ok, ListenSocket} = gen_tcp:listen(0, [binary, {active, false}]),
|
|
{ok, {_, Port}} = inet:sockname(ListenSocket),
|
|
{ok, Pid} = gun:open("localhost", Port, #{
|
|
protocols => [http2],
|
|
http2_opts => #{keepalive => infinity},
|
|
tcp_opts => [
|
|
{send_timeout, 250},
|
|
{send_timeout_close, true},
|
|
{sndbuf, 256},
|
|
{nodelay, true}
|
|
]
|
|
}),
|
|
{ok, ClientSocket} = gen_tcp:accept(ListenSocket, 5000),
|
|
inet:setopts(ClientSocket, [{recbuf, 256}]),
|
|
http2_handshake(ClientSocket, gen_tcp),
|
|
{ok, http2} = gun:await_up(Pid),
|
|
ping_loop(ClientSocket, 1800), %% Send pings triggering ping acks
|
|
receive
|
|
{gun_down, Pid, http2, {error, _}, []} ->
|
|
gun:close(Pid);
|
|
Msg ->
|
|
error({fail, Msg})
|
|
after 5000 ->
|
|
error(timeout)
|
|
end.
|
|
|
|
%% Helpers
|
|
|
|
post_loop(_Pid, 0) ->
|
|
ok;
|
|
post_loop(Pid, Loops) ->
|
|
Body = <<0:1000>>,
|
|
gun:post(Pid, "/organizations/ninenines",
|
|
[{<<"content-type">>, "application/octet-stream"}],
|
|
Body),
|
|
post_loop(Pid, Loops - 1).
|
|
|
|
ping_loop(_Socket, 0) ->
|
|
ok;
|
|
ping_loop(Socket, Loops) ->
|
|
gun_tcp:send(Socket, cow_http2:ping(0)),
|
|
ping_loop(Socket, Loops - 1).
|