From e30d120bd8c9a4a7b469937d5b5be6a8dfe148d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Wed, 10 Aug 2016 17:15:02 +0200 Subject: Make reply functions return Req --- src/cowboy_http.erl | 5 +++++ src/cowboy_http2.erl | 5 +++++ src/cowboy_req.erl | 20 ++++++++++++++------ src/cowboy_stream_h.erl | 6 +++--- src/cowboy_websocket.erl | 4 ++-- 5 files changed, 29 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index 279b11a..773f891 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -804,6 +804,11 @@ commands(State, StreamID, [{flow, _Length}|Tail]) -> %% @todo Set the body reading length to min(Length, BodyLength) + 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. %% diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl index f7a9634..5c71628 100644 --- a/src/cowboy_http2.erl +++ b/src/cowboy_http2.erl @@ -369,6 +369,11 @@ info(State=#state{handler=Handler, streams=Streams}, StreamID, Msg) -> 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. %% %% @todo Kill the stream if it sent a response when one has already been sent. diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl index 3c9ed34..d838c8c 100644 --- a/src/cowboy_req.erl +++ b/src/cowboy_req.erl @@ -592,6 +592,8 @@ reply(Status, Headers, Req) -> -spec reply(cowboy:http_status(), cowboy:http_headers(), resp_body(), Req) -> Req when Req::req(). +reply(_, _, _, #{has_sent_resp := _}) -> + error(function_clause); reply(Status, Headers, SendFile = {sendfile, _, Len, _}, Req) when is_integer(Status); is_binary(Status) -> do_reply(Status, Headers#{ @@ -608,10 +610,13 @@ reply(Status, Headers, Body, Req) %% data around if we can avoid it. do_reply(Status, Headers, _, Req=#{pid := Pid, streamid := StreamID, method := <<"HEAD">>}) -> Pid ! {{Pid, StreamID}, {response, Status, response_headers(Headers, Req), <<>>}}, - ok; + done_replying(Req, true); do_reply(Status, Headers, Body, Req=#{pid := Pid, streamid := StreamID}) -> 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(). stream_reply(Status, Req) -> @@ -619,25 +624,28 @@ stream_reply(Status, Req) -> -spec stream_reply(cowboy:http_status(), cowboy:http_headers(), Req) -> Req when Req::req(). +stream_reply(_, _, #{has_sent_resp := _}) -> + error(function_clause); stream_reply(Status, Headers=#{}, Req=#{pid := Pid, streamid := StreamID}) when is_integer(Status); is_binary(Status) -> Pid ! {{Pid, StreamID}, {headers, Status, response_headers(Headers, Req)}}, - ok. + done_replying(Req, headers). -spec stream_body(iodata(), fin | nofin, req()) -> ok. +%% Error out if headers were not sent. %% Don't send any body for HEAD responses. -stream_body(_, _, #{method := <<"HEAD">>}) -> +stream_body(_, _, #{method := <<"HEAD">>, has_sent_resp := headers}) -> ok; %% Don't send a message if the data is empty, except for the %% 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 0 -> ok; _ -> Pid ! {{Pid, StreamID}, {data, IsFin, Data}}, ok 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}}, ok. diff --git a/src/cowboy_stream_h.erl b/src/cowboy_stream_h.erl index e29ffc5..92be76d 100644 --- a/src/cowboy_stream_h.erl +++ b/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}) -> %% 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.'} ], State}; info(_StreamID, {'EXIT', Pid, {_Reason, [_, {cow_http_hd, _, _, _}|_]}}, State=#state{pid=Pid}) -> %% @todo Have an option to enable/disable this specific crash report? %%report_crash(Ref, StreamID, Pid, Reason, Stacktrace), %% @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}) -> report_crash(Ref, StreamID, Pid, Reason, Stacktrace), {[ - {response, 500, #{<<"content-length">> => <<"0">>}, <<>>}, + {error_response, 500, #{<<"content-length">> => <<"0">>}, <<>>}, {internal_error, Exit, 'Stream process crashed.'} ], State}; %% Request body, no body buffer but IsFin=fin. diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl index 9ce7a5a..bcd5fc4 100644 --- a/src/cowboy_websocket.erl +++ b/src/cowboy_websocket.erl @@ -79,8 +79,8 @@ upgrade(Req, Env, Handler, HandlerState, Timeout, Hibernate) -> websocket_handshake(State2, Req2, HandlerState, Env) catch _:_ -> %% @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. -spec websocket_upgrade(#state{}, Req) -- cgit v1.2.3