aboutsummaryrefslogtreecommitdiffstats
path: root/test/http_SUITE.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2024-01-15 15:18:40 +0100
committerLoïc Hoguin <[email protected]>2024-01-15 15:18:40 +0100
commit1a175e7b563110c4a47c41f2b9c78cdafbcaf063 (patch)
tree961bc984c29a50f9858f337bdfb8f5106a3e2d06 /test/http_SUITE.erl
parent906a7ffc3ceaee166f495b071a92ce703f5ce39d (diff)
downloadcowboy-1a175e7b563110c4a47c41f2b9c78cdafbcaf063.tar.gz
cowboy-1a175e7b563110c4a47c41f2b9c78cdafbcaf063.tar.bz2
cowboy-1a175e7b563110c4a47c41f2b9c78cdafbcaf063.zip
Fix wrong HTTP/1 timeout being used in some cases
Added many tests to ensure the right timeout is picked in the appropriate situation. Should there be any issues remaining we can add more tests.
Diffstat (limited to 'test/http_SUITE.erl')
-rw-r--r--test/http_SUITE.erl164
1 files changed, 164 insertions, 0 deletions
diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl
index 8b8473f..3088383 100644
--- a/test/http_SUITE.erl
+++ b/test/http_SUITE.erl
@@ -227,6 +227,68 @@ http10_keepalive_false(Config) ->
cowboy:stop_listener(?FUNCTION_NAME)
end.
+idle_timeout_read_body(Config) ->
+ doc("Ensure the idle_timeout drops connections when the "
+ "connection is idle too long reading the request body."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ request_timeout => 60000,
+ idle_timeout => 500
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+ {ok, http} = gun:await_up(ConnPid),
+ _StreamRef = gun:post(ConnPid, "/echo/read_body",
+ #{<<"content-length">> => <<"12">>}),
+ {error, {down, {shutdown, closed}}} = gun:await(ConnPid, undefined, 1000)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+idle_timeout_read_body_pipeline(Config) ->
+ doc("Ensure the idle_timeout drops connections when the "
+ "connection is idle too long reading the request body."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ request_timeout => 60000,
+ idle_timeout => 500
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+ {ok, http} = gun:await_up(ConnPid),
+ StreamRef1 = gun:get(ConnPid, "/"),
+ StreamRef2 = gun:get(ConnPid, "/"),
+ _StreamRef3 = gun:post(ConnPid, "/echo/read_body",
+ #{<<"content-length">> => <<"12">>}),
+ {response, nofin, 200, _} = gun:await(ConnPid, StreamRef1),
+ {response, nofin, 200, _} = gun:await(ConnPid, StreamRef2),
+ {error, {down, {shutdown, closed}}} = gun:await(ConnPid, undefined, 1000)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+idle_timeout_skip_body(Config) ->
+ doc("Ensure the idle_timeout drops connections when the "
+ "connection is idle too long skipping the request body."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ request_timeout => 60000,
+ idle_timeout => 500
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+ {ok, http} = gun:await_up(ConnPid),
+ StreamRef = gun:post(ConnPid, "/",
+ #{<<"content-length">> => <<"12">>}),
+ {response, nofin, 200, _} = gun:await(ConnPid, StreamRef),
+ {error, {down, {shutdown, closed}}} = gun:await(ConnPid, undefined, 1000)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
idle_timeout_infinity(Config) ->
doc("Ensure the idle_timeout option accepts the infinity value."),
{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
@@ -352,6 +414,108 @@ do_persistent_term_router(Config) ->
cowboy:stop_listener(?FUNCTION_NAME)
end.
+request_timeout(Config) ->
+ doc("Ensure the request_timeout drops connections when requests "
+ "fail to come in fast enough."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ request_timeout => 500
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+ {ok, http} = gun:await_up(ConnPid),
+ {error, {down, {shutdown, closed}}} = gun:await(ConnPid, undefined, 1000)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+request_timeout_pipeline(Config) ->
+ doc("Ensure the request_timeout drops connections when requests "
+ "fail to come in fast enough after pipelined requests went through."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ request_timeout => 500
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+ {ok, http} = gun:await_up(ConnPid),
+ StreamRef1 = gun:get(ConnPid, "/"),
+ StreamRef2 = gun:get(ConnPid, "/"),
+ StreamRef3 = gun:get(ConnPid, "/"),
+ {response, nofin, 200, _} = gun:await(ConnPid, StreamRef1),
+ {response, nofin, 200, _} = gun:await(ConnPid, StreamRef2),
+ {response, nofin, 200, _} = gun:await(ConnPid, StreamRef3),
+ {error, {down, {shutdown, closed}}} = gun:await(ConnPid, undefined, 1000)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+request_timeout_skip_body(Config) ->
+ doc("Ensure the request_timeout drops connections when requests "
+ "fail to come in fast enough after skipping a request body."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ request_timeout => 500
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
+ ok = raw_send(Client, <<
+ "POST / HTTP/1.1\r\n"
+ "host: localhost\r\n"
+ "content-length: 12\r\n\r\n"
+ >>),
+ Data = raw_recv_head(Client),
+ {'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data),
+ {Headers, Rest} = cow_http:parse_headers(Rest0),
+ {_, Len} = lists:keyfind(<<"content-length">>, 1, Headers),
+ <<"Hello world!">> = raw_recv_rest(Client, binary_to_integer(Len), Rest),
+ %% We then send the request data that should be skipped by Cowboy.
+ timer:sleep(100),
+ raw_send(Client, <<"Hello world!">>),
+ %% Connection should be closed by the request_timeout after that.
+ {error, closed} = raw_recv(Client, 1, 1000)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+request_timeout_skip_body_more(Config) ->
+ doc("Ensure the request_timeout drops connections when requests "
+ "fail to come in fast enough after skipping a request body."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ request_timeout => 500
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
+ ok = raw_send(Client, <<
+ "POST / HTTP/1.1\r\n"
+ "host: localhost\r\n"
+ "content-length: 12\r\n\r\n"
+ >>),
+ Data = raw_recv_head(Client),
+ {'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data),
+ {Headers, Rest} = cow_http:parse_headers(Rest0),
+ {_, Len} = lists:keyfind(<<"content-length">>, 1, Headers),
+ <<"Hello world!">> = raw_recv_rest(Client, binary_to_integer(Len), Rest),
+ %% We then send the request data that should be skipped by Cowboy.
+ timer:sleep(100),
+ raw_send(Client, <<"Hello world!">>),
+ %% Send the start of another request.
+ ok = raw_send(Client, <<
+ "GET / HTTP/1.1\r\n"
+ "host: localhost\r\n"
+ %% Missing final \r\n on purpose.
+ >>),
+ %% Connection should be closed by the request_timeout after that.
+ {error, closed} = raw_recv(Client, 1, 1000)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
request_timeout_infinity(Config) ->
doc("Ensure the request_timeout option accepts the infinity value."),
{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{