Browse Source

Make reply functions return Req

Loïc Hoguin 8 years ago
parent
commit
e30d120bd8

+ 2 - 2
examples/chunked_hello_world/src/toppage_handler.erl

@@ -5,8 +5,8 @@
 
 
 -export([init/2]).
 -export([init/2]).
 
 
-init(Req, Opts) ->
-	cowboy_req:stream_reply(200, Req),
+init(Req0, Opts) ->
+	Req = cowboy_req:stream_reply(200, Req0),
 	cowboy_req:stream_body("Hello\r\n", nofin, Req),
 	cowboy_req:stream_body("Hello\r\n", nofin, Req),
 	timer:sleep(1000),
 	timer:sleep(1000),
 	cowboy_req:stream_body("World\r\n", nofin, Req),
 	cowboy_req:stream_body("World\r\n", nofin, Req),

+ 3 - 3
examples/compress_response/src/toppage_handler.erl

@@ -5,7 +5,7 @@
 
 
 -export([init/2]).
 -export([init/2]).
 
 
-init(Req, Opts) ->
+init(Req0, Opts) ->
 	BigBody =
 	BigBody =
 <<"A cowboy is an animal herder who tends cattle on ranches in North America,
 <<"A cowboy is an animal herder who tends cattle on ranches in North America,
 traditionally on horseback, and often performs a multitude of other ranch-
 traditionally on horseback, and often performs a multitude of other ranch-
@@ -19,5 +19,5 @@ have established the ability to work at virtually identical tasks and obtained
 considerable respect for their achievements. There are also cattle handlers
 considerable respect for their achievements. There are also cattle handlers
 in many other parts of the world, particularly South America and Australia,
 in many other parts of the world, particularly South America and Australia,
 who perform work similar to the cowboy in their respective nations.\n">>,
 who perform work similar to the cowboy in their respective nations.\n">>,
-	Req2 = cowboy_req:reply(200, [], BigBody, Req),
-	{ok, Req2, Opts}.
+	Req = cowboy_req:reply(200, [], BigBody, Req0),
+	{ok, Req, Opts}.

+ 6 - 6
examples/cookie/src/toppage_handler.erl

@@ -5,17 +5,17 @@
 
 
 -export([init/2]).
 -export([init/2]).
 
 
-init(Req, Opts) ->
+init(Req0, Opts) ->
 	NewValue = integer_to_list(rand:uniform(1000000)),
 	NewValue = integer_to_list(rand:uniform(1000000)),
-	Req2 = cowboy_req:set_resp_cookie(<<"server">>, NewValue,
-		#{path => <<"/">>}, Req),
+	Req1 = cowboy_req:set_resp_cookie(<<"server">>, NewValue,
+		#{path => <<"/">>}, Req0),
 	#{client := ClientCookie, server := ServerCookie}
 	#{client := ClientCookie, server := ServerCookie}
 		= cowboy_req:match_cookies([{client, [], <<>>}, {server, [], <<>>}], Req2),
 		= cowboy_req:match_cookies([{client, [], <<>>}, {server, [], <<>>}], Req2),
 	{ok, Body} = toppage_dtl:render([
 	{ok, Body} = toppage_dtl:render([
 		{client, ClientCookie},
 		{client, ClientCookie},
 		{server, ServerCookie}
 		{server, ServerCookie}
 	]),
 	]),
-	cowboy_req:reply(200, #{
+	Req = cowboy_req:reply(200, #{
 		<<"content-type">> => <<"text/html">>
 		<<"content-type">> => <<"text/html">>
-	}, Body, Req2),
-	{ok, Req2, Opts}.
+	}, Body, Req1),
+	{ok, Req1, Opts}.

+ 4 - 4
examples/echo_get/src/toppage_handler.erl

@@ -5,10 +5,10 @@
 
 
 -export([init/2]).
 -export([init/2]).
 
 
-init(Req, Opts) ->
-	Method = cowboy_req:method(Req),
-	#{echo := Echo} = cowboy_req:match_qs([echo], Req),
-	echo(Method, Echo, Req),
+init(Req0, Opts) ->
+	Method = cowboy_req:method(Req0),
+	#{echo := Echo} = cowboy_req:match_qs([echo], Req0),
+	Req = echo(Method, Echo, Req0),
 	{ok, Req, Opts}.
 	{ok, Req, Opts}.
 
 
 echo(<<"GET">>, undefined, Req) ->
 echo(<<"GET">>, undefined, Req) ->

+ 10 - 13
examples/echo_post/src/toppage_handler.erl

@@ -5,24 +5,21 @@
 
 
 -export([init/2]).
 -export([init/2]).
 
 
