aboutsummaryrefslogtreecommitdiffstats
path: root/test/http_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'test/http_SUITE.erl')
-rw-r--r--test/http_SUITE.erl124
1 files changed, 120 insertions, 4 deletions
diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl
index 0325279..9928136 100644
--- a/test/http_SUITE.erl
+++ b/test/http_SUITE.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2018-2024, Loïc Hoguin <[email protected]>
+%% Copyright (c) Loïc Hoguin <[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
@@ -28,9 +28,16 @@
-import(cowboy_test, [raw_recv/3]).
-import(cowboy_test, [raw_expect_recv/2]).
-all() -> [{group, clear}].
+all() ->
+ [{group, clear_no_parallel}, {group, clear}].
-groups() -> [{clear, [parallel], ct_helper:all(?MODULE)}].
+groups() ->
+ [
+ %% cowboy:stop_listener can be slow when called many times
+ %% in parallel so we must run this test separately from the others.
+ {clear_no_parallel, [], [graceful_shutdown_listener]},
+ {clear, [parallel], ct_helper:all(?MODULE) -- [graceful_shutdown_listener]}
+ ].
init_per_group(Name, Config) ->
cowboy_test:init_http(Name, #{
@@ -43,6 +50,7 @@ end_per_group(Name, _) ->
init_dispatch(_) ->
cowboy_router:compile([{"localhost", [
{"/", hello_h, []},
+ {"/delay_hello", delay_hello_h, #{delay => 1000, notify_received => self()}},
{"/echo/:key", echo_h, []},
{"/resp/:key[/:arg]", resp_h, []},
{"/set_options/:key", set_options_h, []},
@@ -198,6 +206,94 @@ do_chunked_body(ChunkSize0, Data, Acc) ->
do_chunked_body(ChunkSize, Rest,
[iolist_to_binary(cow_http_te:chunk(Chunk))|Acc]).
+disable_http1_tls(Config) ->
+ doc("Ensure that we can disable HTTP/1.1 over TLS (force HTTP/2)."),
+ TlsOpts = ct_helper:get_certs_from_ets(),
+ {ok, _} = cowboy:start_tls(?FUNCTION_NAME, TlsOpts ++ [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ alpn_default_protocol => http2
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ {ok, Socket} = ssl:connect("localhost", Port,
+ [binary, {active, false}|TlsOpts]),
+ %% ALPN was not negotiated but we're still over HTTP/2.
+ {error, protocol_not_negotiated} = ssl:negotiated_protocol(Socket),
+ %% Send a valid preface.
+ ok = ssl:send(Socket, [
+ "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
+ cow_http2:settings(#{})]),
+ %% Receive the server preface.
+ {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
+ {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
+ ok
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+disable_http2_prior_knowledge(Config) ->
+ doc("Ensure that we can disable prior knowledge HTTP/2 upgrade."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ protocols => [http]
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ {ok, Socket} = gen_tcp:connect("localhost", Port, [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(#{})]),
+ {ok, <<"HTTP/1.1 501">>} = gen_tcp:recv(Socket, 12, 1000),
+ ok
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+disable_http2_upgrade(Config) ->
+ doc("Ensure that we can disable HTTP/1.1 upgrade to HTTP/2."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ protocols => [http]
+ }),
+ Port = ranch:get_port(?FUNCTION_NAME),
+ try
+ {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}]),
+ %% Send a valid preface.
+ ok = gen_tcp:send(Socket, [
+ "GET / HTTP/1.1\r\n"
+ "Host: localhost\r\n"
+ "Connection: Upgrade, HTTP2-Settings\r\n"
+ "Upgrade: h2c\r\n"
+ "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
+ "\r\n"]),
+ {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
+ ok
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
+hibernate(Config) ->
+ doc("Ensure that we can enable hibernation for HTTP/1.1 connections."),
+ {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
+ env => #{dispatch => init_dispatch(Config)},
+ hibernate => true
+ }),
+ 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),
+ gun:close(ConnPid)
+ after
+ cowboy:stop_listener(?FUNCTION_NAME)
+ end.
+
http10_keepalive_false(Config) ->
doc("Confirm the option http10_keepalive => false disables keep-alive "
"completely for HTTP/1.0 connections."),
@@ -454,6 +550,26 @@ request_timeout_pipeline(Config) ->
cowboy:stop_listener(?FUNCTION_NAME)
end.
+request_timeout_pipeline_delay(Config) ->
+ doc("Ensure the request_timeout does not trigger on requests "
+ "coming in after a large 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
+ ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+ {ok, http} = gun:await_up(ConnPid),
+ StreamRef1 = gun:post(ConnPid, "/", #{}, <<0:8000000>>),
+ StreamRef2 = gun:get(ConnPid, "/delay_hello"),
+ {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.
+
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."),
@@ -779,8 +895,8 @@ graceful_shutdown_listener(Config) ->
send_timeout_close(_Config) ->
doc("Check that connections are closed on send timeout."),
TransOpts = #{
- port => 0,
socket_opts => [
+ {port, 0},
{send_timeout, 100},
{send_timeout_close, true},
{sndbuf, 10}