From 71b31cee92f5e0c92f57e94c4916e9e19bbafb3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sun, 20 Mar 2011 18:03:11 +0100 Subject: Make sure we can only reply to an HTTP request inside Handler:handle. Of course since requests are a record the response state can be explicitly overriden, but standard use prevents errors by making sure only one reply is sent. --- include/http.hrl | 8 +++++++- src/cowboy_http_protocol.erl | 6 ++++-- src/cowboy_http_req.erl | 5 +++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/http.hrl b/include/http.hrl index a36fa93..e0fe4f6 100644 --- a/include/http.hrl +++ b/include/http.hrl @@ -13,9 +13,12 @@ %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -record(http_req, { + %% Transport. socket = undefined :: undefined | socket(), transport = undefined :: undefined | module(), connection = keepalive :: keepalive | close, + + %% Request. method = 'GET' :: http_method(), version = {1, 1} :: http_version(), peer = undefined :: undefined | {Address::ip_address(), Port::port_number()}, @@ -26,6 +29,9 @@ qs_vals = undefined :: undefined | bindings(), raw_qs = undefined :: undefined | string(), bindings = undefined :: undefined | bindings(), - headers = [] :: http_headers() + headers = [] :: http_headers(), %% cookies = undefined :: undefined | http_cookies() %% @todo + + %% Response. + resp_state = locked :: locked | waiting | done }). diff --git a/src/cowboy_http_protocol.erl b/src/cowboy_http_protocol.erl index f6047bf..4b3c40b 100644 --- a/src/cowboy_http_protocol.erl +++ b/src/cowboy_http_protocol.erl @@ -143,7 +143,8 @@ handler_init(Req, State=#state{handler={Handler, Opts}}) -> -spec handler_loop(HandlerState::term(), Req::#http_req{}, State::#state{}) -> ok. handler_loop(HandlerState, Req, State=#state{handler={Handler, _Opts}}) -> - case catch Handler:handle(Req, HandlerState) of + case catch Handler:handle(Req#http_req{resp_state=waiting}, + HandlerState) of {ok, Req2, HandlerState2} -> handler_terminate(HandlerState2, Req2, State); {'EXIT', _Reason} -> @@ -153,7 +154,8 @@ handler_loop(HandlerState, Req, State=#state{handler={Handler, _Opts}}) -> -spec handler_terminate(HandlerState::term(), Req::#http_req{}, State::#state{}) -> ok. handler_terminate(HandlerState, Req, State=#state{handler={Handler, _Opts}}) -> - Res = (catch Handler:terminate(Req, HandlerState)), + Res = (catch Handler:terminate( + Req#http_req{resp_state=locked}, HandlerState)), %% @todo We need to check if the Req has been replied to. %% All requests must have a reply, at worst an error. %% If a request started but wasn't completed, complete it. diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl index 3dcac2f..40af601 100644 --- a/src/cowboy_http_req.erl +++ b/src/cowboy_http_req.erl @@ -135,13 +135,14 @@ headers(Req) -> Body::iolist(), Req::#http_req{}) -> ok. %% @todo Don't be naive about the headers! reply(Code, Headers, Body, Req=#http_req{socket=Socket, - transport=Transport, connection=Connection}) -> + transport=Transport, connection=Connection, + resp_state=waiting}) -> StatusLine = ["HTTP/1.1 ", status(Code), "\r\n"], BaseHeaders = ["Connection: ", atom_to_connection(Connection), "\r\nContent-Length: ", integer_to_list(iolist_size(Body)), "\r\n"], Transport:send(Socket, [StatusLine, BaseHeaders, Headers, "\r\n", Body]), - {ok, Req}. + {ok, Req#http_req{resp_state=done}}. %% Internal. -- cgit v1.2.3