-init(Req, Opts) ->
-	Method = cowboy_req:method(Req),
-	HasBody = cowboy_req:has_body(Req),
-	Req2 = maybe_echo(Method, HasBody, Req),
-	{ok, Req2, Opts}.
+init(Req0, Opts) ->
+	Method = cowboy_req:method(Req0),
+	HasBody = cowboy_req:has_body(Req0),
+	Req = maybe_echo(Method, HasBody, Req0),
+	{ok, Req, Opts}.
 
 
-maybe_echo(<<"POST">>, true, Req) ->
-	{ok, PostVals, Req2} = cowboy_req:read_urlencoded_body(Req),
+maybe_echo(<<"POST">>, true, Req0) ->
+	{ok, PostVals, Req} = cowboy_req:read_urlencoded_body(Req0),
 	Echo = proplists:get_value(<<"echo">>, PostVals),
 	Echo = proplists:get_value(<<"echo">>, PostVals),
-	echo(Echo, Req2),
-	Req2;
+	echo(Echo, Req);
 maybe_echo(<<"POST">>, false, Req) ->
 maybe_echo(<<"POST">>, false, Req) ->
-	cowboy_req:reply(400, [], <<"Missing body.">>, Req),
-	Req;
+	cowboy_req:reply(400, [], <<"Missing body.">>, Req);
 maybe_echo(_, _, Req) ->
 maybe_echo(_, _, Req) ->
 	%% Method not allowed.
 	%% Method not allowed.
-	cowboy_req:reply(405, Req),
-	Req.
+	cowboy_req:reply(405, Req).
 
 
 echo(undefined, Req) ->
 echo(undefined, Req) ->
 	cowboy_req:reply(400, [], <<"Missing echo parameter.">>, Req);
 	cowboy_req:reply(400, [], <<"Missing echo parameter.">>, Req);

+ 3 - 3
examples/eventsource/src/eventsource_handler.erl

@@ -6,10 +6,10 @@
 -export([init/2]).
 -export([init/2]).
 -export([info/3]).
 -export([info/3]).
 
 
-init(Req, Opts) ->
-	cowboy_req:stream_reply(200, #{
+init(Req0, Opts) ->
+	Req = cowboy_req:stream_reply(200, #{
 		<<"content-type">> => <<"text/event-stream">>
 		<<"content-type">> => <<"text/event-stream">>
-	}, Req),
+	}, Req0),
 	erlang:send_after(1000, self(), {message, "Tick"}),
 	erlang:send_after(1000, self(), {message, "Tick"}),
 	{cowboy_loop, Req, Opts, 5000}.
 	{cowboy_loop, Req, Opts, 5000}.
 
 

+ 3 - 3
examples/hello_world/src/toppage_handler.erl

@@ -5,8 +5,8 @@
 
 
 -export([init/2]).
 -export([init/2]).
 
 
-init(Req, Opts) ->
-	cowboy_req:reply(200, #{
+init(Req0, Opts) ->
+	Req = cowboy_req:reply(200, #{
 		<<"content-type">> => <<"text/plain">>
 		<<"content-type">> => <<"text/plain">>
-	}, <<"Hello world!">>, Req),
+	}, <<"Hello world!">>, Req0),
 	{ok, Req, Opts}.
 	{ok, Req, Opts}.

+ 3 - 3
examples/ssl_hello_world/src/toppage_handler.erl

@@ -5,8 +5,8 @@
 
 
 -export([init/2]).
 -export([init/2]).
 
 
-init(Req, Opts) ->
-	cowboy_req:reply(200, #{
+init(Req0, Opts) ->
+	Req = cowboy_req:reply(200, #{
 		<<"content-type">> => <<"text/plain">>
 		<<"content-type">> => <<"text/plain">>
-	}, <<"Hello world!">>, Req),
+	}, <<"Hello world!">>, Req0),
 	{ok, Req, Opts}.
 	{ok, Req, Opts}.

+ 5 - 0
src/cowboy_http.erl

@@ -805,6 +805,11 @@ commands(State, StreamID, [{flow, _Length}|Tail]) ->
 	%% @todo Set the body reading length to min(Length, BodyLength)
 	%% @todo Set the body reading length to min(Length, BodyLength)
 
 
 	commands(State, StreamID, Tail);
 	commands(State, StreamID, Tail);
