aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/http.hrl1
-rw-r--r--src/cowboy_http_protocol.erl24
-rw-r--r--src/cowboy_http_req.erl13
-rw-r--r--src/cowboy_http_websocket.erl4
4 files changed, 31 insertions, 11 deletions
diff --git a/include/http.hrl b/include/http.hrl
index c47a244..a7355f4 100644
--- a/include/http.hrl
+++ b/include/http.hrl
@@ -46,6 +46,7 @@
connection = keepalive :: keepalive | close,
%% Request.
+ pid = undefined :: pid(),
method = 'GET' :: http_method(),
version = {1, 1} :: http_version(),
peer = undefined :: undefined | {inet:ip_address(), inet:ip_port()},
diff --git a/src/cowboy_http_protocol.erl b/src/cowboy_http_protocol.erl
index 9b71e6c..ea59799 100644
--- a/src/cowboy_http_protocol.erl
+++ b/src/cowboy_http_protocol.erl
@@ -120,13 +120,13 @@ request({http_request, Method, {abs_path, AbsPath}, Version},
{Path, RawPath, Qs} = cowboy_dispatcher:split_path(AbsPath, URLDecode),
ConnAtom = version_to_connection(Version),
parse_header(#http_req{socket=Socket, transport=Transport,
- connection=ConnAtom, method=Method, version=Version,
+ connection=ConnAtom, pid=self(), method=Method, version=Version,
path=Path, raw_path=RawPath, raw_qs=Qs, urldecode=URLDec}, State);
request({http_request, Method, '*', Version},
State=#state{socket=Socket, transport=Transport, urldecode=URLDec}) ->
ConnAtom = version_to_connection(Version),
parse_header(#http_req{socket=Socket, transport=Transport,
- connection=ConnAtom, method=Method, version=Version,
+ connection=ConnAtom, pid=self(), method=Method, version=Version,
path='*', raw_path= <<"*">>, raw_qs= <<>>, urldecode=URLDec}, State);
request({http_request, _Method, _URI, _Version}, State) ->
error_terminate(501, State);
@@ -276,7 +276,7 @@ handler_handle(HandlerState, Req, State=#state{handler={Handler, Opts}}) ->
[Handler, Class, Reason, Opts,
HandlerState, Req, erlang:get_stacktrace()]),
handler_terminate(HandlerState, Req, State),
- terminate(State)
+ error_terminate(500, State)
end.
%% We don't listen for Transport closes because that would force us
@@ -331,7 +331,9 @@ handler_call(HandlerState, Req, State=#state{handler={Handler, Opts}},
"** Options were ~p~n** Handler state was ~p~n"
"** Request was ~p~n** Stacktrace: ~p~n~n",
[Handler, Class, Reason, Opts,
- HandlerState, Req, erlang:get_stacktrace()])
+ HandlerState, Req, erlang:get_stacktrace()]),
+ handler_terminate(HandlerState, Req, State),
+ error_terminate(500, State)
end.
-spec handler_terminate(any(), #http_req{}, #state{}) -> ok.
@@ -359,6 +361,8 @@ next_request(Req=#http_req{connection=Conn, buffer=Buffer},
HandlerRes) ->
RespRes = ensure_response(Req),
BodyRes = ensure_body_processed(Req),
+ %% Flush the resp_sent message before moving on.
+ receive {cowboy_http_req, resp_sent} -> ok after 0 -> ok end,
case {HandlerRes, BodyRes, RespRes, Conn} of
{ok, ok, ok, keepalive} when Keepalive < MaxKeepalive ->
?MODULE:parse_request(State#state{
@@ -395,11 +399,17 @@ ensure_response(#http_req{socket=Socket, transport=Transport,
Transport:send(Socket, <<"0\r\n\r\n">>),
close.
+%% Only send an error reply if there is no resp_sent message.
-spec error_terminate(http_status(), #state{}) -> ok.
error_terminate(Code, State=#state{socket=Socket, transport=Transport}) ->
- _ = cowboy_http_req:reply(Code, [], [], #http_req{
- socket=Socket, transport=Transport,
- connection=close, resp_state=waiting}),
+ receive
+ {cowboy_http_req, resp_sent} -> ok
+ after 0 ->
+ _ = cowboy_http_req:reply(Code, #http_req{
+ socket=Socket, transport=Transport,
+ connection=close, pid=self(), resp_state=waiting}),
+ ok
+ end,
terminate(State).
-spec terminate(#state{}) -> ok.
diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl
index 7a0e3a7..b0a0232 100644
--- a/src/cowboy_http_req.erl
+++ b/src/cowboy_http_req.erl
@@ -473,7 +473,7 @@ reply(Status, Headers, Req=#http_req{resp_body=Body}) ->
-spec reply(http_status(), http_headers(), iodata(), #http_req{})
-> {ok, #http_req{}}.
reply(Status, Headers, Body, Req=#http_req{socket=Socket,
- transport=Transport, connection=Connection,
+ transport=Transport, connection=Connection, pid=ReqPid,
method=Method, resp_state=waiting, resp_headers=RespHeaders}) ->
RespConn = response_connection(Headers, Connection),
ContentLen = case Body of {CL, _} -> CL; _ -> iolist_size(Body) end,
@@ -488,6 +488,7 @@ reply(Status, Headers, Body, Req=#http_req{socket=Socket,
{_, {_, StreamFun}} -> Transport:send(Socket, Head), StreamFun();
{_, _} -> Transport:send(Socket, [Head, Body])
end,
+ ReqPid ! {?MODULE, resp_sent},
{ok, Req#http_req{connection=RespConn, resp_state=done,
resp_headers=[], resp_body= <<>>}}.
@@ -500,8 +501,9 @@ chunked_reply(Status, Req) ->
%% @see cowboy_http_req:chunk/2
-spec chunked_reply(http_status(), http_headers(), #http_req{})
-> {ok, #http_req{}}.
-chunked_reply(Status, Headers, Req=#http_req{socket=Socket, transport=Transport,
- connection=Connection, resp_state=waiting, resp_headers=RespHeaders}) ->
+chunked_reply(Status, Headers, Req=#http_req{socket=Socket,
+ transport=Transport, connection=Connection, pid=ReqPid,
+ resp_state=waiting, resp_headers=RespHeaders}) ->
RespConn = response_connection(Headers, Connection),
Head = response_head(Status, Headers, RespHeaders, [
{<<"Connection">>, atom_to_connection(Connection)},
@@ -510,6 +512,7 @@ chunked_reply(Status, Headers, Req=#http_req{socket=Socket, transport=Transport,
{<<"Server">>, <<"Cowboy">>}
]),
Transport:send(Socket, Head),
+ ReqPid ! {?MODULE, resp_sent},
{ok, Req#http_req{connection=RespConn, resp_state=chunks,
resp_headers=[], resp_body= <<>>}}.
@@ -524,14 +527,16 @@ chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) ->
<<"\r\n">>, Data, <<"\r\n">>]).
%% @doc Send an upgrade reply.
+%% @private
-spec upgrade_reply(http_status(), http_headers(), #http_req{})
-> {ok, #http_req{}}.
upgrade_reply(Status, Headers, Req=#http_req{socket=Socket, transport=Transport,
- resp_state=waiting, resp_headers=RespHeaders}) ->
+ pid=ReqPid, resp_state=waiting, resp_headers=RespHeaders}) ->
Head = response_head(Status, Headers, RespHeaders, [
{<<"Connection">>, <<"Upgrade">>}
]),
Transport:send(Socket, Head),
+ ReqPid ! {?MODULE, resp_sent},
{ok, Req#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}.
%% Misc API.
diff --git a/src/cowboy_http_websocket.erl b/src/cowboy_http_websocket.erl
index 058c843..0f0204c 100644
--- a/src/cowboy_http_websocket.erl
+++ b/src/cowboy_http_websocket.erl
@@ -169,6 +169,8 @@ websocket_handshake(State=#state{version=0, origin=Origin,
{<<"Sec-Websocket-Location">>, Location},
{<<"Sec-Websocket-Origin">>, Origin}],
Req#http_req{resp_state=waiting}),
+ %% Flush the resp_sent message before moving on.
+ receive {cowboy_http_req, resp_sent} -> ok after 0 -> ok end,
%% We replied with a proper response. Proxies should be happy enough,
%% we can now read the 8 last bytes of the challenge keys and send
%% the challenge response directly to the socket.
@@ -188,6 +190,8 @@ websocket_handshake(State=#state{challenge=Challenge},
[{<<"Upgrade">>, <<"websocket">>},
{<<"Sec-Websocket-Accept">>, Challenge}],
Req#http_req{resp_state=waiting}),
+ %% Flush the resp_sent message before moving on.
+ receive {cowboy_http_req, resp_sent} -> ok after 0 -> ok end,
handler_before_loop(State#state{messages=Transport:messages()},
Req2, HandlerState, <<>>).