aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_protocol.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/cowboy_protocol.erl')
-rw-r--r--src/cowboy_protocol.erl79
1 files changed, 46 insertions, 33 deletions
diff --git a/src/cowboy_protocol.erl b/src/cowboy_protocol.erl
index b0b5aa6..bf81e52 100644
--- a/src/cowboy_protocol.erl
+++ b/src/cowboy_protocol.erl
@@ -20,11 +20,23 @@
%% <dt>dispatch</dt><dd>The dispatch list for this protocol.</dd>
%% <dt>max_empty_lines</dt><dd>Max number of empty lines before a request.
%% Defaults to 5.</dd>
+%% <dt>max_header_name_length</dt><dd>Max length allowed for header names.
+%% Defaults to 64.</dd>
+%% <dt>max_header_value_length</dt><dd>Max length allowed for header values.
+%% Defaults to 4096.</dd>
+%% <dt>max_headers</dt><dd>Max number of headers allowed.
+%% Defaults to 100.</dd>
+%% <dt>max_keepalive</dt><dd>Max number of requests allowed in a single
+%% keep-alive session. Defaults to infinity.</dd>
+%% <dt>max_request_line_length</dt><dd>Max length allowed for the request
+%% line. Defaults to 4096.</dd>
+%% <dt>onrequest</dt><dd>Optional fun that allows Req interaction before
+%% any dispatching is done. Host info, path info and bindings are thus
+%% not available at this point.</dd>
+%% <dt>onresponse</dt><dd>Optional fun that allows replacing a response
+%% sent by the application based on its status code or headers.</dd>
%% <dt>timeout</dt><dd>Time in milliseconds before an idle
%% connection is closed. Defaults to 5000 milliseconds.</dd>
-%% <dt>urldecode</dt><dd>Function and options argument to use when decoding
-%% URL encoded strings. Defaults to `{fun cowboy_http:urldecode/2, crash}'.
-%% </dd>
%% </dl>
%%
%% Note that there is no need to monitor these processes when using Cowboy as
@@ -56,13 +68,13 @@
dispatch :: cowboy_dispatcher:dispatch_rules(),
onrequest :: undefined | onrequest_fun(),
onresponse = undefined :: undefined | onresponse_fun(),
- urldecode :: {fun((binary(), T) -> binary()), T},
- max_empty_lines :: integer(),
- req_keepalive = 1 :: integer(),
- max_keepalive :: integer(),
- max_request_line_length :: integer(),
- max_header_name_length :: integer(),
- max_header_value_length :: integer(),
+ max_empty_lines :: non_neg_integer(),
+ req_keepalive = 1 :: non_neg_integer(),
+ max_keepalive :: non_neg_integer(),
+ max_request_line_length :: non_neg_integer(),
+ max_header_name_length :: non_neg_integer(),
+ max_header_value_length :: non_neg_integer(),
+ max_headers :: non_neg_integer(),
timeout :: timeout(),
hibernate = false :: boolean(),
loop_timeout = infinity :: timeout(),
@@ -92,24 +104,22 @@ get_value(Key, Opts, Default) ->
init(ListenerPid, Socket, Transport, Opts) ->
Dispatch = get_value(dispatch, Opts, []),
MaxEmptyLines = get_value(max_empty_lines, Opts, 5),
- MaxKeepalive = get_value(max_keepalive, Opts, infinity),
- MaxRequestLineLength = get_value(max_request_line_length, Opts, 4096),
MaxHeaderNameLength = get_value(max_header_name_length, Opts, 64),
MaxHeaderValueLength = get_value(max_header_value_length, Opts, 4096),
+ MaxHeaders = get_value(max_headers, Opts, 100),
+ MaxKeepalive = get_value(max_keepalive, Opts, infinity),
+ MaxRequestLineLength = get_value(max_request_line_length, Opts, 4096),
OnRequest = get_value(onrequest, Opts, undefined),
OnResponse = get_value(onresponse, Opts, undefined),
Timeout = get_value(timeout, Opts, 5000),
- URLDecDefault = {fun cowboy_http:urldecode/2, crash},
- URLDec = get_value(urldecode, Opts, URLDecDefault),
ok = ranch:accept_ack(ListenerPid),
wait_request(<<>>, #state{listener=ListenerPid, socket=Socket,
transport=Transport, dispatch=Dispatch,
max_empty_lines=MaxEmptyLines, max_keepalive=MaxKeepalive,
max_request_line_length=MaxRequestLineLength,
max_header_name_length=MaxHeaderNameLength,
- max_header_value_length=MaxHeaderValueLength,
- timeout=Timeout, onrequest=OnRequest, onresponse=OnResponse,
- urldecode=URLDec}, 0).
+ max_header_value_length=MaxHeaderValueLength, max_headers=MaxHeaders,
+ timeout=Timeout, onrequest=OnRequest, onresponse=OnResponse}, 0).
%% Request parsing.
%%
@@ -139,7 +149,7 @@ parse_request(Buffer, State=#state{max_request_line_length=MaxLength,
max_empty_lines=MaxEmpty}, ReqEmpty) ->
case binary:match(Buffer, <<"\n">>) of
nomatch when byte_size(Buffer) > MaxLength ->
- error_terminate(413, State);
+ error_terminate(414, State);
nomatch ->
wait_request(Buffer, State, ReqEmpty);
{1, _} when ReqEmpty =:= MaxEmpty ->
@@ -207,6 +217,10 @@ parse_version(<< "HTTP/1.0\r\n", Rest/bits >>, S, M, P, Q, F) ->
parse_version(_, State, _, _, _, _) ->
error_terminate(505, State).
+%% Stop receiving data if we have more than allowed number of headers.
+wait_header(_, State=#state{max_headers=MaxHeaders}, _, _, _, _, _, Headers)
+ when length(Headers) >= MaxHeaders ->
+ error_terminate(400, State);
wait_header(Buffer, State=#state{socket=Socket, transport=Transport,
timeout=Timeout}, M, P, Q, F, V, H) ->
case Transport:recv(Socket, 0, Timeout) of
@@ -225,7 +239,7 @@ parse_header(Buffer, State=#state{max_header_name_length=MaxLength},
M, P, Q, F, V, H) ->
case binary:match(Buffer, <<":">>) of
nomatch when byte_size(Buffer) > MaxLength ->
- error_terminate(413, State);
+ error_terminate(400, State);
nomatch ->
wait_header(Buffer, State, M, P, Q, F, V, H);
{_, _} ->
@@ -300,7 +314,7 @@ parse_hd_before_value(Buffer, State=#state{
max_header_value_length=MaxLength}, M, P, Q, F, V, H, N) ->
case binary:match(Buffer, <<"\n">>) of
nomatch when byte_size(Buffer) > MaxLength ->
- error_terminate(413, State);
+ error_terminate(400, State);
nomatch ->
wait_hd_before_value(Buffer, State, M, P, Q, F, V, H, N);
{_, _} ->
@@ -353,7 +367,7 @@ parse_hd_value(<< C, Rest/bits >>, S, M, P, Q, F, V, H, N, SoFar) ->
parse_hd_value(Rest, S, M, P, Q, F, V, H, N, << SoFar/binary, C >>);
parse_hd_value(<<>>, State=#state{max_header_value_length=MaxLength},
_, _, _, _, _, _, _, SoFar) when byte_size(SoFar) > MaxLength ->
- error_terminate(413, State);
+ error_terminate(400, State);
parse_hd_value(<<>>, S, M, P, Q, F, V, H, N, SoFar) ->
wait_hd_value(<<>>, S, M, P, Q, F, V, H, N, SoFar).
@@ -421,11 +435,11 @@ parse_host(<< C, Rest/bits >>, Acc) ->
request(Buffer, State=#state{socket=Socket, transport=Transport,
req_keepalive=ReqKeepalive, max_keepalive=MaxKeepalive,
- onresponse=OnResponse, urldecode=URLDecode},
+ onresponse=OnResponse},
Method, Path, Query, Fragment, Version, Headers, Host, Port) ->
Req = cowboy_req:new(Socket, Transport, Method, Path, Query, Fragment,
Version, Headers, Host, Port, Buffer, ReqKeepalive < MaxKeepalive,
- OnResponse, URLDecode),
+ OnResponse),
onrequest(Req, State, Host, Path).
%% Call the global onrequest callback. The callback can send a reply,
@@ -437,16 +451,14 @@ onrequest(Req, State=#state{onrequest=undefined}, Host, Path) ->
dispatch(Req, State, Host, Path);
onrequest(Req, State=#state{onrequest=OnRequest}, Host, Path) ->
Req2 = OnRequest(Req),
- case cowboy_req:get_resp_state(Req2) of
+ case cowboy_req:get(resp_state, Req2) of
waiting -> dispatch(Req2, State, Host, Path);
_ -> next_request(Req2, State, ok)
end.
-spec dispatch(cowboy_req:req(), #state{}, binary(), binary()) -> ok.
-dispatch(Req, State=#state{dispatch=Dispatch, urldecode={URLDecFun, URLDecArg}},
- Host, Path) ->
- case cowboy_dispatcher:match(Dispatch,
- fun(Bin) -> URLDecFun(Bin, URLDecArg) end, Host, Path) of
+dispatch(Req, State=#state{dispatch=Dispatch}, Host, Path) ->
+ case cowboy_dispatcher:match(Dispatch, Host, Path) of
{ok, Handler, Opts, Bindings, HostInfo, PathInfo} ->
Req2 = cowboy_req:set_bindings(HostInfo, PathInfo, Bindings, Req),
handler_init(Req2, State, Handler, Opts);
@@ -540,6 +552,7 @@ handler_loop_timeout(State=#state{loop_timeout=Timeout,
TRef = erlang:start_timer(Timeout, self(), ?MODULE),
State#state{loop_timeout_ref=TRef}.
+%% @private
-spec handler_loop(cowboy_req:req(), #state{}, module(), any()) -> ok.
handler_loop(Req, State=#state{loop_timeout_ref=TRef}, Handler, HandlerState) ->
receive
@@ -597,13 +610,13 @@ terminate_request(Req, State, Handler, HandlerState) ->
-spec next_request(cowboy_req:req(), #state{}, any()) -> ok.
next_request(Req, State=#state{req_keepalive=Keepalive}, HandlerRes) ->
cowboy_req:ensure_response(Req, 204),
- {BodyRes, Buffer} = case cowboy_req:skip_body(Req) of
- {ok, Req2} -> {ok, cowboy_req:get_buffer(Req2)};
- {error, _} -> {close, <<>>}
+ {BodyRes, [Buffer, Connection]} = case cowboy_req:skip_body(Req) of
+ {ok, Req2} -> {ok, cowboy_req:get([buffer, connection], Req2)};
+ {error, _} -> {close, [<<>>, close]}
end,
%% Flush the resp_sent message before moving on.
receive {cowboy_req, resp_sent} -> ok after 0 -> ok end,
- case {HandlerRes, BodyRes, cowboy_req:get_connection(Req)} of
+ case {HandlerRes, BodyRes, Connection} of
{ok, ok, keepalive} ->
?MODULE:parse_request(Buffer, State#state{
req_keepalive=Keepalive + 1}, 0);
@@ -620,7 +633,7 @@ error_terminate(Code, State=#state{socket=Socket, transport=Transport,
after 0 ->
_ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
<<"GET">>, <<>>, <<>>, <<>>, {1, 1}, [], <<>>, undefined,
- <<>>, false, OnResponse, undefined)),
+ <<>>, false, OnResponse)),
ok
end,
terminate(State).