+%% Error responses are sent only if a response wasn't sent already.
+commands(State=#state{out_state=wait}, StreamID, [{error_response, StatusCode, Headers, Body}|Tail]) ->
+	commands(State, StreamID, [{response, StatusCode, Headers, Body}|Tail]);
+commands(State, StreamID, [{error_response, _, _, _}|Tail]) ->
+	commands(State, StreamID, Tail);
 %% Send a full response.
 %% Send a full response.
 %%
 %%
 %% @todo Kill the stream if it sent a response when one has already been sent.
 %% @todo Kill the stream if it sent a response when one has already been sent.

+ 5 - 0
src/cowboy_http2.erl

@@ -369,6 +369,11 @@ info(State=#state{handler=Handler, streams=Streams}, StreamID, Msg) ->
 
 
 commands(State, Stream, []) ->
 commands(State, Stream, []) ->
 	after_commands(State, Stream);
 	after_commands(State, Stream);
+%% Error responses are sent only if a response wasn't sent already.
+commands(State, Stream=#stream{local=idle}, [{error_response, StatusCode, Headers, Body}|Tail]) ->
+	commands(State, Stream, [{response, StatusCode, Headers, Body}|Tail]);
+commands(State, Stream, [{error_response, _, _, _}|Tail]) ->
+	commands(State, Stream, Tail);
 %% Send response headers.
 %% Send response headers.
 %%
 %%
 %% @todo Kill the stream if it sent a response when one has already been sent.
 %% @todo Kill the stream if it sent a response when one has already been sent.

+ 14 - 6
src/cowboy_req.erl

@@ -592,6 +592,8 @@ reply(Status, Headers, Req) ->
 
 
 -spec reply(cowboy:http_status(), cowboy:http_headers(), resp_body(), Req)
 -spec reply(cowboy:http_status(), cowboy:http_headers(), resp_body(), Req)
 	-> Req when Req::req().
 	-> Req when Req::req().
+reply(_, _, _, #{has_sent_resp := _}) ->
+	error(function_clause);
 reply(Status, Headers, SendFile = {sendfile, _, Len, _}, Req)
 reply(Status, Headers, SendFile = {sendfile, _, Len, _}, Req)
 		when is_integer(Status); is_binary(Status) ->
 		when is_integer(Status); is_binary(Status) ->
 	do_reply(Status, Headers#{
 	do_reply(Status, Headers#{
@@ -608,10 +610,13 @@ reply(Status, Headers, Body, Req)
 %% data around if we can avoid it.
 %% data around if we can avoid it.
 do_reply(Status, Headers, _, Req=#{pid := Pid, streamid := StreamID, method := <<"HEAD">>}) ->
 do_reply(Status, Headers, _, Req=#{pid := Pid, streamid := StreamID, method := <<"HEAD">>}) ->
 	Pid ! {{Pid, StreamID}, {response, Status, response_headers(Headers, Req), <<>>}},
 	Pid ! {{Pid, StreamID}, {response, Status, response_headers(Headers, Req), <<>>}},
-	ok;
+	done_replying(Req, true);
 do_reply(Status, Headers, Body, Req=#{pid := Pid, streamid := StreamID}) ->
 do_reply(Status, Headers, Body, Req=#{pid := Pid, streamid := StreamID}) ->
 	Pid ! {{Pid, StreamID}, {response, Status, response_headers(Headers, Req), Body}},
 	Pid ! {{Pid, StreamID}, {response, Status, response_headers(Headers, Req), Body}},
-	ok.
+	done_replying(Req, true).
+
+done_replying(Req, HasSentResp) ->
+	maps:without([resp_cookies, resp_headers, resp_body], Req#{has_sent_resp => HasSentResp}).
 
 
 -spec stream_reply(cowboy:http_status(), Req) -> Req when Req::req().
 -spec stream_reply(cowboy:http_status(), Req) -> Req when Req::req().
 stream_reply(Status, Req) ->
 stream_reply(Status, Req) ->
@@ -619,25 +624,28 @@ stream_reply(Status, Req) ->
 
 
 -spec stream_reply(cowboy:http_status(), cowboy:http_headers(), Req)
 -spec stream_reply(cowboy:http_status(), cowboy:http_headers(), Req)
 	-> Req when Req::req().
 	-> Req when Req::req().
+stream_reply(_, _, #{has_sent_resp := _}) ->
+	error(function_clause);
 stream_reply(Status, Headers=#{}, Req=#{pid := Pid, streamid := StreamID})
 stream_reply(Status, Headers=#{}, Req=#{pid := Pid, streamid := StreamID})
 		when is_integer(Status); is_binary(Status) ->
 		when is_integer(Status); is_binary(Status) ->
 	Pid ! {{Pid, StreamID}, {headers, Status, response_headers(Headers, Req)}},
 	Pid ! {{Pid, StreamID}, {headers, Status, response_headers(Headers, Req)}},
-	ok.
+	done_replying(Req, headers).
 
 
 -spec stream_body(iodata(), fin | nofin, req()) -> ok.
 -spec stream_body(iodata(), fin | nofin, req()) -> ok.
+%% Error out if headers were not sent.
 %% Don't send any body for HEAD responses.
 %% Don't send any body for HEAD responses.
-stream_body(_, _, #{method := <<"HEAD">>}) ->
+stream_body(_, _, #{method := <<"HEAD">>, has_sent_resp := headers}) ->
 	ok;
 	ok;
 %% Don't send a message if the data is empty, except for the
 %% Don't send a message if the data is empty, except for the
 %% very last message with IsFin=fin.
 %% very last message with IsFin=fin.
-stream_body(Data, IsFin=nofin, #{pid := Pid, streamid := StreamID}) ->
+stream_body(Data, IsFin=nofin, #{pid := Pid, streamid := StreamID, has_sent_resp := headers}) ->
 	case iolist_size(Data) of
 	case iolist_size(Data) of
 		0 -> ok;
 		0 -> ok;
 		_ ->
 		_ ->
 			Pid ! {{Pid, StreamID}, {data, IsFin, Data}},
 			Pid ! {{Pid, StreamID}, {data, IsFin, Data}},
 			ok
 			ok
 	end;
 	end;
-stream_body(Data, IsFin, #{pid := Pid, streamid := StreamID}) ->
+stream_body(Data, IsFin, #{pid := Pid, streamid := StreamID, has_sent_resp := headers}) ->
 	Pid ! {{Pid, StreamID}, {data, IsFin, Data}},
 	Pid ! {{Pid, StreamID}, {data, IsFin, Data}},
 	ok.
 	ok.
 
 

+ 3 - 3
src/cowboy_stream_h.erl

@@ -78,18 +78,18 @@ info(_StreamID, {'EXIT', Pid, normal}, State=#state{pid=Pid}) ->
 info(_StreamID, Exit = {'EXIT', Pid, {cowboy_handler, _}}, State=#state{pid=Pid}) ->
 info(_StreamID, Exit = {'EXIT', Pid, {cowboy_handler, _}}, State=#state{pid=Pid}) ->
 	%% No crash report; one has already been sent.
 	%% No crash report; one has already been sent.
 	{[
 	{[
-		{response, 500, #{<<"content-length">> => <<"0">>}, <<>>},
+		{error_response, 500, #{<<"content-length">> => <<"0">>}, <<>>},
 		{internal_error, Exit, 'Stream process crashed.'}
 		{internal_error, Exit, 'Stream process crashed.'}
 	], State};
 	], State};
 info(_StreamID, {'EXIT', Pid, {_Reason, [_, {cow_http_hd, _, _, _}|_]}}, State=#state{pid=Pid}) ->
 info(_StreamID, {'EXIT', Pid, {_Reason, [_, {cow_http_hd, _, _, _}|_]}}, State=#state{pid=Pid}) ->
 	%% @todo Have an option to enable/disable this specific crash report?
 	%% @todo Have an option to enable/disable this specific crash report?
 	%%report_crash(Ref, StreamID, Pid, Reason, Stacktrace),
 	%%report_crash(Ref, StreamID, Pid, Reason, Stacktrace),
 	%% @todo Headers? Details in body? More stuff in debug only?
 	%% @todo Headers? Details in body? More stuff in debug only?
-	{[{response, 400, #{}, <<>>}, stop], State};
+	{[{error_response, 400, #{}, <<>>}, stop], State};
 info(StreamID, Exit = {'EXIT', Pid, {Reason, Stacktrace}}, State=#state{ref=Ref, pid=Pid}) ->
 info(StreamID, Exit = {'EXIT', Pid, {Reason, Stacktrace}}, State=#state{ref=Ref, pid=Pid}) ->
 	report_crash(Ref, StreamID, Pid, Reason, Stacktrace),
 	report_crash(Ref, StreamID, Pid, Reason, Stacktrace),
 	{[
 	{[
-		{response, 500, #{<<"content-length">> => <<"0">>}, <<>>},
+		{error_response, 500, #{<<"content-length">> => <<"0">>}, <<>>},
 		{internal_error, Exit, 'Stream process crashed.'}
 		{internal_error, Exit, 'Stream process crashed.'}
 	], State};
 	], State};
 %% Request body, no body buffer but IsFin=fin.
 %% Request body, no body buffer but IsFin=fin.

+ 2 - 2
src/cowboy_websocket.erl

@@ -79,8 +79,8 @@ upgrade(Req, Env, Handler, HandlerState, Timeout, Hibernate) ->
 			websocket_handshake(State2, Req2, HandlerState, Env)
 			websocket_handshake(State2, Req2, HandlerState, Env)
 	catch _:_ ->
 	catch _:_ ->
 		%% @todo Test that we can have 2 /ws 400 status code in a row on the same connection.
 		%% @todo Test that we can have 2 /ws 400 status code in a row on the same connection.
-		cowboy_req:reply(400, Req),
-		{ok, Req, Env}
+		%% @todo Does this even work?
+		{ok, cowboy_req:reply(400, Req), Env}
 	end.
 	end.
 
 
 -spec websocket_upgrade(#state{}, Req)
 -spec websocket_upgrade(#state{}, Req)

+ 6 - 12
test/handlers/echo_h.erl

@@ -22,8 +22,7 @@ echo(<<"read_body">>, Req0, Opts) ->
 		<<"/opts", _/bits>> -> cowboy_req:read_body(Req0, Opts);
 		<<"/opts", _/bits>> -> cowboy_req:read_body(Req0, Opts);
 		_ -> cowboy_req:read_body(Req0)
 		_ -> cowboy_req:read_body(Req0)
 	end,
 	end,
-	cowboy_req:reply(200, #{}, Body, Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, Body, Req), Opts};
 echo(<<"read_urlencoded_body">>, Req0, Opts) ->
 echo(<<"read_urlencoded_body">>, Req0, Opts) ->
 	Path = cowboy_req:path(Req0),
 	Path = cowboy_req:path(Req0),
 	case {Path, Opts} of
 	case {Path, Opts} of
@@ -36,8 +35,7 @@ echo(<<"read_urlencoded_body">>, Req0, Opts) ->
 		<<"/crash", _/bits>> -> cowboy_req:read_urlencoded_body(Req0, Opts);
 		<<"/crash", _/bits>> -> cowboy_req:read_urlencoded_body(Req0, Opts);
 		_ -> cowboy_req:read_urlencoded_body(Req0)
 		_ -> cowboy_req:read_urlencoded_body(Req0)
 	end,
 	end,
-	cowboy_req:reply(200, #{}, value_to_iodata(Body), Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, value_to_iodata(Body), Req), Opts};
 echo(<<"uri">>, Req, Opts) ->
 echo(<<"uri">>, Req, Opts) ->
 	Value = case cowboy_req:path_info(Req) of
 	Value = case cowboy_req:path_info(Req) of
 		[<<"origin">>] -> cowboy_req:uri(Req, #{host => undefined});
 		[<<"origin">>] -> cowboy_req:uri(Req, #{host => undefined});
@@ -47,8 +45,7 @@ echo(<<"uri">>, Req, Opts) ->
 		[<<"set-port">>] -> cowboy_req:uri(Req, #{port => 123});
 		[<<"set-port">>] -> cowboy_req:uri(Req, #{port => 123});
 		[] -> cowboy_req:uri(Req)
 		[] -> cowboy_req:uri(Req)
 	end,
 	end,
-	cowboy_req:reply(200, #{}, Value, Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, Value, Req), Opts};
 echo(<<"match">>, Req, Opts) ->
 echo(<<"match">>, Req, Opts) ->
 	[Type|Fields0] = cowboy_req:path_info(Req),
 	[Type|Fields0] = cowboy_req:path_info(Req),
 	Fields = [binary_to_atom(F, latin1) || F <- Fields0],
 	Fields = [binary_to_atom(F, latin1) || F <- Fields0],
@@ -56,13 +53,11 @@ echo(<<"match">>, Req, Opts) ->
 		<<"qs">> -> cowboy_req:match_qs(Fields, Req);
 		<<"qs">> -> cowboy_req:match_qs(Fields, Req);
 		<<"cookies">> -> cowboy_req:match_cookies(Fields, Req)
 		<<"cookies">> -> cowboy_req:match_cookies(Fields, Req)
 	end,
 	end,
-	cowboy_req:reply(200, #{}, value_to_iodata(Value), Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts};
 echo(What, Req, Opts) ->
 echo(What, Req, Opts) ->
 	F = binary_to_atom(What, latin1),
 	F = binary_to_atom(What, latin1),
 	Value = cowboy_req:F(Req),
 	Value = cowboy_req:F(Req),
-	cowboy_req:reply(200, #{}, value_to_iodata(Value), Req),
-	{ok, Req, Opts}.
+	{ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts}.
 
 
 echo_arg(Arg0, Req, Opts) ->
 echo_arg(Arg0, Req, Opts) ->
 	F = binary_to_atom(cowboy_req:binding(key, Req), latin1),
 	F = binary_to_atom(cowboy_req:binding(key, Req), latin1),
@@ -74,8 +69,7 @@ echo_arg(Arg0, Req, Opts) ->
 		undefined -> cowboy_req:F(Arg, Req);
 		undefined -> cowboy_req:F(Arg, Req);
 		Default -> cowboy_req:F(Arg, Req, Default)
 		Default -> cowboy_req:F(Arg, Req, Default)
 	end,
 	end,
-	cowboy_req:reply(200, #{}, value_to_iodata(Value), Req),
-	{ok, Req, Opts}.
+	{ok, cowboy_req:reply(200, #{}, value_to_iodata(Value), Req), Opts}.
 
 
 read_body(Req0, Acc) ->
 read_body(Req0, Acc) ->
 	case cowboy_req:read_body(Req0) of
 	case cowboy_req:read_body(Req0) of

+ 3 - 4
test/handlers/loop_handler_body_h.erl

@@ -13,11 +13,10 @@ init(Req, _) ->
 	self() ! timeout,
 	self() ! timeout,
 	{cowboy_loop, Req, undefined, 5000, hibernate}.
 	{cowboy_loop, Req, undefined, 5000, hibernate}.
 
 
-info(timeout, Req, State) ->
-	{ok, Body, Req2} = cowboy_req:read_body(Req),
+info(timeout, Req0, State) ->
+	{ok, Body, Req} = cowboy_req:read_body(Req0),
 	100000 = byte_size(Body),
 	100000 = byte_size(Body),
-	cowboy_req:reply(200, Req2),
-	{stop, Req, State}.
+	{stop, cowboy_req:reply(200, Req), State}.
 
 
 terminate(stop, _, _) ->
 terminate(stop, _, _) ->
 	ok.
 	ok.

+ 37 - 44
test/handlers/resp_h.erl

@@ -18,38 +18,34 @@ do(<<"set_resp_cookie3">>, Req0, Opts) ->
 			Req1 = cowboy_req:set_resp_cookie(<<"mycookie">>, "myvalue", Req0),
 			Req1 = cowboy_req:set_resp_cookie(<<"mycookie">>, "myvalue", Req0),
 			cowboy_req:set_resp_cookie(<<"mycookie">>, <<"overwrite">>, Req1)
 			cowboy_req:set_resp_cookie(<<"mycookie">>, <<"overwrite">>, Req1)
 	end,
 	end,
-	cowboy_req:reply(200, #{}, "OK", Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
 do(<<"set_resp_cookie4">>, Req0, Opts) ->
 do(<<"set_resp_cookie4">>, Req0, Opts) ->
 	Req = cowboy_req:set_resp_cookie(<<"mycookie">>, "myvalue", #{path => cowboy_req:path(Req0)}, Req0),
 	Req = cowboy_req:set_resp_cookie(<<"mycookie">>, "myvalue", #{path => cowboy_req:path(Req0)}, Req0),
-	cowboy_req:reply(200, #{}, "OK", Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
 do(<<"set_resp_header">>, Req0, Opts) ->
 do(<<"set_resp_header">>, Req0, Opts) ->
 	Req = cowboy_req:set_resp_header(<<"content-type">>, <<"text/plain">>, Req0),
 	Req = cowboy_req:set_resp_header(<<"content-type">>, <<"text/plain">>, Req0),
-	cowboy_req:reply(200, #{}, "OK", Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
 do(<<"set_resp_body">>, Req0, Opts) ->
 do(<<"set_resp_body">>, Req0, Opts) ->
 	Arg = cowboy_req:binding(arg, Req0),
 	Arg = cowboy_req:binding(arg, Req0),
-	Req = case Arg of
+	Req1 = case Arg of
 		<<"sendfile">> ->
 		<<"sendfile">> ->
 			AppFile = code:where_is_file("cowboy.app"),
 			AppFile = code:where_is_file("cowboy.app"),
 			cowboy_req:set_resp_body({sendfile, 0, filelib:file_size(AppFile), AppFile}, Req0);
 			cowboy_req:set_resp_body({sendfile, 0, filelib:file_size(AppFile), AppFile}, Req0);
 		_ ->
 		_ ->
 			cowboy_req:set_resp_body(<<"OK">>, Req0)
 			cowboy_req:set_resp_body(<<"OK">>, Req0)
 	end,
 	end,
-	case Arg of
+	Req = case Arg of
 		<<"override">> ->
 		<<"override">> ->
-			cowboy_req:reply(200, #{}, <<"OVERRIDE">>, Req);
+			cowboy_req:reply(200, #{}, <<"OVERRIDE">>, Req1);
 		_ ->
 		_ ->
-			cowboy_req:reply(200, Req)
+			cowboy_req:reply(200, Req1)
 	end,
 	end,
 	{ok, Req, Opts};
 	{ok, Req, Opts};
 do(<<"has_resp_header">>, Req0, Opts) ->
 do(<<"has_resp_header">>, Req0, Opts) ->
 	false = cowboy_req:has_resp_header(<<"content-type">>, Req0),
 	false = cowboy_req:has_resp_header(<<"content-type">>, Req0),
 	Req = cowboy_req:set_resp_header(<<"content-type">>, <<"text/plain">>, Req0),
 	Req = cowboy_req:set_resp_header(<<"content-type">>, <<"text/plain">>, Req0),
 	true = cowboy_req:has_resp_header(<<"content-type">>, Req),
 	true = cowboy_req:has_resp_header(<<"content-type">>, Req),
-	cowboy_req:reply(200, #{}, "OK", Req),
-	{ok, Req, Opts};
+	{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
 do(<<"has_resp_body">>, Req0, Opts) ->
 do(<<"has_resp_body">>, Req0, Opts) ->
 	case cowboy_req:binding(arg, Req0) of
 	case cowboy_req:binding(arg, Req0) of
 		<<"sendfile">> ->
 		<<"sendfile">> ->
@@ -57,14 +53,12 @@ do(<<"has_resp_body">>, Req0, Opts) ->
 			false = cowboy_req:has_resp_body(Req0),
 			false = cowboy_req:has_resp_body(Req0),
 			Req = cowboy_req:set_resp_body({sendfile, 0, 10, code:where_is_file("cowboy.app")}, Req0),
 			Req = cowboy_req:set_resp_body({sendfile, 0, 10, code:where_is_file("cowboy.app")}, Req0),
 			true = cowboy_req:has_resp_body(Req),
 			true = cowboy_req:has_resp_body(Req),
-			cowboy_req:reply(200, #{}, <<"OK">>, Req),
-			{ok, Req, Opts};
+			{ok, cowboy_req:reply(200, #{}, <<"OK">>, Req), Opts};
 		undefined ->
 		undefined ->
 			false = cowboy_req:has_resp_body(Req0),
 			false = cowboy_req:has_resp_body(Req0),
 			Req = cowboy_req:set_resp_body(<<"OK">>, Req0),
 			Req = cowboy_req:set_resp_body(<<"OK">>, Req0),
 			true = cowboy_req:has_resp_body(Req),
 			true = cowboy_req:has_resp_body(Req),
-			cowboy_req:reply(200, #{}, Req),
-			{ok, Req, Opts}
+			{ok, cowboy_req:reply(200, #{}, Req), Opts}
 	end;
 	end;
 do(<<"delete_resp_header">>, Req0, Opts) ->
 do(<<"delete_resp_header">>, Req0, Opts) ->
 	false = cowboy_req:has_resp_header(<<"content-type">>, Req0),
 	false = cowboy_req:has_resp_header(<<"content-type">>, Req0),
@@ -72,61 +66,61 @@ do(<<"delete_resp_header">>, Req0, Opts) ->
 	true = cowboy_req:has_resp_header(<<"content-type">>, Req1),
 	true = cowboy_req:has_resp_header(<<"content-type">>, Req1),
 	Req = cowboy_req:delete_resp_header(<<"content-type">>, Req1),
 	Req = cowboy_req:delete_resp_header(<<"content-type">>, Req1),
 	false = cowboy_req:has_resp_header(<<"content-type">>, Req),
 	false = cowboy_req:has_resp_header(<<"content-type">>, Req),
-	cowboy_req:reply(200, #{}, "OK", Req),
-	{ok, Req, Opts};
-do(<<"reply2">>, Req, Opts) ->
-	case cowboy_req:binding(arg, Req) of
+	{ok, cowboy_req:reply(200, #{}, "OK", Req), Opts};
+do(<<"reply2">>, Req0, Opts) ->
+	Req = case cowboy_req:binding(arg, Req0) of
 		<<"binary">> ->
 		<<"binary">> ->
-			cowboy_req:reply(<<"200 GOOD">>, Req);
+			cowboy_req:reply(<<"200 GOOD">>, Req0);
 		<<"error">> ->
 		<<"error">> ->
 			ct_helper:ignore(cowboy_req, reply, 4),
 			ct_helper:ignore(cowboy_req, reply, 4),
-			cowboy_req:reply(ok, Req);
+			cowboy_req:reply(ok, Req0);
 		<<"twice">> ->
 		<<"twice">> ->
-			cowboy_req:reply(200, Req),
-			cowboy_req:reply(200, Req);
+			ct_helper:ignore(cowboy_req, reply, 4),
+			Req1 = cowboy_req:reply(200, Req0),
+			cowboy_req:reply(200, Req1);
 		Status ->
 		Status ->
-			cowboy_req:reply(binary_to_integer(Status), Req)
+			cowboy_req:reply(binary_to_integer(Status), Req0)
 	end,
 	end,
 	{ok, Req, Opts};
 	{ok, Req, Opts};
-do(<<"reply3">>, Req, Opts) ->
-	case cowboy_req:binding(arg, Req) of
+do(<<"reply3">>, Req0, Opts) ->
+	Req = case cowboy_req:binding(arg, Req0) of
 		<<"error">> ->
 		<<"error">> ->
 			ct_helper:ignore(cowboy_req, reply, 4),
 			ct_helper:ignore(cowboy_req, reply, 4),
-			cowboy_req:reply(200, ok, Req);
+			cowboy_req:reply(200, ok, Req0);
 		Status ->
 		Status ->
 			cowboy_req:reply(binary_to_integer(Status),
 			cowboy_req:reply(binary_to_integer(Status),
-				#{<<"content-type">> => <<"text/plain">>}, Req)
+				#{<<"content-type">> => <<"text/plain">>}, Req0)
 	end,
 	end,
 	{ok, Req, Opts};
 	{ok, Req, Opts};
-do(<<"reply4">>, Req, Opts) ->
-	case cowboy_req:binding(arg, Req) of
+do(<<"reply4">>, Req0, Opts) ->
+	Req = case cowboy_req:binding(arg, Req0) of
 		<<"error">> ->
 		<<"error">> ->
 			ct_helper:ignore(erlang, iolist_size, 1),
 			ct_helper:ignore(erlang, iolist_size, 1),
-			cowboy_req:reply(200, #{}, ok, Req);
+			cowboy_req:reply(200, #{}, ok, Req0);
 		Status ->
 		Status ->
-			cowboy_req:reply(binary_to_integer(Status), #{}, <<"OK">>, Req)
+			cowboy_req:reply(binary_to_integer(Status), #{}, <<"OK">>, Req0)
 	end,
 	end,
 	{ok, Req, Opts};
 	{ok, Req, Opts};
-do(<<"stream_reply2">>, Req, Opts) ->
-	case cowboy_req:binding(arg, Req) of
+do(<<"stream_reply2">>, Req0, Opts) ->
+	Req = case cowboy_req:binding(arg, Req0) of
 		<<"binary">> ->
 		<<"binary">> ->
-			cowboy_req:stream_reply(<<"200 GOOD">>, Req);
+			cowboy_req:stream_reply(<<"200 GOOD">>, Req0);
 		<<"error">> ->
 		<<"error">> ->
 			ct_helper:ignore(cowboy_req, stream_reply, 3),
 			ct_helper:ignore(cowboy_req, stream_reply, 3),
-			cowboy_req:stream_reply(ok, Req);
+			cowboy_req:stream_reply(ok, Req0);
 		Status ->
 		Status ->
-			cowboy_req:stream_reply(binary_to_integer(Status), Req)
+			cowboy_req:stream_reply(binary_to_integer(Status), Req0)
 	end,
 	end,
 	stream_body(Req),
 	stream_body(Req),
 	{ok, Req, Opts};
 	{ok, Req, Opts};
-do(<<"stream_reply3">>, Req, Opts) ->
-	case cowboy_req:binding(arg, Req) of
+do(<<"stream_reply3">>, Req0, Opts) ->
+	Req = case cowboy_req:binding(arg, Req0) of
 		<<"error">> ->
 		<<"error">> ->
 			ct_helper:ignore(cowboy_req, stream_reply, 3),
 			ct_helper:ignore(cowboy_req, stream_reply, 3),
-			cowboy_req:stream_reply(200, ok, Req);
+			cowboy_req:stream_reply(200, ok, Req0);
 		Status ->
 		Status ->
 			cowboy_req:stream_reply(binary_to_integer(Status),
 			cowboy_req:stream_reply(binary_to_integer(Status),
-				#{<<"content-type">> => <<"text/plain">>}, Req)
+				#{<<"content-type">> => <<"text/plain">>}, Req0)
 	end,
 	end,
 	stream_body(Req),
 	stream_body(Req),
 	{ok, Req, Opts};
 	{ok, Req, Opts};
@@ -150,8 +144,7 @@ do(<<"push">>, Req, Opts) ->
 			%% The text/plain mime is not defined by default, so a 406 will be returned.
 			%% The text/plain mime is not defined by default, so a 406 will be returned.
 			cowboy_req:push("/static/plain.txt", #{<<"accept">> => <<"text/plain">>}, Req)
 			cowboy_req:push("/static/plain.txt", #{<<"accept">> => <<"text/plain">>}, Req)
 	end,
 	end,
-	cowboy_req:reply(200, Req),
-	{ok, Req, Opts}.
+	{ok, cowboy_req:reply(200, Req), Opts}.
 
 
 stream_body(Req) ->
 stream_body(Req) ->
 	_ = [cowboy_req:stream_body(<<0:800000>>, nofin, Req) || _ <- lists:seq(1,9)],
 	_ = [cowboy_req:stream_body(<<0:800000>>, nofin, Req) || _ <- lists:seq(1,9)],