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:
parent
e40001a884
commit
8b02992e6a
3 changed files with 47 additions and 7 deletions
|
@ -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
|
||||||
}).
|
}).
|
||||||
|
|
|
@ -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) ->
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue