100 lines
3.4 KiB
Erlang
100 lines
3.4 KiB
Erlang
|
-module(cowboy_protocol).
|
||
|
|
||
|
%% API.
|
||
|
-export([start_link/4]).
|
||
|
|
||
|
%% Internal.
|
||
|
-export([init/4]).
|
||
|
-export([parse_request/3]).
|
||
|
-export([resume/6]).
|
||
|
|
||
|
-type opts() :: [{compress, boolean()}
|
||
|
| {env, cowboy_middleware:env()}
|
||
|
| {max_empty_lines, non_neg_integer()}
|
||
|
| {max_header_name_length, non_neg_integer()}
|
||
|
| {max_header_value_length, non_neg_integer()}
|
||
|
| {max_headers, non_neg_integer()}
|
||
|
| {max_keepalive, non_neg_integer()}
|
||
|
| {max_request_line_length, non_neg_integer()}
|
||
|
| {middlewares, [module()]}
|
||
|
| {onresponse, cowboy:onresponse_fun()}
|
||
|
| {timeout, timeout()}].
|
||
|
-export_type([opts/0]).
|
||
|
|
||
|
-record(state, {
|
||
|
socket :: inet:socket(),
|
||
|
transport :: module(),
|
||
|
middlewares :: [module()],
|
||
|
compress :: boolean(),
|
||
|
env :: cowboy_middleware:env(),
|
||
|
onresponse = undefined :: undefined | cowboy:onresponse_fun(),
|
||
|
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(),
|
||
|
until :: non_neg_integer() | infinity
|
||
|
}).
|
||
|
|
||
|
-include_lib("cowlib/include/cow_inline.hrl").
|
||
|
-include_lib("cowlib/include/cow_parse.hrl").
|
||
|
|
||
|
%% API.
|
||
|
|
||
|
-spec start_link(ranch:ref(), inet:socket(), module(), opts()) -> {ok, pid()}.
|
||
|
start_link(Ref, Socket, Transport, Opts) ->
|
||
|
Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
|
||
|
{ok, Pid}.
|
||
|
|
||
|
%% Internal.
|
||
|
|
||
|
%% Faster alternative to proplists:get_value/3.
|
||
|
get_value(Key, Opts, Default) ->
|
||
|
case lists:keyfind(Key, 1, Opts) of
|
||
|
{_, Value} -> Value;
|
||
|
_ -> Default
|
||
|
end.
|
||
|
|
||
|
-spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
|
||
|
init(Ref, Socket, Transport, Opts) ->
|
||
|
ok = ranch:accept_ack(Ref),
|
||
|
Timeout = get_value(timeout, Opts, 5000),
|
||
|
Until = until(Timeout),
|
||
|
case recv(Socket, Transport, Until) of
|
||
|
{ok, Data} ->
|
||
|
OnFirstRequest = get_value(onfirstrequest, Opts, undefined),
|
||
|
case OnFirstRequest of
|
||
|
undefined -> ok;
|
||
|
_ -> OnFirstRequest(Ref, Socket, Transport, Opts)
|
||
|
end,
|
||
|
Compress = get_value(compress, Opts, false),
|
||
|
MaxEmptyLines = get_value(max_empty_lines, Opts, 5),
|
||
|
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, 100),
|
||
|
MaxRequestLineLength = get_value(max_request_line_length, Opts, 4096),
|
||
|
Middlewares = get_value(middlewares, Opts, [cowboy_router, cowboy_handler]),
|
||
|
Env = [{listener, Ref}|get_value(env, Opts, [])],
|
||
|
OnResponse = get_value(onresponse, Opts, undefined),
|
||
|
parse_request(Data, #state{socket=Socket, transport=Transport,
|
||
|
middlewares=Middlewares, compress=Compress, env=Env,
|
||
|
max_empty_lines=MaxEmptyLines, max_keepalive=MaxKeepalive,
|
||
|
max_request_line_length=MaxRequestLineLength,
|
||
|
max_header_name_length=MaxHeaderNameLength,
|
||
|
max_header_value_length=MaxHeaderValueLength, max_headers=MaxHeaders,
|
||
|
onresponse=OnResponse, timeout=Timeout, until=Until}, 0);
|
||
|
{error, _} ->
|
||
|
terminate(#state{socket=Socket, transport=Transport}) %% @todo ridiculous
|
||
|
end.
|
||
|
|
||
|
-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.
|