0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

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.
This commit is contained in:
Loïc Hoguin 2011-03-20 18:03:11 +01:00
parent d69d0adfa7
commit 71b31cee92
3 changed files with 14 additions and 5 deletions

View file

@ -13,9 +13,12 @@
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-record(http_req, { -record(http_req, {
%% Transport.
socket = undefined :: undefined | socket(), socket = undefined :: undefined | socket(),
transport = undefined :: undefined | module(), transport = undefined :: undefined | module(),
connection = keepalive :: keepalive | close, connection = keepalive :: keepalive | close,
%% Request.
method = 'GET' :: http_method(), method = 'GET' :: http_method(),
version = {1, 1} :: http_version(), version = {1, 1} :: http_version(),
peer = undefined :: undefined | {Address::ip_address(), Port::port_number()}, peer = undefined :: undefined | {Address::ip_address(), Port::port_number()},
@ -26,6 +29,9 @@
qs_vals = undefined :: undefined | bindings(), qs_vals = undefined :: undefined | bindings(),
raw_qs = undefined :: undefined | string(), raw_qs = undefined :: undefined | string(),
bindings = undefined :: undefined | bindings(), bindings = undefined :: undefined | bindings(),
headers = [] :: http_headers() headers = [] :: http_headers(),
%% cookies = undefined :: undefined | http_cookies() %% @todo %% cookies = undefined :: undefined | http_cookies() %% @todo
%% Response.
resp_state = locked :: locked | waiting | done
}). }).

View file

@ -143,7 +143,8 @@ handler_init(Req, State=#state{handler={Handler, Opts}}) ->
-spec handler_loop(HandlerState::term(), Req::#http_req{}, -spec handler_loop(HandlerState::term(), Req::#http_req{},
State::#state{}) -> ok. State::#state{}) -> ok.
handler_loop(HandlerState, Req, State=#state{handler={Handler, _Opts}}) -> 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} -> {ok, Req2, HandlerState2} ->
handler_terminate(HandlerState2, Req2, State); handler_terminate(HandlerState2, Req2, State);
{'EXIT', _Reason} -> {'EXIT', _Reason} ->
@ -153,7 +154,8 @@ handler_loop(HandlerState, Req, State=#state{handler={Handler, _Opts}}) ->
-spec handler_terminate(HandlerState::term(), Req::#http_req{}, -spec handler_terminate(HandlerState::term(), Req::#http_req{},
State::#state{}) -> ok. State::#state{}) -> ok.
handler_terminate(HandlerState, Req, State=#state{handler={Handler, _Opts}}) -> 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. %% @todo We need to check if the Req has been replied to.
%% All requests must have a reply, at worst an error. %% All requests must have a reply, at worst an error.
%% If a request started but wasn't completed, complete it. %% If a request started but wasn't completed, complete it.

View file

@ -135,13 +135,14 @@ headers(Req) ->
Body::iolist(), Req::#http_req{}) -> ok. Body::iolist(), Req::#http_req{}) -> ok.
%% @todo Don't be naive about the headers! %% @todo Don't be naive about the headers!
reply(Code, Headers, Body, Req=#http_req{socket=Socket, 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"], StatusLine = ["HTTP/1.1 ", status(Code), "\r\n"],
BaseHeaders = ["Connection: ", atom_to_connection(Connection), BaseHeaders = ["Connection: ", atom_to_connection(Connection),
"\r\nContent-Length: ", integer_to_list(iolist_size(Body)), "\r\n"], "\r\nContent-Length: ", integer_to_list(iolist_size(Body)), "\r\n"],
Transport:send(Socket, Transport:send(Socket,
[StatusLine, BaseHeaders, Headers, "\r\n", Body]), [StatusLine, BaseHeaders, Headers, "\r\n", Body]),
{ok, Req}. {ok, Req#http_req{resp_state=done}}.
%% Internal. %% Internal.