aboutsummaryrefslogtreecommitdiff
path: root/server/_build/default/lib/ranch/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/_build/default/lib/ranch/src')
-rw-r--r--server/_build/default/lib/ranch/src/ranch.erl504
-rw-r--r--server/_build/default/lib/ranch/src/ranch_acceptor.erl64
-rw-r--r--server/_build/default/lib/ranch/src/ranch_acceptors_sup.erl71
-rw-r--r--server/_build/default/lib/ranch/src/ranch_app.erl44
-rw-r--r--server/_build/default/lib/ranch/src/ranch_conns_sup.erl325
-rw-r--r--server/_build/default/lib/ranch/src/ranch_crc32c.erl115
-rw-r--r--server/_build/default/lib/ranch/src/ranch_listener_sup.erl41
-rw-r--r--server/_build/default/lib/ranch/src/ranch_protocol.erl24
-rw-r--r--server/_build/default/lib/ranch/src/ranch_proxy_header.erl880
-rw-r--r--server/_build/default/lib/ranch/src/ranch_server.erl233
-rw-r--r--server/_build/default/lib/ranch/src/ranch_ssl.erl243
-rw-r--r--server/_build/default/lib/ranch/src/ranch_sup.erl40
-rw-r--r--server/_build/default/lib/ranch/src/ranch_tcp.erl245
-rw-r--r--server/_build/default/lib/ranch/src/ranch_transport.erl151
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).