diff options
-rw-r--r-- | include/http.hrl | 3 | ||||
-rw-r--r-- | src/cowboy_http_protocol.erl | 28 | ||||
-rw-r--r-- | src/cowboy_http_req.erl | 23 |
3 files changed, 47 insertions, 7 deletions
diff --git a/include/http.hrl b/include/http.hrl index e0fe4f6..e4b9716 100644 --- a/include/http.hrl +++ b/include/http.hrl @@ -32,6 +32,9 @@ headers = [] :: http_headers(), %% cookies = undefined :: undefined | http_cookies() %% @todo + %% Request body. + body_state = waiting :: waiting | done, + %% Response. resp_state = locked :: locked | waiting | done }). diff --git a/src/cowboy_http_protocol.erl b/src/cowboy_http_protocol.erl index 2b5292c..b02f471 100644 --- a/src/cowboy_http_protocol.erl +++ b/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{}, State::#state{}) -> ok. handler_terminate(HandlerState, Req, State=#state{handler={Handler, _Opts}}) -> - Res = (catch Handler:terminate( + HandlerRes = (catch Handler:terminate( 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), - 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) 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. %% Reply with 204 No Content to indicate this. ensure_response(#http_req{resp_state=waiting}, State) -> diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl index 40af601..a1688de 100644 --- a/src/cowboy_http_req.erl +++ b/src/cowboy_http_req.erl @@ -25,6 +25,10 @@ ]). %% Request API. -export([ + body/2 +]). %% Request Body API. + +-export([ reply/4 ]). %% Response API. @@ -115,8 +119,10 @@ bindings(Req) -> -spec header(Name::atom() | string(), Req::#http_req{}) -> {Value::string(), Req::#http_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{}) -> {Value::string() | term(), Req::#http_req{}}. @@ -129,6 +135,19 @@ header(Name, Default, 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. -spec reply(Code::http_status(), Headers::http_headers(), |