diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cowboy_protocol.erl | 56 |
1 files changed, 40 insertions, 16 deletions
diff --git a/src/cowboy_protocol.erl b/src/cowboy_protocol.erl index b82fa2b..48c0b00 100644 --- a/src/cowboy_protocol.erl +++ b/src/cowboy_protocol.erl @@ -38,8 +38,8 @@ %% not available at this point.</dd> %% <dt>onresponse</dt><dd>Optional fun that allows replacing a response %% sent by the application.</dd> -%% <dt>timeout</dt><dd>Time in milliseconds before an idle -%% connection is closed. Defaults to 5000 milliseconds.</dd> +%% <dt>timeout</dt><dd>Time in milliseconds a client has to send the +%% full request line and headers. Defaults to 5000 milliseconds.</dd> %% </dl> %% %% Note that there is no need to monitor these processes when using Cowboy as @@ -74,7 +74,8 @@ max_header_name_length :: non_neg_integer(), max_header_value_length :: non_neg_integer(), max_headers :: non_neg_integer(), - timeout :: timeout() + timeout :: timeout(), + until :: non_neg_integer() | infinity }). %% API. @@ -116,7 +117,15 @@ init(ListenerPid, Socket, Transport, Opts) -> max_request_line_length=MaxRequestLineLength, max_header_name_length=MaxHeaderNameLength, max_header_value_length=MaxHeaderValueLength, max_headers=MaxHeaders, - timeout=Timeout, onrequest=OnRequest, onresponse=OnResponse}, 0). + onrequest=OnRequest, onresponse=OnResponse, + timeout=Timeout, until=until(Timeout)}, 0). + +-spec until(timeout()) -> non_neg_integer() | infinity. +until(infinity) -> + infinity; +until(Timeout) -> + {Me, S, Mi} = os:timestamp(), + Me * 1000000000 + S * 1000 + Mi div 1000 + Timeout. %% Request parsing. %% @@ -125,10 +134,24 @@ init(ListenerPid, Socket, Transport, Opts) -> %% right after the header parsing is finished and the code becomes %% more interesting past that point. +-spec recv(inet:socket(), module(), non_neg_integer() | infinity) + -> {ok, binary()} | {error, closed | timeout | atom()}. +recv(Socket, Transport, infinity) -> + Transport:recv(Socket, 0, infinity); +recv(Socket, Transport, Until) -> + {Me, S, Mi} = os:timestamp(), + Now = Me * 1000000000 + S * 1000 + Mi div 1000, + Timeout = Until - Now, + if Timeout < 0 -> + {error, timeout}; + true -> + Transport:recv(Socket, 0, Timeout) + end. + -spec wait_request(binary(), #state{}, non_neg_integer()) -> ok. wait_request(Buffer, State=#state{socket=Socket, transport=Transport, - timeout=Timeout}, ReqEmpty) -> - case Transport:recv(Socket, 0, Timeout) of + until=Until}, ReqEmpty) -> + case recv(Socket, Transport, Until) of {ok, Data} -> parse_request(<< Buffer/binary, Data/binary >>, State, ReqEmpty); {error, _} -> @@ -219,8 +242,8 @@ 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 + until=Until}, M, P, Q, F, V, H) -> + case recv(Socket, Transport, Until) of {ok, Data} -> parse_header(<< Buffer/binary, Data/binary >>, State, M, P, Q, F, V, H); @@ -291,9 +314,9 @@ parse_hd_name_ws(<< C, Rest/bits >>, S, M, P, Q, F, V, H, Name) -> end. wait_hd_before_value(Buffer, State=#state{ - socket=Socket, transport=Transport, timeout=Timeout}, + socket=Socket, transport=Transport, until=Until}, M, P, Q, F, V, H, N) -> - case Transport:recv(Socket, 0, Timeout) of + case recv(Socket, Transport, Until) of {ok, Data} -> parse_hd_before_value(<< Buffer/binary, Data/binary >>, State, M, P, Q, F, V, H, N); @@ -323,9 +346,9 @@ parse_hd_before_value(Buffer, State=#state{ %% to change the other arguments' position and trigger costy %% operations for no reasons. wait_hd_value(_, State=#state{ - socket=Socket, transport=Transport, timeout=Timeout}, + socket=Socket, transport=Transport, until=Until}, M, P, Q, F, V, H, N, SoFar) -> - case Transport:recv(Socket, 0, Timeout) of + case recv(Socket, Transport, Until) of {ok, Data} -> parse_hd_value(Data, State, M, P, Q, F, V, H, N, SoFar); {error, timeout} -> @@ -338,9 +361,9 @@ wait_hd_value(_, State=#state{ %% to check for multilines allows us to avoid a few tests in %% the critical path, but forces us to have a special function. wait_hd_value_nl(_, State=#state{ - socket=Socket, transport=Transport, timeout=Timeout}, + socket=Socket, transport=Transport, until=Until}, M, P, Q, F, V, Headers, Name, SoFar) -> - case Transport:recv(Socket, 0, Timeout) of + case recv(Socket, Transport, Until) of {ok, << C, Data/bits >>} when C =:= $\s; C =:= $\t -> parse_hd_value(Data, State, M, P, Q, F, V, Headers, Name, SoFar); {ok, Data} -> @@ -492,7 +515,8 @@ resume(State, Env, Tail, Module, Function, Args) -> end. -spec next_request(cowboy_req:req(), #state{}, any()) -> ok. -next_request(Req, State=#state{req_keepalive=Keepalive}, HandlerRes) -> +next_request(Req, State=#state{req_keepalive=Keepalive, timeout=Timeout}, + HandlerRes) -> cowboy_req:ensure_response(Req, 204), {BodyRes, [Buffer, Connection]} = case cowboy_req:skip_body(Req) of {ok, Req2} -> {ok, cowboy_req:get([buffer, connection], Req2)}; @@ -503,7 +527,7 @@ next_request(Req, State=#state{req_keepalive=Keepalive}, HandlerRes) -> case {HandlerRes, BodyRes, Connection} of {ok, ok, keepalive} -> ?MODULE:parse_request(Buffer, State#state{ - req_keepalive=Keepalive + 1}, 0); + req_keepalive=Keepalive + 1, until=until(Timeout)}, 0); _Closed -> terminate(State) end. |