aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_http.erl5
-rw-r--r--src/cowboy_http2.erl5
-rw-r--r--src/cowboy_req.erl20
-rw-r--r--src/cowboy_stream_h.erl6
-rw-r--r--src/cowboy_websocket.erl4
5 files changed, 29 insertions, 11 deletions
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
@@ -805,6 +805,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.
%%
%% @todo Kill the stream if it sent a response when one has already been sent.
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)