aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorViktor Söderqvist <[email protected]>2022-10-24 14:09:59 +0200
committerLoïc Hoguin <[email protected]>2022-10-24 14:44:15 +0200
commit1cc3b32b8ef6c187b8be3601319e21c1ba04fa27 (patch)
tree790f27c916f38858d9cca3e18a9f76a6f5ed163c /test
parenta4425a1d7db2d9e35f246aa078c324988ea395f0 (diff)
downloadgun-1cc3b32b8ef6c187b8be3601319e21c1ba04fa27.tar.gz
gun-1cc3b32b8ef6c187b8be3601319e21c1ba04fa27.tar.bz2
gun-1cc3b32b8ef6c187b8be3601319e21c1ba04fa27.zip
Handle send errors
Diffstat (limited to 'test')
-rw-r--r--test/send_errors_SUITE.erl139
1 files changed, 139 insertions, 0 deletions
diff --git a/test/send_errors_SUITE.erl b/test/send_errors_SUITE.erl
new file mode 100644
index 0000000..7a0fd02
--- /dev/null
+++ b/test/send_errors_SUITE.erl
@@ -0,0 +1,139 @@
+%% Copyright (c) 2020, Björn Svensson <[email protected]>
+%%
+%% 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, "linux only due to socket juggling"}
+ end.
+
+end_per_suite(_) -> ok.
+
+%% Tests.
+
+http2_send_request_fail(_) ->
+ doc("Handle send failures of requests in http2."),
+ {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 http2."),
+ {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 http2."),
+ {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).