1: -module(ejabberd_listener_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: 5: -include_lib("eunit/include/eunit.hrl"). 6: 7: -import(ejabberd_helper, [copy/2, 8: data/2]). 9: 10: -define(DEFAULT_PORTS, [5222, 5280, 5269]). 11: 12: all() -> 13: [tcp_socket_is_started_with_default_backlog, 14: tcp_socket_is_started_with_options, 15: tcp_socket_supports_proxy_protocol, 16: tcp_socket_has_connection_details, 17: tcp_socket_supports_proxy_protocol, 18: udp_socket_is_started_with_defaults, 19: tcp_start_stop_reload 20: ]. 21: 22: init_per_testcase(_Case, Config) -> 23: meck:new([gen_udp, gen_tcp], [unstick, passthrough]), 24: meck:expect(gen_udp, open, 25: fun(Port, Opts) -> meck:passthrough([Port, Opts]) end), 26: meck:expect(gen_tcp, listen, 27: fun(Port, Opts) -> meck:passthrough([Port, Opts]) end), 28: Config. 29: 30: end_per_testcase(_Case, Config) -> 31: meck:unload(), 32: Config. 33: 34: init_per_suite(C) -> 35: C. 36: 37: end_per_suite(_C) -> 38: mnesia:stop(), 39: mnesia:delete_schema([node()]). 40: 41: tcp_socket_is_started_with_default_backlog(_C) -> 42: {ok, _Pid} = listener_started([]), 43: 44: [{_Pid, {gen_tcp, listen, [_, Opts]}, _Result}] = meck:history(gen_tcp), 45: 46: 100 = proplists:get_value(backlog, Opts). 47: 48: 49: tcp_socket_is_started_with_options(_C) -> 50: 51: OverrideBacklog = {backlog, 50}, 52: {ok, _Pid} = listener_started([OverrideBacklog]), 53: 54: [{_Pid, {gen_tcp, listen, [_, Opts]}, _Result}] = meck:history(gen_tcp), 55: 56: 50 = proplists:get_value(backlog, Opts). 57: 58: tcp_socket_has_connection_details(_C) -> 59: {ok, _Pid} = listener_started([]), 60: 61: {Port, _, _} = tcp_port_ip(), 62: 63: meck:new(ejabberd_socket), 64: TestPid = self(), 65: meck:expect(ejabberd_socket, start, 66: fun(_Module, _SockMode, Socket, Opts) -> 67: TestPid ! {socket_started, Socket, Opts}, 68: ok 69: end), 70: 71: {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), 72: {ok, SrcPort} = inet:port(Socket), 73: 74: receive 75: {socket_started, _Socket, Opts} -> 76: ConnectionDetails = proplists:get_value(connection_details, Opts), 77: ?assertEqual(#{proxy => false, 78: src_address => {127, 0, 0, 1}, 79: src_port => SrcPort, 80: dest_address => {127, 0, 0, 1}, 81: dest_port => Port}, ConnectionDetails) 82: after 83: 5000 -> 84: ct:fail(timeout_waiting_for_tcp) 85: end. 86: 87: tcp_socket_supports_proxy_protocol(_C) -> 88: ProxyProtocol = {proxy_protocol, true}, 89: {ok, _Pid} = listener_started([ProxyProtocol]), 90: 91: CommonProxyInfo = #{src_address => {1, 2, 3, 4}, 92: src_port => 444, 93: dest_address => {192, 168, 0, 1}, 94: dest_port => 443, 95: version => 2}, 96: RanchProxyInfo = CommonProxyInfo#{command => proxy, 97: transport_family => ipv4, 98: transport_protocol => stream}, 99: {Port, _, _} = tcp_port_ip(), 100: 101: meck:new(ejabberd_socket), 102: TestPid = self(), 103: meck:expect(ejabberd_socket, start, 104: fun(_Module, _SockMode, Socket, Opts) -> 105: TestPid ! {socket_started, Socket, Opts}, 106: ok 107: end), 108: 109: {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), 110: ok = gen_tcp:send(Socket, [ranch_proxy_header:header(RanchProxyInfo)]), 111: 112: receive 113: {socket_started, _Socket, Opts} -> 114: ConnectionDetails = proplists:get_value(connection_details, Opts), 115: ?assertEqual(CommonProxyInfo#{proxy => true}, ConnectionDetails) 116: after 117: 5000 -> 118: ct:fail(timeout_waiting_for_tcp_with_proxy_protocol) 119: end. 120: 121: udp_socket_is_started_with_defaults(_C) -> 122: {ok, _Pid} = receiver_started([]), 123: 124: [{_Pid, {gen_udp, open, [_, Opts]}, _Result}] = meck:history(gen_udp), 125: 126: {0,0,0,0} = proplists:get_value(ip, Opts). 127: 128: listener_started(RawOpts) -> 129: {tcp, Opts, SockOpts, Port, IPS} = 130: ejabberd_listener:opts_to_listener_args(tcp_port_ip(), [{acceptors_num, 5} | RawOpts]), 131: mongoose_tcp_listener:start_link(tcp_port_ip(), ?MODULE, Opts, SockOpts, Port, IPS). 132: 133: receiver_started(RawOpts) -> 134: ets:new(listen_sockets, [named_table, public]), 135: {udp, Opts, SockOpts, Port, IPS} = 136: ejabberd_listener:opts_to_listener_args(udp_port_ip(), RawOpts), 137: mongoose_udp_listener:start_link(udp_port_ip(), ?MODULE, Opts, SockOpts, Port, IPS). 138: 139: udp_port_ip() -> 140: {1805, {0,0,0,0}, udp}. 141: 142: tcp_port_ip() -> 143: {1805, {0,0,0,0}, tcp}. 144: 145: tcp_start_stop_reload(C) -> 146: %% start server 147: copy(data(C, "mongooseim.basic.toml"), data(C, "mongooseim.toml")), 148: ejabberd_helper:start_ejabberd_with_config(C, "mongooseim.toml"), 149: ?assert(lists:keymember(mongooseim, 1, application:which_applications())), 150: %% make sure all ports are open 151: lists:map(fun assert_open/1, ?DEFAULT_PORTS), 152: %% stop listeners, now they should be closed 153: ejabberd_listener:stop_listeners(), 154: lists:map(fun assert_closed/1, ?DEFAULT_PORTS), 155: %% and start them all again 156: ejabberd_listener:start_listeners(), 157: lists:map(fun assert_open/1, ?DEFAULT_PORTS), 158: 159: %% we want to make sure that connection to an unchanged port survives reload 160: UnchPort = 5222, 161: {ok, Sock} = gen_tcp:connect("localhost", UnchPort,[{active, false}, {packet, 2}]), 162: assert_connected(Sock, UnchPort), 163: 164: %% and that to the changed port does too (this is current implementation) 165: ChgPort = 5269, 166: {ok, Sock2} = gen_tcp:connect("localhost", ChgPort,[{active, false}, {packet, 2}]), 167: assert_connected(Sock2, ChgPort), 168: 169: ok = ejabberd_helper:stop_ejabberd(), 170: ok. 171: 172: assert_open(PortNo) -> 173: case gen_tcp:connect("localhost", PortNo, [{active, false}, {packet, 2}]) of 174: {ok, Socket} -> 175: gen_tcp:close(Socket), 176: ok; 177: E -> 178: ct:fail("Failed: port ~p is closed, should be open; error was: ~p", [PortNo, E]) 179: end . 180: 181: assert_closed(PortNo) -> 182: F = fun() -> 183: gen_tcp:connect("localhost", PortNo, [{active, false}, {packet, 2}]) 184: end, 185: async_helper:wait_until(F, {error, econnrefused}). 186: 187: 188: assert_connected(Sock, Port) -> 189: case gen_tcp:recv(Sock, 0, 500) of 190: {error, timeout} -> 191: ok; 192: Else -> 193: ct:fail("Failed: connection to ~p is broken, error was: ~p", [Port, Else]) 194: end. 195: 196: %%assert_disconnected(Sock, Port) -> 197: %% case gen_tcp:recv(Sock, 0, 500) of 198: %% {error, timeout} -> 199: %% ct:fail("Failed: connection to ~p is still open", [Port]), 200: %% ok; 201: %% _ -> 202: %% ok 203: %% end.