1 |
|
%% @doc Utilities related to listener configuration options |
2 |
|
|
3 |
|
-module(mongoose_listener_config). |
4 |
|
|
5 |
|
-export([ensure_ip_options/1, |
6 |
|
verify_unique_listeners/1, |
7 |
|
prepare_opts/1, |
8 |
|
filter_socket_opts/1, |
9 |
|
listener_id/1]). |
10 |
|
|
11 |
|
-type listener() :: #{port := inet:port_number(), |
12 |
|
ip_tuple := inet:ip_address(), |
13 |
|
ip_address := string(), |
14 |
|
ip_version := 4 | 6, |
15 |
|
proto := proto(), |
16 |
|
any() => any()}. |
17 |
|
-type listener_id() :: {inet:port_number(), inet:ip_address(), proto()}. |
18 |
|
-type proto() :: tcp | udp. |
19 |
|
|
20 |
|
-export_type([listener/0, listener_id/0, proto/0]). |
21 |
|
|
22 |
|
%% @doc Fill in IP-related options that can be calculated automatically. |
23 |
|
%% Apart from these options, the input should be a complete listener configuration. |
24 |
|
-spec ensure_ip_options(map()) -> listener(). |
25 |
|
ensure_ip_options(Opts = #{ip_address := IPAddr, ip_version := 4}) -> |
26 |
:-( |
{ok, IPTuple} = inet:parse_ipv4_address(IPAddr), |
27 |
:-( |
Opts#{ip_tuple => IPTuple}; |
28 |
|
ensure_ip_options(Opts = #{ip_address := IPAddr, ip_version := 6}) -> |
29 |
:-( |
{ok, IPTuple} = inet:parse_ipv6_address(IPAddr), |
30 |
:-( |
Opts#{ip_tuple => IPTuple}; |
31 |
|
ensure_ip_options(Opts = #{ip_address := IPAddr}) -> |
32 |
931 |
{ok, IPTuple} = inet:parse_address(IPAddr), |
33 |
931 |
Opts#{ip_tuple => IPTuple, |
34 |
|
ip_version => ip_version(IPTuple)}; |
35 |
|
ensure_ip_options(Opts = #{ip_version := 6}) -> |
36 |
:-( |
ensure_ip_options(Opts#{ip_address => "::"}); |
37 |
|
ensure_ip_options(Opts) -> |
38 |
478 |
ensure_ip_options(Opts#{ip_address => "0"}). |
39 |
|
|
40 |
931 |
ip_version(T) when tuple_size(T) =:= 4 -> 4; |
41 |
:-( |
ip_version(T) when tuple_size(T) =:= 8 -> 6. |
42 |
|
|
43 |
|
%% @doc Verify that all listeners have unique socket addresses |
44 |
|
-spec verify_unique_listeners([listener()]) -> [listener()]. |
45 |
|
verify_unique_listeners(Listeners) -> |
46 |
80 |
Counts = lists:foldl(fun(L, Cts) -> |
47 |
931 |
maps:update_with(listener_id(L), fun(Ct) -> Ct + 1 end, 1, Cts) |
48 |
|
end, #{}, Listeners), |
49 |
80 |
case [K || {K, V} <- maps:to_list(Counts), V > 1] of |
50 |
80 |
[] -> Listeners; |
51 |
:-( |
Dups -> error(#{what => duplicate_listeners, duplicates => Dups, |
52 |
|
text => <<"Some listeners have duplicate listening socket addresses">>}) |
53 |
|
end. |
54 |
|
|
55 |
|
%% @doc Convert listener configuration to options that can be passed to listener modules |
56 |
|
-spec prepare_opts(listener()) -> proplists:proplist(). |
57 |
|
prepare_opts(Listener) -> |
58 |
980 |
lists:flatmap(fun prepare_opt/1, maps:to_list(Listener)). |
59 |
|
|
60 |
253 |
prepare_opt({tls, Opts}) -> Opts; |
61 |
980 |
prepare_opt({ip_version, 4}) -> [inet]; |
62 |
:-( |
prepare_opt({ip_version, 6}) -> [inet6]; |
63 |
980 |
prepare_opt({ip_tuple, Val}) -> [{ip, Val}]; |
64 |
6602 |
prepare_opt(Opt) -> [Opt]. |
65 |
|
|
66 |
|
%% @doc Filter listener options, leaving only socket-related ones |
67 |
|
-spec filter_socket_opts(proplists:proplist()) -> proplists:proplist(). |
68 |
|
filter_socket_opts(Opts) -> |
69 |
416 |
lists:filter(fun filter_socket_opt/1, Opts). |
70 |
|
|
71 |
416 |
filter_socket_opt(inet) -> true; |
72 |
:-( |
filter_socket_opt(inet6) -> true; |
73 |
416 |
filter_socket_opt({ip, _}) -> true; |
74 |
:-( |
filter_socket_opt({backlog, _}) -> true; |
75 |
3593 |
filter_socket_opt(_) -> false. |
76 |
|
|
77 |
|
%% @doc Create a unique ID based on the listening socket address |
78 |
|
-spec listener_id(listener()) -> listener_id(). |
79 |
|
listener_id(#{port := Port, ip_tuple := IPTuple, proto := Proto}) -> |
80 |
2891 |
{Port, IPTuple, Proto}. |