From 405de8c986fd22c05f398036edac809e98149292 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Wed, 13 Jan 2010 10:53:07 +0000 Subject: Add test suite for the ssl application --- lib/ssl/test/ssl_test_MACHINE.erl | 935 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 935 insertions(+) create mode 100644 lib/ssl/test/ssl_test_MACHINE.erl (limited to 'lib/ssl/test/ssl_test_MACHINE.erl') diff --git a/lib/ssl/test/ssl_test_MACHINE.erl b/lib/ssl/test/ssl_test_MACHINE.erl new file mode 100644 index 0000000000..e75f7079ed --- /dev/null +++ b/lib/ssl/test/ssl_test_MACHINE.erl @@ -0,0 +1,935 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(ssl_test_MACHINE). + +-export([many_conns/0, mk_ssl_cert_opts/1, test_one_listener/7, + test_server_only/6]). + +-export([process_init/3, do_start/1]). + + +-include("test_server.hrl"). +-include("ssl_test_MACHINE.hrl"). + +-define(WAIT_TIMEOUT, 10000). +-define(CLOSE_WAIT, 1000). + +%% +%% many_conns() -> ManyConnections +%% +%% Choose a suitable number of "many connections" depending on platform +%% and current limit for file descriptors. +%% +many_conns() -> + case os:type() of + {unix,_} -> many_conns_1(); + _ -> 10 + end. + +many_conns_1() -> + N0 = os:cmd("ulimit -n"), + N1 = lists:reverse(N0), + N2 = lists:dropwhile(fun($\r) -> true; + ($\n) -> true; + (_) -> false + end, N1), + N = list_to_integer(lists:reverse(N2)), + lists:min([(N - 10) div 2, 501]). + +%% +%% mk_ssl_cert_opts(Config) -> {ok, {COpts, SOpts}} +%% +%% +mk_ssl_cert_opts(_Config) -> + Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + COpts = [{cacertfile, filename:join([Dir, "client", "cacerts.pem"])}, + {certfile, filename:join([Dir, "client", "cert.pem"])}, + {keyfile, filename:join([Dir, "client", "key.pem"])}], + SOpts = [{cacertfile, filename:join([Dir, "server", "cacerts.pem"])}, + {certfile, filename:join([Dir, "server", "cert.pem"])}, + {keyfile, filename:join([Dir, "server", "key.pem"])}], + {ok, {COpts, SOpts}}. + +%% +%% Cmds: +%% {protomod, gen_tcp | ssl} default = ssl +%% {serialize_accept, true | false} default = false +%% {timeout, Timeout} +%% {sockopts, Opts} +%% {sslopts, Opts} +%% {protocols, Protocols} [sslv2|sslv3|tlsv1] +%% {listen, Port} +%% {lsock, LSock} listen socket for acceptor +%% peercert +%% accept +%% {connect, {Host, Port}} +%% {recv, N} +%% {send, N} +%% {echo, N} async echo back +%% close close connection socket +%% {close, Time} wait time and then close socket +%% lclose close listen socket +%% await_close wait for close +%% wait_sync listener's wait for sync from parent +%% connection_info +%% {exit, Reason} exit +%% +%% +%% We cannot have more than `backlog' acceptors at the same time. +%% + + +%% +%% test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, Suite, Config) +%% +%% Creates one client and one server node, and runs one listener on +%% the server node (according to LCmds), and creates NConns acceptors +%% on the server node, and the same number of connectors on the client +%% node. The acceptors and and connectors execute according to ACmds +%% and CCmds, respectively. +%% +%% It is a good idea to have the backlog size in LCmds set to +%% be at least as large as NConns. +%% +test_one_listener(NConns, LCmds0, ACmds0, CCmds0, Timeout, Suite, Config) -> + ProtoMod = get_protomod(Config), + SerializeAccept = get_serialize_accept(Config), + ?line {ok, {CNode, SNode}} = start_client_server_nodes(Suite), + case ProtoMod of + ssl -> + ?line ok = start_ssl([CNode, SNode], Config); + gen_tcp -> + ok + end, + LCmds = [{protomod, ProtoMod}| LCmds0], + ACmds = [{protomod, ProtoMod}, {serialize_accept, SerializeAccept}| + ACmds0], + CCmds = [{protomod, ProtoMod}| CCmds0], + + ?line {ok, Listener} = start_process(SNode, self(), LCmds, listener), + ?line {ok, LSock} = wait_lsock(Listener, ?WAIT_TIMEOUT), + ?line {ok, Accs0} = start_processes(NConns, SNode, self(), + [{lsock, LSock}| ACmds], acceptor), + Accs = case ProtoMod of + gen_tcp -> + [Acc1| Accs1] = Accs0, + Acc1 ! {continue_accept, self()}, + Accs1; + ssl -> + Accs0 + end, + ?line {ok, Conns} = start_processes(NConns, CNode, self(), + CCmds, connector), + ?line case wait_ack(Accs, Accs0 ++ Conns, Timeout) of + ok -> + ?line sync([Listener]), + ?line wait_ack([], [Listener], ?WAIT_TIMEOUT); + {error, Reason} -> + ?line stop_node(SNode), + ?line stop_node(CNode), + exit(Reason) + end, + ?line stop_node(SNode), + ?line stop_node(CNode), + ok. + +%% +%% test_server_only(NConns, LCmds, ACmds, Timeout, Suite, Config) +%% +%% Creates only one server node, and runs one listener on +%% the server node (according to LCmds), and creates NConns acceptors +%% on the server node. The acceptors execute according to ACmds. +%% There are no connectors. +%% +test_server_only(NConns, LCmds0, ACmds0, Timeout, Suite, Config) -> + ProtoMod = get_protomod(Config), + ?line {ok, SNode} = start_server_node(Suite), + case ProtoMod of + ssl -> + ?line ok = start_ssl([SNode], Config); + gen_tcp -> + ok + end, + LCmds = [{protomod, ProtoMod}| LCmds0], + ACmds = [{protomod, ProtoMod}| ACmds0], + ?line {ok, Listener} = start_process(SNode, self(), LCmds, listener), + ?line {ok, LSock} = wait_lsock(Listener, ?WAIT_TIMEOUT), + ?line {ok, Accs0} = start_processes(NConns, SNode, self(), + [{lsock, LSock}| ACmds], acceptor), + Accs = case ProtoMod of + gen_tcp -> + [Acc1| Accs1] = Accs0, + Acc1 ! {continue_accept, self()}, + Accs1; + ssl -> + Accs0 + end, + ?line case wait_ack(Accs, Accs0, Timeout) of + ok -> + ?line sync([Listener]), + ?line wait_ack([], [Listener], ?WAIT_TIMEOUT); + {error, Reason} -> + ?line stop_node(SNode), + exit(Reason) + end, + ?line stop_node(SNode), + ok. + +%% +%% start_client_server_nodes(Suite) -> {ok, {CNode, SNode}} +%% +start_client_server_nodes(Suite) -> + {ok, CNode} = start_client_node(Suite), + {ok, SNode} = start_server_node(Suite), + {ok, {CNode, SNode}}. + +start_client_node(Suite) -> + start_node(lists:concat([Suite, "_client"])). + +start_server_node(Suite) -> + start_node(lists:concat([Suite, "_server"])). + +%% +%% start_ssl(Nodes, Config) +%% +start_ssl(Nodes, Config) -> + Env0 = lists:flatten([Env00 || {env, Env00} <- Config]), + Env1 = case os:getenv("SSL_DEBUG") of + false -> + []; + _ -> + Dir = ?config(priv_dir, Config), + [{debug, true}, {debugdir, Dir}] + end, + Env = Env0 ++ Env1, + lists:foreach( + fun(Node) -> rpc:call(Node, ?MODULE, do_start, [Env]) end, Nodes), + ok. + +do_start(Env) -> + application:load(ssl), + lists:foreach( + fun({Par, Val}) -> application:set_env(ssl, Par, Val) end, Env), + application:start(ssl), + application:start(crypto). + +%% +%% start_node(Name) -> {ok, Node} +%% start_node(Name, ExtraParams) -> {ok, Node} +%% +start_node(Name) -> + start_node(Name, []). +start_node(Name, ExtraParams) -> + Params = "-pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ + ExtraParams, + test_server:start_node(Name, slave, [{args, Params}]). + +stop_node(Node) -> + test_server:stop_node(Node). + +%% +%% start_processes(N, Node, Parent, Cmds, Type) -> {ok, Pids} +%% +start_processes(M, Node, Parent, Cmds, Type) -> + start_processes1(0, M, Node, Parent, Cmds, Type, []). +start_processes1(M, M, _, _, _, _, Pids) -> + {ok, lists:reverse(Pids)}; +start_processes1(N, M, Node, Parent, Cmds, Type, Pids) -> + {ok, Pid} = start_process(Node, Parent, Cmds, {Type, N + 1}), + start_processes1(N + 1, M, Node, Parent, Cmds, Type, [Pid| Pids]). + +%% +%% start_process(Node, Parent, Cmds, Type) -> {ok, Pid} +%% +start_process(Node, Parent, Cmds0, Type) -> + Cmds = case os:type() of + {win32, _} -> + lists:map(fun(close) -> {close, ?CLOSE_WAIT}; + (Term) -> Term end, Cmds0); + _ -> + Cmds0 + end, + Pid = spawn_link(Node, ?MODULE, process_init, [Parent, Cmds, Type]), + {ok, Pid}. + +process_init(Parent, Cmds, Type) -> + ?debug("#### ~w start~n", [{Type, self()}]), + pre_main_loop(Cmds, #st{parent = Parent, type = Type}). + +%% +%% pre_main_loop +%% +pre_main_loop([], St) -> + ?debug("#### ~w end~n", [{St#st.type, self()}]), + main_loop([], St); +pre_main_loop(Cmds, St) -> + ?debug("#### ~w -> ~w~n", + [{St#st.type, self(), St#st.sock, St#st.port, + St#st.peer, St#st.active}, hd(Cmds)]), + main_loop(Cmds, St). + +%% +%% main_loop(Cmds, St) +%% +main_loop([{protomod, ProtoMod}| Cmds], St) -> + pre_main_loop(Cmds, St#st{protomod = ProtoMod}); + +main_loop([{serialize_accept, Bool}| Cmds], St) -> + pre_main_loop(Cmds, St#st{serialize_accept = Bool}); + +main_loop([{sockopts, Opts}| Cmds], St) -> + pre_main_loop(Cmds, St#st{sockopts = Opts}); + +main_loop([{sslopts, Opts}| Cmds], St) -> + pre_main_loop(Cmds, St#st{sslopts = Opts}); + +main_loop([{protocols, Protocols}| Cmds], St) -> + pre_main_loop(Cmds, St#st{protocols = Protocols}); + +main_loop([{timeout, T}| Cmds], St) -> + pre_main_loop(Cmds, St#st{timeout = T}); + +main_loop([{lsock, LSock}| Cmds], St) -> + pre_main_loop(Cmds, St#st{lsock = LSock}); + +main_loop([{seed, Data}| Cmds], St) -> + case ssl:seed("tjosan") of + ok -> + pre_main_loop(Cmds, St); + {error, Reason} -> + ?error("#### ~w(~w) in seed: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([{listen, Port}| Cmds], St) -> + case listen(St, Port) of + {ok, LSock} -> + ack_lsock(St#st.parent, LSock), + NSt = get_active(St#st{port = Port, sock = LSock, lsock = LSock}), + pre_main_loop(Cmds, St); + {error, Reason} -> + ?error("#### ~w(~w) in listen: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([accept| Cmds], St) -> + case St#st.serialize_accept of + true -> + Parent = St#st.parent, + receive + {continue_accept, Parent} -> + ok + end; + false -> + ok + end, + case accept(St) of + {ok, Sock, Port, Peer} -> + case St#st.serialize_accept of + true -> + St#st.parent ! {one_accept_done, self()}; + false -> + ok + end, + NSt = get_active(St#st{sock = Sock, port = Port, peer = Peer}), + pre_main_loop(Cmds, NSt); + {error, Reason} -> + ?error("#### ~w(~w) in accept: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([accept_timeout| Cmds], St) -> + case accept(St) of + {error, timeout} -> + pre_main_loop(Cmds, St); + {error, Reason} -> + ?error("#### ~w(~w) in accept_timeout: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + + +main_loop([{connect, {Host, Port}}| Cmds], St) -> + case connect(St, Host, Port) of + {ok, Sock, LPort, Peer} -> + NSt = get_active(St#st{sock = Sock, port = LPort, peer = Peer}), + pre_main_loop(Cmds, NSt); + {error, Reason} -> + ?error("#### ~w(~w) in connect: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([connection_info| Cmds], St) -> + case connection_info(St) of + {ok, ProtoInfo} -> + io:fwrite("Got connection_info:~n~p~n", [ProtoInfo]), + pre_main_loop(Cmds, St); + {error, Reason} -> + ?error("#### ~w(~w) in connection_info: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([peercert| Cmds], St) -> + case peercert(St) of + {ok, Cert} -> + io:fwrite("Got cert:~n~p~n", [Cert]), + pre_main_loop(Cmds, St); + {error, Reason} -> + ?error("#### ~w(~w) in peercert: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([nopeercert| Cmds], St) -> + case peercert(St) of + {error, Reason} -> + io:fwrite("Got no cert as expected. reason:~n~p~n", [Reason]), + pre_main_loop(Cmds, St); + {ok, Cert} -> + ?error("#### ~w(~w) in peercert: error: got cert: ~p~n", + [St#st.type, self(), Cert]), + exit(peercert) + end; + +main_loop([{recv, N}| Cmds], St) -> + recv_loop([{recv, N}| Cmds], fun recv/1, St); % Returns to main_loop/2. + +main_loop([{send, N}| Cmds], St) -> + Msg = mk_msg(N), + case send(St, Msg) of + ok -> + pre_main_loop(Cmds, St); + {error, Reason} -> + ?error("#### ~w(~w) in send: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([{echo, N}| Cmds], St) -> + recv_loop([{echo, N}| Cmds], fun echo/1, St); % Returns to main_loop/2. + +main_loop([{close, WaitTime}| Cmds], St) -> + wait(WaitTime), + pre_main_loop([close| Cmds], St); + +main_loop([close| Cmds], St) -> + case close(St) of + ok -> + pre_main_loop(Cmds, St#st{sock = nil}); + {error, Reason} -> + ?error("#### ~w(~w) in close: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([lclose| Cmds], St) -> + case lclose(St) of + ok -> + pre_main_loop(Cmds, St#st{lsock = nil}); + {error, Reason} -> + ?error("#### ~w(~w) in lclose: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([await_close| Cmds], St) -> + case await_close(St) of + ok -> + pre_main_loop(Cmds, St#st{sock = nil}); + {error, Reason} -> + ?error("#### ~w(~w) in await_close: error: ~w~n", + [St#st.type, self(), Reason]), + exit(Reason) + end; + +main_loop([wait_sync| Cmds], St) -> + wait_sync(St), + pre_main_loop(Cmds, St); + +main_loop({exit, Reason}, _St) -> + exit(Reason); + +main_loop([], _St) -> + ok. + +%% +%% recv_loop(Cmds, F, St) +%% +%% F = recv/1 | echo/1 +%% +recv_loop([{_Tag, 0}| Cmds], _, St) -> + pre_main_loop(Cmds, St); +recv_loop([{_Tag, N}| _Cmds], _, St) when N < 0 -> + ?error("#### ~w(~w) in recv_loop: error: too much: ~w~n", + [St#st.type, self(), N]), + exit(toomuch); % XXX or {error, Reason}? +recv_loop([{Tag, N}| Cmds], F, St) -> + case F(St) of + {ok, Len} -> + NSt = St#st{active = new_active(St#st.active)}, + if + Len == N -> + pre_main_loop(Cmds, NSt); + true -> + ?debug("#### ~w -> ~w~n", + [{NSt#st.type, self(), NSt#st.sock, NSt#st.port, + NSt#st.peer, NSt#st.active}, {Tag, N - Len}]), + recv_loop([{Tag, N - Len}| Cmds], F, NSt) + end; + {error, Reason} -> + ?error("#### ~w(~w) in recv_loop: error: ~w, ~w bytes remain~n", + [St#st.type, self(), Reason, N]), + exit(Reason) + end. + +new_active(once) -> + false; +new_active(A) -> + A. + +get_active(St) -> + A = case proplists:get_value(active, St#st.sockopts, undefined) of + undefined -> + Mod = case St#st.protomod of + ssl -> + ssl; + gen_tcp -> + inet + end, + {ok, [{active, Ax}]} = Mod:getopts(St#st.sock, [active]), + Ax; + Ay -> + Ay + end, + ?debug("#### ~w(~w) get_active: ~p\n", [St#st.type, self(), A]), + St#st{active = A}. + + +%% +%% SOCKET FUNCTIONS +%% + +%% +%% ssl +%% + +%% +%% listen(St, LPort) -> {ok, LSock} | {error, Reason} +%% +listen(St, LPort) -> + case St#st.protomod of + ssl -> + ssl:listen(LPort, St#st.sockopts ++ St#st.sslopts); + gen_tcp -> + gen_tcp:listen(LPort, St#st.sockopts) + end. + +%% +%% accept(St) -> {ok, Sock} | {error, Reason} +%% +accept(St) -> + case St#st.protomod of + ssl -> + case ssl:transport_accept(St#st.lsock, St#st.timeout) of + {ok, Sock} -> + case ssl:ssl_accept(Sock, St#st.timeout) of + ok -> + {ok, Port} = ssl:sockname(Sock), + {ok, Peer} = ssl:peername(Sock), + {ok, Sock, Port, Peer}; + Other -> + Other + end; + Other -> + Other + end; + gen_tcp -> + case gen_tcp:accept(St#st.lsock, St#st.timeout) of + {ok, Sock} -> + {ok, Port} = inet:port(Sock), + {ok, Peer} = inet:peername(Sock), + {ok, Sock, Port, Peer}; + Other -> + Other + end + end. + +%% +%% connect(St, Host, Port) -> {ok, Sock} | {error, Reason} +%% +connect(St, Host, Port) -> + + case St#st.protomod of + ssl -> + case ssl:connect(Host, Port, St#st.sockopts ++ St#st.sslopts, + St#st.timeout) of + {ok, Sock} -> + {ok, LPort} = ssl:sockname(Sock), + {ok, Peer} = ssl:peername(Sock), + {ok, Sock, LPort, Peer}; + Other -> + Other + end; + gen_tcp -> + case gen_tcp:connect(Host, Port, St#st.sockopts, St#st.timeout) of + {ok, Sock} -> + {ok, LPort} = inet:port(Sock), + {ok, Peer} = inet:peername(Sock), + {ok, Sock, LPort, Peer}; + Other -> + Other + end + end. + +%% +%% peercert(St) -> {ok, Cert} | {error, Reason} +%% +peercert(St) -> + case St#st.protomod of + ssl -> + ssl:peercert(St#st.sock, [ssl]); + gen_tcp -> + {ok, <<>>} + end. + +%% +%% connection_info(St) -> {ok, ProtoInfo} | {error, Reason} +%% +connection_info(St) -> + case St#st.protomod of + ssl -> + case ssl:connection_info(St#st.sock) of + Res = {ok, {Proto, _}} -> + case St#st.protocols of + [] -> + Res; + Protocols -> + case lists:member(Proto, Protocols) of + true -> + Res; + false -> + {error, Proto} + end + end; + Error -> + Error + end; + gen_tcp -> + {ok, <<>>} + end. + +%% +%% close(St) -> ok | {error, Reason} +%% + +close(St) -> + Mod = St#st.protomod, + case St#st.sock of + nil -> + ok; + _ -> + Mod:close(St#st.sock) + end. + +%% +%% lclose(St) -> ok | {error, Reason} +%% +lclose(St) -> + Mod = St#st.protomod, + case St#st.lsock of + nil -> + ok; + _ -> + Mod:close(St#st.lsock) + end. + +%% +%% recv(St) = {ok, Len} | {error, Reason} +%% +recv(St) -> + case do_recv(St) of + {ok, Msg} -> + {ok, length(Msg)}; + {error, Reason} -> + {error, Reason} + end. + +do_recv(St) when St#st.active == false -> + %% First check that we do *not* have any ssl/gen_tcp messages in the + %% message queue, then call the receive function. + Sock = St#st.sock, + case St#st.protomod of + ssl -> + receive + M = {ssl, Sock, _Msg} -> + {error, {unexpected_messagex, M}}; + M = {ssl_closed, Sock} -> + {error, {unexpected_message, M}}; + M = {ssl_error, Sock, _Reason} -> + {error, {unexpected_message, M}} + after 0 -> + ssl:recv(St#st.sock, 0, St#st.timeout) + end; + gen_tcp -> + receive + M = {tcp, Sock, _Msg} -> + {error, {unexpected_message, M}}; + M = {tcp_closed, Sock} -> + {error, {unexpected_message, M}}; + M = {tcp_error, Sock, _Reason} -> + {error, {unexpected_message, M}} + after 0 -> + gen_tcp:recv(St#st.sock, 0, St#st.timeout) + end + end; +do_recv(St) -> + Sock = St#st.sock, + Timeout = St#st.timeout, + case St#st.protomod of + ssl -> + receive + {ssl, Sock, Msg} -> + {ok, Msg}; + {ssl_closed, Sock} -> + {error, closed}; + {ssl_error, Sock, Reason} -> + {error, Reason} + after Timeout -> + {error, timeout} + end; + gen_tcp -> + receive + {tcp, Sock, Msg} -> + {ok, Msg}; + {tcp_closed, Sock} -> + {error, closed}; + {tcp_error, Sock, Reason} -> + {error, Reason} + after Timeout -> + {error, timeout} + end + end. + +%% +%% echo(St) = {ok, Len} | {error, Reason} +%% +echo(St) -> + Sock = St#st.sock, + case do_recv(St) of + {ok, Msg} -> + Mod = St#st.protomod, + case Mod:send(Sock, Msg) of + ok -> + {ok, length(Msg)}; + {error, Reason} -> + {error, Reason} + end; + {error, Reason} -> + {error, Reason} + end. + +%% +%% send(St, Msg) -> ok | {error, Reason} +%% +send(St, Msg) -> + Mod = St#st.protomod, + Mod:send(St#st.sock, Msg). + +%% +%% await_close(St) -> ok | {error, Reason} +%% +await_close(St) when St#st.active == false -> + %% First check that we do *not* have any ssl/gen_tcp messages in the + %% message queue, then call the receive function. + Sock = St#st.sock, + Res = case St#st.protomod of + ssl -> + receive + M = {ssl, Sock, _Msg0} -> + {error, {unexpected_message, M}}; + M = {ssl_closed, Sock} -> + {error, {unexpected_message, M}}; + M = {ssl_error, Sock, _Reason} -> + {error, {unexpected_message, M}} + after 0 -> + ok + end; + gen_tcp -> + receive + M = {tcp, Sock, _Msg0} -> + {error, {unexpected_message, M}}; + M = {tcp_closed, Sock} -> + {error, {unexpected_message, M}}; + M = {tcp_error, Sock, _Reason} -> + {error, {unexpected_message, M}} + after 0 -> + ok + end + end, + case Res of + ok -> + Mod = St#st.protomod, + case Mod:recv(St#st.sock, 0, St#st.timeout) of + {ok, _Msg} -> + {error, toomuch}; + {error, _} -> + ok + end; + _ -> + Res + end; +await_close(St) -> + Sock = St#st.sock, + Timeout = St#st.timeout, + case St#st.protomod of + ssl -> + receive + {ssl, Sock, _Msg} -> + {error, toomuch}; + {ssl_closed, Sock} -> + ok; + {ssl_error, Sock, Reason} -> + {error, Reason} + after Timeout -> + {error, timeout} + end; + gen_tcp -> + receive + {tcp, Sock, _Msg} -> + {error, toomuch}; + {tcp_closed, Sock} -> + ok; + {tcp_error, Sock, Reason} -> + {error, Reason} + after Timeout -> + {error, timeout} + end + end. + + +%% +%% HELP FUNCTIONS +%% + +wait_ack(_, [], _) -> + ok; +wait_ack(AccPids0, Pids, Timeout) -> + ?debug("#### CONTROLLER: waiting for ~w~n", [Pids]), + receive + {one_accept_done, Pid} -> + case lists:delete(Pid, AccPids0) of + [] -> + wait_ack([], Pids, Timeout); + [AccPid| AccPids1] -> + AccPid ! {continue_accept, self()}, + wait_ack(AccPids1, Pids, Timeout) + end; + {'EXIT', Pid, normal} -> + wait_ack(AccPids0, lists:delete(Pid, Pids), Timeout); + {'EXIT', Pid, Reason} -> + ?error("#### CONTROLLER got abnormal exit: ~w, ~w~n", + [Pid, Reason]), + {error, Reason} + after Timeout -> + ?error("#### CONTROLLER exiting because of timeout = ~w~n", + [Timeout]), + {error, Timeout} + end. + + +%% +%% ack_lsock(Pid, LSock) +%% +ack_lsock(Pid, LSock) -> + Pid ! {lsock, self(), LSock}. + +wait_lsock(Pid, Timeout) -> + receive + {lsock, Pid, LSock} -> + {ok, LSock} + after Timeout -> + exit(timeout) + end. + +%% +%% sync(Pids) +%% +sync(Pids) -> + lists:foreach(fun (Pid) -> Pid ! {self(), sync} end, Pids). + +%% +%% wait_sync(St) +%% +wait_sync(St) -> + Pid = St#st.parent, + receive + {Pid, sync} -> + ok + end. + +%% +%% wait(Time) +%% +wait(Time) -> + receive + after Time -> + ok + end. + +%% +%% mk_msg(Size) +%% +mk_msg(Size) -> + mk_msg(0, Size, []). + +mk_msg(_, 0, Acc) -> + Acc; +mk_msg(Pos, Size, Acc) -> + C = (((Pos + Size) rem 256) - 1) band 255, + mk_msg(Pos, Size - 1, [C| Acc]). + +%% +%% get_protomod(Config) +%% +get_protomod(Config) -> + case lists:keysearch(protomod, 1, Config) of + {value, {_, ProtoMod}} -> + ProtoMod; + false -> + ssl + end. + +%% +%% get_serialize_accept(Config) +%% +get_serialize_accept(Config) -> + case lists:keysearch(serialize_accept, 1, Config) of + {value, {_, Val}} -> + Val; + false -> + false + end. + -- cgit v1.2.3