Просмотр исходного кода

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.
Loïc Hoguin 14 лет назад
Родитель
Сommit
71b31cee92
3 измененных файлов с 14 добавлено и 5 удалено
  1. 7 1
      include/http.hrl
  2. 4 2
      src/cowboy_http_protocol.erl
  3. 3 2
      src/cowboy_http_req.erl

+ 7 - 1
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
 }).

+ 4 - 2
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.

+ 3 - 2
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.