diff options
Diffstat (limited to 'server/_build/default/lib/ranch/src')
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch.erl | 504 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_acceptor.erl | 64 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_acceptors_sup.erl | 71 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_app.erl | 44 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_conns_sup.erl | 325 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_crc32c.erl | 115 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_listener_sup.erl | 41 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_protocol.erl | 24 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_proxy_header.erl | 880 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_server.erl | 233 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_ssl.erl | 243 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_sup.erl | 40 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_tcp.erl | 245 | ||||
-rw-r--r-- | server/_build/default/lib/ranch/src/ranch_transport.erl | 151 |
14 files changed, 2980 insertions, 0 deletions
diff --git a/server/_build/default/lib/ranch/src/ranch.erl b/server/_build/default/lib/ranch/src/ranch.erl new file mode 100644 index 0000000..814e928 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch.erl @@ -0,0 +1,504 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch). + +-export([start_listener/5]). +-export([start_listener/6]). +-export([normalize_opts/1]). +-export([stop_listener/1]). +-export([suspend_listener/1]). +-export([resume_listener/1]). +-export([child_spec/5]). +-export([child_spec/6]). +-export([accept_ack/1]). +-export([handshake/1]). +-export([handshake/2]). +-export([recv_proxy_header/2]). +-export([remove_connection/1]). +-export([get_status/1]). +-export([get_addr/1]). +-export([get_port/1]). +-export([get_max_connections/1]). +-export([set_max_connections/2]). +-export([get_transport_options/1]). +-export([set_transport_options/2]). +-export([get_protocol_options/1]). +-export([set_protocol_options/2]). +-export([info/0]). +-export([info/1]). +-export([procs/2]). +-export([wait_for_connections/3]). +-export([wait_for_connections/4]). +-export([filter_options/3]). +-export([set_option_default/3]). +-export([require/1]). +-export([log/4]). + +-deprecated([start_listener/6, child_spec/6, accept_ack/1]). + +-type max_conns() :: non_neg_integer() | infinity. +-export_type([max_conns/0]). + +%% This type is deprecated and will be removed in Ranch 2.0. +-type opt() :: {ack_timeout, timeout()} + | {connection_type, worker | supervisor} + | {max_connections, max_conns()} + | {num_acceptors, pos_integer()} + | {shutdown, timeout() | brutal_kill} + | {socket, any()}. +-export_type([opt/0]). + +-type opts() :: any() | #{ + connection_type => worker | supervisor, + handshake_timeout => timeout(), + max_connections => max_conns(), + logger => module(), + num_acceptors => pos_integer(), + shutdown => timeout() | brutal_kill, + socket => any(), + socket_opts => any() +}. +-export_type([opts/0]). + +-type ref() :: any(). +-export_type([ref/0]). + +-spec start_listener(ref(), module(), opts(), module(), any()) + -> supervisor:startchild_ret(). +start_listener(Ref, Transport, TransOpts0, Protocol, ProtoOpts) + when is_atom(Transport), is_atom(Protocol) -> + TransOpts = normalize_opts(TransOpts0), + _ = code:ensure_loaded(Transport), + case erlang:function_exported(Transport, name, 0) of + false -> + {error, badarg}; + true -> + Res = supervisor:start_child(ranch_sup, child_spec(Ref, + Transport, TransOpts, Protocol, ProtoOpts)), + Socket = maps:get(socket, TransOpts, undefined), + case Res of + {ok, Pid} when Socket =/= undefined -> + %% Give ownership of the socket to ranch_acceptors_sup + %% to make sure the socket stays open as long as the + %% listener is alive. If the socket closes however there + %% will be no way to recover because we don't know how + %% to open it again. + Children = supervisor:which_children(Pid), + {_, AcceptorsSup, _, _} + = lists:keyfind(ranch_acceptors_sup, 1, Children), + Transport:controlling_process(Socket, AcceptorsSup); + _ -> + ok + end, + maybe_started(Res) + end. + +-spec start_listener(ref(), non_neg_integer(), module(), opts(), module(), any()) + -> supervisor:startchild_ret(). +start_listener(Ref, NumAcceptors, Transport, TransOpts0, Protocol, ProtoOpts) + when is_integer(NumAcceptors), is_atom(Transport), is_atom(Protocol) -> + TransOpts = normalize_opts(TransOpts0), + start_listener(Ref, Transport, TransOpts#{num_acceptors => NumAcceptors}, + Protocol, ProtoOpts). + +-spec normalize_opts(opts()) -> opts(). +normalize_opts(Map) when is_map(Map) -> + Map; +normalize_opts(List0) when is_list(List0) -> + Map0 = #{}, + {Map1, List1} = case take(ack_timeout, List0) of + {value, HandshakeTimeout, Tail0} -> + {Map0#{handshake_timeout => HandshakeTimeout}, Tail0}; + false -> + {Map0, List0} + end, + {Map, List} = lists:foldl(fun(Key, {Map2, List2}) -> + case take(Key, List2) of + {value, ConnectionType, Tail2} -> + {Map2#{Key => ConnectionType}, Tail2}; + false -> + {Map2, List2} + end + end, {Map1, List1}, [connection_type, max_connections, num_acceptors, shutdown, socket]), + if + Map =:= #{} -> + ok; + true -> + log(warning, + "Setting Ranch options together with socket options " + "is deprecated. Please use the new map syntax that allows " + "specifying socket options separately from other options.~n", + [], Map) + end, + case List of + [] -> Map; + _ -> Map#{socket_opts => List} + end; +normalize_opts(Any) -> + #{socket_opts => Any}. + +take(Key, List) -> + take(Key, List, []). + +take(_, [], _) -> + false; +take(Key, [{Key, Value}|Tail], Acc) -> + {value, Value, lists:reverse(Acc, Tail)}; +take(Key, [Value|Tail], Acc) -> + take(Key, Tail, [Value|Acc]). + +maybe_started({error, {{shutdown, + {failed_to_start_child, ranch_acceptors_sup, + {listen_error, _, Reason}}}, _}} = Error) -> + start_error(Reason, Error); +maybe_started(Res) -> + Res. + +start_error(E=eaddrinuse, _) -> {error, E}; +start_error(E=eacces, _) -> {error, E}; +start_error(E=no_cert, _) -> {error, E}; +start_error(_, Error) -> Error. + +-spec stop_listener(ref()) -> ok | {error, not_found}. +stop_listener(Ref) -> + case supervisor:terminate_child(ranch_sup, {ranch_listener_sup, Ref}) of + ok -> + _ = supervisor:delete_child(ranch_sup, {ranch_listener_sup, Ref}), + ranch_server:cleanup_listener_opts(Ref); + {error, Reason} -> + {error, Reason} + end. + +-spec suspend_listener(ref()) -> ok | {error, any()}. +suspend_listener(Ref) -> + case get_status(Ref) of + running -> + ListenerSup = ranch_server:get_listener_sup(Ref), + ok = ranch_server:set_addr(Ref, {undefined, undefined}), + supervisor:terminate_child(ListenerSup, ranch_acceptors_sup); + suspended -> + ok + end. + +-spec resume_listener(ref()) -> ok | {error, any()}. +resume_listener(Ref) -> + case get_status(Ref) of + running -> + ok; + suspended -> + ListenerSup = ranch_server:get_listener_sup(Ref), + Res = supervisor:restart_child(ListenerSup, ranch_acceptors_sup), + maybe_resumed(Res) + end. + +maybe_resumed(Error={error, {listen_error, _, Reason}}) -> + start_error(Reason, Error); +maybe_resumed({ok, _}) -> + ok; +maybe_resumed({ok, _, _}) -> + ok; +maybe_resumed(Res) -> + Res. + +-spec child_spec(ref(), module(), opts(), module(), any()) + -> supervisor:child_spec(). +child_spec(Ref, Transport, TransOpts0, Protocol, ProtoOpts) -> + TransOpts = normalize_opts(TransOpts0), + {{ranch_listener_sup, Ref}, {ranch_listener_sup, start_link, [ + Ref, Transport, TransOpts, Protocol, ProtoOpts + ]}, permanent, infinity, supervisor, [ranch_listener_sup]}. + +-spec child_spec(ref(), non_neg_integer(), module(), opts(), module(), any()) + -> supervisor:child_spec(). +child_spec(Ref, NumAcceptors, Transport, TransOpts0, Protocol, ProtoOpts) + when is_integer(NumAcceptors), is_atom(Transport), is_atom(Protocol) -> + TransOpts = normalize_opts(TransOpts0), + child_spec(Ref, Transport, TransOpts#{num_acceptors => NumAcceptors}, + Protocol, ProtoOpts). + +-spec accept_ack(ref()) -> ok. +accept_ack(Ref) -> + {ok, _} = handshake(Ref), + ok. + +-spec handshake(ref()) -> {ok, ranch_transport:socket()}. +handshake(Ref) -> + handshake(Ref, []). + +-spec handshake(ref(), any()) -> {ok, ranch_transport:socket()}. +handshake(Ref, Opts) -> + receive {handshake, Ref, Transport, CSocket, HandshakeTimeout} -> + case Transport:handshake(CSocket, Opts, HandshakeTimeout) of + OK = {ok, _} -> + OK; + %% Garbage was most likely sent to the socket, don't error out. + {error, {tls_alert, _}} -> + ok = Transport:close(CSocket), + exit(normal); + %% Socket most likely stopped responding, don't error out. + {error, Reason} when Reason =:= timeout; Reason =:= closed -> + ok = Transport:close(CSocket), + exit(normal); + {error, Reason} -> + ok = Transport:close(CSocket), + error(Reason) + end + end. + +%% Unlike handshake/2 this function always return errors because +%% the communication between the proxy and the server are expected +%% to be reliable. If there is a problem while receiving the proxy +%% header, we probably want to know about it. +-spec recv_proxy_header(ref(), timeout()) + -> {ok, ranch_proxy_header:proxy_info()} + | {error, closed | atom()} + | {error, protocol_error, atom()}. +recv_proxy_header(Ref, Timeout) -> + receive HandshakeState={handshake, Ref, Transport, CSocket, _} -> + self() ! HandshakeState, + Transport:recv_proxy_header(CSocket, Timeout) + end. + +-spec remove_connection(ref()) -> ok. +remove_connection(Ref) -> + ConnsSup = ranch_server:get_connections_sup(Ref), + ConnsSup ! {remove_connection, Ref, self()}, + ok. + +-spec get_status(ref()) -> running | suspended. +get_status(Ref) -> + ListenerSup = ranch_server:get_listener_sup(Ref), + Children = supervisor:which_children(ListenerSup), + case lists:keyfind(ranch_acceptors_sup, 1, Children) of + {_, undefined, _, _} -> + suspended; + _ -> + running + end. + +-spec get_addr(ref()) -> {inet:ip_address(), inet:port_number()} | {undefined, undefined}. +get_addr(Ref) -> + ranch_server:get_addr(Ref). + +-spec get_port(ref()) -> inet:port_number() | undefined. +get_port(Ref) -> + {_, Port} = get_addr(Ref), + Port. + +-spec get_max_connections(ref()) -> max_conns(). +get_max_connections(Ref) -> + ranch_server:get_max_connections(Ref). + +-spec set_max_connections(ref(), max_conns()) -> ok. +set_max_connections(Ref, MaxConnections) -> + ranch_server:set_max_connections(Ref, MaxConnections). + +-spec get_transport_options(ref()) -> any(). +get_transport_options(Ref) -> + ranch_server:get_transport_options(Ref). + +-spec set_transport_options(ref(), opts()) -> ok | {error, running}. +set_transport_options(Ref, TransOpts0) -> + TransOpts = normalize_opts(TransOpts0), + case get_status(Ref) of + suspended -> + ok = ranch_server:set_transport_options(Ref, TransOpts); + running -> + {error, running} + end. + +-spec get_protocol_options(ref()) -> opts(). +get_protocol_options(Ref) -> + ranch_server:get_protocol_options(Ref). + +-spec set_protocol_options(ref(), any()) -> ok. +set_protocol_options(Ref, Opts) -> + ranch_server:set_protocol_options(Ref, Opts). + +-spec info() -> [{any(), [{atom(), any()}]}]. +info() -> + [{Ref, listener_info(Ref, Pid)} + || {Ref, Pid} <- ranch_server:get_listener_sups()]. + +-spec info(ref()) -> [{atom(), any()}]. +info(Ref) -> + Pid = ranch_server:get_listener_sup(Ref), + listener_info(Ref, Pid). + +listener_info(Ref, Pid) -> + [_, Transport, _, Protocol, _] = ranch_server:get_listener_start_args(Ref), + ConnsSup = ranch_server:get_connections_sup(Ref), + Status = get_status(Ref), + {IP, Port} = get_addr(Ref), + MaxConns = get_max_connections(Ref), + TransOpts = ranch_server:get_transport_options(Ref), + ProtoOpts = get_protocol_options(Ref), + [ + {pid, Pid}, + {status, Status}, + {ip, IP}, + {port, Port}, + {max_connections, MaxConns}, + {active_connections, ranch_conns_sup:active_connections(ConnsSup)}, + {all_connections, proplists:get_value(active, supervisor:count_children(ConnsSup))}, + {transport, Transport}, + {transport_options, TransOpts}, + {protocol, Protocol}, + {protocol_options, ProtoOpts} + ]. + +-spec procs(ref(), acceptors | connections) -> [pid()]. +procs(Ref, acceptors) -> + procs1(Ref, ranch_acceptors_sup); +procs(Ref, connections) -> + procs1(Ref, ranch_conns_sup). + +procs1(Ref, Sup) -> + ListenerSup = ranch_server:get_listener_sup(Ref), + {_, SupPid, _, _} = lists:keyfind(Sup, 1, + supervisor:which_children(ListenerSup)), + try + [Pid || {_, Pid, _, _} <- supervisor:which_children(SupPid)] + catch exit:{noproc, _} when Sup =:= ranch_acceptors_sup -> + [] + end. + +-spec wait_for_connections + (ref(), '>' | '>=' | '==' | '=<', non_neg_integer()) -> ok; + (ref(), '<', pos_integer()) -> ok. +wait_for_connections(Ref, Op, NumConns) -> + wait_for_connections(Ref, Op, NumConns, 1000). + +-spec wait_for_connections + (ref(), '>' | '>=' | '==' | '=<', non_neg_integer(), non_neg_integer()) -> ok; + (ref(), '<', pos_integer(), non_neg_integer()) -> ok. +wait_for_connections(Ref, Op, NumConns, Interval) -> + validate_op(Op, NumConns), + validate_num_conns(NumConns), + validate_interval(Interval), + wait_for_connections_loop(Ref, Op, NumConns, Interval). + +validate_op('>', _) -> ok; +validate_op('>=', _) -> ok; +validate_op('==', _) -> ok; +validate_op('=<', _) -> ok; +validate_op('<', NumConns) when NumConns > 0 -> ok; +validate_op(_, _) -> error(badarg). + +validate_num_conns(NumConns) when is_integer(NumConns), NumConns >= 0 -> ok; +validate_num_conns(_) -> error(badarg). + +validate_interval(Interval) when is_integer(Interval), Interval >= 0 -> ok; +validate_interval(_) -> error(badarg). + +wait_for_connections_loop(Ref, Op, NumConns, Interval) -> + CurConns = try + ConnsSup = ranch_server:get_connections_sup(Ref), + proplists:get_value(active, supervisor:count_children(ConnsSup)) + catch _:_ -> + 0 + end, + case erlang:Op(CurConns, NumConns) of + true -> + ok; + false when Interval =:= 0 -> + wait_for_connections_loop(Ref, Op, NumConns, Interval); + false -> + timer:sleep(Interval), + wait_for_connections_loop(Ref, Op, NumConns, Interval) + end. + +-spec filter_options([inet | inet6 | {atom(), any()} | {raw, any(), any(), any()}], + [atom()], Acc) -> Acc when Acc :: [any()]. +filter_options(UserOptions, DisallowedKeys, DefaultOptions) -> + AllowedOptions = filter_user_options(UserOptions, DisallowedKeys), + lists:foldl(fun merge_options/2, DefaultOptions, AllowedOptions). + +%% 2-tuple options. +filter_user_options([Opt = {Key, _}|Tail], DisallowedKeys) -> + case lists:member(Key, DisallowedKeys) of + false -> + [Opt|filter_user_options(Tail, DisallowedKeys)]; + true -> + filter_options_warning(Opt), + filter_user_options(Tail, DisallowedKeys) + end; +%% Special option forms. +filter_user_options([inet|Tail], DisallowedKeys) -> + [inet|filter_user_options(Tail, DisallowedKeys)]; +filter_user_options([inet6|Tail], DisallowedKeys) -> + [inet6|filter_user_options(Tail, DisallowedKeys)]; +filter_user_options([Opt = {raw, _, _, _}|Tail], DisallowedKeys) -> + [Opt|filter_user_options(Tail, DisallowedKeys)]; +filter_user_options([Opt|Tail], DisallowedKeys) -> + filter_options_warning(Opt), + filter_user_options(Tail, DisallowedKeys); +filter_user_options([], _) -> + []. + +filter_options_warning(Opt) -> + Logger = case get(logger) of + undefined -> error_logger; + Logger0 -> Logger0 + end, + log(warning, + "Transport option ~p unknown or invalid.~n", + [Opt], Logger). + +merge_options({Key, _} = Option, OptionList) -> + lists:keystore(Key, 1, OptionList, Option); +merge_options(Option, OptionList) -> + [Option|OptionList]. + +-spec set_option_default(Opts, atom(), any()) + -> Opts when Opts :: [{atom(), any()}]. +set_option_default(Opts, Key, Value) -> + case lists:keymember(Key, 1, Opts) of + true -> Opts; + false -> [{Key, Value}|Opts] + end. + +-spec require([atom()]) -> ok. +require([]) -> + ok; +require([App|Tail]) -> + case application:start(App) of + ok -> ok; + {error, {already_started, App}} -> ok + end, + require(Tail). + +-spec log(logger:level(), io:format(), list(), module() | #{logger => module()}) -> ok. +log(Level, Format, Args, Logger) when is_atom(Logger) -> + log(Level, Format, Args, #{logger => Logger}); +log(Level, Format, Args, #{logger := Logger}) + when Logger =/= error_logger -> + _ = Logger:Level(Format, Args), + ok; +%% We use error_logger by default. Because error_logger does +%% not have all the levels we accept we have to do some +%% mapping to error_logger functions. +log(Level, Format, Args, _) -> + Function = case Level of + emergency -> error_msg; + alert -> error_msg; + critical -> error_msg; + error -> error_msg; + warning -> warning_msg; + notice -> warning_msg; + info -> info_msg; + debug -> info_msg + end, + error_logger:Function(Format, Args). diff --git a/server/_build/default/lib/ranch/src/ranch_acceptor.erl b/server/_build/default/lib/ranch/src/ranch_acceptor.erl new file mode 100644 index 0000000..3e426bd --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_acceptor.erl @@ -0,0 +1,64 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_acceptor). + +-export([start_link/4]). +-export([loop/4]). + +-spec start_link(inet:socket(), module(), module(), pid()) + -> {ok, pid()}. +start_link(LSocket, Transport, Logger, ConnsSup) -> + Pid = spawn_link(?MODULE, loop, [LSocket, Transport, Logger, ConnsSup]), + {ok, Pid}. + +-spec loop(inet:socket(), module(), module(), pid()) -> no_return(). +loop(LSocket, Transport, Logger, ConnsSup) -> + _ = case Transport:accept(LSocket, infinity) of + {ok, CSocket} -> + case Transport:controlling_process(CSocket, ConnsSup) of + ok -> + %% This call will not return until process has been started + %% AND we are below the maximum number of connections. + ranch_conns_sup:start_protocol(ConnsSup, CSocket); + {error, _} -> + Transport:close(CSocket) + end; + %% Reduce the accept rate if we run out of file descriptors. + %% We can't accept anymore anyway, so we might as well wait + %% a little for the situation to resolve itself. + {error, emfile} -> + ranch:log(warning, + "Ranch acceptor reducing accept rate: out of file descriptors~n", + [], Logger), + receive after 100 -> ok end; + %% Exit if the listening socket got closed. + {error, closed} -> + exit(closed); + %% Continue otherwise. + {error, _} -> + ok + end, + flush(Logger), + ?MODULE:loop(LSocket, Transport, Logger, ConnsSup). + +flush(Logger) -> + receive Msg -> + ranch:log(warning, + "Ranch acceptor received unexpected message: ~p~n", + [Msg], Logger), + flush(Logger) + after 0 -> + ok + end. diff --git a/server/_build/default/lib/ranch/src/ranch_acceptors_sup.erl b/server/_build/default/lib/ranch/src/ranch_acceptors_sup.erl new file mode 100644 index 0000000..73dc9ea --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_acceptors_sup.erl @@ -0,0 +1,71 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_acceptors_sup). +-behaviour(supervisor). + +-export([start_link/2]). +-export([init/1]). + +-spec start_link(ranch:ref(), module()) + -> {ok, pid()}. +start_link(Ref, Transport) -> + supervisor:start_link(?MODULE, [Ref, Transport]). + +init([Ref, Transport]) -> + ConnsSup = ranch_server:get_connections_sup(Ref), + TransOpts = ranch_server:get_transport_options(Ref), + NumAcceptors = maps:get(num_acceptors, TransOpts, 10), + Logger = maps:get(logger, TransOpts, error_logger), + LSocket = case maps:get(socket, TransOpts, undefined) of + undefined -> + SocketOpts = maps:get(socket_opts, TransOpts, []), + %% We temporarily put the logger in the process dictionary + %% so that it can be used from ranch:filter_options. The + %% interface as it currently is does not allow passing it + %% down otherwise. + put(logger, Logger), + case Transport:listen(SocketOpts) of + {ok, Socket} -> + erase(logger), + Socket; + {error, Reason} -> + listen_error(Ref, Transport, SocketOpts, Reason, Logger) + end; + Socket -> + Socket + end, + {ok, Addr} = Transport:sockname(LSocket), + ranch_server:set_addr(Ref, Addr), + Procs = [ + {{acceptor, self(), N}, {ranch_acceptor, start_link, [ + LSocket, Transport, Logger, ConnsSup + ]}, permanent, brutal_kill, worker, []} + || N <- lists:seq(1, NumAcceptors)], + {ok, {{one_for_one, 1, 5}, Procs}}. + +-spec listen_error(any(), module(), any(), atom(), module()) -> no_return(). +listen_error(Ref, Transport, SocketOpts0, Reason, Logger) -> + SocketOpts1 = [{cert, '...'}|proplists:delete(cert, SocketOpts0)], + SocketOpts2 = [{key, '...'}|proplists:delete(key, SocketOpts1)], + SocketOpts = [{cacerts, '...'}|proplists:delete(cacerts, SocketOpts2)], + ranch:log(error, + "Failed to start Ranch listener ~p in ~p:listen(~999999p) for reason ~p (~s)~n", + [Ref, Transport, SocketOpts, Reason, format_error(Reason)], Logger), + exit({listen_error, Ref, Reason}). + +format_error(no_cert) -> + "no certificate provided; see cert, certfile, sni_fun or sni_hosts options"; +format_error(Reason) -> + inet:format_error(Reason). diff --git a/server/_build/default/lib/ranch/src/ranch_app.erl b/server/_build/default/lib/ranch/src/ranch_app.erl new file mode 100644 index 0000000..8ac470e --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_app.erl @@ -0,0 +1,44 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_app). +-behaviour(application). + +-export([start/2]). +-export([stop/1]). +-export([profile_output/0]). + +start(_, _) -> + _ = consider_profiling(), + ranch_sup:start_link(). + +stop(_) -> + ok. + +-spec profile_output() -> ok. +profile_output() -> + eprof:stop_profiling(), + eprof:log("procs.profile"), + eprof:analyze(procs), + eprof:log("total.profile"), + eprof:analyze(total). + +consider_profiling() -> + case application:get_env(profile) of + {ok, true} -> + {ok, _Pid} = eprof:start(), + eprof:start_profiling([self()]); + _ -> + not_profiling + end. diff --git a/server/_build/default/lib/ranch/src/ranch_conns_sup.erl b/server/_build/default/lib/ranch/src/ranch_conns_sup.erl new file mode 100644 index 0000000..fe2237a --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_conns_sup.erl @@ -0,0 +1,325 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +%% Make sure to never reload this module outside a release upgrade, +%% as calling l(ranch_conns_sup) twice will kill the process and all +%% the currently open connections. +-module(ranch_conns_sup). + +%% API. +-export([start_link/3]). +-export([start_protocol/2]). +-export([active_connections/1]). + +%% Supervisor internals. +-export([init/4]). +-export([system_continue/3]). +-export([system_terminate/4]). +-export([system_code_change/4]). + +-type conn_type() :: worker | supervisor. +-type shutdown() :: brutal_kill | timeout(). + +-record(state, { + parent = undefined :: pid(), + ref :: ranch:ref(), + conn_type :: conn_type(), + shutdown :: shutdown(), + transport = undefined :: module(), + protocol = undefined :: module(), + opts :: any(), + handshake_timeout :: timeout(), + max_conns = undefined :: ranch:max_conns(), + logger = undefined :: module() +}). + +%% API. + +-spec start_link(ranch:ref(), module(), module()) -> {ok, pid()}. +start_link(Ref, Transport, Protocol) -> + proc_lib:start_link(?MODULE, init, + [self(), Ref, Transport, Protocol]). + +%% We can safely assume we are on the same node as the supervisor. +%% +%% We can also safely avoid having a monitor and a timeout here +%% because only three things can happen: +%% * The supervisor died; rest_for_one strategy killed all acceptors +%% so this very calling process is going to di-- +%% * There's too many connections, the supervisor will resume the +%% acceptor only when we get below the limit again. +%% * The supervisor is overloaded, there's either too many acceptors +%% or the max_connections limit is too large. It's better if we +%% don't keep accepting connections because this leaves +%% more room for the situation to be resolved. +%% +%% We do not need the reply, we only need the ok from the supervisor +%% to continue. The supervisor sends its own pid when the acceptor can +%% continue. +-spec start_protocol(pid(), inet:socket()) -> ok. +start_protocol(SupPid, Socket) -> + SupPid ! {?MODULE, start_protocol, self(), Socket}, + receive SupPid -> ok end. + +%% We can't make the above assumptions here. This function might be +%% called from anywhere. +-spec active_connections(pid()) -> non_neg_integer(). +active_connections(SupPid) -> + Tag = erlang:monitor(process, SupPid), + catch erlang:send(SupPid, {?MODULE, active_connections, self(), Tag}, + [noconnect]), + receive + {Tag, Ret} -> + erlang:demonitor(Tag, [flush]), + Ret; + {'DOWN', Tag, _, _, noconnection} -> + exit({nodedown, node(SupPid)}); + {'DOWN', Tag, _, _, Reason} -> + exit(Reason) + after 5000 -> + erlang:demonitor(Tag, [flush]), + exit(timeout) + end. + +%% Supervisor internals. + +-spec init(pid(), ranch:ref(), module(), module()) -> no_return(). +init(Parent, Ref, Transport, Protocol) -> + process_flag(trap_exit, true), + ok = ranch_server:set_connections_sup(Ref, self()), + MaxConns = ranch_server:get_max_connections(Ref), + TransOpts = ranch_server:get_transport_options(Ref), + ConnType = maps:get(connection_type, TransOpts, worker), + Shutdown = maps:get(shutdown, TransOpts, 5000), + HandshakeTimeout = maps:get(handshake_timeout, TransOpts, 5000), + Logger = maps:get(logger, TransOpts, error_logger), + ProtoOpts = ranch_server:get_protocol_options(Ref), + ok = proc_lib:init_ack(Parent, {ok, self()}), + loop(#state{parent=Parent, ref=Ref, conn_type=ConnType, + shutdown=Shutdown, transport=Transport, protocol=Protocol, + opts=ProtoOpts, handshake_timeout=HandshakeTimeout, + max_conns=MaxConns, logger=Logger}, 0, 0, []). + +loop(State=#state{parent=Parent, ref=Ref, conn_type=ConnType, + transport=Transport, protocol=Protocol, opts=Opts, + max_conns=MaxConns, logger=Logger}, CurConns, NbChildren, Sleepers) -> + receive + {?MODULE, start_protocol, To, Socket} -> + try Protocol:start_link(Ref, Socket, Transport, Opts) of + {ok, Pid} -> + handshake(State, CurConns, NbChildren, Sleepers, To, Socket, Pid, Pid); + {ok, SupPid, ProtocolPid} when ConnType =:= supervisor -> + handshake(State, CurConns, NbChildren, Sleepers, To, Socket, SupPid, ProtocolPid); + Ret -> + To ! self(), + ranch:log(error, + "Ranch listener ~p connection process start failure; " + "~p:start_link/4 returned: ~999999p~n", + [Ref, Protocol, Ret], Logger), + Transport:close(Socket), + loop(State, CurConns, NbChildren, Sleepers) + catch Class:Reason -> + To ! self(), + ranch:log(error, + "Ranch listener ~p connection process start failure; " + "~p:start_link/4 crashed with reason: ~p:~999999p~n", + [Ref, Protocol, Class, Reason], Logger), + loop(State, CurConns, NbChildren, Sleepers) + end; + {?MODULE, active_connections, To, Tag} -> + To ! {Tag, CurConns}, + loop(State, CurConns, NbChildren, Sleepers); + %% Remove a connection from the count of connections. + {remove_connection, Ref, Pid} -> + case put(Pid, removed) of + active -> + loop(State, CurConns - 1, NbChildren, Sleepers); + remove -> + loop(State, CurConns, NbChildren, Sleepers); + undefined -> + _ = erase(Pid), + loop(State, CurConns, NbChildren, Sleepers) + end; + %% Upgrade the max number of connections allowed concurrently. + %% We resume all sleeping acceptors if this number increases. + {set_max_conns, MaxConns2} when MaxConns2 > MaxConns -> + _ = [To ! self() || To <- Sleepers], + loop(State#state{max_conns=MaxConns2}, + CurConns, NbChildren, []); + {set_max_conns, MaxConns2} -> + loop(State#state{max_conns=MaxConns2}, + CurConns, NbChildren, Sleepers); + %% Upgrade the protocol options. + {set_opts, Opts2} -> + loop(State#state{opts=Opts2}, + CurConns, NbChildren, Sleepers); + {'EXIT', Parent, Reason} -> + terminate(State, Reason, NbChildren); + {'EXIT', Pid, Reason} when Sleepers =:= [] -> + case erase(Pid) of + active -> + report_error(Logger, Ref, Protocol, Pid, Reason), + loop(State, CurConns - 1, NbChildren - 1, Sleepers); + removed -> + report_error(Logger, Ref, Protocol, Pid, Reason), + loop(State, CurConns, NbChildren - 1, Sleepers); + undefined -> + loop(State, CurConns, NbChildren, Sleepers) + end; + %% Resume a sleeping acceptor if needed. + {'EXIT', Pid, Reason} -> + case erase(Pid) of + active when CurConns > MaxConns -> + report_error(Logger, Ref, Protocol, Pid, Reason), + loop(State, CurConns - 1, NbChildren - 1, Sleepers); + active -> + report_error(Logger, Ref, Protocol, Pid, Reason), + [To|Sleepers2] = Sleepers, + To ! self(), + loop(State, CurConns - 1, NbChildren - 1, Sleepers2); + removed -> + report_error(Logger, Ref, Protocol, Pid, Reason), + loop(State, CurConns, NbChildren - 1, Sleepers); + undefined -> + loop(State, CurConns, NbChildren, Sleepers) + end; + {system, From, Request} -> + sys:handle_system_msg(Request, From, Parent, ?MODULE, [], + {State, CurConns, NbChildren, Sleepers}); + %% Calls from the supervisor module. + {'$gen_call', {To, Tag}, which_children} -> + Children = [{Protocol, Pid, ConnType, [Protocol]} + || {Pid, Type} <- get(), + Type =:= active orelse Type =:= removed], + To ! {Tag, Children}, + loop(State, CurConns, NbChildren, Sleepers); + {'$gen_call', {To, Tag}, count_children} -> + Counts = case ConnType of + worker -> [{supervisors, 0}, {workers, NbChildren}]; + supervisor -> [{supervisors, NbChildren}, {workers, 0}] + end, + Counts2 = [{specs, 1}, {active, NbChildren}|Counts], + To ! {Tag, Counts2}, + loop(State, CurConns, NbChildren, Sleepers); + {'$gen_call', {To, Tag}, _} -> + To ! {Tag, {error, ?MODULE}}, + loop(State, CurConns, NbChildren, Sleepers); + Msg -> + ranch:log(error, + "Ranch listener ~p received unexpected message ~p~n", + [Ref, Msg], Logger), + loop(State, CurConns, NbChildren, Sleepers) + end. + +handshake(State=#state{ref=Ref, transport=Transport, handshake_timeout=HandshakeTimeout, + max_conns=MaxConns}, CurConns, NbChildren, Sleepers, To, Socket, SupPid, ProtocolPid) -> + case Transport:controlling_process(Socket, ProtocolPid) of + ok -> + ProtocolPid ! {handshake, Ref, Transport, Socket, HandshakeTimeout}, + put(SupPid, active), + CurConns2 = CurConns + 1, + if CurConns2 < MaxConns -> + To ! self(), + loop(State, CurConns2, NbChildren + 1, Sleepers); + true -> + loop(State, CurConns2, NbChildren + 1, [To|Sleepers]) + end; + {error, _} -> + Transport:close(Socket), + %% Only kill the supervised pid, because the connection's pid, + %% when different, is supposed to be sitting under it and linked. + exit(SupPid, kill), + To ! self(), + loop(State, CurConns, NbChildren, Sleepers) + end. + +-spec terminate(#state{}, any(), non_neg_integer()) -> no_return(). +terminate(#state{shutdown=brutal_kill}, Reason, _) -> + kill_children(get_keys(active)), + kill_children(get_keys(removed)), + exit(Reason); +%% Attempt to gracefully shutdown all children. +terminate(#state{shutdown=Shutdown}, Reason, NbChildren) -> + shutdown_children(get_keys(active)), + shutdown_children(get_keys(removed)), + _ = if + Shutdown =:= infinity -> + ok; + true -> + erlang:send_after(Shutdown, self(), kill) + end, + wait_children(NbChildren), + exit(Reason). + +%% Kill all children and then exit. We unlink first to avoid +%% getting a message for each child getting killed. +kill_children(Pids) -> + _ = [begin + unlink(P), + exit(P, kill) + end || P <- Pids], + ok. + +%% Monitor processes so we can know which ones have shutdown +%% before the timeout. Unlink so we avoid receiving an extra +%% message. Then send a shutdown exit signal. +shutdown_children(Pids) -> + _ = [begin + monitor(process, P), + unlink(P), + exit(P, shutdown) + end || P <- Pids], + ok. + +wait_children(0) -> + ok; +wait_children(NbChildren) -> + receive + {'DOWN', _, process, Pid, _} -> + case erase(Pid) of + active -> wait_children(NbChildren - 1); + removed -> wait_children(NbChildren - 1); + _ -> wait_children(NbChildren) + end; + kill -> + Active = get_keys(active), + _ = [exit(P, kill) || P <- Active], + Removed = get_keys(removed), + _ = [exit(P, kill) || P <- Removed], + ok + end. + +system_continue(_, _, {State, CurConns, NbChildren, Sleepers}) -> + loop(State, CurConns, NbChildren, Sleepers). + +-spec system_terminate(any(), _, _, _) -> no_return(). +system_terminate(Reason, _, _, {State, _, NbChildren, _}) -> + terminate(State, Reason, NbChildren). + +system_code_change(Misc, _, _, _) -> + {ok, Misc}. + +%% We use ~999999p here instead of ~w because the latter doesn't +%% support printable strings. +report_error(_, _, _, _, normal) -> + ok; +report_error(_, _, _, _, shutdown) -> + ok; +report_error(_, _, _, _, {shutdown, _}) -> + ok; +report_error(Logger, Ref, Protocol, Pid, Reason) -> + ranch:log(error, + "Ranch listener ~p had connection process started with " + "~p:start_link/4 at ~p exit with reason: ~999999p~n", + [Ref, Protocol, Pid, Reason], Logger). diff --git a/server/_build/default/lib/ranch/src/ranch_crc32c.erl b/server/_build/default/lib/ranch/src/ranch_crc32c.erl new file mode 100644 index 0000000..fc9be35 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_crc32c.erl @@ -0,0 +1,115 @@ +%% Copyright (c) 2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_crc32c). + +-export([crc32c/1]). +-export([crc32c/2]). + +-define(CRC32C_TABLE, { + 16#00000000, 16#F26B8303, 16#E13B70F7, 16#1350F3F4, + 16#C79A971F, 16#35F1141C, 16#26A1E7E8, 16#D4CA64EB, + 16#8AD958CF, 16#78B2DBCC, 16#6BE22838, 16#9989AB3B, + 16#4D43CFD0, 16#BF284CD3, 16#AC78BF27, 16#5E133C24, + 16#105EC76F, 16#E235446C, 16#F165B798, 16#030E349B, + 16#D7C45070, 16#25AFD373, 16#36FF2087, 16#C494A384, + 16#9A879FA0, 16#68EC1CA3, 16#7BBCEF57, 16#89D76C54, + 16#5D1D08BF, 16#AF768BBC, 16#BC267848, 16#4E4DFB4B, + 16#20BD8EDE, 16#D2D60DDD, 16#C186FE29, 16#33ED7D2A, + 16#E72719C1, 16#154C9AC2, 16#061C6936, 16#F477EA35, + 16#AA64D611, 16#580F5512, 16#4B5FA6E6, 16#B93425E5, + 16#6DFE410E, 16#9F95C20D, 16#8CC531F9, 16#7EAEB2FA, + 16#30E349B1, 16#C288CAB2, 16#D1D83946, 16#23B3BA45, + 16#F779DEAE, 16#05125DAD, 16#1642AE59, 16#E4292D5A, + 16#BA3A117E, 16#4851927D, 16#5B016189, 16#A96AE28A, + 16#7DA08661, 16#8FCB0562, 16#9C9BF696, 16#6EF07595, + 16#417B1DBC, 16#B3109EBF, 16#A0406D4B, 16#522BEE48, + 16#86E18AA3, 16#748A09A0, 16#67DAFA54, 16#95B17957, + 16#CBA24573, 16#39C9C670, 16#2A993584, 16#D8F2B687, + 16#0C38D26C, 16#FE53516F, 16#ED03A29B, 16#1F682198, + 16#5125DAD3, 16#A34E59D0, 16#B01EAA24, 16#42752927, + 16#96BF4DCC, 16#64D4CECF, 16#77843D3B, 16#85EFBE38, + 16#DBFC821C, 16#2997011F, 16#3AC7F2EB, 16#C8AC71E8, + 16#1C661503, 16#EE0D9600, 16#FD5D65F4, 16#0F36E6F7, + 16#61C69362, 16#93AD1061, 16#80FDE395, 16#72966096, + 16#A65C047D, 16#5437877E, 16#4767748A, 16#B50CF789, + 16#EB1FCBAD, 16#197448AE, 16#0A24BB5A, 16#F84F3859, + 16#2C855CB2, 16#DEEEDFB1, 16#CDBE2C45, 16#3FD5AF46, + 16#7198540D, 16#83F3D70E, 16#90A324FA, 16#62C8A7F9, + 16#B602C312, 16#44694011, 16#5739B3E5, 16#A55230E6, + 16#FB410CC2, 16#092A8FC1, 16#1A7A7C35, 16#E811FF36, + 16#3CDB9BDD, 16#CEB018DE, 16#DDE0EB2A, 16#2F8B6829, + 16#82F63B78, 16#709DB87B, 16#63CD4B8F, 16#91A6C88C, + 16#456CAC67, 16#B7072F64, 16#A457DC90, 16#563C5F93, + 16#082F63B7, 16#FA44E0B4, 16#E9141340, 16#1B7F9043, + 16#CFB5F4A8, 16#3DDE77AB, 16#2E8E845F, 16#DCE5075C, + 16#92A8FC17, 16#60C37F14, 16#73938CE0, 16#81F80FE3, + 16#55326B08, 16#A759E80B, 16#B4091BFF, 16#466298FC, + 16#1871A4D8, 16#EA1A27DB, 16#F94AD42F, 16#0B21572C, + 16#DFEB33C7, 16#2D80B0C4, 16#3ED04330, 16#CCBBC033, + 16#A24BB5A6, 16#502036A5, 16#4370C551, 16#B11B4652, + 16#65D122B9, 16#97BAA1BA, 16#84EA524E, 16#7681D14D, + 16#2892ED69, 16#DAF96E6A, 16#C9A99D9E, 16#3BC21E9D, + 16#EF087A76, 16#1D63F975, 16#0E330A81, 16#FC588982, + 16#B21572C9, 16#407EF1CA, 16#532E023E, 16#A145813D, + 16#758FE5D6, 16#87E466D5, 16#94B49521, 16#66DF1622, + 16#38CC2A06, 16#CAA7A905, 16#D9F75AF1, 16#2B9CD9F2, + 16#FF56BD19, 16#0D3D3E1A, 16#1E6DCDEE, 16#EC064EED, + 16#C38D26C4, 16#31E6A5C7, 16#22B65633, 16#D0DDD530, + 16#0417B1DB, 16#F67C32D8, 16#E52CC12C, 16#1747422F, + 16#49547E0B, 16#BB3FFD08, 16#A86F0EFC, 16#5A048DFF, + 16#8ECEE914, 16#7CA56A17, 16#6FF599E3, 16#9D9E1AE0, + 16#D3D3E1AB, 16#21B862A8, 16#32E8915C, 16#C083125F, + 16#144976B4, 16#E622F5B7, 16#F5720643, 16#07198540, + 16#590AB964, 16#AB613A67, 16#B831C993, 16#4A5A4A90, + 16#9E902E7B, 16#6CFBAD78, 16#7FAB5E8C, 16#8DC0DD8F, + 16#E330A81A, 16#115B2B19, 16#020BD8ED, 16#F0605BEE, + 16#24AA3F05, 16#D6C1BC06, 16#C5914FF2, 16#37FACCF1, + 16#69E9F0D5, 16#9B8273D6, 16#88D28022, 16#7AB90321, + 16#AE7367CA, 16#5C18E4C9, 16#4F48173D, 16#BD23943E, + 16#F36E6F75, 16#0105EC76, 16#12551F82, 16#E03E9C81, + 16#34F4F86A, 16#C69F7B69, 16#D5CF889D, 16#27A40B9E, + 16#79B737BA, 16#8BDCB4B9, 16#988C474D, 16#6AE7C44E, + 16#BE2DA0A5, 16#4C4623A6, 16#5F16D052, 16#AD7D5351 +}). + +%% The interface mirrors erlang:crc32/1,2. +-spec crc32c(iodata()) -> non_neg_integer(). +crc32c(Data) -> + do_crc32c(16#ffffffff, iolist_to_binary(Data)). + +-spec crc32c(CRC, iodata()) -> CRC when CRC::non_neg_integer(). +crc32c(OldCrc, Data) -> + do_crc32c(OldCrc bxor 16#ffffffff, iolist_to_binary(Data)). + +do_crc32c(OldCrc, <<C, Rest/bits>>) -> + do_crc32c((OldCrc bsr 8) bxor element(1 + ((OldCrc bxor C) band 16#ff), ?CRC32C_TABLE), + Rest); +do_crc32c(OldCrc, <<>>) -> + OldCrc bxor 16#ffffffff. + +-ifdef(TEST). +crc32c_test_() -> + Tests = [ + %% Tests from RFC3720 B.4. + {<<0:32/unit:8>>, 16#8a9136aa}, + {iolist_to_binary([16#ff || _ <- lists:seq(1, 32)]), 16#62a8ab43}, + {iolist_to_binary([N || N <- lists:seq(0, 16#1f)]), 16#46dd794e}, + {iolist_to_binary([N || N <- lists:seq(16#1f, 0, -1)]), 16#113fdb5c}, + {<<16#01c00000:32, 0:32, 0:32, 0:32, 16#14000000:32, 16#00000400:32, 16#00000014:32, + 16#00000018:32, 16#28000000:32, 0:32, 16#02000000:32, 0:32>>, 16#d9963a56} + ], + [{iolist_to_binary(io_lib:format("16#~8.16.0b", [R])), + fun() -> R = crc32c(V) end} || {V, R} <- Tests]. +-endif. diff --git a/server/_build/default/lib/ranch/src/ranch_listener_sup.erl b/server/_build/default/lib/ranch/src/ranch_listener_sup.erl new file mode 100644 index 0000000..3853425 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_listener_sup.erl @@ -0,0 +1,41 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_listener_sup). +-behaviour(supervisor). + +-export([start_link/5]). +-export([init/1]). + +-spec start_link(ranch:ref(), module(), any(), module(), any()) + -> {ok, pid()}. +start_link(Ref, Transport, TransOpts, Protocol, ProtoOpts) -> + MaxConns = maps:get(max_connections, TransOpts, 1024), + ranch_server:set_new_listener_opts(Ref, MaxConns, TransOpts, ProtoOpts, + [Ref, Transport, TransOpts, Protocol, ProtoOpts]), + supervisor:start_link(?MODULE, { + Ref, Transport, Protocol + }). + +init({Ref, Transport, Protocol}) -> + ok = ranch_server:set_listener_sup(Ref, self()), + ChildSpecs = [ + {ranch_conns_sup, {ranch_conns_sup, start_link, + [Ref, Transport, Protocol]}, + permanent, infinity, supervisor, [ranch_conns_sup]}, + {ranch_acceptors_sup, {ranch_acceptors_sup, start_link, + [Ref, Transport]}, + permanent, infinity, supervisor, [ranch_acceptors_sup]} + ], + {ok, {{rest_for_one, 1, 5}, ChildSpecs}}. diff --git a/server/_build/default/lib/ranch/src/ranch_protocol.erl b/server/_build/default/lib/ranch/src/ranch_protocol.erl new file mode 100644 index 0000000..30a5b51 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_protocol.erl @@ -0,0 +1,24 @@ +%% Copyright (c) 2012-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_protocol). + +%% Start a new connection process for the given socket. +-callback start_link( + Ref::ranch:ref(), + Socket::any(), + Transport::module(), + ProtocolOptions::any()) + -> {ok, ConnectionPid::pid()} + | {ok, SupPid::pid(), ConnectionPid::pid()}. diff --git a/server/_build/default/lib/ranch/src/ranch_proxy_header.erl b/server/_build/default/lib/ranch/src/ranch_proxy_header.erl new file mode 100644 index 0000000..081157f --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_proxy_header.erl @@ -0,0 +1,880 @@ +%% Copyright (c) 2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_proxy_header). + +-export([parse/1]). +-export([header/1]). +-export([header/2]). + +-type proxy_info() :: #{ + %% Mandatory part. + version := 1 | 2, + command := local | proxy, + transport_family => undefined | ipv4 | ipv6 | unix, + transport_protocol => undefined | stream | dgram, + %% Addresses. + src_address => inet:ip_address() | binary(), + src_port => inet:port_number(), + dest_address => inet:ip_address() | binary(), + dest_port => inet:port_number(), + %% Extra TLV-encoded data. + alpn => binary(), %% US-ASCII. + authority => binary(), %% UTF-8. + ssl => #{ + client := [ssl | cert_conn | cert_sess], + verified := boolean(), + version => binary(), %% US-ASCII. + cipher => binary(), %% US-ASCII. + sig_alg => binary(), %% US-ASCII. + key_alg => binary(), %% US-ASCII. + cn => binary() %% UTF-8. + }, + netns => binary(), %% US-ASCII. + %% Unknown TLVs can't be parsed so the raw data is given. + raw_tlvs => [{0..255, binary()}] +}. +-export_type([proxy_info/0]). + +-type build_opts() :: #{ + checksum => crc32c, + padding => pos_integer() %% >= 3 +}. + +%% Parsing. + +-spec parse(Data) -> {ok, proxy_info(), Data} | {error, atom()} when Data::binary(). +parse(<<"\r\n\r\n\0\r\nQUIT\n", Rest/bits>>) -> + parse_v2(Rest); +parse(<<"PROXY ", Rest/bits>>) -> + parse_v1(Rest); +parse(_) -> + {error, 'The PROXY protocol header signature was not recognized. (PP 2.1, PP 2.2)'}. + +-ifdef(TEST). +parse_unrecognized_header_test() -> + {error, _} = parse(<<"GET / HTTP/1.1\r\n">>), + ok. +-endif. + +%% Human-readable header format (Version 1). +parse_v1(<<"TCP4 ", Rest/bits>>) -> + parse_v1(Rest, ipv4); +parse_v1(<<"TCP6 ", Rest/bits>>) -> + parse_v1(Rest, ipv6); +parse_v1(<<"UNKNOWN\r\n", Rest/bits>>) -> + {ok, #{ + version => 1, + command => proxy, + transport_family => undefined, + transport_protocol => undefined + }, Rest}; +parse_v1(<<"UNKNOWN ", Rest0/bits>>) -> + case binary:split(Rest0, <<"\r\n">>) of + [_, Rest] -> + {ok, #{ + version => 1, + command => proxy, + transport_family => undefined, + transport_protocol => undefined + }, Rest}; + [_] -> + {error, 'Malformed or incomplete PROXY protocol header line. (PP 2.1)'} + end; +parse_v1(_) -> + {error, 'The INET protocol and family string was not recognized. (PP 2.1)'}. + +parse_v1(Rest0, Family) -> + try + {ok, SrcAddr, Rest1} = parse_ip(Rest0, Family), + {ok, DestAddr, Rest2} = parse_ip(Rest1, Family), + {ok, SrcPort, Rest3} = parse_port(Rest2, $\s), + {ok, DestPort, Rest4} = parse_port(Rest3, $\r), + <<"\n", Rest/bits>> = Rest4, + {ok, #{ + version => 1, + command => proxy, + transport_family => Family, + transport_protocol => stream, + src_address => SrcAddr, + src_port => SrcPort, + dest_address => DestAddr, + dest_port => DestPort + }, Rest} + catch + throw:parse_ipv4_error -> + {error, 'Failed to parse an IPv4 address in the PROXY protocol header line. (PP 2.1)'}; + throw:parse_ipv6_error -> + {error, 'Failed to parse an IPv6 address in the PROXY protocol header line. (PP 2.1)'}; + throw:parse_port_error -> + {error, 'Failed to parse a port number in the PROXY protocol header line. (PP 2.1)'}; + _:_ -> + {error, 'Malformed or incomplete PROXY protocol header line. (PP 2.1)'} + end. + +parse_ip(<<Addr:7/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:8/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:9/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:10/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:11/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:12/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:13/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:14/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(<<Addr:15/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest); +parse_ip(Data, ipv6) -> + [Addr, Rest] = binary:split(Data, <<$\s>>), + parse_ipv6(Addr, Rest). + +parse_ipv4(Addr0, Rest) -> + case inet:parse_ipv4strict_address(binary_to_list(Addr0)) of + {ok, Addr} -> {ok, Addr, Rest}; + {error, einval} -> throw(parse_ipv4_error) + end. + +parse_ipv6(Addr0, Rest) -> + case inet:parse_ipv6strict_address(binary_to_list(Addr0)) of + {ok, Addr} -> {ok, Addr, Rest}; + {error, einval} -> throw(parse_ipv6_error) + end. + +parse_port(<<Port:1/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest); +parse_port(<<Port:2/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest); +parse_port(<<Port:3/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest); +parse_port(<<Port:4/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest); +parse_port(<<Port:5/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest); + +parse_port(Port0, Rest) -> + try binary_to_integer(Port0) of + Port when Port > 0, Port =< 65535 -> + {ok, Port, Rest}; + _ -> + throw(parse_port_error) + catch _:_ -> + throw(parse_port_error) + end. + +-ifdef(TEST). +parse_v1_test() -> + %% Examples taken from the PROXY protocol header specification. + {ok, #{ + version := 1, + command := proxy, + transport_family := ipv4, + transport_protocol := stream, + src_address := {255, 255, 255, 255}, + src_port := 65535, + dest_address := {255, 255, 255, 255}, + dest_port := 65535 + }, <<>>} = parse(<<"PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n">>), + {ok, #{ + version := 1, + command := proxy, + transport_family := ipv6, + transport_protocol := stream, + src_address := {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535}, + src_port := 65535, + dest_address := {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535}, + dest_port := 65535 + }, <<>>} = parse(<<"PROXY TCP6 " + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff " + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n">>), + {ok, #{ + version := 1, + command := proxy, + transport_family := undefined, + transport_protocol := undefined + }, <<>>} = parse(<<"PROXY UNKNOWN\r\n">>), + {ok, #{ + version := 1, + command := proxy, + transport_family := undefined, + transport_protocol := undefined + }, <<>>} = parse(<<"PROXY UNKNOWN " + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff " + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n">>), + {ok, #{ + version := 1, + command := proxy, + transport_family := ipv4, + transport_protocol := stream, + src_address := {192, 168, 0, 1}, + src_port := 56324, + dest_address := {192, 168, 0, 11}, + dest_port := 443 + }, <<"GET / HTTP/1.1\r\nHost: 192.168.0.11\r\n\r\n">>} = parse(<< + "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n" + "GET / HTTP/1.1\r\n" + "Host: 192.168.0.11\r\n" + "\r\n">>), + %% Test cases taken from tomciopp/proxy_protocol. + {ok, #{ + version := 1, + command := proxy, + transport_family := ipv4, + transport_protocol := stream, + src_address := {192, 168, 0, 1}, + src_port := 56324, + dest_address := {192, 168, 0, 11}, + dest_port := 443 + }, <<"GET / HTTP/1.1\r">>} = parse(<< + "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\nGET / HTTP/1.1\r">>), + {error, _} = parse(<<"PROXY TCP4 192.1638.0.1 192.168.0.11 56324 443\r\nGET / HTTP/1.1\r">>), + {error, _} = parse(<<"PROXY TCP4 192.168.0.1 192.168.0.11 1111111 443\r\nGET / HTTP/1.1\r">>), + {ok, #{ + version := 1, + command := proxy, + transport_family := ipv6, + transport_protocol := stream, + src_address := {8193, 3512, 0, 66, 0, 35374, 880, 29492}, + src_port := 4124, + dest_address := {8193, 3512, 0, 66, 0, 35374, 880, 29493}, + dest_port := 443 + }, <<"GET / HTTP/1.1\r">>} = parse(<<"PROXY TCP6 " + "2001:0db8:0000:0042:0000:8a2e:0370:7334 " + "2001:0db8:0000:0042:0000:8a2e:0370:7335 4124 443\r\nGET / HTTP/1.1\r">>), + {error, _} = parse(<<"PROXY TCP6 " + "2001:0db8:0000:0042:0000:8a2e:0370:7334 " + "2001:0db8:00;0:0042:0000:8a2e:0370:7335 4124 443\r\nGET / HTTP/1.1\r">>), + {error, _} = parse(<<"PROXY TCP6 " + "2001:0db8:0000:0042:0000:8a2e:0370:7334 " + "2001:0db8:0000:0042:0000:8a2e:0370:7335 4124 foo\r\nGET / HTTP/1.1\r">>), + {ok, #{ + version := 1, + command := proxy, + transport_family := undefined, + transport_protocol := undefined + }, <<"GET / HTTP/1.1\r">>} = parse(<<"PROXY UNKNOWN 4124 443\r\nGET / HTTP/1.1\r">>), + {ok, #{ + version := 1, + command := proxy, + transport_family := undefined, + transport_protocol := undefined + }, <<"GET / HTTP/1.1\r">>} = parse(<<"PROXY UNKNOWN\r\nGET / HTTP/1.1\r">>), + ok. +-endif. + +%% Binary header format (version 2). + +%% LOCAL. +parse_v2(<<2:4, 0:4, _:8, Len:16, Rest0/bits>>) -> + case Rest0 of + <<_:Len/binary, Rest/bits>> -> + {ok, #{ + version => 2, + command => local + }, Rest}; + _ -> + {error, 'Missing data in the PROXY protocol binary header. (PP 2.2)'} + end; +%% PROXY. +parse_v2(<<2:4, 1:4, Family:4, Protocol:4, Len:16, Rest/bits>>) + when Family =< 3, Protocol =< 2 -> + case Rest of + <<Header:Len/binary, _/bits>> -> + parse_v2(Rest, Len, parse_family(Family), parse_protocol(Protocol), + <<Family:4, Protocol:4, Len:16, Header:Len/binary>>); + _ -> + {error, 'Missing data in the PROXY protocol binary header. (PP 2.2)'} + end; +%% Errors. +parse_v2(<<Version:4, _/bits>>) when Version =/= 2 -> + {error, 'Invalid version in the PROXY protocol binary header. (PP 2.2)'}; +parse_v2(<<_:4, Command:4, _/bits>>) when Command > 1 -> + {error, 'Invalid command in the PROXY protocol binary header. (PP 2.2)'}; +parse_v2(<<_:8, Family:4, _/bits>>) when Family > 3 -> + {error, 'Invalid address family in the PROXY protocol binary header. (PP 2.2)'}; +parse_v2(<<_:12, Protocol:4, _/bits>>) when Protocol > 2 -> + {error, 'Invalid transport protocol in the PROXY protocol binary header. (PP 2.2)'}. + +parse_family(0) -> undefined; +parse_family(1) -> ipv4; +parse_family(2) -> ipv6; +parse_family(3) -> unix. + +parse_protocol(0) -> undefined; +parse_protocol(1) -> stream; +parse_protocol(2) -> dgram. + +parse_v2(Data, Len, Family, Protocol, _) + when Family =:= undefined; Protocol =:= undefined -> + <<_:Len/binary, Rest/bits>> = Data, + {ok, #{ + version => 2, + command => proxy, + %% In case only one value was undefined, we set both explicitly. + %% It doesn't make sense to have only one known value. + transport_family => undefined, + transport_protocol => undefined + }, Rest}; +parse_v2(<< + S1, S2, S3, S4, + D1, D2, D3, D4, + SrcPort:16, DestPort:16, Rest/bits>>, Len, Family=ipv4, Protocol, Header) + when Len >= 12 -> + parse_tlv(Rest, Len - 12, #{ + version => 2, + command => proxy, + transport_family => Family, + transport_protocol => Protocol, + src_address => {S1, S2, S3, S4}, + src_port => SrcPort, + dest_address => {D1, D2, D3, D4}, + dest_port => DestPort + }, Header); +parse_v2(<< + S1:16, S2:16, S3:16, S4:16, S5:16, S6:16, S7:16, S8:16, + D1:16, D2:16, D3:16, D4:16, D5:16, D6:16, D7:16, D8:16, + SrcPort:16, DestPort:16, Rest/bits>>, Len, Family=ipv6, Protocol, Header) + when Len >= 36 -> + parse_tlv(Rest, Len - 36, #{ + version => 2, + command => proxy, + transport_family => Family, + transport_protocol => Protocol, + src_address => {S1, S2, S3, S4, S5, S6, S7, S8}, + src_port => SrcPort, + dest_address => {D1, D2, D3, D4, D5, D6, D7, D8}, + dest_port => DestPort + }, Header); +parse_v2(<<SrcAddr0:108/binary, DestAddr0:108/binary, Rest/bits>>, + Len, Family=unix, Protocol, Header) + when Len >= 216 -> + try + [SrcAddr, _] = binary:split(SrcAddr0, <<0>>), + true = byte_size(SrcAddr) > 0, + [DestAddr, _] = binary:split(DestAddr0, <<0>>), + true = byte_size(DestAddr) > 0, + parse_tlv(Rest, Len - 216, #{ + version => 2, + command => proxy, + transport_family => Family, + transport_protocol => Protocol, + src_address => SrcAddr, + dest_address => DestAddr + }, Header) + catch _:_ -> + {error, 'Invalid UNIX address in PROXY protocol binary header. (PP 2.2)'} + end; +parse_v2(_, _, _, _, _) -> + {error, 'Invalid length in the PROXY protocol binary header. (PP 2.2)'}. + +-ifdef(TEST). +parse_v2_test() -> + %% Test cases taken from tomciopp/proxy_protocol. + {ok, #{ + version := 2, + command := proxy, + transport_family := ipv4, + transport_protocol := stream, + src_address := {127, 0, 0, 1}, + src_port := 444, + dest_address := {192, 168, 0, 1}, + dest_port := 443 + }, <<"GET / HTTP/1.1\r\n">>} = parse(<< + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature. + 33, %% Version and command. + 17, %% Family and protocol. + 0, 12, %% Length. + 127, 0, 0, 1, %% Source address. + 192, 168, 0, 1, %% Destination address. + 1, 188, %% Source port. + 1, 187, %% Destination port. + "GET / HTTP/1.1\r\n">>), + {ok, #{ + version := 2, + command := proxy, + transport_family := ipv4, + transport_protocol := dgram, + src_address := {127, 0, 0, 1}, + src_port := 444, + dest_address := {192, 168, 0, 1}, + dest_port := 443 + }, <<"GET / HTTP/1.1\r\n">>} = parse(<< + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature. + 33, %% Version and command. + 18, %% Family and protocol. + 0, 12, %% Length. + 127, 0, 0, 1, %% Source address. + 192, 168, 0, 1, %% Destination address. + 1, 188, %% Source port. + 1, 187, %% Destination port. + "GET / HTTP/1.1\r\n">>), + {ok, #{ + version := 2, + command := proxy, + transport_family := ipv6, + transport_protocol := stream, + src_address := {5532, 4240, 1, 0, 0, 0, 0, 0}, + src_port := 444, + dest_address := {8193, 3512, 1, 0, 0, 0, 0, 0}, + dest_port := 443 + }, <<"GET / HTTP/1.1\r\n">>} = parse(<< + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature. + 33, %% Version and command. + 33, %% Family and protocol. + 0, 36, %% Length. + 21, 156, 16, 144, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Source address. + 32, 1, 13, 184, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Destination address. + 1, 188, %% Source port. + 1, 187, %% Destination port. + "GET / HTTP/1.1\r\n">>), + {ok, #{ + version := 2, + command := proxy, + transport_family := ipv6, + transport_protocol := dgram, + src_address := {5532, 4240, 1, 0, 0, 0, 0, 0}, + src_port := 444, + dest_address := {8193, 3512, 1, 0, 0, 0, 0, 0}, + dest_port := 443 + }, <<"GET / HTTP/1.1\r\n">>} = parse(<< + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature. + 33, %% Version and command. + 34, %% Family and protocol. + 0, 36, %% Length. + 21, 156, 16, 144, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Source address. + 32, 1, 13, 184, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Destination address. + 1, 188, %% Source port. + 1, 187, %% Destination port. + "GET / HTTP/1.1\r\n">>), + Path = <<"/var/pgsql_sock">>, + Len = byte_size(Path), + Padding = 8 * (108 - Len), + {ok, #{ + version := 2, + command := proxy, + transport_family := unix, + transport_protocol := stream, + src_address := Path, + dest_address := Path + }, <<"GET / HTTP/1.1\r\n">>} = parse(<< + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, + 33, + 49, + 0, 216, + Path/binary, 0:Padding, + Path/binary, 0:Padding, + "GET / HTTP/1.1\r\n">>), + {ok, #{ + version := 2, + command := proxy, + transport_family := unix, + transport_protocol := dgram, + src_address := Path, + dest_address := Path + }, <<"GET / HTTP/1.1\r\n">>} = parse(<< + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, + 33, + 50, + 0, 216, + Path/binary, 0:Padding, + Path/binary, 0:Padding, + "GET / HTTP/1.1\r\n">>), + ok. + +parse_v2_regression_test() -> + %% Real packet received from AWS. We confirm that the CRC32C + %% check succeeds only (in other words that ok is returned). + {ok, _, <<>>} = parse(<< + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, 33, 17, 0, 84, + 172, 31, 7, 113, 172, 31, 10, 31, 200, 242, 0, 80, 3, 0, 4, + 232, 214, 137, 45, 234, 0, 23, 1, 118, 112, 99, 101, 45, 48, + 56, 100, 50, 98, 102, 49, 53, 102, 97, 99, 53, 48, 48, 49, 99, + 57, 4, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>), + ok. +-endif. + +parse_tlv(Rest, 0, Info, _) -> + {ok, Info, Rest}; +%% PP2_TYPE_ALPN. +parse_tlv(<<16#1, TLVLen:16, ALPN:TLVLen/binary, Rest/bits>>, Len, Info, Header) -> + parse_tlv(Rest, Len - TLVLen - 3, Info#{alpn => ALPN}, Header); +%% PP2_TYPE_AUTHORITY. +parse_tlv(<<16#2, TLVLen:16, Authority:TLVLen/binary, Rest/bits>>, Len, Info, Header) -> + parse_tlv(Rest, Len - TLVLen - 3, Info#{authority => Authority}, Header); +%% PP2_TYPE_CRC32C. +parse_tlv(<<16#3, TLVLen:16, CRC32C:32, Rest/bits>>, Len0, Info, Header) when TLVLen =:= 4 -> + Len = Len0 - TLVLen - 3, + BeforeLen = byte_size(Header) - Len - TLVLen, + <<Before:BeforeLen/binary, _:32, After:Len/binary>> = Header, + %% The initial CRC is ranch_crc32c:crc32c(<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4>>). + case ranch_crc32c:crc32c(2900412422, [Before, <<0:32>>, After]) of + CRC32C -> + parse_tlv(Rest, Len, Info, Header); + _ -> + {error, 'Failed CRC32C verification in PROXY protocol binary header. (PP 2.2)'} + end; +%% PP2_TYPE_NOOP. +parse_tlv(<<16#4, TLVLen:16, _:TLVLen/binary, Rest/bits>>, Len, Info, Header) -> + parse_tlv(Rest, Len - TLVLen - 3, Info, Header); +%% PP2_TYPE_SSL. +parse_tlv(<<16#20, TLVLen:16, Client, Verify:32, Rest0/bits>>, Len, Info, Header) -> + SubsLen = TLVLen - 5, + case Rest0 of + <<Subs:SubsLen/binary, Rest/bits>> -> + SSL0 = #{ + client => parse_client(<<Client>>), + verified => Verify =:= 0 + }, + case parse_ssl_tlv(Subs, SubsLen, SSL0) of + {ok, SSL, <<>>} -> + parse_tlv(Rest, Len - TLVLen - 3, Info#{ssl => SSL}, Header); + Error={error, _} -> + Error + end; + _ -> + {error, 'Invalid TLV length in the PROXY protocol binary header. (PP 2.2)'} + end; +%% PP2_TYPE_NETNS. +parse_tlv(<<16#30, TLVLen:16, NetNS:TLVLen/binary, Rest/bits>>, Len, Info, Header) -> + parse_tlv(Rest, Len - TLVLen - 3, Info#{netns => NetNS}, Header); +%% Unknown TLV. +parse_tlv(<<TLVType, TLVLen:16, TLVValue:TLVLen/binary, Rest/bits>>, Len, Info, Header) -> + RawTLVs = maps:get(raw_tlvs, Info, []), + parse_tlv(Rest, Len - TLVLen - 3, Info#{raw_tlvs => [{TLVType, TLVValue}|RawTLVs]}, Header); +%% Invalid TLV length. +parse_tlv(_, _, _, _) -> + {error, 'Invalid TLV length in the PROXY protocol binary header. (PP 2.2)'}. + +parse_client(<<_:5, ClientCertSess:1, ClientCertConn:1, ClientSSL:1>>) -> + Client0 = case ClientCertSess of + 0 -> []; + 1 -> [cert_sess] + end, + Client1 = case ClientCertConn of + 0 -> Client0; + 1 -> [cert_conn|Client0] + end, + case ClientSSL of + 0 -> Client1; + 1 -> [ssl|Client1] + end. + +parse_ssl_tlv(Rest, 0, Info) -> + {ok, Info, Rest}; +%% Valid TLVs. +parse_ssl_tlv(<<TLVType, TLVLen:16, TLVValue:TLVLen/binary, Rest/bits>>, Len, Info) -> + case ssl_subtype(TLVType) of + undefined -> + {error, 'Invalid TLV subtype for PP2_TYPE_SSL in PROXY protocol binary header. (PP 2.2)'}; + Type -> + parse_ssl_tlv(Rest, Len - TLVLen - 3, Info#{Type => TLVValue}) + end; +%% Invalid TLV length. +parse_ssl_tlv(_, _, _) -> + {error, 'Invalid TLV length in the PROXY protocol binary header. (PP 2.2)'}. + +ssl_subtype(16#21) -> version; +ssl_subtype(16#22) -> cn; +ssl_subtype(16#23) -> cipher; +ssl_subtype(16#24) -> sig_alg; +ssl_subtype(16#25) -> key_alg; +ssl_subtype(_) -> undefined. + +%% Building. + +-spec header(proxy_info()) -> iodata(). +header(ProxyInfo) -> + header(ProxyInfo, #{}). + +-spec header(proxy_info(), build_opts()) -> iodata(). +header(#{version := 2, command := local}, _) -> + <<"\r\n\r\n\0\r\nQUIT\n", 2:4, 0:28>>; +header(#{version := 2, command := proxy, + transport_family := Family, + transport_protocol := Protocol}, _) + when Family =:= undefined; Protocol =:= undefined -> + <<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4, 0:24>>; +header(ProxyInfo=#{version := 2, command := proxy, + transport_family := Family, + transport_protocol := Protocol}, Opts) -> + Addresses = addresses(ProxyInfo), + TLVs = tlvs(ProxyInfo, Opts), + ExtraLen = case Opts of + #{checksum := crc32c} -> 7; + _ -> 0 + end, + Len = iolist_size(Addresses) + iolist_size(TLVs) + ExtraLen, + Header = [ + <<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4>>, + <<(family(Family)):4, (protocol(Protocol)):4>>, + <<Len:16>>, + Addresses, + TLVs + ], + case Opts of + #{checksum := crc32c} -> + CRC32C = ranch_crc32c:crc32c([Header, <<16#3, 4:16, 0:32>>]), + [Header, <<16#3, 4:16, CRC32C:32>>]; + _ -> + Header + end; +header(#{version := 1, command := proxy, + transport_family := undefined, + transport_protocol := undefined}, _) -> + <<"PROXY UNKNOWN\r\n">>; +header(#{version := 1, command := proxy, + transport_family := Family0, + transport_protocol := stream, + src_address := SrcAddress, src_port := SrcPort, + dest_address := DestAddress, dest_port := DestPort}, _) + when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 -> + [ + <<"PROXY ">>, + case Family0 of + ipv4 when tuple_size(SrcAddress) =:= 4, tuple_size(DestAddress) =:= 4 -> + [<<"TCP4 ">>, inet:ntoa(SrcAddress), $\s, inet:ntoa(DestAddress)]; + ipv6 when tuple_size(SrcAddress) =:= 8, tuple_size(DestAddress) =:= 8 -> + [<<"TCP6 ">>, inet:ntoa(SrcAddress), $\s, inet:ntoa(DestAddress)] + end, + $\s, + integer_to_binary(SrcPort), + $\s, + integer_to_binary(DestPort), + $\r, $\n + ]. + +family(ipv4) -> 1; +family(ipv6) -> 2; +family(unix) -> 3. + +protocol(stream) -> 1; +protocol(dgram) -> 2. + +addresses(#{transport_family := ipv4, + src_address := {S1, S2, S3, S4}, src_port := SrcPort, + dest_address := {D1, D2, D3, D4}, dest_port := DestPort}) + when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 -> + <<S1, S2, S3, S4, D1, D2, D3, D4, SrcPort:16, DestPort:16>>; +addresses(#{transport_family := ipv6, + src_address := {S1, S2, S3, S4, S5, S6, S7, S8}, src_port := SrcPort, + dest_address := {D1, D2, D3, D4, D5, D6, D7, D8}, dest_port := DestPort}) + when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 -> + << + S1:16, S2:16, S3:16, S4:16, S5:16, S6:16, S7:16, S8:16, + D1:16, D2:16, D3:16, D4:16, D5:16, D6:16, D7:16, D8:16, + SrcPort:16, DestPort:16 + >>; +addresses(#{transport_family := unix, + src_address := SrcAddress, dest_address := DestAddress}) + when byte_size(SrcAddress) =< 108, byte_size(DestAddress) =< 108 -> + SrcPadding = 8 * (108 - byte_size(SrcAddress)), + DestPadding = 8 * (108 - byte_size(DestAddress)), + << + SrcAddress/binary, 0:SrcPadding, + DestAddress/binary, 0:DestPadding + >>. + +tlvs(ProxyInfo, Opts) -> + [ + binary_tlv(ProxyInfo, alpn, 16#1), + binary_tlv(ProxyInfo, authority, 16#2), + ssl_tlv(ProxyInfo), + binary_tlv(ProxyInfo, netns, 16#30), + raw_tlvs(ProxyInfo), + noop_tlv(Opts) + ]. + +binary_tlv(Info, Key, Type) -> + case Info of + #{Key := Bin} -> + Len = byte_size(Bin), + <<Type, Len:16, Bin/binary>>; + _ -> + <<>> + end. + +noop_tlv(#{padding := Len0}) when Len0 >= 3 -> + Len = Len0 - 3, + <<16#4, Len:16, 0:Len/unit:8>>; +noop_tlv(_) -> + <<>>. + +ssl_tlv(#{ssl := Info=#{client := Client0, verified := Verify0}}) -> + Client = client(Client0, 0), + Verify = if + Verify0 -> 0; + not Verify0 -> 1 + end, + TLVs = [ + binary_tlv(Info, version, 16#21), + binary_tlv(Info, cn, 16#22), + binary_tlv(Info, cipher, 16#23), + binary_tlv(Info, sig_alg, 16#24), + binary_tlv(Info, key_alg, 16#25) + ], + Len = iolist_size(TLVs) + 5, + [<<16#20, Len:16, Client, Verify:32>>, TLVs]; +ssl_tlv(_) -> + <<>>. + +client([], Client) -> Client; +client([ssl|Tail], Client) -> client(Tail, Client bor 16#1); +client([cert_conn|Tail], Client) -> client(Tail, Client bor 16#2); +client([cert_sess|Tail], Client) -> client(Tail, Client bor 16#4). + +raw_tlvs(Info) -> + [begin + Len = byte_size(Bin), + <<Type, Len:16, Bin/binary>> + end || {Type, Bin} <- maps:get(raw_tlvs, Info, [])]. + +-ifdef(TEST). +v1_test() -> + Test1 = #{ + version => 1, + command => proxy, + transport_family => undefined, + transport_protocol => undefined + }, + {ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))), + Test2 = #{ + version => 1, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 1234, + dest_address => {10, 11, 12, 13}, + dest_port => 23456 + }, + {ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))), + Test3 = #{ + version => 1, + command => proxy, + transport_family => ipv6, + transport_protocol => stream, + src_address => {1, 2, 3, 4, 5, 6, 7, 8}, + src_port => 1234, + dest_address => {65535, 55555, 2222, 333, 1, 9999, 777, 8}, + dest_port => 23456 + }, + {ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))), + ok. + +v2_test() -> + Test0 = #{ + version => 2, + command => local + }, + {ok, Test0, <<>>} = parse(iolist_to_binary(header(Test0))), + Test1 = #{ + version => 2, + command => proxy, + transport_family => undefined, + transport_protocol => undefined + }, + {ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))), + Test2 = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 1234, + dest_address => {10, 11, 12, 13}, + dest_port => 23456 + }, + {ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))), + Test3 = #{ + version => 2, + command => proxy, + transport_family => ipv6, + transport_protocol => stream, + src_address => {1, 2, 3, 4, 5, 6, 7, 8}, + src_port => 1234, + dest_address => {65535, 55555, 2222, 333, 1, 9999, 777, 8}, + dest_port => 23456 + }, + {ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))), + Test4 = #{ + version => 2, + command => proxy, + transport_family => unix, + transport_protocol => dgram, + src_address => <<"/run/source.sock">>, + dest_address => <<"/run/destination.sock">> + }, + {ok, Test4, <<>>} = parse(iolist_to_binary(header(Test4))), + ok. + +v2_tlvs_test() -> + Common = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 1234, + dest_address => {10, 11, 12, 13}, + dest_port => 23456 + }, + Test1 = Common#{alpn => <<"h2">>}, + {ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))), + Test2 = Common#{authority => <<"internal.example.org">>}, + {ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))), + Test3 = Common#{netns => <<"/var/run/netns/example">>}, + {ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))), + Test4 = Common#{ssl => #{ + client => [ssl, cert_conn, cert_sess], + verified => true, + version => <<"TLSv1.3">>, %% Note that I'm not sure this example value is correct. + cipher => <<"ECDHE-RSA-AES128-GCM-SHA256">>, + sig_alg => <<"SHA256">>, + key_alg => <<"RSA2048">>, + cn => <<"example.com">> + }}, + {ok, Test4, <<>>} = parse(iolist_to_binary(header(Test4))), + %% Note that the raw_tlvs order is not relevant and therefore + %% the parser does not reverse the list it builds. + Test5In = Common#{raw_tlvs => RawTLVs=[ + %% The only custom TLV I am aware of is defined at: + %% https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol + {16#ea, <<16#1, "instance-id">>}, + %% This TLV is entirely fictional. + {16#ff, <<1, 2, 3, 4, 5, 6, 7, 8, 9, 0>>} + ]}, + Test5Out = Test5In#{raw_tlvs => lists:reverse(RawTLVs)}, + {ok, Test5Out, <<>>} = parse(iolist_to_binary(header(Test5In))), + ok. + +v2_checksum_test() -> + Test = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 1234, + dest_address => {10, 11, 12, 13}, + dest_port => 23456 + }, + {ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{checksum => crc32c}))), + ok. + +v2_padding_test() -> + Test = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 1234, + dest_address => {10, 11, 12, 13}, + dest_port => 23456 + }, + {ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{padding => 123}))), + ok. +-endif. diff --git a/server/_build/default/lib/ranch/src/ranch_server.erl b/server/_build/default/lib/ranch/src/ranch_server.erl new file mode 100644 index 0000000..a767cd8 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_server.erl @@ -0,0 +1,233 @@ +%% Copyright (c) 2012-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_server). +-behaviour(gen_server). + +%% API. +-export([start_link/0]). +-export([set_new_listener_opts/5]). +-export([cleanup_listener_opts/1]). +-export([set_connections_sup/2]). +-export([get_connections_sup/1]). +-export([get_connections_sups/0]). +-export([set_listener_sup/2]). +-export([get_listener_sup/1]). +-export([get_listener_sups/0]). +-export([set_addr/2]). +-export([get_addr/1]). +-export([set_max_connections/2]). +-export([get_max_connections/1]). +-export([set_transport_options/2]). +-export([get_transport_options/1]). +-export([set_protocol_options/2]). +-export([get_protocol_options/1]). +-export([get_listener_start_args/1]). +-export([count_connections/1]). + +%% gen_server. +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_info/2]). +-export([terminate/2]). +-export([code_change/3]). + +-define(TAB, ?MODULE). + +-type monitors() :: [{{reference(), pid()}, any()}]. +-record(state, { + monitors = [] :: monitors() +}). + +%% API. + +-spec start_link() -> {ok, pid()}. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +-spec set_new_listener_opts(ranch:ref(), ranch:max_conns(), any(), any(), [any()]) -> ok. +set_new_listener_opts(Ref, MaxConns, TransOpts, ProtoOpts, StartArgs) -> + gen_server:call(?MODULE, {set_new_listener_opts, Ref, MaxConns, TransOpts, ProtoOpts, StartArgs}). + +-spec cleanup_listener_opts(ranch:ref()) -> ok. +cleanup_listener_opts(Ref) -> + _ = ets:delete(?TAB, {addr, Ref}), + _ = ets:delete(?TAB, {max_conns, Ref}), + _ = ets:delete(?TAB, {trans_opts, Ref}), + _ = ets:delete(?TAB, {proto_opts, Ref}), + _ = ets:delete(?TAB, {listener_start_args, Ref}), + %% We also remove the pid of the connections supervisor. + %% Depending on the timing, it might already have been deleted + %% when we handled the monitor DOWN message. However, in some + %% cases when calling stop_listener followed by get_connections_sup, + %% we could end up with the pid still being returned, when we + %% expected a crash (because the listener was stopped). + %% Deleting it explictly here removes any possible confusion. + _ = ets:delete(?TAB, {conns_sup, Ref}), + %% Ditto for the listener supervisor. + _ = ets:delete(?TAB, {listener_sup, Ref}), + ok. + +-spec set_connections_sup(ranch:ref(), pid()) -> ok. +set_connections_sup(Ref, Pid) -> + gen_server:call(?MODULE, {set_connections_sup, Ref, Pid}). + +-spec get_connections_sup(ranch:ref()) -> pid(). +get_connections_sup(Ref) -> + ets:lookup_element(?TAB, {conns_sup, Ref}, 2). + +-spec get_connections_sups() -> [{ranch:ref(), pid()}]. +get_connections_sups() -> + [{Ref, Pid} || [Ref, Pid] <- ets:match(?TAB, {{conns_sup, '$1'}, '$2'})]. + +-spec set_listener_sup(ranch:ref(), pid()) -> ok. +set_listener_sup(Ref, Pid) -> + gen_server:call(?MODULE, {set_listener_sup, Ref, Pid}). + +-spec get_listener_sup(ranch:ref()) -> pid(). +get_listener_sup(Ref) -> + ets:lookup_element(?TAB, {listener_sup, Ref}, 2). + +-spec get_listener_sups() -> [{ranch:ref(), pid()}]. +get_listener_sups() -> + [{Ref, Pid} || [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})]. + +-spec set_addr(ranch:ref(), {inet:ip_address(), inet:port_number()} | {undefined, undefined}) -> ok. +set_addr(Ref, Addr) -> + gen_server:call(?MODULE, {set_addr, Ref, Addr}). + +-spec get_addr(ranch:ref()) -> {inet:ip_address(), inet:port_number()} | {undefined, undefined}. +get_addr(Ref) -> + ets:lookup_element(?TAB, {addr, Ref}, 2). + +-spec set_max_connections(ranch:ref(), ranch:max_conns()) -> ok. +set_max_connections(Ref, MaxConnections) -> + gen_server:call(?MODULE, {set_max_conns, Ref, MaxConnections}). + +-spec get_max_connections(ranch:ref()) -> ranch:max_conns(). +get_max_connections(Ref) -> + ets:lookup_element(?TAB, {max_conns, Ref}, 2). + +-spec set_transport_options(ranch:ref(), any()) -> ok. +set_transport_options(Ref, TransOpts) -> + gen_server:call(?MODULE, {set_trans_opts, Ref, TransOpts}). + +-spec get_transport_options(ranch:ref()) -> any(). +get_transport_options(Ref) -> + ets:lookup_element(?TAB, {trans_opts, Ref}, 2). + +-spec set_protocol_options(ranch:ref(), any()) -> ok. +set_protocol_options(Ref, ProtoOpts) -> + gen_server:call(?MODULE, {set_proto_opts, Ref, ProtoOpts}). + +-spec get_protocol_options(ranch:ref()) -> any(). +get_protocol_options(Ref) -> + ets:lookup_element(?TAB, {proto_opts, Ref}, 2). + +-spec get_listener_start_args(ranch:ref()) -> [any()]. +get_listener_start_args(Ref) -> + ets:lookup_element(?TAB, {listener_start_args, Ref}, 2). + +-spec count_connections(ranch:ref()) -> non_neg_integer(). +count_connections(Ref) -> + ranch_conns_sup:active_connections(get_connections_sup(Ref)). + +%% gen_server. + +init([]) -> + ConnMonitors = [{{erlang:monitor(process, Pid), Pid}, {conns_sup, Ref}} || + [Ref, Pid] <- ets:match(?TAB, {{conns_sup, '$1'}, '$2'})], + ListenerMonitors = [{{erlang:monitor(process, Pid), Pid}, {listener_sup, Ref}} || + [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})], + {ok, #state{monitors=ConnMonitors++ListenerMonitors}}. + +handle_call({set_new_listener_opts, Ref, MaxConns, TransOpts, ProtoOpts, StartArgs}, _, State) -> + ets:insert_new(?TAB, {{max_conns, Ref}, MaxConns}), + ets:insert_new(?TAB, {{trans_opts, Ref}, TransOpts}), + ets:insert_new(?TAB, {{proto_opts, Ref}, ProtoOpts}), + ets:insert_new(?TAB, {{listener_start_args, Ref}, StartArgs}), + {reply, ok, State}; +handle_call({set_connections_sup, Ref, Pid}, _, State0) -> + State = set_monitored_process({conns_sup, Ref}, Pid, State0), + {reply, ok, State}; +handle_call({set_listener_sup, Ref, Pid}, _, State0) -> + State = set_monitored_process({listener_sup, Ref}, Pid, State0), + {reply, ok, State}; +handle_call({set_addr, Ref, Addr}, _, State) -> + true = ets:insert(?TAB, {{addr, Ref}, Addr}), + {reply, ok, State}; +handle_call({set_max_conns, Ref, MaxConns}, _, State) -> + ets:insert(?TAB, {{max_conns, Ref}, MaxConns}), + ConnsSup = get_connections_sup(Ref), + ConnsSup ! {set_max_conns, MaxConns}, + {reply, ok, State}; +handle_call({set_trans_opts, Ref, Opts}, _, State) -> + ets:insert(?TAB, {{trans_opts, Ref}, Opts}), + {reply, ok, State}; +handle_call({set_proto_opts, Ref, Opts}, _, State) -> + ets:insert(?TAB, {{proto_opts, Ref}, Opts}), + ConnsSup = get_connections_sup(Ref), + ConnsSup ! {set_opts, Opts}, + {reply, ok, State}; +handle_call(_Request, _From, State) -> + {reply, ignore, State}. + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_info({'DOWN', MonitorRef, process, Pid, Reason}, + State=#state{monitors=Monitors}) -> + {_, TypeRef} = lists:keyfind({MonitorRef, Pid}, 1, Monitors), + ok = case {TypeRef, Reason} of + {{listener_sup, Ref}, normal} -> + cleanup_listener_opts(Ref); + {{listener_sup, Ref}, shutdown} -> + cleanup_listener_opts(Ref); + {{listener_sup, Ref}, {shutdown, _}} -> + cleanup_listener_opts(Ref); + _ -> + _ = ets:delete(?TAB, TypeRef), + ok + end, + Monitors2 = lists:keydelete({MonitorRef, Pid}, 1, Monitors), + {noreply, State#state{monitors=Monitors2}}; +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%% Internal. + +set_monitored_process(Key, Pid, State=#state{monitors=Monitors0}) -> + %% First we cleanup the monitor if a residual one exists. + %% This can happen during crashes when the restart is faster + %% than the cleanup. + Monitors = case lists:keytake(Key, 2, Monitors0) of + false -> + Monitors0; + {value, {{OldMonitorRef, _}, _}, Monitors1} -> + true = erlang:demonitor(OldMonitorRef, [flush]), + Monitors1 + end, + %% Then we unconditionally insert in the ets table. + %% If residual data is there, it will be overwritten. + true = ets:insert(?TAB, {Key, Pid}), + %% Finally we start monitoring this new process. + MonitorRef = erlang:monitor(process, Pid), + State#state{monitors=[{{MonitorRef, Pid}, Key}|Monitors]}. diff --git a/server/_build/default/lib/ranch/src/ranch_ssl.erl b/server/_build/default/lib/ranch/src/ranch_ssl.erl new file mode 100644 index 0000000..f769868 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_ssl.erl @@ -0,0 +1,243 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_ssl). +-behaviour(ranch_transport). + +-export([name/0]). +-export([secure/0]). +-export([messages/0]). +-export([listen/1]). +-export([disallowed_listen_options/0]). +-export([accept/2]). +-export([accept_ack/2]). +-export([handshake/3]). +-export([connect/3]). +-export([connect/4]). +-export([recv/3]). +-export([recv_proxy_header/2]). +-export([send/2]). +-export([sendfile/2]). +-export([sendfile/4]). +-export([sendfile/5]). +-export([setopts/2]). +-export([getopts/2]). +-export([getstat/1]). +-export([getstat/2]). +-export([controlling_process/2]). +-export([peername/1]). +-export([sockname/1]). +-export([shutdown/2]). +-export([close/1]). + +-type ssl_opt() :: {alpn_preferred_protocols, [binary()]} + | {beast_mitigation, one_n_minus_one | zero_n | disabled} + | {cacertfile, string()} + | {cacerts, [public_key:der_encoded()]} + | {cert, public_key:der_encoded()} + | {certfile, string()} + | {ciphers, [ssl_cipher:erl_cipher_suite()]} + | {client_renegotiation, boolean()} + | {crl_cache, {module(), {internal | any(), list()}}} + | {crl_check, boolean() | peer | best_effort} + | {depth, 0..255} + | {dh, public_key:der_encoded()} + | {dhfile, string()} + | {fail_if_no_peer_cert, boolean()} + | {hibernate_after, integer() | undefined} + | {honor_cipher_order, boolean()} + | {key, {'RSAPrivateKey' | 'DSAPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()}} + | {keyfile, string()} + | {log_alert, boolean()} + | {next_protocols_advertised, [binary()]} + | {padding_check, boolean()} + | {partial_chain, fun(([public_key:der_encoded()]) -> {trusted_ca, public_key:der_encoded()} | unknown_ca)} + | {password, string()} + | {psk_identity, string()} + | {reuse_session, fun()} + | {reuse_sessions, boolean()} + | {secure_renegotiate, boolean()} + | {signature_algs, [{atom(), atom()}]} + | {sni_fun, fun()} + | {sni_hosts, [{string(), ssl_opt()}]} + | {user_lookup_fun, {fun(), any()}} + | {v2_hello_compatible, boolean()} + | {verify, verify_none | verify_peer} + | {verify_fun, {fun(), any()}} + | {versions, [atom()]}. +-export_type([ssl_opt/0]). + +-type opt() :: ranch_tcp:opt() | ssl_opt(). +-export_type([opt/0]). + +-type opts() :: [opt()]. +-export_type([opts/0]). + +name() -> ssl. + +-spec secure() -> boolean(). +secure() -> + true. + +messages() -> {ssl, ssl_closed, ssl_error}. + +-spec listen(opts()) -> {ok, ssl:sslsocket()} | {error, atom()}. +listen(Opts) -> + case lists:keymember(cert, 1, Opts) + orelse lists:keymember(certfile, 1, Opts) + orelse lists:keymember(sni_fun, 1, Opts) + orelse lists:keymember(sni_hosts, 1, Opts) of + true -> + do_listen(Opts); + false -> + {error, no_cert} + end. + +do_listen(Opts0) -> + Opts1 = ranch:set_option_default(Opts0, backlog, 1024), + Opts2 = ranch:set_option_default(Opts1, nodelay, true), + Opts3 = ranch:set_option_default(Opts2, send_timeout, 30000), + Opts = ranch:set_option_default(Opts3, send_timeout_close, true), + %% We set the port to 0 because it is given in the Opts directly. + %% The port in the options takes precedence over the one in the + %% first argument. + ssl:listen(0, ranch:filter_options(Opts, disallowed_listen_options(), + [binary, {active, false}, {packet, raw}, {reuseaddr, true}])). + +%% 'binary' and 'list' are disallowed but they are handled +%% specifically as they do not have 2-tuple equivalents. +disallowed_listen_options() -> + [alpn_advertised_protocols, client_preferred_next_protocols, + fallback, server_name_indication, srp_identity + |ranch_tcp:disallowed_listen_options()]. + +-spec accept(ssl:sslsocket(), timeout()) + -> {ok, ssl:sslsocket()} | {error, closed | timeout | atom()}. +accept(LSocket, Timeout) -> + ssl:transport_accept(LSocket, Timeout). + +-spec accept_ack(ssl:sslsocket(), timeout()) -> ok. +accept_ack(CSocket, Timeout) -> + {ok, _} = handshake(CSocket, [], Timeout), + ok. + +-spec handshake(inet:socket() | ssl:sslsocket(), opts(), timeout()) + -> {ok, ssl:sslsocket()} | {error, any()}. +handshake(CSocket, Opts, Timeout) -> + case ssl:handshake(CSocket, Opts, Timeout) of + {ok, NewSocket} -> + {ok, NewSocket}; + Error = {error, _} -> + Error + end. + +%% @todo Probably filter Opts? +-spec connect(inet:ip_address() | inet:hostname(), + inet:port_number(), any()) + -> {ok, inet:socket()} | {error, atom()}. +connect(Host, Port, Opts) when is_integer(Port) -> + ssl:connect(Host, Port, + Opts ++ [binary, {active, false}, {packet, raw}]). + +%% @todo Probably filter Opts? +-spec connect(inet:ip_address() | inet:hostname(), + inet:port_number(), any(), timeout()) + -> {ok, inet:socket()} | {error, atom()}. +connect(Host, Port, Opts, Timeout) when is_integer(Port) -> + ssl:connect(Host, Port, + Opts ++ [binary, {active, false}, {packet, raw}], + Timeout). + +-spec recv(ssl:sslsocket(), non_neg_integer(), timeout()) + -> {ok, any()} | {error, closed | atom()}. +recv(Socket, Length, Timeout) -> + ssl:recv(Socket, Length, Timeout). + +-spec recv_proxy_header(ssl:sslsocket(), timeout()) + -> {ok, ranch_proxy_header:proxy_info()} + | {error, closed | atom()} + | {error, protocol_error, atom()}. +recv_proxy_header(SSLSocket, Timeout) -> + %% There's currently no documented way to perform a TCP recv + %% on an sslsocket(), even before the TLS handshake. However + %% nothing prevents us from retrieving the TCP socket and using + %% it. Since it's an undocumented interface this may however + %% make forward-compatibility more difficult. + {sslsocket, {gen_tcp, TCPSocket, _, _}, _} = SSLSocket, + ranch_tcp:recv_proxy_header(TCPSocket, Timeout). + +-spec send(ssl:sslsocket(), iodata()) -> ok | {error, atom()}. +send(Socket, Packet) -> + ssl:send(Socket, Packet). + +-spec sendfile(ssl:sslsocket(), file:name_all() | file:fd()) + -> {ok, non_neg_integer()} | {error, atom()}. +sendfile(Socket, Filename) -> + sendfile(Socket, Filename, 0, 0, []). + +-spec sendfile(ssl:sslsocket(), file:name_all() | file:fd(), + non_neg_integer(), non_neg_integer()) + -> {ok, non_neg_integer()} | {error, atom()}. +sendfile(Socket, File, Offset, Bytes) -> + sendfile(Socket, File, Offset, Bytes, []). + +%% Unlike with TCP, no syscall can be used here, so sending files +%% through SSL will be much slower in comparison. Note that unlike +%% file:sendfile/5 this function accepts either a file or a file name. +-spec sendfile(ssl:sslsocket(), file:name_all() | file:fd(), + non_neg_integer(), non_neg_integer(), ranch_transport:sendfile_opts()) + -> {ok, non_neg_integer()} | {error, atom()}. +sendfile(Socket, File, Offset, Bytes, Opts) -> + ranch_transport:sendfile(?MODULE, Socket, File, Offset, Bytes, Opts). + +%% @todo Probably filter Opts? +-spec setopts(ssl:sslsocket(), list()) -> ok | {error, atom()}. +setopts(Socket, Opts) -> + ssl:setopts(Socket, Opts). + +-spec getopts(ssl:sslsocket(), [atom()]) -> {ok, list()} | {error, atom()}. +getopts(Socket, Opts) -> + ssl:getopts(Socket, Opts). + +-spec getstat(ssl:sslsocket()) -> {ok, list()} | {error, atom()}. +getstat(Socket) -> + ssl:getstat(Socket). + +-spec getstat(ssl:sslsocket(), [atom()]) -> {ok, list()} | {error, atom()}. +getstat(Socket, OptionNames) -> + ssl:getstat(Socket, OptionNames). + +-spec controlling_process(ssl:sslsocket(), pid()) + -> ok | {error, closed | not_owner | atom()}. +controlling_process(Socket, Pid) -> + ssl:controlling_process(Socket, Pid). + +-spec peername(ssl:sslsocket()) + -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. +peername(Socket) -> + ssl:peername(Socket). + +-spec sockname(ssl:sslsocket()) + -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. +sockname(Socket) -> + ssl:sockname(Socket). + +-spec shutdown(ssl:sslsocket(), read | write | read_write) + -> ok | {error, atom()}. +shutdown(Socket, How) -> + ssl:shutdown(Socket, How). + +-spec close(ssl:sslsocket()) -> ok. +close(Socket) -> + ssl:close(Socket). diff --git a/server/_build/default/lib/ranch/src/ranch_sup.erl b/server/_build/default/lib/ranch/src/ranch_sup.erl new file mode 100644 index 0000000..3cb0cd6 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_sup.erl @@ -0,0 +1,40 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_sup). +-behaviour(supervisor). + +-export([start_link/0]). +-export([init/1]). + +-spec start_link() -> {ok, pid()}. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + Intensity = case application:get_env(ranch_sup_intensity) of + {ok, Value1} -> Value1; + undefined -> 1 + end, + Period = case application:get_env(ranch_sup_period) of + {ok, Value2} -> Value2; + undefined -> 5 + end, + ranch_server = ets:new(ranch_server, [ + ordered_set, public, named_table]), + Procs = [ + {ranch_server, {ranch_server, start_link, []}, + permanent, 5000, worker, [ranch_server]} + ], + {ok, {{one_for_one, Intensity, Period}, Procs}}. diff --git a/server/_build/default/lib/ranch/src/ranch_tcp.erl b/server/_build/default/lib/ranch/src/ranch_tcp.erl new file mode 100644 index 0000000..b7ece5b --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_tcp.erl @@ -0,0 +1,245 @@ +%% Copyright (c) 2011-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_tcp). +-behaviour(ranch_transport). + +-export([name/0]). +-export([secure/0]). +-export([messages/0]). +-export([listen/1]). +-export([disallowed_listen_options/0]). +-export([accept/2]). +-export([accept_ack/2]). +-export([handshake/3]). +-export([connect/3]). +-export([connect/4]). +-export([recv/3]). +-export([recv_proxy_header/2]). +-export([send/2]). +-export([sendfile/2]). +-export([sendfile/4]). +-export([sendfile/5]). +-export([setopts/2]). +-export([getopts/2]). +-export([getstat/1]). +-export([getstat/2]). +-export([controlling_process/2]). +-export([peername/1]). +-export([sockname/1]). +-export([shutdown/2]). +-export([close/1]). + +-type opt() :: {backlog, non_neg_integer()} + | {buffer, non_neg_integer()} + | {delay_send, boolean()} + | {dontroute, boolean()} + | {exit_on_close, boolean()} + | {fd, non_neg_integer()} + | {high_msgq_watermark, non_neg_integer()} + | {high_watermark, non_neg_integer()} + | inet + | inet6 + | {ip, inet:ip_address()} + | {ipv6_v6only, boolean()} + | {keepalive, boolean()} + | {linger, {boolean(), non_neg_integer()}} + | {low_msgq_watermark, non_neg_integer()} + | {low_watermark, non_neg_integer()} + | {nodelay, boolean()} + | {port, inet:port_number()} + | {priority, integer()} + | {raw, non_neg_integer(), non_neg_integer(), binary()} + | {recbuf, non_neg_integer()} + | {send_timeout, timeout()} + | {send_timeout_close, boolean()} + | {sndbuf, non_neg_integer()} + | {tos, integer()}. +-export_type([opt/0]). + +-type opts() :: [opt()]. +-export_type([opts/0]). + +name() -> tcp. + +-spec secure() -> boolean(). +secure() -> + false. + +messages() -> {tcp, tcp_closed, tcp_error}. + +-spec listen(opts()) -> {ok, inet:socket()} | {error, atom()}. +listen(Opts) -> + Opts2 = ranch:set_option_default(Opts, backlog, 1024), + Opts3 = ranch:set_option_default(Opts2, nodelay, true), + Opts4 = ranch:set_option_default(Opts3, send_timeout, 30000), + Opts5 = ranch:set_option_default(Opts4, send_timeout_close, true), + %% We set the port to 0 because it is given in the Opts directly. + %% The port in the options takes precedence over the one in the + %% first argument. + gen_tcp:listen(0, ranch:filter_options(Opts5, disallowed_listen_options(), + [binary, {active, false}, {packet, raw}, {reuseaddr, true}])). + +%% 'binary' and 'list' are disallowed but they are handled +%% specifically as they do not have 2-tuple equivalents. +disallowed_listen_options() -> + [active, header, mode, packet, packet_size, line_delimiter, reuseaddr]. + +-spec accept(inet:socket(), timeout()) + -> {ok, inet:socket()} | {error, closed | timeout | atom()}. +accept(LSocket, Timeout) -> + gen_tcp:accept(LSocket, Timeout). + +-spec accept_ack(inet:socket(), timeout()) -> ok. +accept_ack(CSocket, Timeout) -> + {ok, _} = handshake(CSocket, [], Timeout), + ok. + +-spec handshake(inet:socket(), opts(), timeout()) -> {ok, inet:socket()}. +handshake(CSocket, _, _) -> + {ok, CSocket}. + +%% @todo Probably filter Opts? +-spec connect(inet:ip_address() | inet:hostname(), + inet:port_number(), any()) + -> {ok, inet:socket()} | {error, atom()}. +connect(Host, Port, Opts) when is_integer(Port) -> + gen_tcp:connect(Host, Port, + Opts ++ [binary, {active, false}, {packet, raw}]). + +%% @todo Probably filter Opts? +-spec connect(inet:ip_address() | inet:hostname(), + inet:port_number(), any(), timeout()) + -> {ok, inet:socket()} | {error, atom()}. +connect(Host, Port, Opts, Timeout) when is_integer(Port) -> + gen_tcp:connect(Host, Port, + Opts ++ [binary, {active, false}, {packet, raw}], + Timeout). + +-spec recv(inet:socket(), non_neg_integer(), timeout()) + -> {ok, any()} | {error, closed | atom()}. +recv(Socket, Length, Timeout) -> + gen_tcp:recv(Socket, Length, Timeout). + +-spec recv_proxy_header(inet:socket(), timeout()) + -> {ok, ranch_proxy_header:proxy_info()} + | {error, closed | atom()} + | {error, protocol_error, atom()}. +recv_proxy_header(Socket, Timeout) -> + case recv(Socket, 0, Timeout) of + {ok, Data} -> + case ranch_proxy_header:parse(Data) of + {ok, ProxyInfo, <<>>} -> + {ok, ProxyInfo}; + {ok, ProxyInfo, Rest} -> + case gen_tcp:unrecv(Socket, Rest) of + ok -> + {ok, ProxyInfo}; + Error -> + Error + end; + {error, HumanReadable} -> + {error, protocol_error, HumanReadable} + end; + Error -> + Error + end. + +-spec send(inet:socket(), iodata()) -> ok | {error, atom()}. +send(Socket, Packet) -> + gen_tcp:send(Socket, Packet). + +-spec sendfile(inet:socket(), file:name_all() | file:fd()) + -> {ok, non_neg_integer()} | {error, atom()}. +sendfile(Socket, Filename) -> + sendfile(Socket, Filename, 0, 0, []). + +-spec sendfile(inet:socket(), file:name_all() | file:fd(), non_neg_integer(), + non_neg_integer()) + -> {ok, non_neg_integer()} | {error, atom()}. +sendfile(Socket, File, Offset, Bytes) -> + sendfile(Socket, File, Offset, Bytes, []). + +-spec sendfile(inet:socket(), file:name_all() | file:fd(), non_neg_integer(), + non_neg_integer(), [{chunk_size, non_neg_integer()}]) + -> {ok, non_neg_integer()} | {error, atom()}. +sendfile(Socket, Filename, Offset, Bytes, Opts) + when is_list(Filename) orelse is_atom(Filename) + orelse is_binary(Filename) -> + case file:open(Filename, [read, raw, binary]) of + {ok, RawFile} -> + try sendfile(Socket, RawFile, Offset, Bytes, Opts) of + Result -> Result + after + ok = file:close(RawFile) + end; + {error, _} = Error -> + Error + end; +sendfile(Socket, RawFile, Offset, Bytes, Opts) -> + Opts2 = case Opts of + [] -> [{chunk_size, 16#1FFF}]; + _ -> Opts + end, + try file:sendfile(RawFile, Socket, Offset, Bytes, Opts2) of + Result -> Result + catch + error:{badmatch, {error, enotconn}} -> + %% file:sendfile/5 might fail by throwing a + %% {badmatch, {error, enotconn}}. This is because its + %% implementation fails with a badmatch in + %% prim_file:sendfile/10 if the socket is not connected. + {error, closed} + end. + +%% @todo Probably filter Opts? +-spec setopts(inet:socket(), list()) -> ok | {error, atom()}. +setopts(Socket, Opts) -> + inet:setopts(Socket, Opts). + +-spec getopts(inet:socket(), [atom()]) -> {ok, list()} | {error, atom()}. +getopts(Socket, Opts) -> + inet:getopts(Socket, Opts). + +-spec getstat(inet:socket()) -> {ok, list()} | {error, atom()}. +getstat(Socket) -> + inet:getstat(Socket). + +-spec getstat(inet:socket(), [atom()]) -> {ok, list()} | {error, atom()}. +getstat(Socket, OptionNames) -> + inet:getstat(Socket, OptionNames). + +-spec controlling_process(inet:socket(), pid()) + -> ok | {error, closed | not_owner | atom()}. +controlling_process(Socket, Pid) -> + gen_tcp:controlling_process(Socket, Pid). + +-spec peername(inet:socket()) + -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. +peername(Socket) -> + inet:peername(Socket). + +-spec sockname(inet:socket()) + -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. +sockname(Socket) -> + inet:sockname(Socket). + +-spec shutdown(inet:socket(), read | write | read_write) + -> ok | {error, atom()}. +shutdown(Socket, How) -> + gen_tcp:shutdown(Socket, How). + +-spec close(inet:socket()) -> ok. +close(Socket) -> + gen_tcp:close(Socket). diff --git a/server/_build/default/lib/ranch/src/ranch_transport.erl b/server/_build/default/lib/ranch/src/ranch_transport.erl new file mode 100644 index 0000000..486c6d6 --- /dev/null +++ b/server/_build/default/lib/ranch/src/ranch_transport.erl @@ -0,0 +1,151 @@ +%% Copyright (c) 2012-2018, Loïc Hoguin <essen@ninenines.eu> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(ranch_transport). + +-export([sendfile/6]). + +-type socket() :: any(). +-export_type([socket/0]). + +-type opts() :: any(). +-type stats() :: any(). +-type sendfile_opts() :: [{chunk_size, non_neg_integer()}]. +-export_type([sendfile_opts/0]). + +-callback name() -> atom(). +-callback secure() -> boolean(). +-callback messages() -> {OK::atom(), Closed::atom(), Error::atom()}. +-callback listen(opts()) -> {ok, socket()} | {error, atom()}. +-callback accept(socket(), timeout()) + -> {ok, socket()} | {error, closed | timeout | atom()}. +-callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}. +-callback connect(string(), inet:port_number(), opts()) + -> {ok, socket()} | {error, atom()}. +-callback connect(string(), inet:port_number(), opts(), timeout()) + -> {ok, socket()} | {error, atom()}. +-callback recv(socket(), non_neg_integer(), timeout()) + -> {ok, any()} | {error, closed | timeout | atom()}. +-callback recv_proxy_header(socket(), timeout()) + -> {ok, ranch_proxy_header:proxy_info()} + | {error, closed | atom()} + | {error, protocol_error, atom()}. +-callback send(socket(), iodata()) -> ok | {error, atom()}. +-callback sendfile(socket(), file:name_all() | file:fd()) + -> {ok, non_neg_integer()} | {error, atom()}. +-callback sendfile(socket(), file:name_all() | file:fd(), non_neg_integer(), + non_neg_integer()) -> {ok, non_neg_integer()} | {error, atom()}. +-callback sendfile(socket(), file:name_all() | file:fd(), non_neg_integer(), + non_neg_integer(), sendfile_opts()) + -> {ok, non_neg_integer()} | {error, atom()}. +-callback setopts(socket(), opts()) -> ok | {error, atom()}. +-callback getopts(socket(), [atom()]) -> {ok, opts()} | {error, atom()}. +-callback getstat(socket()) -> {ok, stats()} | {error, atom()}. +-callback getstat(socket(), [atom()]) -> {ok, stats()} | {error, atom()}. +-callback controlling_process(socket(), pid()) + -> ok | {error, closed | not_owner | atom()}. +-callback peername(socket()) + -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. +-callback sockname(socket()) + -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}. +-callback shutdown(socket(), read | write | read_write) + -> ok | {error, atom()}. +-callback close(socket()) -> ok. + +%% A fallback for transports that don't have a native sendfile implementation. +%% Note that the ordering of arguments is different from file:sendfile/5 and +%% that this function accepts either a raw file or a file name. +-spec sendfile(module(), socket(), file:name_all() | file:fd(), + non_neg_integer(), non_neg_integer(), sendfile_opts()) + -> {ok, non_neg_integer()} | {error, atom()}. +sendfile(Transport, Socket, Filename, Offset, Bytes, Opts) + when is_list(Filename) orelse is_atom(Filename) + orelse is_binary(Filename) -> + ChunkSize = chunk_size(Opts), + case file:open(Filename, [read, raw, binary]) of + {ok, RawFile} -> + _ = case Offset of + 0 -> + ok; + _ -> + {ok, _} = file:position(RawFile, {bof, Offset}) + end, + try + sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize) + after + ok = file:close(RawFile) + end; + {error, _Reason} = Error -> + Error + end; +sendfile(Transport, Socket, RawFile, Offset, Bytes, Opts) -> + ChunkSize = chunk_size(Opts), + Initial2 = case file:position(RawFile, {cur, 0}) of + {ok, Offset} -> + Offset; + {ok, Initial} -> + {ok, _} = file:position(RawFile, {bof, Offset}), + Initial + end, + case sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize) of + {ok, _Sent} = Result -> + {ok, _} = file:position(RawFile, {bof, Initial2}), + Result; + {error, _Reason} = Error -> + Error + end. + +-spec chunk_size(sendfile_opts()) -> pos_integer(). +chunk_size(Opts) -> + case lists:keyfind(chunk_size, 1, Opts) of + {chunk_size, ChunkSize} + when is_integer(ChunkSize) andalso ChunkSize > 0 -> + ChunkSize; + {chunk_size, 0} -> + 16#1FFF; + false -> + 16#1FFF + end. + +-spec sendfile_loop(module(), socket(), file:fd(), non_neg_integer(), + non_neg_integer(), pos_integer()) + -> {ok, non_neg_integer()} | {error, any()}. +sendfile_loop(_Transport, _Socket, _RawFile, Sent, Sent, _ChunkSize) + when Sent =/= 0 -> + %% All requested data has been read and sent, return number of bytes sent. + {ok, Sent}; +sendfile_loop(Transport, Socket, RawFile, Bytes, Sent, ChunkSize) -> + ReadSize = read_size(Bytes, Sent, ChunkSize), + case file:read(RawFile, ReadSize) of + {ok, IoData} -> + case Transport:send(Socket, IoData) of + ok -> + Sent2 = iolist_size(IoData) + Sent, + sendfile_loop(Transport, Socket, RawFile, Bytes, Sent2, + ChunkSize); + {error, _Reason} = Error -> + Error + end; + eof -> + {ok, Sent}; + {error, _Reason} = Error -> + Error + end. + +-spec read_size(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> + non_neg_integer(). +read_size(0, _Sent, ChunkSize) -> + ChunkSize; +read_size(Bytes, Sent, ChunkSize) -> + min(Bytes - Sent, ChunkSize). |