emqx_client_SUITE.erl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2018-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_client_SUITE).
  17. -compile(export_all).
  18. -compile(nowarn_export_all).
  19. -import(lists, [nth/2]).
  20. -include_lib("emqx/include/emqx_mqtt.hrl").
  21. -include_lib("emqx/include/emqx_hooks.hrl").
  22. -include_lib("emqx/include/asserts.hrl").
  23. -include_lib("eunit/include/eunit.hrl").
  24. -include_lib("common_test/include/ct.hrl").
  25. -include_lib("snabbkaffe/include/snabbkaffe.hrl").
  26. -define(TOPICS, [
  27. <<"TopicA">>,
  28. <<"TopicA/B">>,
  29. <<"Topic/C">>,
  30. <<"TopicA/C">>,
  31. <<"/TopicA">>
  32. ]).
  33. -define(WILD_TOPICS, [
  34. <<"TopicA/+">>,
  35. <<"+/C">>,
  36. <<"#">>,
  37. <<"/#">>,
  38. <<"/+">>,
  39. <<"+/+">>,
  40. <<"TopicA/#">>
  41. ]).
  42. -define(WAIT(EXPR, ATTEMPTS), ?retry(1000, ATTEMPTS, EXPR)).
  43. all() ->
  44. [
  45. {group, mqttv3},
  46. {group, mqttv4},
  47. {group, mqttv5},
  48. {group, others}
  49. ].
  50. groups() ->
  51. [
  52. {mqttv3, [non_parallel_tests], [t_basic_v3]},
  53. {mqttv4, [non_parallel_tests], [
  54. t_basic_v4,
  55. t_cm,
  56. t_cm_registry,
  57. %% t_will_message,
  58. %% t_offline_message_queueing,
  59. t_overlapping_subscriptions,
  60. %% t_keepalive,
  61. %% t_redelivery_on_reconnect,
  62. %% subscribe_failure_test,
  63. t_dollar_topics,
  64. t_sub_non_utf8_topic
  65. ]},
  66. {mqttv5, [non_parallel_tests], [t_basic_with_props_v5, t_v5_receive_maximim_in_connack]},
  67. {others, [non_parallel_tests], [
  68. t_username_as_clientid,
  69. t_certcn_as_alias,
  70. t_certdn_as_alias,
  71. t_client_attr_from_user_property,
  72. t_certcn_as_clientid_default_config_tls,
  73. t_certcn_as_clientid_tlsv1_3,
  74. t_certcn_as_clientid_tlsv1_2,
  75. t_peercert_preserved_before_connected
  76. ]}
  77. ].
  78. init_per_suite(Config) ->
  79. Apps = emqx_cth_suite:start(
  80. [{emqx, "listeners.ssl.default.ssl_options.verify = verify_peer"}],
  81. #{work_dir => emqx_cth_suite:work_dir(Config)}
  82. ),
  83. [{apps, Apps} | Config].
  84. end_per_suite(Config) ->
  85. emqx_cth_suite:stop(?config(apps, Config)).
  86. init_per_testcase(_Case, Config) ->
  87. Config.
  88. end_per_testcase(_Case, _Config) ->
  89. emqx_config:put_zone_conf(default, [mqtt, idle_timeout], 15000).
  90. %%--------------------------------------------------------------------
  91. %% Test cases for MQTT v3
  92. %%--------------------------------------------------------------------
  93. t_basic_v3(_) ->
  94. run_basic([{proto_ver, v3}]).
  95. %%--------------------------------------------------------------------
  96. %% Test cases for MQTT v4
  97. %%--------------------------------------------------------------------
  98. t_basic_v4(_Config) ->
  99. run_basic([{proto_ver, v4}]).
  100. t_cm(_) ->
  101. emqx_config:put_zone_conf(default, [mqtt, idle_timeout], 1000),
  102. ClientId = atom_to_binary(?FUNCTION_NAME),
  103. {ok, C} = emqtt:start_link([{clientid, ClientId}]),
  104. {ok, _} = emqtt:connect(C),
  105. ?WAIT(#{clientinfo := #{clientid := ClientId}} = emqx_cm:get_chan_info(ClientId), 2),
  106. emqtt:subscribe(C, <<"mytopic">>, 0),
  107. ?WAIT(
  108. begin
  109. Stats = emqx_cm:get_chan_stats(ClientId),
  110. ?assertEqual(1, proplists:get_value(subscriptions_cnt, Stats))
  111. end,
  112. 2
  113. ),
  114. ok.
  115. t_idle_timeout_infinity(_) ->
  116. emqx_config:put_zone_conf(default, [mqtt, idle_timeout], infinity),
  117. ClientId = atom_to_binary(?FUNCTION_NAME),
  118. {ok, C} = emqtt:start_link([{clientid, ClientId}]),
  119. {ok, _} = emqtt:connect(C),
  120. ?WAIT(#{clientinfo := #{clientid := ClientId}} = emqx_cm:get_chan_info(ClientId), 2),
  121. emqtt:subscribe(C, <<"mytopic">>, 0),
  122. ?WAIT(
  123. begin
  124. Stats = emqx_cm:get_chan_stats(ClientId),
  125. ?assertEqual(1, proplists:get_value(subscriptions_cnt, Stats))
  126. end,
  127. 2
  128. ),
  129. ok.
  130. t_cm_registry(_) ->
  131. Children = supervisor:which_children(emqx_cm_sup),
  132. {_, Pid, _, _} = lists:keyfind(emqx_cm_registry, 1, Children),
  133. ignored = gen_server:call(Pid, <<"Unexpected call">>),
  134. gen_server:cast(Pid, <<"Unexpected cast">>),
  135. Pid ! <<"Unexpected info">>.
  136. t_will_message(_Config) ->
  137. {ok, C1} = emqtt:start_link([
  138. {clean_start, true},
  139. {will_topic, nth(3, ?TOPICS)},
  140. {will_payload, <<"client disconnected">>},
  141. {keepalive, 1}
  142. ]),
  143. {ok, _} = emqtt:connect(C1),
  144. {ok, C2} = emqtt:start_link(),
  145. {ok, _} = emqtt:connect(C2),
  146. {ok, _, [2]} = emqtt:subscribe(C2, nth(3, ?TOPICS), 2),
  147. timer:sleep(5),
  148. ok = emqtt:stop(C1),
  149. timer:sleep(5),
  150. ?assertEqual(1, length(recv_msgs(1))),
  151. ok = emqtt:disconnect(C2),
  152. ct:pal("Will message test succeeded").
  153. t_offline_message_queueing(_) ->
  154. {ok, C1} = emqtt:start_link([
  155. {clean_start, false},
  156. {clientid, <<"c1">>}
  157. ]),
  158. {ok, _} = emqtt:connect(C1),
  159. {ok, _, [2]} = emqtt:subscribe(C1, nth(6, ?WILD_TOPICS), 2),
  160. ok = emqtt:disconnect(C1),
  161. {ok, C2} = emqtt:start_link([
  162. {clean_start, true},
  163. {clientid, <<"c2">>}
  164. ]),
  165. {ok, _} = emqtt:connect(C2),
  166. ok = emqtt:publish(C2, nth(2, ?TOPICS), <<"qos 0">>, 0),
  167. {ok, _} = emqtt:publish(C2, nth(3, ?TOPICS), <<"qos 1">>, 1),
  168. {ok, _} = emqtt:publish(C2, nth(4, ?TOPICS), <<"qos 2">>, 2),
  169. timer:sleep(10),
  170. emqtt:disconnect(C2),
  171. {ok, C3} = emqtt:start_link([{clean_start, false}, {clientid, <<"c1">>}]),
  172. {ok, _} = emqtt:connect(C3),
  173. timer:sleep(10),
  174. emqtt:disconnect(C3),
  175. ?assertEqual(3, length(recv_msgs(3))).
  176. t_overlapping_subscriptions(_) ->
  177. {ok, C} = emqtt:start_link([]),
  178. {ok, _} = emqtt:connect(C),
  179. {ok, _, [2, 1]} = emqtt:subscribe(C, [
  180. {nth(7, ?WILD_TOPICS), 2},
  181. {nth(1, ?WILD_TOPICS), 1}
  182. ]),
  183. timer:sleep(10),
  184. {ok, _} = emqtt:publish(C, nth(4, ?TOPICS), <<"overlapping topic filters">>, 2),
  185. timer:sleep(10),
  186. Num = length(recv_msgs(2)),
  187. ?assert(lists:member(Num, [1, 2])),
  188. if
  189. Num == 1 ->
  190. ct:pal(
  191. "This server is publishing one message for all\n"
  192. " matching overlapping subscriptions, not one for each."
  193. );
  194. Num == 2 ->
  195. ct:pal(
  196. "This server is publishing one message per each\n"
  197. " matching overlapping subscription."
  198. );
  199. true ->
  200. ok
  201. end,
  202. emqtt:disconnect(C).
  203. %% t_keepalive_test(_) ->
  204. %% ct:print("Keepalive test starting"),
  205. %% {ok, C1, _} = emqtt:start_link([{clean_start, true},
  206. %% {keepalive, 5},
  207. %% {will_flag, true},
  208. %% {will_topic, nth(5, ?TOPICS)},
  209. %% %% {will_qos, 2},
  210. %% {will_payload, <<"keepalive expiry">>}]),
  211. %% ok = emqtt:pause(C1),
  212. %% {ok, C2, _} = emqtt:start_link([{clean_start, true},
  213. %% {keepalive, 0}]),
  214. %% {ok, _, [2]} = emqtt:subscribe(C2, nth(5, ?TOPICS), 2),
  215. %% ok = emqtt:disconnect(C2),
  216. %% ?assertEqual(1, length(recv_msgs(1))),
  217. %% ct:print("Keepalive test succeeded").
  218. t_redelivery_on_reconnect(_) ->
  219. ct:pal("Redelivery on reconnect test starting"),
  220. {ok, C1} = emqtt:start_link([{clean_start, false}, {clientid, <<"c">>}]),
  221. {ok, _} = emqtt:connect(C1),
  222. {ok, _, [2]} = emqtt:subscribe(C1, nth(7, ?WILD_TOPICS), 2),
  223. timer:sleep(10),
  224. ok = emqtt:pause(C1),
  225. {ok, _} = emqtt:publish(
  226. C1,
  227. nth(2, ?TOPICS),
  228. <<>>,
  229. [{qos, 1}, {retain, false}]
  230. ),
  231. {ok, _} = emqtt:publish(
  232. C1,
  233. nth(4, ?TOPICS),
  234. <<>>,
  235. [{qos, 2}, {retain, false}]
  236. ),
  237. timer:sleep(10),
  238. ok = emqtt:disconnect(C1),
  239. ?assertEqual(0, length(recv_msgs(2))),
  240. {ok, C2} = emqtt:start_link([{clean_start, false}, {clientid, <<"c">>}]),
  241. {ok, _} = emqtt:connect(C2),
  242. timer:sleep(10),
  243. ok = emqtt:disconnect(C2),
  244. ?assertEqual(2, length(recv_msgs(2))).
  245. %% t_subscribe_sys_topics(_) ->
  246. %% ct:print("Subscribe failure test starting"),
  247. %% {ok, C, _} = emqtt:start_link([]),
  248. %% {ok, _, [2]} = emqtt:subscribe(C, <<"$SYS/#">>, 2),
  249. %% timer:sleep(10),
  250. %% ct:print("Subscribe failure test succeeded").
  251. t_dollar_topics(_) ->
  252. ct:pal("$ topics test starting"),
  253. {ok, C} = emqtt:start_link([
  254. {clean_start, true},
  255. {keepalive, 0}
  256. ]),
  257. {ok, _} = emqtt:connect(C),
  258. {ok, _, [1]} = emqtt:subscribe(C, nth(6, ?WILD_TOPICS), 1),
  259. {ok, _} = emqtt:publish(
  260. C,
  261. <<<<"$">>/binary, (nth(2, ?TOPICS))/binary>>,
  262. <<"test">>,
  263. [{qos, 1}, {retain, false}]
  264. ),
  265. timer:sleep(10),
  266. ?assertEqual(0, length(recv_msgs(1))),
  267. ok = emqtt:disconnect(C),
  268. ct:pal("$ topics test succeeded").
  269. t_sub_non_utf8_topic(_) ->
  270. {ok, Socket} = gen_tcp:connect({127, 0, 0, 1}, 1883, [{active, true}, binary]),
  271. ConnPacket = emqx_frame:serialize(#mqtt_packet{
  272. header = #mqtt_packet_header{type = 1},
  273. variable = #mqtt_packet_connect{
  274. clientid = <<"abcdefg">>
  275. }
  276. }),
  277. ok = gen_tcp:send(Socket, ConnPacket),
  278. receive
  279. {tcp, _, _ConnAck = <<32, 2, 0, 0>>} -> ok
  280. after 3000 -> ct:fail({connect_ack_not_recv, process_info(self(), messages)})
  281. end,
  282. SubHeader = <<130, 18, 25, 178>>,
  283. SubTopicLen = <<0, 13>>,
  284. %% this is not a valid utf8 topic
  285. SubTopic = <<128, 10, 10, 12, 178, 159, 162, 47, 115, 1, 1, 1, 1>>,
  286. SubQoS = <<1>>,
  287. SubPacket = <<SubHeader/binary, SubTopicLen/binary, SubTopic/binary, SubQoS/binary>>,
  288. ok = gen_tcp:send(Socket, SubPacket),
  289. receive
  290. {tcp_closed, _} -> ok
  291. after 3000 -> ct:fail({should_get_disconnected, process_info(self(), messages)})
  292. end,
  293. timer:sleep(1000),
  294. ListenerCounts = emqx_listeners:shutdown_count('tcp:default', {{0, 0, 0, 0}, 1883}),
  295. TopicInvalidCount = proplists:get_value(topic_filter_invalid, ListenerCounts),
  296. ?assert(is_integer(TopicInvalidCount) andalso TopicInvalidCount > 0),
  297. ok.
  298. %%--------------------------------------------------------------------
  299. %% Test cases for MQTT v5
  300. %%--------------------------------------------------------------------
  301. v5_conn_props(ReceiveMaximum) ->
  302. [
  303. {proto_ver, v5},
  304. {properties, #{'Receive-Maximum' => ReceiveMaximum}}
  305. ].
  306. t_basic_with_props_v5(_) ->
  307. run_basic(v5_conn_props(4)).
  308. t_v5_receive_maximim_in_connack(_) ->
  309. ReceiveMaximum = 7,
  310. {ok, C} = emqtt:start_link(v5_conn_props(ReceiveMaximum)),
  311. {ok, Props} = emqtt:connect(C),
  312. ?assertMatch(#{'Receive-Maximum' := ReceiveMaximum}, Props),
  313. ok = emqtt:disconnect(C),
  314. ok.
  315. %%--------------------------------------------------------------------
  316. %% General test cases.
  317. %%--------------------------------------------------------------------
  318. run_basic(Opts) ->
  319. Topic = nth(1, ?TOPICS),
  320. {ok, C} = emqtt:start_link(Opts),
  321. {ok, _} = emqtt:connect(C),
  322. {ok, _, [1]} = emqtt:subscribe(C, Topic, qos1),
  323. {ok, _, [2]} = emqtt:subscribe(C, Topic, qos2),
  324. {ok, _} = emqtt:publish(C, Topic, <<"qos 2">>, 2),
  325. {ok, _} = emqtt:publish(C, Topic, <<"qos 2">>, 2),
  326. {ok, _} = emqtt:publish(C, Topic, <<"qos 2">>, 2),
  327. ?assertEqual(3, length(recv_msgs(3))),
  328. ok = emqtt:disconnect(C).
  329. t_username_as_clientid(_) ->
  330. emqx_config:put_zone_conf(default, [mqtt, use_username_as_clientid], true),
  331. Username = <<"usera">>,
  332. {ok, C} = emqtt:start_link([{username, Username}]),
  333. {ok, _} = emqtt:connect(C),
  334. #{clientinfo := #{clientid := Username}} = emqx_cm:get_chan_info(Username),
  335. erlang:process_flag(trap_exit, true),
  336. {ok, C1} = emqtt:start_link([{username, <<>>}]),
  337. ?assertEqual({error, {client_identifier_not_valid, undefined}}, emqtt:connect(C1)),
  338. receive
  339. {'EXIT', _, {shutdown, client_identifier_not_valid}} -> ok
  340. after 100 ->
  341. throw({error, "expect_client_identifier_not_valid"})
  342. end,
  343. emqtt:disconnect(C).
  344. t_certcn_as_alias(_) ->
  345. test_cert_extraction_as_alias(cn).
  346. t_certdn_as_alias(_) ->
  347. test_cert_extraction_as_alias(dn).
  348. test_cert_extraction_as_alias(Which) ->
  349. %% extract the first two chars
  350. ClientId = iolist_to_binary(["ClientIdFor_", atom_to_list(Which)]),
  351. {ok, Compiled} = emqx_variform:compile("substr(" ++ atom_to_list(Which) ++ ",0,2)"),
  352. emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], [
  353. #{
  354. expression => Compiled,
  355. set_as_attr => <<"alias">>
  356. }
  357. ]),
  358. SslConf = emqx_common_test_helpers:client_mtls('tlsv1.2'),
  359. {ok, Client} = emqtt:start_link([
  360. {clientid, ClientId}, {port, 8883}, {ssl, true}, {ssl_opts, SslConf}
  361. ]),
  362. {ok, _} = emqtt:connect(Client),
  363. %% assert only two chars are extracted
  364. ?assertMatch(
  365. #{clientinfo := #{client_attrs := #{<<"alias">> := <<_, _>>}}},
  366. emqx_cm:get_chan_info(ClientId)
  367. ),
  368. emqtt:disconnect(Client).
  369. t_client_attr_from_user_property(_Config) ->
  370. ClientId = atom_to_binary(?FUNCTION_NAME),
  371. {ok, Compiled} = emqx_variform:compile("user_property.group"),
  372. emqx_config:put_zone_conf(default, [mqtt, client_attrs_init], [
  373. #{
  374. expression => Compiled,
  375. set_as_attr => <<"group">>
  376. },
  377. #{
  378. expression => Compiled,
  379. set_as_attr => <<"group2">>
  380. }
  381. ]),
  382. SslConf = emqx_common_test_helpers:client_mtls('tlsv1.3'),
  383. {ok, Client} = emqtt:start_link([
  384. {clientid, ClientId},
  385. {port, 8883},
  386. {ssl, true},
  387. {ssl_opts, SslConf},
  388. {proto_ver, v5},
  389. {properties, #{'User-Property' => [{<<"group">>, <<"g1">>}]}}
  390. ]),
  391. {ok, _} = emqtt:connect(Client),
  392. %% assert only two chars are extracted
  393. ?assertMatch(
  394. #{clientinfo := #{client_attrs := #{<<"group">> := <<"g1">>, <<"group2">> := <<"g1">>}}},
  395. emqx_cm:get_chan_info(ClientId)
  396. ),
  397. emqtt:disconnect(Client).
  398. t_certcn_as_clientid_default_config_tls(_) ->
  399. tls_certcn_as_clientid(default).
  400. t_certcn_as_clientid_tlsv1_3(_) ->
  401. tls_certcn_as_clientid('tlsv1.3').
  402. t_certcn_as_clientid_tlsv1_2(_) ->
  403. tls_certcn_as_clientid('tlsv1.2').
  404. t_peercert_preserved_before_connected(_) ->
  405. ok = emqx_config:put_zone_conf(default, [mqtt, peer_cert_as_clientid], false),
  406. ok = emqx_hooks:add(
  407. 'client.connect',
  408. {?MODULE, on_hook, ['client.connect', self()]},
  409. ?HP_HIGHEST
  410. ),
  411. ok = emqx_hooks:add(
  412. 'client.connected',
  413. {?MODULE, on_hook, ['client.connected', self()]},
  414. ?HP_HIGHEST
  415. ),
  416. ClientId = atom_to_binary(?FUNCTION_NAME),
  417. SslConf = emqx_common_test_helpers:client_mtls(default),
  418. {ok, Client} = emqtt:start_link([
  419. {port, 8883},
  420. {clientid, ClientId},
  421. {ssl, true},
  422. {ssl_opts, SslConf}
  423. ]),
  424. {ok, _} = emqtt:connect(Client),
  425. _ = ?assertReceive({'client.connect', #{peercert := PC}} when is_binary(PC)),
  426. _ = ?assertReceive({'client.connected', #{peercert := PC}} when is_binary(PC)),
  427. [ConnPid] = emqx_cm:lookup_channels(ClientId),
  428. ?assertMatch(
  429. #{conninfo := ConnInfo} when not is_map_key(peercert, ConnInfo),
  430. emqx_connection:info(ConnPid)
  431. ).
  432. on_hook(ConnInfo, _, 'client.connect' = HP, Pid) ->
  433. _ = Pid ! {HP, ConnInfo},
  434. ok;
  435. on_hook(_ClientInfo, ConnInfo, 'client.connected' = HP, Pid) ->
  436. _ = Pid ! {HP, ConnInfo},
  437. ok.
  438. %%--------------------------------------------------------------------
  439. %% Helper functions
  440. %%--------------------------------------------------------------------
  441. recv_msgs(Count) ->
  442. recv_msgs(Count, []).
  443. recv_msgs(0, Msgs) ->
  444. Msgs;
  445. recv_msgs(Count, Msgs) ->
  446. receive
  447. {publish, Msg} ->
  448. recv_msgs(Count - 1, [Msg | Msgs]);
  449. %%TODO:: remove the branch?
  450. _Other ->
  451. recv_msgs(Count, Msgs)
  452. after 100 ->
  453. Msgs
  454. end.
  455. confirm_tls_version(Client, RequiredProtocol) ->
  456. Info = emqtt:info(Client),
  457. SocketInfo = proplists:get_value(socket, Info),
  458. %% emqtt_sock has #ssl_socket.ssl
  459. SSLSocket = element(3, SocketInfo),
  460. {ok, SSLInfo} = ssl:connection_information(SSLSocket),
  461. Protocol = proplists:get_value(protocol, SSLInfo),
  462. RequiredProtocol = Protocol.
  463. tls_certcn_as_clientid(default = TLSVsn) ->
  464. tls_certcn_as_clientid(TLSVsn, 'tlsv1.3');
  465. tls_certcn_as_clientid(TLSVsn) ->
  466. tls_certcn_as_clientid(TLSVsn, TLSVsn).
  467. tls_certcn_as_clientid(TLSVsn, RequiredTLSVsn) ->
  468. CN = <<"Client">>,
  469. emqx_config:put_zone_conf(default, [mqtt, peer_cert_as_clientid], cn),
  470. SslConf = emqx_common_test_helpers:client_mtls(TLSVsn),
  471. {ok, Client} = emqtt:start_link([{port, 8883}, {ssl, true}, {ssl_opts, SslConf}]),
  472. {ok, _} = emqtt:connect(Client),
  473. #{clientinfo := #{clientid := CN}} = emqx_cm:get_chan_info(CN),
  474. confirm_tls_version(Client, RequiredTLSVsn),
  475. emqtt:disconnect(Client).