diff options
Diffstat (limited to 'test/stream_handler_SUITE.erl')
-rw-r--r-- | test/stream_handler_SUITE.erl | 167 |
1 files changed, 96 insertions, 71 deletions
diff --git a/test/stream_handler_SUITE.erl b/test/stream_handler_SUITE.erl index bd87e40..f8e2200 100644 --- a/test/stream_handler_SUITE.erl +++ b/test/stream_handler_SUITE.erl @@ -31,50 +31,42 @@ groups() -> %% We set this module as a logger in order to silence expected errors. init_per_group(Name = http, Config) -> - cowboy_test:init_http(Name, #{ - logger => ?MODULE, - stream_handlers => [stream_handler_h] - }, Config); + cowboy_test:init_http(Name, init_plain_opts(), Config); init_per_group(Name = https, Config) -> - cowboy_test:init_https(Name, #{ - logger => ?MODULE, - stream_handlers => [stream_handler_h] - }, Config); + cowboy_test:init_https(Name, init_plain_opts(), Config); init_per_group(Name = h2, Config) -> - cowboy_test:init_http2(Name, #{ - logger => ?MODULE, - stream_handlers => [stream_handler_h] - }, Config); + cowboy_test:init_http2(Name, init_plain_opts(), Config); init_per_group(Name = h2c, Config) -> - Config1 = cowboy_test:init_http(Name, #{ - logger => ?MODULE, - stream_handlers => [stream_handler_h] - }, Config), + Config1 = cowboy_test:init_http(Name, init_plain_opts(), Config), lists:keyreplace(protocol, 1, Config1, {protocol, http2}); +init_per_group(Name = h3, Config) -> + cowboy_test:init_http3(Name, init_plain_opts(), Config); init_per_group(Name = http_compress, Config) -> - cowboy_test:init_http(Name, #{ - logger => ?MODULE, - stream_handlers => [cowboy_compress_h, stream_handler_h] - }, Config); + cowboy_test:init_http(Name, init_compress_opts(), Config); init_per_group(Name = https_compress, Config) -> - cowboy_test:init_https(Name, #{ - logger => ?MODULE, - stream_handlers => [cowboy_compress_h, stream_handler_h] - }, Config); + cowboy_test:init_https(Name, init_compress_opts(), Config); init_per_group(Name = h2_compress, Config) -> - cowboy_test:init_http2(Name, #{ - logger => ?MODULE, - stream_handlers => [cowboy_compress_h, stream_handler_h] - }, Config); + cowboy_test:init_http2(Name, init_compress_opts(), Config); init_per_group(Name = h2c_compress, Config) -> - Config1 = cowboy_test:init_http(Name, #{ - logger => ?MODULE, - stream_handlers => [cowboy_compress_h, stream_handler_h] - }, Config), - lists:keyreplace(protocol, 1, Config1, {protocol, http2}). + Config1 = cowboy_test:init_http(Name, init_compress_opts(), Config), + lists:keyreplace(protocol, 1, Config1, {protocol, http2}); +init_per_group(Name = h3_compress, Config) -> + cowboy_test:init_http3(Name, init_compress_opts(), Config). end_per_group(Name, _) -> - cowboy:stop_listener(Name). + cowboy_test:stop_group(Name). + +init_plain_opts() -> + #{ + logger => ?MODULE, + stream_handlers => [stream_handler_h] + }. + +init_compress_opts() -> + #{ + logger => ?MODULE, + stream_handlers => [cowboy_compress_h, stream_handler_h] + }. %% Logger function silencing the expected crashes. @@ -99,15 +91,20 @@ crash_in_init(Config) -> %% Confirm terminate/3 is NOT called. We have no state to give to it. receive {Self, Pid, terminate, _, _, _} -> error(terminate) after 1000 -> ok end, %% Confirm early_error/5 is called in HTTP/1.1's case. - %% HTTP/2 does not send a response back so there is no early_error call. + %% HTTP/2 and HTTP/3 do not send a response back so there is no early_error call. case config(protocol, Config) of http -> receive {Self, Pid, early_error, _, _, _, _, _} -> ok after 1000 -> error(timeout) end; - http2 -> ok + http2 -> ok; + http3 -> ok end, - %% Receive a 500 error response. - case gun:await(ConnPid, Ref) of - {response, fin, 500, _} -> ok; - {error, {stream_error, {stream_error, internal_error, _}}} -> ok + do_await_internal_error(ConnPid, Ref, Config). + +do_await_internal_error(ConnPid, Ref, Config) -> + Protocol = config(protocol, Config), + case {Protocol, gun:await(ConnPid, Ref)} of + {http, {response, fin, 500, _}} -> ok; + {http2, {error, {stream_error, {stream_error, internal_error, _}}}} -> ok; + {http3, {error, {stream_error, {stream_error, h3_internal_error, _}}}} -> ok end. crash_in_data(Config) -> @@ -126,11 +123,7 @@ crash_in_data(Config) -> gun:data(ConnPid, Ref, fin, <<"Hello!">>), %% Confirm terminate/3 is called, indicating the stream ended. receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end, - %% Receive a 500 error response. - case gun:await(ConnPid, Ref) of - {response, fin, 500, _} -> ok; - {error, {stream_error, {stream_error, internal_error, _}}} -> ok - end. + do_await_internal_error(ConnPid, Ref, Config). crash_in_info(Config) -> doc("Confirm an error is sent when a stream handler crashes in info/3."), @@ -144,14 +137,14 @@ crash_in_info(Config) -> %% Confirm init/3 is called. Pid = receive {Self, P, init, _, _, _} -> P after 1000 -> error(timeout) end, %% Send a message to make the stream handler crash. - Pid ! {{Pid, 1}, crash}, + StreamID = case config(protocol, Config) of + http3 -> 0; + _ -> 1 + end, + Pid ! {{Pid, StreamID}, crash}, %% Confirm terminate/3 is called, indicating the stream ended. receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end, - %% Receive a 500 error response. - case gun:await(ConnPid, Ref) of - {response, fin, 500, _} -> ok; - {error, {stream_error, {stream_error, internal_error, _}}} -> ok - end. + do_await_internal_error(ConnPid, Ref, Config). crash_in_terminate(Config) -> doc("Confirm the state is correct when a stream handler crashes in terminate/3."), @@ -185,10 +178,12 @@ crash_in_terminate(Config) -> {ok, <<"Hello world!">>} = gun:await_body(ConnPid, Ref2), ok. +%% @todo The callbacks ARE used for HTTP/2 and HTTP/3 CONNECT/TRACE requests. crash_in_early_error(Config) -> case config(protocol, Config) of http -> do_crash_in_early_error(Config); - http2 -> doc("The callback early_error/5 is not currently used for HTTP/2.") + http2 -> doc("The callback early_error/5 is not currently used for HTTP/2."); + http3 -> doc("The callback early_error/5 is not currently used for HTTP/3.") end. do_crash_in_early_error(Config) -> @@ -225,10 +220,12 @@ do_crash_in_early_error(Config) -> {response, fin, 500, _} = gun:await(ConnPid, Ref2), ok. +%% @todo The callbacks ARE used for HTTP/2 and HTTP/3 CONNECT/TRACE requests. crash_in_early_error_fatal(Config) -> case config(protocol, Config) of http -> do_crash_in_early_error_fatal(Config); - http2 -> doc("The callback early_error/5 is not currently used for HTTP/2.") + http2 -> doc("The callback early_error/5 is not currently used for HTTP/2."); + http3 -> doc("The callback early_error/5 is not currently used for HTTP/3.") end. do_crash_in_early_error_fatal(Config) -> @@ -262,7 +259,8 @@ early_error_stream_error_reason(Config) -> %% reason in both protocols. {Method, Headers, Status, Error} = case config(protocol, Config) of http -> {<<"GET">>, [{<<"host">>, <<"host:port">>}], 400, protocol_error}; - http2 -> {<<"TRACE">>, [], 501, no_error} + http2 -> {<<"TRACE">>, [], 501, no_error}; + http3 -> {<<"TRACE">>, [], 501, h3_no_error} end, Ref = gun:request(ConnPid, Method, "/long_polling", [ {<<"accept-encoding">>, <<"gzip">>}, @@ -355,11 +353,20 @@ shutdown_on_socket_close(Config) -> Spawn ! {Self, ready}, %% Close the socket. ok = gun:close(ConnPid), - %% Confirm terminate/3 is called, indicating the stream ended. - receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end, - %% Confirm we receive a DOWN message for the child process. - receive {'DOWN', MRef, process, Spawn, shutdown} -> ok after 1000 -> error(timeout) end, - ok. + Protocol = config(protocol, Config), + try + %% Confirm terminate/3 is called, indicating the stream ended. + receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end, + %% Confirm we receive a DOWN message for the child process. + receive {'DOWN', MRef, process, Spawn, shutdown} -> ok after 1000 -> error(timeout) end, + ok + catch error:timeout when Protocol =:= http3 -> + %% @todo Figure out why this happens. Could be a timing issue + %% or a legitimate bug. I suspect that the server just + %% doesn't receive the GOAWAY frame from Gun because + %% Gun is too quick to close the connection. + shutdown_on_socket_close(Config) + end. shutdown_timeout_on_stream_stop(Config) -> doc("Confirm supervised processes are killed " @@ -406,33 +413,45 @@ shutdown_timeout_on_socket_close(Config) -> Spawn ! {Self, ready}, %% Close the socket. ok = gun:close(ConnPid), - %% Confirm terminate/3 is called, indicating the stream ended. - receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end, - %% We should NOT receive a DOWN message immediately. - receive {'DOWN', MRef, process, Spawn, killed} -> error(killed) after 1500 -> ok end, - %% We should receive it now. - receive {'DOWN', MRef, process, Spawn, killed} -> ok after 1000 -> error(timeout) end, - ok. + Protocol = config(protocol, Config), + try + %% Confirm terminate/3 is called, indicating the stream ended. + receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end, + %% We should NOT receive a DOWN message immediately. + receive {'DOWN', MRef, process, Spawn, killed} -> error(killed) after 1500 -> ok end, + %% We should receive it now. + receive {'DOWN', MRef, process, Spawn, killed} -> ok after 1000 -> error(timeout) end, + ok + catch error:timeout when Protocol =:= http3 -> + %% @todo Figure out why this happens. Could be a timing issue + %% or a legitimate bug. I suspect that the server just + %% doesn't receive the GOAWAY frame from Gun because + %% Gun is too quick to close the connection. + shutdown_timeout_on_socket_close(Config) + end. switch_protocol_after_headers(Config) -> case config(protocol, Config) of http -> do_switch_protocol_after_response( <<"switch_protocol_after_headers">>, Config); - http2 -> doc("The switch_protocol command is not currently supported for HTTP/2.") + http2 -> doc("The switch_protocol command is not currently supported for HTTP/2."); + http3 -> doc("The switch_protocol command is not currently supported for HTTP/3.") end. switch_protocol_after_headers_data(Config) -> case config(protocol, Config) of http -> do_switch_protocol_after_response( <<"switch_protocol_after_headers_data">>, Config); - http2 -> doc("The switch_protocol command is not currently supported for HTTP/2.") + http2 -> doc("The switch_protocol command is not currently supported for HTTP/2."); + http3 -> doc("The switch_protocol command is not currently supported for HTTP/3.") end. switch_protocol_after_response(Config) -> case config(protocol, Config) of http -> do_switch_protocol_after_response( <<"switch_protocol_after_response">>, Config); - http2 -> doc("The switch_protocol command is not currently supported for HTTP/2.") + http2 -> doc("The switch_protocol command is not currently supported for HTTP/2."); + http3 -> doc("The switch_protocol command is not currently supported for HTTP/3.") end. do_switch_protocol_after_response(TestCase, Config) -> @@ -502,7 +521,12 @@ terminate_on_stop(Config) -> {response, fin, 204, _} = gun:await(ConnPid, Ref), %% Confirm the stream is still alive even though we %% received the response fully, and tell it to stop. - Pid ! {{Pid, 1}, please_stop}, + StreamID = case config(protocol, Config) of + http -> 1; + http2 -> 1; + http3 -> 0 + end, + Pid ! {{Pid, StreamID}, please_stop}, receive {Self, Pid, info, _, please_stop, _} -> ok after 1000 -> error(timeout) end, %% Confirm terminate/3 is called. receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end, @@ -511,7 +535,8 @@ terminate_on_stop(Config) -> terminate_on_switch_protocol(Config) -> case config(protocol, Config) of http -> do_terminate_on_switch_protocol(Config); - http2 -> doc("The switch_protocol command is not currently supported for HTTP/2.") + http2 -> doc("The switch_protocol command is not currently supported for HTTP/2."); + http3 -> doc("The switch_protocol command is not currently supported for HTTP/3.") end. do_terminate_on_switch_protocol(Config) -> |