diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cowboy_acceptors_sup.erl | 2 | ||||
-rw-r--r-- | src/cowboy_http.erl | 307 | ||||
-rw-r--r-- | src/cowboy_http_protocol.erl | 2 | ||||
-rw-r--r-- | src/cowboy_http_req.erl | 79 | ||||
-rw-r--r-- | src/cowboy_http_websocket.erl | 6 | ||||
-rw-r--r-- | src/cowboy_listener.erl | 8 | ||||
-rw-r--r-- | src/cowboy_listener_sup.erl | 2 | ||||
-rw-r--r-- | src/cowboy_sup.erl | 2 |
8 files changed, 340 insertions, 68 deletions
diff --git a/src/cowboy_acceptors_sup.erl b/src/cowboy_acceptors_sup.erl index 3d57610..17849a6 100644 --- a/src/cowboy_acceptors_sup.erl +++ b/src/cowboy_acceptors_sup.erl @@ -38,6 +38,6 @@ init([NbAcceptors, Transport, TransOpts, Procs = [{{acceptor, self(), N}, {cowboy_acceptor, start_link, [ LSocket, Transport, Protocol, ProtoOpts, MaxConns, ListenerPid, ReqsPid - ]}, permanent, brutal_kill, worker, dynamic} + ]}, permanent, brutal_kill, worker, []} || N <- lists:seq(1, NbAcceptors)], {ok, {{one_for_one, 10, 10}, Procs}}. diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index b05611b..9b8a178 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -16,7 +16,9 @@ -module(cowboy_http). %% Parsing. --export([list/2, nonempty_list/2, token/2, token_ci/2]). +-export([list/2, nonempty_list/2, + media_range/2, charset/2, + token/2, token_ci/2, quoted_string/2]). %% Interpretation. -export([connection_to_atom/1]). @@ -44,53 +46,211 @@ list(Data, Fun) -> end. -spec list(binary(), fun(), [binary()]) -> [any()] | {error, badarg}. -list(<<>>, _Fun, Acc) -> - Acc; %% From the RFC: %% <blockquote>Wherever this construct is used, null elements are allowed, %% but do not contribute to the count of elements present. %% That is, "(element), , (element) " is permitted, but counts %% as only two elements. Therefore, where at least one element is required, %% at least one non-null element MUST be present.</blockquote> -list(<< $,, Rest/bits >>, Fun, Acc) -> - list(Rest, Fun, Acc); list(Data, Fun, Acc) -> - Fun(Data, - fun (R, <<>>) -> list_separator(R, - fun (D) -> list(D, Fun, Acc) end); - (R, I) -> list_separator(R, - fun (D) -> list(D, Fun, [I|Acc]) end) + whitespace(Data, + fun (<<>>) -> Acc; + (<< $,, Rest/bits >>) -> list(Rest, Fun, Acc); + (Rest) -> Fun(Rest, + fun (D, I) -> whitespace(D, + fun (<<>>) -> [I|Acc]; + (<< $,, R/bits >>) -> list(R, Fun, [I|Acc]); + (_Any) -> {error, badarg} + end) + end) end). --spec list_separator(binary(), fun()) -> any(). -list_separator(<<>>, Fun) -> - Fun(<<>>); -list_separator(<< $,, Rest/bits >>, Fun) -> - Fun(Rest); -list_separator(<< C, Rest/bits >>, Fun) +%% @doc Parse a media range. +-spec media_range(binary(), fun()) -> any(). +media_range(Data, Fun) -> + token_ci(Data, + fun (_Rest, <<>>) -> {error, badarg}; + (Rest, Type) -> whitespace(Rest, + fun (<< $/, Rest2/bits >>) -> whitespace(Rest2, + fun (<<>>) -> {error, badarg}; + (Rest3) -> media_range_subtype(Rest3, Fun, Type) + end); + (_Rest2) -> {error, badarg} + end) + end). + +-spec media_range_subtype(binary(), fun(), binary()) -> any(). +media_range_subtype(Data, Fun, Type) -> + token_ci(Data, + fun (_Rest, <<>>) -> {error, badarg}; + (Rest, SubType) -> media_range_params(Rest, Fun, Type, SubType, []) + end). + +-spec media_range_params(binary(), fun(), binary(), binary(), + [{binary(), binary()}]) -> any(). +media_range_params(Data, Fun, Type, SubType, Acc) -> + whitespace(Data, + fun (<< $;, Rest/bits >>) -> + whitespace(Rest, + fun (Rest2) -> + media_range_param_attr(Rest2, Fun, Type, SubType, Acc) + end); + (Rest) -> Fun(Rest, {{Type, SubType, lists:reverse(Acc)}, 1000, []}) + end). + +-spec media_range_param_attr(binary(), fun(), binary(), binary(), + [{binary(), binary()}]) -> any(). +media_range_param_attr(Data, Fun, Type, SubType, Acc) -> + token_ci(Data, + fun (_Rest, <<>>) -> {error, badarg}; + (Rest, Attr) -> + whitespace(Rest, + fun (<< $=, Rest2/bits >>) -> + whitespace(Rest2, + fun (<<>>) -> {error, badarg}; + (Rest3) -> + media_range_param_value(Rest3, Fun, + Type, SubType, Acc, Attr) + end); + (_Rest2) -> + {error, badarg} + end) + end). + +-spec media_range_param_value(binary(), fun(), binary(), binary(), + [{binary(), binary()}], binary()) -> any(). +media_range_param_value(Data, Fun, Type, SubType, Acc, <<"q">>) -> + qvalue(Data, + fun (Rest, Quality) -> + accept_ext(Rest, Fun, Type, SubType, Acc, Quality, []) + end); +media_range_param_value(Data = << $", _/bits >>, Fun, + Type, SubType, Acc, Attr) -> + quoted_string(Data, + fun (Rest, Value) -> + media_range_params(Rest, Fun, + Type, SubType, [{Attr, Value}|Acc]) + end); +media_range_param_value(Data, Fun, Type, SubType, Acc, Attr) -> + token(Data, + fun (_Rest, <<>>) -> {error, badarg}; + (Rest, Value) -> + media_range_params(Rest, Fun, + Type, SubType, [{Attr, Value}|Acc]) + end). + +-spec accept_ext(binary(), fun(), binary(), binary(), + [{binary(), binary()}], 0..1000, + [{binary(), binary()} | binary()]) -> any(). +accept_ext(Data, Fun, Type, SubType, Params, Quality, Acc) -> + whitespace(Data, + fun (<< $;, Rest/bits >>) -> + whitespace(Rest, + fun (Rest2) -> + accept_ext_attr(Rest2, Fun, + Type, SubType, Params, Quality, Acc) + end); + (Rest) -> + Fun(Rest, {{Type, SubType, lists:reverse(Params)}, + Quality, lists:reverse(Acc)}) + end). + +-spec accept_ext_attr(binary(), fun(), binary(), binary(), + [{binary(), binary()}], 0..1000, + [{binary(), binary()} | binary()]) -> any(). +accept_ext_attr(Data, Fun, Type, SubType, Params, Quality, Acc) -> + token_ci(Data, + fun (_Rest, <<>>) -> {error, badarg}; + (Rest, Attr) -> + whitespace(Rest, + fun (<< $=, Rest2/bits >>) -> + whitespace(Rest2, + fun (<<>>) -> {error, badarg}; + (Rest3) -> + accept_ext_value(Rest3, Fun, + Type, SubType, Params, + Quality, Acc, Attr) + end); + (Rest2) -> + accept_ext(Rest2, Fun, + Type, SubType, Params, + Quality, [Attr|Acc]) + end) + end). + +-spec accept_ext_value(binary(), fun(), binary(), binary(), + [{binary(), binary()}], 0..1000, + [{binary(), binary()} | binary()], binary()) -> any(). +accept_ext_value(Data = << $", _/bits >>, Fun, + Type, SubType, Params, Quality, Acc, Attr) -> + quoted_string(Data, + fun (Rest, Value) -> + accept_ext(Rest, Fun, + Type, SubType, Params, Quality, [{Attr, Value}|Acc]) + end); +accept_ext_value(Data, Fun, Type, SubType, Params, Quality, Acc, Attr) -> + token(Data, + fun (_Rest, <<>>) -> {error, badarg}; + (Rest, Value) -> + accept_ext(Rest, Fun, + Type, SubType, Params, Quality, [{Attr, Value}|Acc]) + end). + +%% @doc Parse a charset, followed by an optional quality value. +-spec charset(binary(), fun()) -> any(). +charset(Data, Fun) -> + token_ci(Data, + fun (_Rest, <<>>) -> {error, badarg}; + (Rest, Charset) -> + whitespace(Rest, + fun (<< $;, Rest2/bits >>) -> + whitespace(Rest2, + fun (Rest3) -> + qparam(Rest3, + fun (Rest4, Quality) -> + Fun(Rest4, {Charset, Quality}) + end) + end); + (Rest2) -> + Fun(Rest2, {Charset, 1000}) + end) + end). + +%% Parse a quality parameter string (for example q=0.500). +-spec qparam(binary(), fun()) -> any(). +qparam(<< $q, Rest/bits >>, Fun) -> + whitespace(Rest, + fun (<< $=, Rest2/bits >>) -> + whitespace(Rest2, + fun (Rest3) -> + qvalue(Rest3, + fun (Rest4, Quality) -> + Fun(Rest4, Quality) + end) + end); + (_Rest2) -> + {error, badarg} + end). + +%% @doc Skip whitespace. +-spec whitespace(binary(), fun()) -> any(). +whitespace(<< C, Rest/bits >>, Fun) when C =:= $\s; C =:= $\t -> - list_separator(Rest, Fun); -list_separator(_Data, _Fun) -> - {error, badarg}. + whitespace(Rest, Fun); +whitespace(Data, Fun) -> + Fun(Data). %% @doc Parse a case-insensitive token. %% %% Changes all characters to lowercase. -spec token_ci(binary(), fun()) -> any(). token_ci(Data, Fun) -> - token(Data, Fun, ci). + token(Data, Fun, ci, <<>>). %% @doc Parse a token. -spec token(binary(), fun()) -> any(). token(Data, Fun) -> - token(Data, Fun, cs). - --spec token(binary(), fun(), ci | cs) -> any(). -token(<< C, Rest/bits >>, Fun, Case) - when C =:= $\s; C =:= $\t -> - token(Rest, Fun, Case); -token(Data, Fun, Case) -> - token(Data, Fun, Case, <<>>). + token(Data, Fun, cs, <<>>). -spec token(binary(), fun(), ci | cs, binary()) -> any(). token(<<>>, Fun, _Case, Acc) -> @@ -108,6 +268,48 @@ token(<< C, Rest/bits >>, Fun, Case = ci, Acc) -> token(<< C, Rest/bits >>, Fun, Case, Acc) -> token(Rest, Fun, Case, << Acc/binary, C >>). +%% @doc Parse a quoted string. +-spec quoted_string(binary(), fun()) -> any(). +quoted_string(<< $", Rest/bits >>, Fun) -> + quoted_string(Rest, Fun, <<>>). + +-spec quoted_string(binary(), fun(), binary()) -> any(). +quoted_string(<<>>, _Fun, _Acc) -> + {error, badarg}; +quoted_string(<< $", Rest/bits >>, Fun, Acc) -> + Fun(Rest, Acc); +quoted_string(<< $\\, C, Rest/bits >>, Fun, Acc) -> + quoted_string(Rest, Fun, << Acc/binary, C >>); +quoted_string(<< C, Rest/bits >>, Fun, Acc) -> + quoted_string(Rest, Fun, << Acc/binary, C >>). + +%% @doc Parse a quality value. +-spec qvalue(binary(), fun()) -> any(). +qvalue(<< $0, $., Rest/bits >>, Fun) -> + qvalue(Rest, Fun, 0, 100); +qvalue(<< $0, Rest/bits >>, Fun) -> + Fun(Rest, 0); +qvalue(<< $1, $., $0, $0, $0, Rest/bits >>, Fun) -> + Fun(Rest, 1000); +qvalue(<< $1, $., $0, $0, Rest/bits >>, Fun) -> + Fun(Rest, 1000); +qvalue(<< $1, $., $0, Rest/bits >>, Fun) -> + Fun(Rest, 1000); +qvalue(<< $1, Rest/bits >>, Fun) -> + Fun(Rest, 1000); +qvalue(_Data, _Fun) -> + {error, badarg}. + +-spec qvalue(binary(), fun(), integer(), 1 | 10 | 100) -> any(). +qvalue(Data, Fun, Q, 0) -> + Fun(Data, Q); +qvalue(<< C, Rest/bits >>, Fun, Q, M) + when C =:= $0; C =:= $1; C =:= $2; C =:= $3; C =:= $4; + C =:= $5; C =:= $6; C =:= $7; C =:= $8; C =:= $9 -> + qvalue(Rest, Fun, Q + (C - $0) * M, M div 10); +qvalue(Data, Fun, Q, _M) -> + Fun(Data, Q). + %% Interpretation. %% @doc Walk through a tokens list and return whether @@ -128,6 +330,17 @@ connection_to_atom([_Any|Tail]) -> -ifdef(TEST). +nonempty_charset_list_test_() -> + %% {Value, Result} + Tests = [ + {<<>>, {error, badarg}}, + {<<"iso-8859-5, unicode-1-1;q=0.8">>, [ + {<<"iso-8859-5">>, 1000}, + {<<"unicode-1-1">>, 800} + ]} + ], + [{V, fun() -> R = nonempty_list(V, fun charset/2) end} || {V, R} <- Tests]. + nonempty_token_list_test_() -> %% {Value, Result} Tests = [ @@ -144,6 +357,44 @@ nonempty_token_list_test_() -> ], [{V, fun() -> R = nonempty_list(V, fun token/2) end} || {V, R} <- Tests]. +media_range_list_test_() -> + %% {Tokens, Result} + Tests = [ + {<<"audio/*; q=0.2, audio/basic">>, [ + {{<<"audio">>, <<"*">>, []}, 200, []}, + {{<<"audio">>, <<"basic">>, []}, 1000, []} + ]}, + {<<"text/plain; q=0.5, text/html, " + "text/x-dvi; q=0.8, text/x-c">>, [ + {{<<"text">>, <<"plain">>, []}, 500, []}, + {{<<"text">>, <<"html">>, []}, 1000, []}, + {{<<"text">>, <<"x-dvi">>, []}, 800, []}, + {{<<"text">>, <<"x-c">>, []}, 1000, []} + ]}, + {<<"text/*, text/html, text/html;level=1, */*">>, [ + {{<<"text">>, <<"*">>, []}, 1000, []}, + {{<<"text">>, <<"html">>, []}, 1000, []}, + {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []}, + {{<<"*">>, <<"*">>, []}, 1000, []} + ]}, + {<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, " + "text/html;level=2;q=0.4, */*;q=0.5">>, [ + {{<<"text">>, <<"*">>, []}, 300, []}, + {{<<"text">>, <<"html">>, []}, 700, []}, + {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []}, + {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []}, + {{<<"*">>, <<"*">>, []}, 500, []} + ]}, + {<<"text/html;level=1;quoted=\"hi hi hi\";" + "q=0.123;standalone;complex=gits, text/plain">>, [ + {{<<"text">>, <<"html">>, + [{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123, + [<<"standalone">>, {<<"complex">>, <<"gits">>}]}, + {{<<"text">>, <<"plain">>, []}, 1000, []} + ]} + ], + [{V, fun() -> R = list(V, fun media_range/2) end} || {V, R} <- Tests]. + connection_to_atom_test_() -> %% {Tokens, Result} Tests = [ diff --git a/src/cowboy_http_protocol.erl b/src/cowboy_http_protocol.erl index b91101a..b0ee590 100644 --- a/src/cowboy_http_protocol.erl +++ b/src/cowboy_http_protocol.erl @@ -172,7 +172,7 @@ header({http_header, _I, 'Host', _R, _V}, Req, State) -> header({http_header, _I, 'Connection', _R, Connection}, Req=#http_req{headers=Headers}, State) -> Req2 = Req#http_req{headers=[{'Connection', Connection}|Headers]}, - {tokens, ConnTokens, Req3} + {ConnTokens, Req3} = cowboy_http_req:parse_header('Connection', Req2), ConnAtom = cowboy_http:connection_to_atom(ConnTokens), parse_header(Req3#http_req{connection=ConnAtom}, State); diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl index 539c7f0..3a3dd2f 100644 --- a/src/cowboy_http_req.erl +++ b/src/cowboy_http_req.erl @@ -191,47 +191,62 @@ headers(Req) -> %% returned is used as a return value. %% @see parse_header/3 -spec parse_header(http_header(), #http_req{}) - -> {tokens, [binary()], #http_req{}} - | {undefined, binary(), #http_req{}} - | {error, badarg}. -parse_header('Connection', Req) -> - parse_header('Connection', Req, []); -parse_header(Name, Req) -> - parse_header(Name, Req, undefined). + -> {any(), #http_req{}} | {error, badarg}. +parse_header(Name, Req=#http_req{p_headers=PHeaders}) -> + case lists:keyfind(Name, 1, PHeaders) of + false -> parse_header(Name, Req, parse_header_default(Name)); + {Name, Value} -> {Value, Req} + end. + +%% @doc Default values for semantic header parsing. +-spec parse_header_default(http_header()) -> any(). +parse_header_default('Accept') -> []; +parse_header_default('Accept-Charset') -> []; +parse_header_default('Accept-Encoding') -> []; +parse_header_default('Connection') -> []; +parse_header_default(_Name) -> undefined. %% @doc Semantically parse headers. %% -%% When the header is known, a named tuple is returned containing -%% {Type, P, Req} with Type being the type of value found in P. -%% For example, the header 'Connection' is a list of tokens, therefore -%% the value returned will be a list of binary values and Type will be -%% 'tokens'. -%% -%% When the header is known but not found, the tuple {Type, Default, Req} -%% is returned instead. -%% -%% When the header is unknown, the value is returned directly as an -%% 'undefined' tagged tuple. +%% When the header is unknown, the value is returned directly without parsing. -spec parse_header(http_header(), #http_req{}, any()) - -> {tokens, [binary()], #http_req{}} - | {undefined, binary(), #http_req{}} - | {error, badarg}. -parse_header(Name, Req=#http_req{p_headers=PHeaders}, Default) - when Name =:= 'Connection' -> + -> {any(), #http_req{}} | {error, badarg}. +parse_header(Name, Req, Default) when Name =:= 'Accept' -> + parse_header(Name, Req, Default, + fun (Value) -> + cowboy_http:list(Value, fun cowboy_http:media_range/2) + end); +parse_header(Name, Req, Default) when Name =:= 'Accept-Charset' -> + parse_header(Name, Req, Default, + fun (Value) -> + cowboy_http:nonempty_list(Value, fun cowboy_http:charset/2) + end); +parse_header(Name, Req, Default) when Name =:= 'Accept-Encoding' -> + parse_header(Name, Req, Default, + fun (Value) -> + cowboy_http:list(Value, fun cowboy_http:token_ci/2) + end); +parse_header(Name, Req, Default) when Name =:= 'Connection' -> + parse_header(Name, Req, Default, + fun (Value) -> + cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) + end); +parse_header(Name, Req, Default) -> + {Value, Req2} = header(Name, Req, Default), + {undefined, Value, Req2}. + +parse_header(Name, Req=#http_req{p_headers=PHeaders}, Default, Fun) -> case header(Name, Req) of - {undefined, Req2} -> {tokens, Default, Req2}; + {undefined, Req2} -> + {Default, Req2#http_req{p_headers=[{Name, Default}|PHeaders]}}; {Value, Req2} -> - case cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) of + case Fun(Value) of {error, badarg} -> {error, badarg}; P -> - {tokens, P, Req2#http_req{ - p_headers=[{Name, P}|PHeaders]}} + {P, Req2#http_req{p_headers=[{Name, P}|PHeaders]}} end - end; -parse_header(Name, Req, Default) -> - {Value, Req2} = header(Name, Req, Default), - {undefined, Value, Req2}. + end. %% @equiv cookie(Name, Req, undefined) -spec cookie(binary(), #http_req{}) @@ -368,7 +383,7 @@ chunked_reply(Status, Headers, Req=#http_req{socket=Socket, transport=Transport, %% @doc Send a chunk of data. %% %% A chunked reply must have been initiated before calling this function. --spec chunk(iodata(), #http_req{}) -> ok. +-spec chunk(iodata(), #http_req{}) -> ok | {error, atom()}. chunk(_Data, #http_req{socket=_Socket, transport=_Transport, method='HEAD'}) -> ok; chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) -> diff --git a/src/cowboy_http_websocket.erl b/src/cowboy_http_websocket.erl index d378a2a..08a0c90 100644 --- a/src/cowboy_http_websocket.erl +++ b/src/cowboy_http_websocket.erl @@ -77,7 +77,7 @@ upgrade(ListenerPid, Handler, Opts, Req) -> %% @todo Upgrade is a list of products and should be parsed as such. -spec websocket_upgrade(#state{}, #http_req{}) -> {ok, #state{}, #http_req{}}. websocket_upgrade(State, Req) -> - {tokens, ConnTokens, Req2} + {ConnTokens, Req2} = cowboy_http_req:parse_header('Connection', Req), true = lists:member(<<"upgrade">>, ConnTokens), {WS, Req3} = cowboy_http_req:header('Upgrade', Req2), @@ -378,11 +378,11 @@ handler_call(State=#state{handler=Handler, opts=Opts}, Req, HandlerState, websocket_close(State, Req2, HandlerState2, {normal, shutdown}) catch Class:Reason -> error_logger:error_msg( - "** Handler ~p terminating in websocket_handle/3~n" + "** Handler ~p terminating in ~p/3~n" " for the reason ~p:~p~n** Message was ~p~n" "** Options were ~p~n** Handler state was ~p~n" "** Request was ~p~n** Stacktrace: ~p~n~n", - [Handler, Class, Reason, Message, Opts, + [Handler, Callback, Class, Reason, Message, Opts, HandlerState, Req, erlang:get_stacktrace()]), websocket_close(State, Req, HandlerState, {error, handler}) end. diff --git a/src/cowboy_listener.erl b/src/cowboy_listener.erl index 8b656ba..c19d079 100644 --- a/src/cowboy_listener.erl +++ b/src/cowboy_listener.erl @@ -30,9 +30,15 @@ %% API. %% @private +%% +%% We set the process priority to high because cowboy_listener is the central +%% gen_server in Cowboy and is used to manage all the incoming connections. +%% Setting the process priority to high ensures the connection-related code +%% will always be executed when a connection needs it, allowing Cowboy to +%% scale far beyond what it would with a normal priority. -spec start_link() -> {ok, pid()}. start_link() -> - gen_server:start_link(?MODULE, [], []). + gen_server:start_link(?MODULE, [], [{spawn_opt, [{priority, high}]}]). %% @private -spec stop(pid()) -> stopped. diff --git a/src/cowboy_listener_sup.erl b/src/cowboy_listener_sup.erl index adf5262..aca2b0b 100644 --- a/src/cowboy_listener_sup.erl +++ b/src/cowboy_listener_sup.erl @@ -27,7 +27,7 @@ start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) -> {ok, SupPid} = supervisor:start_link(?MODULE, []), {ok, ListenerPid} = supervisor:start_child(SupPid, {cowboy_listener, {cowboy_listener, start_link, []}, - permanent, 5000, worker, dynamic}), + permanent, 5000, worker, [cowboy_listener]}), {ok, ReqsPid} = supervisor:start_child(SupPid, {cowboy_requests_sup, {cowboy_requests_sup, start_link, []}, permanent, 5000, supervisor, [cowboy_requests_sup]}), diff --git a/src/cowboy_sup.erl b/src/cowboy_sup.erl index 9c52486..34591bc 100644 --- a/src/cowboy_sup.erl +++ b/src/cowboy_sup.erl @@ -32,5 +32,5 @@ start_link() -> -spec init([]) -> {ok, {{one_for_one, 10, 10}, [{_, _, _, _, _, _}, ...]}}. init([]) -> Procs = [{cowboy_clock, {cowboy_clock, start_link, []}, - permanent, 5000, worker, dynamic}], + permanent, 5000, worker, [cowboy_clock]}], {ok, {{one_for_one, 10, 10}, Procs}}. |