1: -module(mongoose_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: tcp_start_stop_reload 19: ]. 20: 21: init_per_testcase(_Case, Config) -> 22: meck:new([gen_tcp], [unstick, passthrough]), 23: meck:expect(gen_tcp, listen, 24: fun(Port, Opts) -> meck:passthrough([Port, Opts]) end), 25: Config. 26: 27: end_per_testcase(_Case, Config) -> 28: meck:unload(), 29: Config. 30: 31: init_per_suite(C) -> 32: mnesia:start(), 33: C. 34: 35: end_per_suite(_C) -> 36: mnesia:stop(), 37: mnesia:delete_schema([node()]). 38: 39: tcp_socket_is_started_with_default_backlog(_C) -> 40: ok = listener_started(#{}), 41: [{_Pid, {gen_tcp, listen, [_, Opts]}, _Result}] = meck:history(gen_tcp), 42: 100 = proplists:get_value(backlog, Opts). 43: 44: tcp_socket_is_started_with_options(_C) -> 45: ok = listener_started(#{backlog => 50}), 46: [{_Pid, {gen_tcp, listen, [_, Opts]}, _Result}] = meck:history(gen_tcp), 47: 50 = proplists:get_value(backlog, Opts). 48: 49: tcp_socket_has_connection_details(_C) -> 50: ok = listener_started(#{}), 51: {Port, _, _} = tcp_port_ip(), 52: 53: meck:new(mongoose_transport), 54: TestPid = self(), 55: meck:expect(mongoose_transport, accept, 56: fun(_Module, Socket, Opts, ConnectionDetails) -> 57: TestPid ! {socket_started, Socket, Opts, ConnectionDetails}, 58: ok 59: end), 60: 61: {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), 62: {ok, SrcPort} = inet:port(Socket), 63: 64: receive 65: {socket_started, _Socket, _Opts, ConnectionDetails} -> 66: ?assertEqual(#{proxy => false, 67: src_address => {127, 0, 0, 1}, 68: src_port => SrcPort, 69: dest_address => {127, 0, 0, 1}, 70: dest_port => Port}, ConnectionDetails) 71: after 72: 5000 -> 73: ct:fail(timeout_waiting_for_tcp) 74: end. 75: 76: tcp_socket_supports_proxy_protocol(_C) -> 77: ok = listener_started(#{proxy_protocol => true}), 78: 79: CommonProxyInfo = #{src_address => {1, 2, 3, 4}, 80: src_port => 444, 81: dest_address => {192, 168, 0, 1}, 82: dest_port => 443, 83: version => 2}, 84: RanchProxyInfo = CommonProxyInfo#{command => proxy, 85: transport_family => ipv4, 86: transport_protocol => stream}, 87: {Port, _, _} = tcp_port_ip(), 88: 89: meck:new(mongoose_transport), 90: TestPid = self(), 91: meck:expect(mongoose_transport, accept, 92: fun(_Module, Socket, Opts, ConnectionDetails) -> 93: TestPid ! {socket_started, Socket, Opts, ConnectionDetails}, 94: ok 95: end), 96: 97: {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), 98: ok = gen_tcp:send(Socket, [ranch_proxy_header:header(RanchProxyInfo)]), 99: 100: receive 101: {socket_started, _Socket, _Opts, ConnectionDetails} -> 102: ?assertEqual(CommonProxyInfo#{proxy => true}, ConnectionDetails) 103: after 104: 5000 -> 105: ct:fail(timeout_waiting_for_tcp_with_proxy_protocol) 106: end. 107: 108: listener_started(Opts) -> 109: mim_ct_sup:start_link(ejabberd_sup), 110: mongoose_listener_sup:start_link(), 111: mongoose_listener:start_listener(maps:merge(listener_opts(), Opts)). 112: 113: tcp_port_ip() -> 114: {1805, {0, 0, 0, 0}, tcp}. 115: 116: listener_opts() -> 117: #{module => ?MODULE, 118: port => 1805, 119: ip_tuple => {0, 0, 0, 0}, 120: ip_address => "0", 121: ip_version => 4, 122: proto => tcp, 123: num_acceptors => 1, 124: backlog => 100, 125: proxy_protocol => false}. 126: 127: tcp_start_stop_reload(C) -> 128: %% start server 129: copy(data(C, "mongooseim.basic.toml"), data(C, "mongooseim.toml")), 130: ejabberd_helper:start_ejabberd_with_config(C, "mongooseim.toml"), 131: ?assert(lists:keymember(mongooseim, 1, application:which_applications())), 132: %% make sure all ports are open 133: lists:map(fun assert_open/1, ?DEFAULT_PORTS), 134: %% stop listeners, now they should be closed 135: Listeners = mongoose_config:get_opt(listen), 136: lists:foreach(fun mongoose_listener:stop_listener/1, Listeners), 137: lists:map(fun assert_closed/1, ?DEFAULT_PORTS), 138: %% and start them all again 139: lists:foreach(fun mongoose_listener:start_listener/1, Listeners), 140: lists:map(fun assert_open/1, ?DEFAULT_PORTS), 141: 142: %% we want to make sure that connection to an unchanged port survives reload 143: UnchPort = 5222, 144: {ok, Sock} = gen_tcp:connect("localhost", UnchPort,[{active, false}, {packet, 2}]), 145: assert_connected(Sock, UnchPort), 146: 147: %% and that to the changed port does too (this is current implementation) 148: ChgPort = 5269, 149: {ok, Sock2} = gen_tcp:connect("localhost", ChgPort,[{active, false}, {packet, 2}]), 150: assert_connected(Sock2, ChgPort), 151: 152: ok = mongooseim:stop(), 153: ok. 154: 155: assert_open(PortNo) -> 156: case gen_tcp:connect("localhost", PortNo, [{active, false}, {packet, 2}]) of 157: {ok, Socket} -> 158: gen_tcp:close(Socket), 159: ok; 160: E -> 161: ct:fail("Failed: port ~p is closed, should be open; error was: ~p", [PortNo, E]) 162: end . 163: 164: assert_closed(PortNo) -> 165: F = fun() -> 166: gen_tcp:connect("localhost", PortNo, [{active, false}, {packet, 2}]) 167: end, 168: async_helper:wait_until(F, {error, econnrefused}). 169: 170: 171: assert_connected(Sock, Port) -> 172: case gen_tcp:recv(Sock, 0, 500) of 173: {error, timeout} -> 174: ok; 175: Else -> 176: ct:fail("Failed: connection to ~p is broken, error was: ~p", [Port, Else]) 177: end. 178: 179: start_listener(Opts) -> 180: mongoose_tcp_listener:start_listener(Opts).