Browse Source

Use try..after in tests that start their own listeners

Loïc Hoguin 6 years ago
parent
commit
d2f367fba3
5 changed files with 631 additions and 524 deletions
  1. 123 93
      test/http2_SUITE.erl
  2. 156 121
      test/http_SUITE.erl
  3. 24 18
      test/misc_SUITE.erl
  4. 12 9
      test/rfc7230_SUITE.erl
  5. 316 283
      test/rfc7540_SUITE.erl

+ 123 - 93
test/http2_SUITE.erl

@@ -58,11 +58,14 @@ idle_timeout(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{ok, Socket} = do_handshake([{port, Port}|Config]),
-	timer:sleep(1000),
-	%% Receive a GOAWAY frame back with NO_ERROR.
-	{ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 1000),
-	ok.
+	try
+		{ok, Socket} = do_handshake([{port, Port}|Config]),
+		timer:sleep(1000),
+		%% Receive a GOAWAY frame back with NO_ERROR.
+		{ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 idle_timeout_infinity(Config) ->
 idle_timeout_infinity(Config) ->
 	doc("Ensure the idle_timeout option accepts the infinity value."),
 	doc("Ensure the idle_timeout option accepts the infinity value."),
@@ -72,11 +75,14 @@ idle_timeout_infinity(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{ok, Socket} = do_handshake([{port, Port}|Config]),
-	timer:sleep(1000),
-	%% Don't receive a GOAWAY frame.
-	{error, timeout} = gen_tcp:recv(Socket, 17, 1000),
-	ok.
+	try
+		{ok, Socket} = do_handshake([{port, Port}|Config]),
+		timer:sleep(1000),
+		%% Don't receive a GOAWAY frame.
+		{error, timeout} = gen_tcp:recv(Socket, 17, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 idle_timeout_reset_on_data(Config) ->
 idle_timeout_reset_on_data(Config) ->
 	doc("Terminate when the idle timeout is reached."),
 	doc("Terminate when the idle timeout is reached."),
@@ -86,23 +92,26 @@ idle_timeout_reset_on_data(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{ok, Socket} = do_handshake([{port, Port}|Config]),
-	%% We wait a little, send a PING, receive a PING ack.
-	{error, timeout} = gen_tcp:recv(Socket, 17, 500),
-	ok = gen_tcp:send(Socket, cow_http2:ping(0)),
-	{ok, <<8:24, 6:8, 0:7, 1:1, 0:96>>} = gen_tcp:recv(Socket, 17, 1000),
-	%% Again.
-	{error, timeout} = gen_tcp:recv(Socket, 17, 500),
-	ok = gen_tcp:send(Socket, cow_http2:ping(0)),
-	{ok, <<8:24, 6:8, 0:7, 1:1, 0:96>>} = gen_tcp:recv(Socket, 17, 1000),
-	%% And one more time.
-	{error, timeout} = gen_tcp:recv(Socket, 17, 500),
-	ok = gen_tcp:send(Socket, cow_http2:ping(0)),
-	{ok, <<8:24, 6:8, 0:7, 1:1, 0:96>>} = gen_tcp:recv(Socket, 17, 1000),
-	%% The connection goes away soon after we stop sending data.
-	timer:sleep(1000),
-	{ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 1000),
-	ok.
+	try
+		{ok, Socket} = do_handshake([{port, Port}|Config]),
+		%% We wait a little, send a PING, receive a PING ack.
+		{error, timeout} = gen_tcp:recv(Socket, 17, 500),
+		ok = gen_tcp:send(Socket, cow_http2:ping(0)),
+		{ok, <<8:24, 6:8, 0:7, 1:1, 0:96>>} = gen_tcp:recv(Socket, 17, 1000),
+		%% Again.
+		{error, timeout} = gen_tcp:recv(Socket, 17, 500),
+		ok = gen_tcp:send(Socket, cow_http2:ping(0)),
+		{ok, <<8:24, 6:8, 0:7, 1:1, 0:96>>} = gen_tcp:recv(Socket, 17, 1000),
+		%% And one more time.
+		{error, timeout} = gen_tcp:recv(Socket, 17, 500),
+		ok = gen_tcp:send(Socket, cow_http2:ping(0)),
+		{ok, <<8:24, 6:8, 0:7, 1:1, 0:96>>} = gen_tcp:recv(Socket, 17, 1000),
+		%% The connection goes away soon after we stop sending data.
+		timer:sleep(1000),
+		{ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 inactivity_timeout(Config) ->
 inactivity_timeout(Config) ->
 	doc("Terminate when the inactivity timeout is reached."),
 	doc("Terminate when the inactivity timeout is reached."),
@@ -112,11 +121,14 @@ inactivity_timeout(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{ok, Socket} = do_handshake([{port, Port}|Config]),
-	receive after 1000 -> ok end,
-	%% Receive a GOAWAY frame back with an INTERNAL_ERROR.
-	{ok, << _:24, 7:8, _:72, 2:32 >>} = gen_tcp:recv(Socket, 17, 1000),
-	ok.
+	try
+		{ok, Socket} = do_handshake([{port, Port}|Config]),
+		receive after 1000 -> ok end,
+		%% Receive a GOAWAY frame back with an INTERNAL_ERROR.
+		{ok, << _:24, 7:8, _:72, 2:32 >>} = gen_tcp:recv(Socket, 17, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 initial_connection_window_size(Config) ->
 initial_connection_window_size(Config) ->
 	doc("Confirm a WINDOW_UPDATE frame is sent when the configured "
 	doc("Confirm a WINDOW_UPDATE frame is sent when the configured "
@@ -128,16 +140,19 @@ initial_connection_window_size(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{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(#{})]),
-	%% Receive the server preface.
-	{ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
-	{ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
-	%% Receive a WINDOW_UPDATE frame incrementing the connection window to 100000.
-	{ok, <<4:24, 8:8, 0:41, Size:31>>} = gen_tcp:recv(Socket, 13, 1000),
-	ConfiguredSize = Size + 65535,
-	ok.
+	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(#{})]),
+		%% Receive the server preface.
+		{ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
+		{ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
+		%% Receive a WINDOW_UPDATE frame incrementing the connection window to 100000.
+		{ok, <<4:24, 8:8, 0:41, Size:31>>} = gen_tcp:recv(Socket, 13, 1000),
+		ConfiguredSize = Size + 65535
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 max_frame_size_sent(Config) ->
 max_frame_size_sent(Config) ->
 	doc("Confirm that frames sent by Cowboy are limited in size "
 	doc("Confirm that frames sent by Cowboy are limited in size "
@@ -149,35 +164,38 @@ max_frame_size_sent(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{ok, Socket} = do_handshake(#{max_frame_size => MaxFrameSize + 10000}, [{port, Port}|Config]),
-	%% Send a request with a 30000 bytes body.
-	{HeadersBlock, _} = cow_hpack:encode([
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/echo/read_body">>}
-	]),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, nofin, <<0:16384/unit:8>>),
-		cow_http2:data(1, fin, <<0:13616/unit:8>>)
-	]),
-	%% Receive a HEADERS frame as a response.
-	{ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = case gen_tcp:recv(Socket, 9, 1000) of
-		%% We received a WINDOW_UPDATE first. Skip it and the next.
-		{ok, <<4:24, 8:8, 0:40>>} ->
-			{ok, _} = gen_tcp:recv(Socket, 4 + 13, 1000),
-			gen_tcp:recv(Socket, 9, 1000);
-		Res ->
-			Res
-	end,
-	{ok, _} = gen_tcp:recv(Socket, SkipLen, 6000),
-	%% The DATA frames following must have lengths of 20000
-	%% and then 10000 due to the limit.
-	{ok, <<20000:24, 0:8, _:40, _:20000/unit:8>>} = gen_tcp:recv(Socket, 20009, 6000),
-	{ok, <<10000:24, 0:8, _:40, _:10000/unit:8>>} = gen_tcp:recv(Socket, 10009, 6000),
-	%% Stop the listener.
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		{ok, Socket} = do_handshake(#{max_frame_size => MaxFrameSize + 10000},
+			[{port, Port}|Config]),
+		%% Send a request with a 30000 bytes body.
+		{HeadersBlock, _} = cow_hpack:encode([
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/echo/read_body">>}
+		]),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, nofin, <<0:16384/unit:8>>),
+			cow_http2:data(1, fin, <<0:13616/unit:8>>)
+		]),
+		%% Receive a HEADERS frame as a response.
+		{ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = case gen_tcp:recv(Socket, 9, 1000) of
+			%% We received a WINDOW_UPDATE first. Skip it and the next.
+			{ok, <<4:24, 8:8, 0:40>>} ->
+				{ok, _} = gen_tcp:recv(Socket, 4 + 13, 1000),
+				gen_tcp:recv(Socket, 9, 1000);
+			Res ->
+				Res
+		end,
+		{ok, _} = gen_tcp:recv(Socket, SkipLen, 6000),
+		%% The DATA frames following must have lengths of 20000
+		%% and then 10000 due to the limit.
+		{ok, <<20000:24, 0:8, _:40, _:20000/unit:8>>} = gen_tcp:recv(Socket, 20009, 6000),
+		{ok, <<10000:24, 0:8, _:40, _:10000/unit:8>>} = gen_tcp:recv(Socket, 10009, 6000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 preface_timeout_infinity(Config) ->
 preface_timeout_infinity(Config) ->
 	doc("Ensure infinity for preface_timeout is accepted."),
 	doc("Ensure infinity for preface_timeout is accepted."),
@@ -187,13 +205,17 @@ preface_timeout_infinity(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{ok, Socket} = do_handshake([{port, Port}|Config]),
-	Pid = get_remote_pid_tcp(Socket),
-	Ref = erlang:monitor(process, Pid),
-	receive
-		{'DOWN', Ref, process, Pid, Reason} ->
-			error(Reason)
-	after 1000 ->
+	try
+		{ok, Socket} = do_handshake([{port, Port}|Config]),
+		Pid = get_remote_pid_tcp(Socket),
+		Ref = erlang:monitor(process, Pid),
+		receive
+			{'DOWN', Ref, process, Pid, Reason} ->
+				error(Reason)
+		after 1000 ->
+			ok
+		end
+	after
 		cowboy:stop_listener(?FUNCTION_NAME)
 		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.
 
 
@@ -206,14 +228,18 @@ resp_iolist_body(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	ConnPid = gun_open([{type, tcp}, {protocol, http2}, {port, Port}|Config]),
-	Ref = gun:get(ConnPid, "/resp_iolist_body"),
-	{response, nofin, 200, RespHeaders} = gun:await(ConnPid, Ref),
-	{_, BinLen} = lists:keyfind(<<"content-length">>, 1, RespHeaders),
-	Len = binary_to_integer(BinLen),
-	{ok, RespBody} = gun:await_body(ConnPid, Ref),
-	Len = iolist_size(RespBody),
-	gun:close(ConnPid).
+	try
+		ConnPid = gun_open([{type, tcp}, {protocol, http2}, {port, Port}|Config]),
+		Ref = gun:get(ConnPid, "/resp_iolist_body"),
+		{response, nofin, 200, RespHeaders} = gun:await(ConnPid, Ref),
+		{_, BinLen} = lists:keyfind(<<"content-length">>, 1, RespHeaders),
+		Len = binary_to_integer(BinLen),
+		{ok, RespBody} = gun:await_body(ConnPid, Ref),
+		Len = iolist_size(RespBody),
+		gun:close(ConnPid)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 settings_timeout_infinity(Config) ->
 settings_timeout_infinity(Config) ->
 	doc("Ensure infinity for settings_timeout is accepted."),
 	doc("Ensure infinity for settings_timeout is accepted."),
@@ -223,12 +249,16 @@ settings_timeout_infinity(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	{ok, Socket} = do_handshake([{port, Port}|Config]),
-	Pid = get_remote_pid_tcp(Socket),
-	Ref = erlang:monitor(process, Pid),
-	receive
-		{'DOWN', Ref, process, Pid, Reason} ->
-			error(Reason)
-	after 1000 ->
+	try
+		{ok, Socket} = do_handshake([{port, Port}|Config]),
+		Pid = get_remote_pid_tcp(Socket),
+		Ref = erlang:monitor(process, Pid),
+		receive
+			{'DOWN', Ref, process, Pid, Reason} ->
+				error(Reason)
+		after 1000 ->
+			ok
+		end
+	after
 		cowboy:stop_listener(?FUNCTION_NAME)
 		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.

+ 156 - 121
test/http_SUITE.erl

@@ -47,23 +47,26 @@ chunked_false(Config) ->
 		chunked => false
 		chunked => false
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	Request = "GET /resp/stream_reply2/200 HTTP/1.1\r\nhost: localhost\r\n\r\n",
-	Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
-	ok = raw_send(Client, Request),
-	Rest = case catch raw_recv_head(Client) of
-		{'EXIT', _} -> error(closed);
-		Data ->
-			%% Cowboy always advertises itself as HTTP/1.1.
-			{'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data),
-			{Headers, Rest1} = cow_http:parse_headers(Rest0),
-			false = lists:keyfind(<<"content-length">>, 1, Headers),
-			false = lists:keyfind(<<"transfer-encoding">>, 1, Headers),
-			Rest1
-	end,
-	Bits = 8000000 - bit_size(Rest),
-	raw_expect_recv(Client, <<0:Bits>>),
-	{error, closed} = raw_recv(Client, 1, 1000),
-	ok.
+	try
+		Request = "GET /resp/stream_reply2/200 HTTP/1.1\r\nhost: localhost\r\n\r\n",
+		Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
+		ok = raw_send(Client, Request),
+		Rest = case catch raw_recv_head(Client) of
+			{'EXIT', _} -> error(closed);
+			Data ->
+				%% Cowboy always advertises itself as HTTP/1.1.
+				{'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data),
+				{Headers, Rest1} = cow_http:parse_headers(Rest0),
+				false = lists:keyfind(<<"content-length">>, 1, Headers),
+				false = lists:keyfind(<<"transfer-encoding">>, 1, Headers),
+				Rest1
+		end,
+		Bits = 8000000 - bit_size(Rest),
+		raw_expect_recv(Client, <<0:Bits>>),
+		{error, closed} = raw_recv(Client, 1, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 http10_keepalive_false(Config) ->
 http10_keepalive_false(Config) ->
 	doc("Confirm the option http10_keepalive => false disables keep-alive "
 	doc("Confirm the option http10_keepalive => false disables keep-alive "
@@ -73,21 +76,25 @@ http10_keepalive_false(Config) ->
 		http10_keepalive => false
 		http10_keepalive => false
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	Keepalive = "GET / HTTP/1.0\r\nhost: localhost\r\nConnection: keep-alive\r\n\r\n",
-	Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
-	ok = raw_send(Client, Keepalive),
-	_ = case catch raw_recv_head(Client) of
-		{'EXIT', _} -> error(closed);
-		Data ->
-			%% Cowboy always advertises itself as HTTP/1.1.
-			{'HTTP/1.1', 200, _, Rest} = cow_http:parse_status_line(Data),
-			{Headers, _} = cow_http:parse_headers(Rest),
-			{_, <<"close">>} = lists:keyfind(<<"connection">>, 1, Headers)
-	end,
-	ok = raw_send(Client, Keepalive),
-	case catch raw_recv_head(Client) of
-		{'EXIT', _} -> closed;
-		_ -> error(not_closed)
+	try
+		Keepalive = "GET / HTTP/1.0\r\nhost: localhost\r\nConnection: keep-alive\r\n\r\n",
+		Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
+		ok = raw_send(Client, Keepalive),
+		_ = case catch raw_recv_head(Client) of
+			{'EXIT', _} -> error(closed);
+			Data ->
+				%% Cowboy always advertises itself as HTTP/1.1.
+				{'HTTP/1.1', 200, _, Rest} = cow_http:parse_status_line(Data),
+				{Headers, _} = cow_http:parse_headers(Rest),
+				{_, <<"close">>} = lists:keyfind(<<"connection">>, 1, Headers)
+		end,
+		ok = raw_send(Client, Keepalive),
+		case catch raw_recv_head(Client) of
+			{'EXIT', _} -> closed;
+			_ -> error(not_closed)
+		end
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.
 
 
 idle_timeout_infinity(Config) ->
 idle_timeout_infinity(Config) ->
@@ -98,17 +105,21 @@ idle_timeout_infinity(Config) ->
 		idle_timeout => infinity
 		idle_timeout => infinity
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
-	_ = gun:post(ConnPid, "/echo/read_body",
-		[{<<"content-type">>, <<"text/plain">>}]),
-	#{socket := Socket} = gun:info(ConnPid),
-	Pid = get_remote_pid_tcp(Socket),
-	Ref = erlang:monitor(process, Pid),
-	receive
-		{'DOWN', Ref, process, Pid, Reason} ->
-			error(Reason)
-	after 1000 ->
-		ok
+	try
+		ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+		_ = gun:post(ConnPid, "/echo/read_body",
+			[{<<"content-type">>, <<"text/plain">>}]),
+		#{socket := Socket} = gun:info(ConnPid),
+		Pid = get_remote_pid_tcp(Socket),
+		Ref = erlang:monitor(process, Pid),
+		receive
+			{'DOWN', Ref, process, Pid, Reason} ->
+				error(Reason)
+		after 1000 ->
+			ok
+		end
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.
 
 
 request_timeout_infinity(Config) ->
 request_timeout_infinity(Config) ->
@@ -118,15 +129,19 @@ request_timeout_infinity(Config) ->
 		idle_timeout => infinity
 		idle_timeout => infinity
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
-	#{socket := Socket} = gun:info(ConnPid),
-	Pid = get_remote_pid_tcp(Socket),
-	Ref = erlang:monitor(process, Pid),
-	receive
-		{'DOWN', Ref, process, Pid, Reason} ->
-			error(Reason)
-	after 1000 ->
-		ok
+	try
+		ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+		#{socket := Socket} = gun:info(ConnPid),
+		Pid = get_remote_pid_tcp(Socket),
+		Ref = erlang:monitor(process, Pid),
+		receive
+			{'DOWN', Ref, process, Pid, Reason} ->
+				error(Reason)
+		after 1000 ->
+			ok
+		end
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.
 
 
 set_options_chunked_false(Config) ->
 set_options_chunked_false(Config) ->
@@ -138,21 +153,24 @@ set_options_chunked_false(Config) ->
 		chunked => true
 		chunked => true
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	Request = "GET /set_options/chunked_false HTTP/1.1\r\nhost: localhost\r\n\r\n",
-	Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
-	ok = raw_send(Client, Request),
-	_ = case catch raw_recv_head(Client) of
-		{'EXIT', _} -> error(closed);
-		Data ->
-			%% Cowboy always advertises itself as HTTP/1.1.
-			{'HTTP/1.1', 200, _, Rest} = cow_http:parse_status_line(Data),
-			{Headers, <<>>} = cow_http:parse_headers(Rest),
-			false = lists:keyfind(<<"content-length">>, 1, Headers),
-			false = lists:keyfind(<<"transfer-encoding">>, 1, Headers)
-	end,
-	raw_expect_recv(Client, <<0:8000000>>),
-	{error, closed} = raw_recv(Client, 1, 1000),
-	ok.
+	try
+		Request = "GET /set_options/chunked_false HTTP/1.1\r\nhost: localhost\r\n\r\n",
+		Client = raw_open([{type, tcp}, {port, Port}, {opts, []}|Config]),
+		ok = raw_send(Client, Request),
+		_ = case catch raw_recv_head(Client) of
+			{'EXIT', _} -> error(closed);
+			Data ->
+				%% Cowboy always advertises itself as HTTP/1.1.
+				{'HTTP/1.1', 200, _, Rest} = cow_http:parse_status_line(Data),
+				{Headers, <<>>} = cow_http:parse_headers(Rest),
+				false = lists:keyfind(<<"content-length">>, 1, Headers),
+				false = lists:keyfind(<<"transfer-encoding">>, 1, Headers)
+		end,
+		raw_expect_recv(Client, <<0:8000000>>),
+		{error, closed} = raw_recv(Client, 1, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 set_options_chunked_false_ignored(Config) ->
 set_options_chunked_false_ignored(Config) ->
 	doc("Confirm the option chunked can be dynamically set to disable "
 	doc("Confirm the option chunked can be dynamically set to disable "
@@ -163,18 +181,21 @@ set_options_chunked_false_ignored(Config) ->
 		chunked => true
 		chunked => true
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
-	%% We do a first request setting the option but not
-	%% using chunked transfer-encoding in the response.
-	StreamRef1 = gun:get(ConnPid, "/set_options/chunked_false_ignored"),
-	{response, nofin, 200, _} = gun:await(ConnPid, StreamRef1),
-	{ok, <<"Hello world!">>} = gun:await_body(ConnPid, StreamRef1),
-	%% We then do a second request to confirm that chunked
-	%% is not disabled for that second request.
-	StreamRef2 = gun:get(ConnPid, "/resp/stream_reply2/200"),
-	{response, nofin, 200, Headers} = gun:await(ConnPid, StreamRef2),
-	{_, <<"chunked">>} = lists:keyfind(<<"transfer-encoding">>, 1, Headers),
-	ok.
+	try
+		ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+		%% We do a first request setting the option but not
+		%% using chunked transfer-encoding in the response.
+		StreamRef1 = gun:get(ConnPid, "/set_options/chunked_false_ignored"),
+		{response, nofin, 200, _} = gun:await(ConnPid, StreamRef1),
+		{ok, <<"Hello world!">>} = gun:await_body(ConnPid, StreamRef1),
+		%% We then do a second request to confirm that chunked
+		%% is not disabled for that second request.
+		StreamRef2 = gun:get(ConnPid, "/resp/stream_reply2/200"),
+		{response, nofin, 200, Headers} = gun:await(ConnPid, StreamRef2),
+		{_, <<"chunked">>} = lists:keyfind(<<"transfer-encoding">>, 1, Headers)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 set_options_idle_timeout(Config) ->
 set_options_idle_timeout(Config) ->
 	doc("Confirm that the idle_timeout option can be dynamically "
 	doc("Confirm that the idle_timeout option can be dynamically "
@@ -185,17 +206,21 @@ set_options_idle_timeout(Config) ->
 		idle_timeout => 60000
 		idle_timeout => 60000
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
-	_ = gun:post(ConnPid, "/set_options/idle_timeout_short",
-		[{<<"content-type">>, <<"text/plain">>}]),
-	#{socket := Socket} = gun:info(ConnPid),
-	Pid = get_remote_pid_tcp(Socket),
-	Ref = erlang:monitor(process, Pid),
-	receive
-		{'DOWN', Ref, process, Pid, _} ->
-			ok
-	after 2000 ->
-		error(timeout)
+	try
+		ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+		_ = gun:post(ConnPid, "/set_options/idle_timeout_short",
+			[{<<"content-type">>, <<"text/plain">>}]),
+		#{socket := Socket} = gun:info(ConnPid),
+		Pid = get_remote_pid_tcp(Socket),
+		Ref = erlang:monitor(process, Pid),
+		receive
+			{'DOWN', Ref, process, Pid, _} ->
+				ok
+		after 2000 ->
+			error(timeout)
+		end
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.
 
 
 set_options_idle_timeout_only_applies_to_current_request(Config) ->
 set_options_idle_timeout_only_applies_to_current_request(Config) ->
@@ -206,30 +231,34 @@ set_options_idle_timeout_only_applies_to_current_request(Config) ->
 		idle_timeout => 500
 		idle_timeout => 500
 	}),
 	}),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
-	StreamRef = gun:post(ConnPid, "/set_options/idle_timeout_long",
-		[{<<"content-type">>, <<"text/plain">>}]),
-	#{socket := Socket} = gun:info(ConnPid),
-	Pid = get_remote_pid_tcp(Socket),
-	Ref = erlang:monitor(process, Pid),
-	receive
-		{'DOWN', Ref, process, Pid, Reason} ->
-			error(Reason)
-	after 2000 ->
-		ok
-	end,
-	%% Finish the first request and start a second one to confirm
-	%% the idle_timeout option is back to normal.
-	gun:data(ConnPid, StreamRef, fin, <<"Hello!">>),
-	{response, nofin, 200, _} = gun:await(ConnPid, StreamRef),
-	{ok, <<"Hello!">>} = gun:await_body(ConnPid, StreamRef),
-	_ = gun:post(ConnPid, "/echo/read_body",
-		[{<<"content-type">>, <<"text/plain">>}]),
-	receive
-		{'DOWN', Ref, process, Pid, _} ->
+	try
+		ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
+		StreamRef = gun:post(ConnPid, "/set_options/idle_timeout_long",
+			[{<<"content-type">>, <<"text/plain">>}]),
+		#{socket := Socket} = gun:info(ConnPid),
+		Pid = get_remote_pid_tcp(Socket),
+		Ref = erlang:monitor(process, Pid),
+		receive
+			{'DOWN', Ref, process, Pid, Reason} ->
+				error(Reason)
+		after 2000 ->
 			ok
 			ok
-	after 2000 ->
-		error(timeout)
+		end,
+		%% Finish the first request and start a second one to confirm
+		%% the idle_timeout option is back to normal.
+		gun:data(ConnPid, StreamRef, fin, <<"Hello!">>),
+		{response, nofin, 200, _} = gun:await(ConnPid, StreamRef),
+		{ok, <<"Hello!">>} = gun:await_body(ConnPid, StreamRef),
+		_ = gun:post(ConnPid, "/echo/read_body",
+			[{<<"content-type">>, <<"text/plain">>}]),
+		receive
+			{'DOWN', Ref, process, Pid, _} ->
+				ok
+		after 2000 ->
+			error(timeout)
+		end
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.
 
 
 switch_protocol_flush(Config) ->
 switch_protocol_flush(Config) ->
@@ -240,12 +269,18 @@ switch_protocol_flush(Config) ->
 	},
 	},
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts),
 	Port = ranch:get_port(?FUNCTION_NAME),
 	Port = ranch:get_port(?FUNCTION_NAME),
-	Self = self(),
-	ConnPid = gun_open([{port, Port}, {type, tcp}, {protocol, http}|Config]),
-	_ = gun:get(ConnPid, "/", [
-		{<<"x-test-pid">>, pid_to_list(Self)}
-	]),
-	receive
-		{Self, Events} ->
-			switch_protocol_flush_h:validate(Events)
+	try
+		Self = self(),
+		ConnPid = gun_open([{port, Port}, {type, tcp}, {protocol, http}|Config]),
+		_ = gun:get(ConnPid, "/", [
+			{<<"x-test-pid">>, pid_to_list(Self)}
+		]),
+		receive
+			{Self, Events} ->
+				switch_protocol_flush_h:validate(Events)
+		after 5000 ->
+			error(timeout)
+		end
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
 	end.
 	end.

+ 24 - 18
test/misc_SUITE.erl

@@ -82,25 +82,31 @@ set_env(Config0) ->
 	Config = cowboy_test:init_http(?FUNCTION_NAME, #{
 	Config = cowboy_test:init_http(?FUNCTION_NAME, #{
 		env => #{dispatch => []}
 		env => #{dispatch => []}
 	}, Config0),
 	}, Config0),
-	ConnPid1 = gun_open(Config),
-	Ref1 = gun:get(ConnPid1, "/"),
-	{response, _, 400, _} = gun:await(ConnPid1, Ref1),
-	cowboy:set_env(?FUNCTION_NAME, dispatch, init_dispatch(Config)),
-	%% Only new connections get the updated environment.
-	ConnPid2 = gun_open(Config),
-	Ref2 = gun:get(ConnPid2, "/"),
-	{response, _, 200, _} = gun:await(ConnPid2, Ref2),
-	ok.
+	try
+		ConnPid1 = gun_open(Config),
+		Ref1 = gun:get(ConnPid1, "/"),
+		{response, _, 400, _} = gun:await(ConnPid1, Ref1),
+		cowboy:set_env(?FUNCTION_NAME, dispatch, init_dispatch(Config)),
+		%% Only new connections get the updated environment.
+		ConnPid2 = gun_open(Config),
+		Ref2 = gun:get(ConnPid2, "/"),
+		{response, _, 200, _} = gun:await(ConnPid2, Ref2)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 set_env_missing(Config0) ->
 set_env_missing(Config0) ->
 	doc("Live replace a middleware environment value when env was not provided."),
 	doc("Live replace a middleware environment value when env was not provided."),
 	Config = cowboy_test:init_http(?FUNCTION_NAME, #{}, Config0),
 	Config = cowboy_test:init_http(?FUNCTION_NAME, #{}, Config0),
-	ConnPid1 = gun_open(Config),
-	Ref1 = gun:get(ConnPid1, "/"),
-	{response, _, 500, _} = gun:await(ConnPid1, Ref1),
-	cowboy:set_env(?FUNCTION_NAME, dispatch, []),
-	%% Only new connections get the updated environment.
-	ConnPid2 = gun_open(Config),
-	Ref2 = gun:get(ConnPid2, "/"),
-	{response, _, 400, _} = gun:await(ConnPid2, Ref2),
-	ok.
+	try
+		ConnPid1 = gun_open(Config),
+		Ref1 = gun:get(ConnPid1, "/"),
+		{response, _, 500, _} = gun:await(ConnPid1, Ref1),
+		cowboy:set_env(?FUNCTION_NAME, dispatch, []),
+		%% Only new connections get the updated environment.
+		ConnPid2 = gun_open(Config),
+		Ref2 = gun:get(ConnPid2, "/"),
+		{response, _, 400, _} = gun:await(ConnPid2, Ref2)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.

+ 12 - 9
test/rfc7230_SUITE.erl

@@ -1593,15 +1593,18 @@ empty_host(Config0) ->
 	Config = cowboy_test:init_http(?FUNCTION_NAME, #{
 	Config = cowboy_test:init_http(?FUNCTION_NAME, #{
 		env => #{dispatch => cowboy_router:compile(Routes)}
 		env => #{dispatch => cowboy_router:compile(Routes)}
 	}, Config0),
 	}, Config0),
-	#{code := 200, body := <<>>} = do_raw(Config, [
-		"GET /echo/host HTTP/1.1\r\n"
-		"Host:\r\n"
-		"\r\n"]),
-	#{code := 200, body := <<>>} = do_raw(Config, [
-		"GET /echo/host HTTP/1.1\r\n"
-		"Host: \r\n"
-		"\r\n"]),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		#{code := 200, body := <<>>} = do_raw(Config, [
+			"GET /echo/host HTTP/1.1\r\n"
+			"Host:\r\n"
+			"\r\n"]),
+		#{code := 200, body := <<>>} = do_raw(Config, [
+			"GET /echo/host HTTP/1.1\r\n"
+			"Host: \r\n"
+			"\r\n"])
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 %% The effective request URI can be rebuilt by concatenating scheme,
 %% The effective request URI can be rebuilt by concatenating scheme,
 %% "://", authority, path and query components. (RFC7230 5.5)
 %% "://", authority, path and query components. (RFC7230 5.5)

+ 316 - 283
test/rfc7540_SUITE.erl

@@ -1327,27 +1327,30 @@ max_frame_size_allow_exactly_custom(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		max_frame_size_received => 30000
 		max_frame_size_received => 30000
 	}, Config0),
 	}, Config0),
-	%% Do the handshake.
-	{ok, Socket} = do_handshake(Config),
-	%% Send a HEADERS frame initiating a stream followed by
-	%% a single 30000 bytes DATA frame.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, fin, <<0:30000/unit:8>>)
-	]),
-	%% Receive a proper response.
-	{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
-	{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
-	%% No errors follow due to our sending of a 25000 bytes frame.
-	{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% Do the handshake.
+		{ok, Socket} = do_handshake(Config),
+		%% Send a HEADERS frame initiating a stream followed by
+		%% a single 30000 bytes DATA frame.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, fin, <<0:30000/unit:8>>)
+		]),
+		%% Receive a proper response.
+		{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
+		{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
+		%% No errors follow due to our sending of a 25000 bytes frame.
+		{error, timeout} = gen_tcp:recv(Socket, 0, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 max_frame_size_reject_larger_than_custom(Config0) ->
 max_frame_size_reject_larger_than_custom(Config0) ->
 	doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must reject frames "
 	doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must reject frames "
@@ -1357,24 +1360,27 @@ max_frame_size_reject_larger_than_custom(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		max_frame_size_received => 30000
 		max_frame_size_received => 30000
 	}, Config0),
 	}, Config0),
-	%% Do the handshake.
-	{ok, Socket} = do_handshake(Config),
-	%% Send a HEADERS frame initiating a stream followed by
-	%% a single DATA frame larger than 30000 bytes.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, fin, <<0:30001/unit:8>>)
-	]),
-	%% Receive a FRAME_SIZE_ERROR connection error.
-	{ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% Do the handshake.
+		{ok, Socket} = do_handshake(Config),
+		%% Send a HEADERS frame initiating a stream followed by
+		%% a single DATA frame larger than 30000 bytes.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, fin, <<0:30001/unit:8>>)
+		]),
+		%% Receive a FRAME_SIZE_ERROR connection error.
+		{ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 %% I am using FRAME_SIZE_ERROR here because the information in the
 %% I am using FRAME_SIZE_ERROR here because the information in the
 %% frame header tells us this frame is at least 1 byte long, while
 %% frame header tells us this frame is at least 1 byte long, while
@@ -2555,40 +2561,43 @@ settings_header_table_size_server(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		max_decode_table_size => HeaderTableSize
 		max_decode_table_size => HeaderTableSize
 	}, Config0),
 	}, Config0),
-	%% Do the handhsake.
-	{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{header_table_size => HeaderTableSize})]),
-	%% Receive the server preface.
-	{ok, << Len0:24 >>} = gen_tcp:recv(Socket, 3, 1000),
-	{ok, Data = <<_:48, _:Len0/binary>>} = gen_tcp:recv(Socket, 6 + Len0, 1000),
-	%% Confirm the server's SETTINGS_HEADERS_TABLE_SIZE uses HeaderTableSize.
-	{ok, {settings, #{header_table_size := HeaderTableSize}}, <<>>}
-		= cow_http2:parse(<<Len0:24, Data/binary>>),
-	%% Send the SETTINGS ack.
-	ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
-	%% Receive the SETTINGS ack.
-	{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
-	%% Initialize decoding/encoding states.
-	DecodeState = cow_hpack:init(),
-	EncodeState = cow_hpack:set_max_size(HeaderTableSize, cow_hpack:init()),
-	%% Send a HEADERS frame as a request.
-	{ReqHeadersBlock1, _} = cow_hpack:encode([
-		{<<":method">>, <<"GET">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/">>}
-	], EncodeState),
-	ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, ReqHeadersBlock1)),
-	%% Receive a HEADERS frame as a response.
-	{ok, << Len1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
-	{ok, RespHeadersBlock1} = gen_tcp:recv(Socket, Len1, 6000),
-	{RespHeaders, _} = cow_hpack:decode(RespHeadersBlock1, DecodeState),
-	{_, <<"200">>} = lists:keyfind(<<":status">>, 1, RespHeaders),
-	%% The decoding succeeded on the server, confirming that
-	%% the table size was updated to HeaderTableSize.
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% Do the handhsake.
+		{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{header_table_size => HeaderTableSize})]),
+		%% Receive the server preface.
+		{ok, << Len0:24 >>} = gen_tcp:recv(Socket, 3, 1000),
+		{ok, Data = <<_:48, _:Len0/binary>>} = gen_tcp:recv(Socket, 6 + Len0, 1000),
+		%% Confirm the server's SETTINGS_HEADERS_TABLE_SIZE uses HeaderTableSize.
+		{ok, {settings, #{header_table_size := HeaderTableSize}}, <<>>}
+			= cow_http2:parse(<<Len0:24, Data/binary>>),
+		%% Send the SETTINGS ack.
+		ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
+		%% Receive the SETTINGS ack.
+		{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
+		%% Initialize decoding/encoding states.
+		DecodeState = cow_hpack:init(),
+		EncodeState = cow_hpack:set_max_size(HeaderTableSize, cow_hpack:init()),
+		%% Send a HEADERS frame as a request.
+		{ReqHeadersBlock1, _} = cow_hpack:encode([
+			{<<":method">>, <<"GET">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/">>}
+		], EncodeState),
+		ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, ReqHeadersBlock1)),
+		%% Receive a HEADERS frame as a response.
+		{ok, << Len1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
+		{ok, RespHeadersBlock1} = gen_tcp:recv(Socket, Len1, 6000),
+		{RespHeaders, _} = cow_hpack:decode(RespHeadersBlock1, DecodeState),
+		{_, <<"200">>} = lists:keyfind(<<":status">>, 1, RespHeaders)
+		%% The decoding succeeded on the server, confirming that
+		%% the table size was updated to HeaderTableSize.
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 settings_max_concurrent_streams(Config0) ->
 settings_max_concurrent_streams(Config0) ->
 	doc("The SETTINGS_MAX_CONCURRENT_STREAMS setting can be used to "
 	doc("The SETTINGS_MAX_CONCURRENT_STREAMS setting can be used to "
@@ -2598,23 +2607,26 @@ settings_max_concurrent_streams(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		max_concurrent_streams => 1
 		max_concurrent_streams => 1
 	}, Config0),
 	}, Config0),
-	{ok, Socket} = do_handshake(Config),
-	%% Send two HEADERS frames as two separate streams.
-	Headers = [
-		{<<":method">>, <<"GET">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{ReqHeadersBlock1, EncodeState} = cow_hpack:encode(Headers),
-	{ReqHeadersBlock2, _} = cow_hpack:encode(Headers, EncodeState),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, fin, ReqHeadersBlock1),
-		cow_http2:headers(3, fin, ReqHeadersBlock2)
-	]),
-	%% Receive a REFUSED_STREAM stream error.
-	{ok, << _:24, 3:8, _:8, 3:32, 7:32 >>} = gen_tcp:recv(Socket, 13, 6000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		{ok, Socket} = do_handshake(Config),
+		%% Send two HEADERS frames as two separate streams.
+		Headers = [
+			{<<":method">>, <<"GET">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{ReqHeadersBlock1, EncodeState} = cow_hpack:encode(Headers),
+		{ReqHeadersBlock2, _} = cow_hpack:encode(Headers, EncodeState),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, fin, ReqHeadersBlock1),
+			cow_http2:headers(3, fin, ReqHeadersBlock2)
+		]),
+		%% Receive a REFUSED_STREAM stream error.
+		{ok, << _:24, 3:8, _:8, 3:32, 7:32 >>} = gen_tcp:recv(Socket, 13, 6000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 settings_max_concurrent_streams_0(Config0) ->
 settings_max_concurrent_streams_0(Config0) ->
 	doc("The SETTINGS_MAX_CONCURRENT_STREAMS setting can be set to "
 	doc("The SETTINGS_MAX_CONCURRENT_STREAMS setting can be set to "
@@ -2624,18 +2636,21 @@ settings_max_concurrent_streams_0(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		max_concurrent_streams => 0
 		max_concurrent_streams => 0
 	}, Config0),
 	}, Config0),
-	{ok, Socket} = do_handshake(Config),
-	%% Send a HEADERS frame.
-	{HeadersBlock, _} = cow_hpack:encode([
-		{<<":method">>, <<"GET">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	]),
-	ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
-	%% Receive a REFUSED_STREAM stream error.
-	{ok, << _:24, 3:8, _:8, 1:32, 7:32 >>} = gen_tcp:recv(Socket, 13, 6000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		{ok, Socket} = do_handshake(Config),
+		%% Send a HEADERS frame.
+		{HeadersBlock, _} = cow_hpack:encode([
+			{<<":method">>, <<"GET">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		]),
+		ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
+		%% Receive a REFUSED_STREAM stream error.
+		{ok, << _:24, 3:8, _:8, 1:32, 7:32 >>} = gen_tcp:recv(Socket, 13, 6000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 %% @todo The client can limit the number of concurrent streams too. (RFC7540 5.1.2)
 %% @todo The client can limit the number of concurrent streams too. (RFC7540 5.1.2)
 %
 %
@@ -2663,44 +2678,47 @@ settings_initial_window_size(Config0) ->
 		initial_connection_window_size => 100000,
 		initial_connection_window_size => 100000,
 		initial_stream_window_size => 100000
 		initial_stream_window_size => 100000
 	}, Config0),
 	}, Config0),
-	%% We need to do the handshake manually because a WINDOW_UPDATE
-	%% frame will be sent to update the connection window.
-	{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
-	%% Receive the server preface.
-	{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
-	{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
-	%% Send the SETTINGS ack.
-	ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
-	%% Receive the WINDOW_UPDATE for the connection.
-	{ok, << 4:24, 8:8, 0:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
-	%% Receive the SETTINGS ack.
-	{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
-	%% Send a HEADERS frame initiating a stream followed by
-	%% DATA frames totaling 90000 bytes of body.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, fin, <<0:15000/unit:8>>)
-	]),
-	%% Receive a proper response.
-	{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
-	{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
-	%% No errors follow due to our sending of more than 65535 bytes of data.
-	{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% We need to do the handshake manually because a WINDOW_UPDATE
+		%% frame will be sent to update the connection window.
+		{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
+		%% Receive the server preface.
+		{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
+		{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
+		%% Send the SETTINGS ack.
+		ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
+		%% Receive the WINDOW_UPDATE for the connection.
+		{ok, << 4:24, 8:8, 0:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
+		%% Receive the SETTINGS ack.
+		{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
+		%% Send a HEADERS frame initiating a stream followed by
+		%% DATA frames totaling 90000 bytes of body.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, fin, <<0:15000/unit:8>>)
+		]),
+		%% Receive a proper response.
+		{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
+		{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
+		%% No errors follow due to our sending of more than 65535 bytes of data.
+		{error, timeout} = gen_tcp:recv(Socket, 0, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 settings_initial_window_size_after_ack(Config0) ->
 settings_initial_window_size_after_ack(Config0) ->
 	doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
 	doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
@@ -2711,36 +2729,39 @@ settings_initial_window_size_after_ack(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		initial_stream_window_size => 0
 		initial_stream_window_size => 0
 	}, Config0),
 	}, Config0),
-	%% We need to do the handshake manually because we don't
-	%% want to send the SETTINGS ack immediately.
-	{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
-	%% Receive the server preface.
-	{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
-	{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
-	%%
-	%% Don't send the SETTINGS ack yet! We want to create a stream first.
-	%%
-	%% Receive the SETTINGS ack.
-	{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
-	%% Send a HEADERS frame initiating a stream, a SETTINGS ack
-	%% and a small DATA frame despite no window available in the stream.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:settings_ack(),
-		cow_http2:data(1, fin, <<0:32/unit:8>>)
-	]),
-	%% Receive a FLOW_CONTROL_ERROR stream error.
-	{ok, << _:24, 3:8, _:8, 1:32, 3:32 >>} = gen_tcp:recv(Socket, 13, 6000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% We need to do the handshake manually because we don't
+		%% want to send the SETTINGS ack immediately.
+		{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
+		%% Receive the server preface.
+		{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
+		{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
+		%%
+		%% Don't send the SETTINGS ack yet! We want to create a stream first.
+		%%
+		%% Receive the SETTINGS ack.
+		{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
+		%% Send a HEADERS frame initiating a stream, a SETTINGS ack
+		%% and a small DATA frame despite no window available in the stream.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:settings_ack(),
+			cow_http2:data(1, fin, <<0:32/unit:8>>)
+		]),
+		%% Receive a FLOW_CONTROL_ERROR stream error.
+		{ok, << _:24, 3:8, _:8, 1:32, 3:32 >>} = gen_tcp:recv(Socket, 13, 6000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 settings_initial_window_size_before_ack(Config0) ->
 settings_initial_window_size_before_ack(Config0) ->
 	doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
 	doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
@@ -2751,41 +2772,44 @@ settings_initial_window_size_before_ack(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		initial_stream_window_size => 0
 		initial_stream_window_size => 0
 	}, Config0),
 	}, Config0),
-	%% We need to do the handshake manually because we don't
-	%% want to send the SETTINGS ack.
-	{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
-	%% Receive the server preface.
-	{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
-	{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
-	%%
-	%% Don't send the SETTINGS ack! We want the server to keep the original settings.
-	%%
-	%% Receive the SETTINGS ack.
-	{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
-	%% Send a HEADERS frame initiating a stream followed by
-	%% DATA frames totaling 60000 bytes of body.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, fin, <<0:15000/unit:8>>)
-	]),
-	%% Receive a proper response.
-	{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
-	{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
-	%% No errors follow due to our sending of more than 0 bytes of data.
-	{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% We need to do the handshake manually because we don't
+		%% want to send the SETTINGS ack.
+		{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
+		%% Receive the server preface.
+		{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
+		{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
+		%%
+		%% Don't send the SETTINGS ack! We want the server to keep the original settings.
+		%%
+		%% Receive the SETTINGS ack.
+		{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
+		%% Send a HEADERS frame initiating a stream followed by
+		%% DATA frames totaling 60000 bytes of body.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, fin, <<0:15000/unit:8>>)
+		]),
+		%% Receive a proper response.
+		{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
+		{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
+		%% No errors follow due to our sending of more than 0 bytes of data.
+		{error, timeout} = gen_tcp:recv(Socket, 0, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 settings_max_frame_size(Config0) ->
 settings_max_frame_size(Config0) ->
 	doc("The SETTINGS_MAX_FRAME_SIZE setting can be used to "
 	doc("The SETTINGS_MAX_FRAME_SIZE setting can be used to "
@@ -2795,27 +2819,30 @@ settings_max_frame_size(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		max_frame_size_received => 30000
 		max_frame_size_received => 30000
 	}, Config0),
 	}, Config0),
-	%% Do the handshake.
-	{ok, Socket} = do_handshake(Config),
-	%% Send a HEADERS frame initiating a stream followed by
-	%% a single 25000 bytes DATA frame.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, fin, <<0:25000/unit:8>>)
-	]),
-	%% Receive a proper response.
-	{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
-	{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
-	%% No errors follow due to our sending of a 25000 bytes frame.
-	{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% Do the handshake.
+		{ok, Socket} = do_handshake(Config),
+		%% Send a HEADERS frame initiating a stream followed by
+		%% a single 25000 bytes DATA frame.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, fin, <<0:25000/unit:8>>)
+		]),
+		%% Receive a proper response.
+		{ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
+		{ok, _} = gen_tcp:recv(Socket, Len2, 6000),
+		%% No errors follow due to our sending of a 25000 bytes frame.
+		{error, timeout} = gen_tcp:recv(Socket, 0, 1000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 settings_max_frame_size_reject_too_small(Config) ->
 settings_max_frame_size_reject_too_small(Config) ->
 	doc("A SETTINGS_MAX_FRAME_SIZE smaller than 16384 must be rejected "
 	doc("A SETTINGS_MAX_FRAME_SIZE smaller than 16384 must be rejected "
@@ -3015,28 +3042,31 @@ data_reject_overflow(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		initial_stream_window_size => 100000
 		initial_stream_window_size => 100000
 	}, Config0),
 	}, Config0),
-	{ok, Socket} = do_handshake(Config),
-	%% Send a HEADERS frame initiating a stream followed by
-	%% DATA frames totaling 90000 bytes of body.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, fin, <<0:15000/unit:8>>)
-	]),
-	%% Receive a FLOW_CONTROL_ERROR connection error.
-	{ok, << _:24, 7:8, _:72, 3:32 >>} = gen_tcp:recv(Socket, 17, 6000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		{ok, Socket} = do_handshake(Config),
+		%% Send a HEADERS frame initiating a stream followed by
+		%% DATA frames totaling 90000 bytes of body.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, fin, <<0:15000/unit:8>>)
+		]),
+		%% Receive a FLOW_CONTROL_ERROR connection error.
+		{ok, << _:24, 7:8, _:72, 3:32 >>} = gen_tcp:recv(Socket, 17, 6000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 data_reject_overflow_stream(Config0) ->
 data_reject_overflow_stream(Config0) ->
 	doc("DATA frames that cause the stream flow control window "
 	doc("DATA frames that cause the stream flow control window "
@@ -3047,41 +3077,44 @@ data_reject_overflow_stream(Config0) ->
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
 		initial_connection_window_size => 100000
 		initial_connection_window_size => 100000
 	}, Config0),
 	}, Config0),
-	%% We need to do the handshake manually because a WINDOW_UPDATE
-	%% frame will be sent to update the connection window.
-	{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
-	%% Receive the server preface.
-	{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
-	{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
-	%% Send the SETTINGS ack.
-	ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
-	%% Receive the WINDOW_UPDATE for the connection.
-	{ok, << 4:24, 8:8, 0:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
-	%% Receive the SETTINGS ack.
-	{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
-	%% Send a HEADERS frame initiating a stream followed by
-	%% DATA frames totaling 90000 bytes of body.
-	Headers = [
-		{<<":method">>, <<"POST">>},
-		{<<":scheme">>, <<"http">>},
-		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
-		{<<":path">>, <<"/long_polling">>}
-	],
-	{HeadersBlock, _} = cow_hpack:encode(Headers),
-	ok = gen_tcp:send(Socket, [
-		cow_http2:headers(1, nofin, HeadersBlock),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, nofin, <<0:15000/unit:8>>),
-		cow_http2:data(1, fin, <<0:15000/unit:8>>)
-	]),
-	%% Receive a FLOW_CONTROL_ERROR stream error.
-	{ok, << _:24, 3:8, _:8, 1:32, 3:32 >>} = gen_tcp:recv(Socket, 13, 6000),
-	cowboy:stop_listener(?FUNCTION_NAME).
+	try
+		%% We need to do the handshake manually because a WINDOW_UPDATE
+		%% frame will be sent to update the connection window.
+		{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [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(#{})]),
+		%% Receive the server preface.
+		{ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
+		{ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
+		%% Send the SETTINGS ack.
+		ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
+		%% Receive the WINDOW_UPDATE for the connection.
+		{ok, << 4:24, 8:8, 0:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
+		%% Receive the SETTINGS ack.
+		{ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
+		%% Send a HEADERS frame initiating a stream followed by
+		%% DATA frames totaling 90000 bytes of body.
+		Headers = [
+			{<<":method">>, <<"POST">>},
+			{<<":scheme">>, <<"http">>},
+			{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+			{<<":path">>, <<"/long_polling">>}
+		],
+		{HeadersBlock, _} = cow_hpack:encode(Headers),
+		ok = gen_tcp:send(Socket, [
+			cow_http2:headers(1, nofin, HeadersBlock),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, nofin, <<0:15000/unit:8>>),
+			cow_http2:data(1, fin, <<0:15000/unit:8>>)
+		]),
+		%% Receive a FLOW_CONTROL_ERROR stream error.
+		{ok, << _:24, 3:8, _:8, 1:32, 3:32 >>} = gen_tcp:recv(Socket, 13, 6000)
+	after
+		cowboy:stop_listener(?FUNCTION_NAME)
+	end.
 
 
 %% (RFC7540 6.9.1)
 %% (RFC7540 6.9.1)
 %   Frames with zero length with the END_STREAM flag set (that
 %   Frames with zero length with the END_STREAM flag set (that