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

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

This commit is contained in:
Loïc Hoguin 2011-03-21 17:26:00 +01:00
parent e40001a884
commit 8b02992e6a
3 changed files with 47 additions and 7 deletions

View file

@ -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
}). }).

View file

@ -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 BodyRes = ensure_body_processed(Req),
%% before processing another.
ensure_response(Req, State), ensure_response(Req, State),
case {Res, State#state.connection} of case {HandlerRes, BodyRes, State#state.connection} of
{ok, keepalive} -> next_request(State); {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) ->

View file

@ -24,6 +24,10 @@
%% cookie/2, cookie/3, cookies/1 @todo %% cookie/2, cookie/3, cookies/1 @todo
]). %% Request API. ]). %% Request API.
-export([
body/2
]). %% Request Body API.
-export([ -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), case lists:keyfind(Name, 1, Req#http_req.headers) of
{Value, Req}. {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(),