Browse Source

Skip the request body if it hasn't been read by the handler.

Loïc Hoguin 14 years ago
parent
commit
8b02992e6a
3 changed files with 47 additions and 7 deletions
  1. 3 0
      include/http.hrl
  2. 23 5
      src/cowboy_http_protocol.erl
  3. 21 2
      src/cowboy_http_req.erl

+ 3 - 0
include/http.hrl

@@ -32,6 +32,9 @@
 	headers    = []        :: http_headers(),
 	headers    = []        :: http_headers(),
 %%	cookies    = undefined :: undefined | http_cookies() %% @todo
 %%	cookies    = undefined :: undefined | http_cookies() %% @todo
 
 
+	%% Request body.
+	body_state = waiting   :: waiting | done,
+
 	%% Response.
 	%% Response.
 	resp_state = locked    :: locked | waiting | done
 	resp_state = locked    :: locked | waiting | done
 }).
 }).

+ 23 - 5
src/cowboy_http_protocol.erl

@@ -154,16 +154,34 @@ 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(
+	HandlerRes = (catch Handler:terminate(
 		Req#http_req{resp_state=locked}, HandlerState)),
 		Req#http_req{resp_state=locked}, HandlerState)),
-	%% @todo We must skip any body data from the request
-	%%       before processing another.
+	BodyRes = ensure_body_processed(Req),
 	ensure_response(Req, State),
 	ensure_response(Req, State),
-	case {Res, State#state.connection} of
-		{ok, keepalive} -> next_request(State);
+	case {HandlerRes, BodyRes, State#state.connection} of
+		{ok, ok, keepalive} -> next_request(State);
 		_Closed -> terminate(State)
 		_Closed -> terminate(State)
 	end.
 	end.
 
 
+-spec ensure_body_processed(Req::#http_req{}) -> ok | close.
+ensure_body_processed(#http_req{body_state=done}) ->
+	ok;
+ensure_body_processed(Req=#http_req{body_state=waiting}) ->
+	{Length, Req2} = cowboy_http_req:header('Content-Length', Req),
+	case Length of
+		"" -> ok;
+		_Any ->
+			Length2 = list_to_integer(Length),
+			skip_body(Length2, Req2)
+	end.
+
+-spec skip_body(Length::non_neg_integer(), Req::#http_req{}) -> ok | close.
+skip_body(Length, Req) ->
+	case cowboy_http_req:body(Length, Req) of
+		{error, _Reason} -> close;
+		_Any -> ok
+	end.
+
 %% No response has been sent but everything apparently went fine.
 %% No response has been sent but everything apparently went fine.
 %% Reply with 204 No Content to indicate this.
 %% Reply with 204 No Content to indicate this.
 ensure_response(#http_req{resp_state=waiting}, State) ->
 ensure_response(#http_req{resp_state=waiting}, State) ->

+ 21 - 2
src/cowboy_http_req.erl

@@ -25,6 +25,10 @@
 ]). %% Request API.
 ]). %% Request API.
 
 
 -export([
 -export([
+	body/2
+]). %% Request Body API.
+
+-export([
 	reply/4
 	reply/4
 ]). %% Response API.
 ]). %% Response API.
 
 
@@ -115,8 +119,10 @@ bindings(Req) ->
 -spec header(Name::atom() | string(), Req::#http_req{})
 -spec header(Name::atom() | string(), Req::#http_req{})
 	-> {Value::string(), Req::#http_req{}}.
 	-> {Value::string(), Req::#http_req{}}.
 header(Name, Req) ->
 header(Name, Req) ->
-	{Name, Value} = lists:keyfind(Name, 1, Req#http_req.headers),
-	{Value, Req}.
+	case lists:keyfind(Name, 1, Req#http_req.headers) of
+		{Name, Value} -> {Value, Req};
+		false -> {"", Req}
+	end.
 
 
 -spec header(Name::atom() | string(), Default::term(), Req::#http_req{})
 -spec header(Name::atom() | string(), Default::term(), Req::#http_req{})
 	-> {Value::string() | term(), Req::#http_req{}}.
 	-> {Value::string() | term(), Req::#http_req{}}.
@@ -129,6 +135,19 @@ header(Name, Default, Req) ->
 headers(Req) ->
 headers(Req) ->
 	{Req#http_req.headers, Req}.
 	{Req#http_req.headers, Req}.
 
 
+%% Request Body API.
+
+%% @todo We probably want to configure the timeout.
+%% @todo We probably want to allow a max length.
+-spec body(Length::non_neg_integer(), Req::#http_req{})
+	-> {Body::binary(), Req::#http_req{}} | {error, Reason::posix()}.
+body(Length, Req=#http_req{socket=Socket, transport=Transport, body_state=waiting}) ->
+	Transport:setopts(Socket, [{packet, raw}]),
+	case Transport:recv(Socket, Length, 5000) of
+		{ok, Body} -> {ok, Body, Req#http_req{body_state=done}};
+		{error, Reason} -> {error, Reason}
+	end.
+
 %% Response API.
 %% Response API.
 
 
 -spec reply(Code::http_status(), Headers::http_headers(),
 -spec reply(Code::http_status(), Headers::http_headers(),