fix(schema): support hostname.domain:port for mqtt bridge

This commit is contained in:
Zaiming (Stone) Shi 2022-09-06 16:52:22 +02:00
parent b2d69b85dc
commit 00e4b4da5a
5 changed files with 78 additions and 20 deletions

View File

@ -40,6 +40,7 @@
-type comma_separated_atoms() :: [atom()].
-type bar_separated_list() :: list().
-type ip_port() :: tuple().
-type host_port() :: tuple().
-type cipher() :: map().
-typerefl_from_string({duration/0, emqx_schema, to_duration}).
@ -52,6 +53,7 @@
-typerefl_from_string({comma_separated_binary/0, emqx_schema, to_comma_separated_binary}).
-typerefl_from_string({bar_separated_list/0, emqx_schema, to_bar_separated_list}).
-typerefl_from_string({ip_port/0, emqx_schema, to_ip_port}).
-typerefl_from_string({host_port/0, emqx_schema, to_host_port}).
-typerefl_from_string({cipher/0, emqx_schema, to_erl_cipher_suite}).
-typerefl_from_string({comma_separated_atoms/0, emqx_schema, to_comma_separated_atoms}).
@ -78,6 +80,7 @@
to_comma_separated_binary/1,
to_bar_separated_list/1,
to_ip_port/1,
to_host_port/1,
to_erl_cipher_suite/1,
to_comma_separated_atoms/1
]).
@ -96,6 +99,7 @@
comma_separated_binary/0,
bar_separated_list/0,
ip_port/0,
host_port/0,
cipher/0,
comma_separated_atoms/0
]).
@ -2167,33 +2171,60 @@ to_bar_separated_list(Str) ->
%% - :1883
%% - :::1883
to_ip_port(Str) ->
case split_ip_port(Str) of
{"", Port} ->
{ok, {{0, 0, 0, 0}, list_to_integer(Port)}};
{Ip, Port} ->
to_host_port(Str, ip_addr).
%% @doc support the following format:
%% - 127.0.0.1:1883
%% - ::1:1883
%% - [::1]:1883
%% - :1883
%% - :::1883
%% - example.com:80
to_host_port(Str) ->
to_host_port(Str, hostname).
%% - example.com:80
to_host_port(Str, IpOrHost) ->
case split_host_port(Str) of
{"", Port} when IpOrHost =:= ip_addr ->
%% this is a local address
{ok, list_to_integer(Port)};
{"", _Port} ->
%% must specify host part when it's a remote endpoint
{error, bad_host_port};
{MaybeIp, Port} ->
PortVal = list_to_integer(Port),
case inet:parse_address(Ip) of
{ok, R} ->
{ok, {R, PortVal}};
_ ->
case inet:parse_address(MaybeIp) of
{ok, IpTuple} ->
{ok, {IpTuple, PortVal}};
_ when IpOrHost =:= hostname ->
%% check is a rfc1035's hostname
case inet_parse:domain(Ip) of
case inet_parse:domain(MaybeIp) of
true ->
{ok, {Ip, PortVal}};
{ok, {MaybeIp, PortVal}};
_ ->
{error, Str}
end
{error, bad_hostname}
end;
_ ->
{error, Str}
{error, bad_ip_port}
end;
_ ->
{error, bad_ip_port}
end.
split_ip_port(Str0) ->
split_host_port(Str0) ->
Str = re:replace(Str0, " ", "", [{return, list}, global]),
case lists:split(string:rchr(Str, $:), Str) of
%% no port
%% no colon
{[], Str} ->
error;
try
%% if it's just a port number, then return as-is
_ = list_to_integer(Str),
{"", Str}
catch
_:_ ->
error
end;
{IpPlusColon, PortString} ->
IpStr0 = lists:droplast(IpPlusColon),
case IpStr0 of

View File

@ -175,3 +175,30 @@ ssl_opts_gc_after_handshake_test_not_rancher_listener_test() ->
Checked
),
ok.
to_ip_port_test_() ->
Ip = fun emqx_schema:to_ip_port/1,
Host = fun(Str) ->
case Ip(Str) of
{ok, {_, _} = Res} ->
%% assert
{ok, Res} = emqx_schema:to_host_port(Str);
_ ->
emqx_schema:to_host_port(Str)
end
end,
[
?_assertEqual({ok, 80}, Ip("80")),
?_assertEqual({error, bad_host_port}, Host("80")),
?_assertEqual({ok, 80}, Ip(":80")),
?_assertEqual({error, bad_host_port}, Host(":80")),
?_assertEqual({error, bad_ip_port}, Ip("localhost:80")),
?_assertEqual({ok, {"localhost", 80}}, Host("localhost:80")),
?_assertEqual({ok, {"example.com", 80}}, Host("example.com:80")),
?_assertEqual({ok, {{127, 0, 0, 1}, 80}}, Ip("127.0.0.1:80")),
?_assertEqual({error, bad_ip_port}, Ip("$:1900")),
?_assertEqual({error, bad_hostname}, Host("$:1900")),
?_assertMatch({ok, {_, 1883}}, Ip("[::1]:1883")),
?_assertMatch({ok, {_, 1883}}, Ip("::1:1883")),
?_assertMatch({ok, {_, 1883}}, Ip(":::1883"))
].

View File

@ -594,7 +594,7 @@ t_remote(_) ->
try
{ok, ClientPidLocal} = emqtt:connect(ConnPidLocal),
{ok, ClientPidRemote} = emqtt:connect(ConnPidRemote),
{ok, _ClientPidRemote} = emqtt:connect(ConnPidRemote),
emqtt:subscribe(ConnPidRemote, {<<"$share/remote_group/", Topic/binary>>, 0}),

View File

@ -55,7 +55,7 @@ fields("connector") ->
)},
{server,
sc(
emqx_schema:ip_port(),
emqx_schema:host_port(),
#{
required => true,
desc => ?DESC("server")

View File

@ -656,8 +656,8 @@ typename_to_spec("file()", _Mod) ->
#{type => string, example => <<"/path/to/file">>};
typename_to_spec("ip_port()", _Mod) ->
#{type => string, example => <<"127.0.0.1:80">>};
typename_to_spec("ip_ports()", _Mod) ->
#{type => string, example => <<"127.0.0.1:80, 127.0.0.2:80">>};
typename_to_spec("host_port()", _Mod) ->
#{type => string, example => <<"example.host.domain:80">>};
typename_to_spec("url()", _Mod) ->
#{type => string, example => <<"http://127.0.0.1">>};
typename_to_spec("connect_timeout()", Mod) ->