%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-2011. 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(old_transport_accept_SUITE).
-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").

%% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
-define(application, ssh).

-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
	 init_per_group/2,end_per_group/2,
	 init_per_testcase/2,
	 end_per_testcase/2,
	 config/1,
	 echo_once/1,
	 echo_twice/1,
	 close_before_ssl_accept/1,
	 server/5,
	 tolerant_server/5,
	 client/5
	]).

init_per_testcase(_Case, Config) ->
    WatchDog = ssl_test_lib:timetrap(?default_timeout),
    [{watchdog, WatchDog}, {protomod, gen_tcp}, {serialize_accept, true}| 
     Config].

end_per_testcase(_Case, Config) ->
    WatchDog = ?config(watchdog, Config),
    test_server:timetrap_cancel(WatchDog).

suite() -> [{ct_hooks,[ts_install_cth]}].

all() -> 
    [config, echo_once, echo_twice, close_before_ssl_accept].

groups() -> 
    [].

init_per_suite(Config) ->
    try crypto:start() of
	ok ->
	    Config
    catch _:_ ->
	    {skip, "Crypto did not start"}
    end.

end_per_suite(_Config) ->
    application:stop(crypto),
    ok.

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


config(doc) ->
    "Want to se what Config contains.";
config(suite) ->
    [];
config(Config) ->
    io:format("Config: ~p~n", [Config]),
    ok.

echo_once(doc) ->
    "Client sends 256 bytes to server, that receives them, sends them "
	"back, and closes. Client waits for close. Both have certs.";
echo_once(suite) ->
    [];
echo_once(Config) when list(Config) ->
    process_flag(trap_exit, true),
    LPort = 3456,
    {ok, Host} = inet:gethostname(),
    {ok, {COpts, SOpts}} = ssl_test_MACHINE:mk_ssl_cert_opts(Config),
    N = 1,
    Msg = lists:seq(0, 255),
    Self = self(),
    Params = "-pa " ++ filename:dirname(code:which(?MODULE)),
    Node = start_node(server, Params),
    CNode = start_node(client, Params),
    Server = spawn_link(Node, ?MODULE, server, [Self, LPort, SOpts, Msg, N]),
    Client = spawn_link(Node, ?MODULE, client, [Host, LPort, COpts, Msg, N]),
    ok = receive
	     {Server, listening} ->
		 Client ! {Server, listening},
		 ok;
	     E ->
		 io:format("bad receive (1) ~p\n", [E]),
		 E
	 end,
    receive
	{Server, done} ->
	    ok
    end,
    test_server:stop_node(Node),
    test_server:stop_node(CNode).

close_before_ssl_accept(doc) ->
    "Client sends 256 bytes to server, that receives them, sends them "
	"back, and closes. Client waits for close. Both have certs.";
close_before_ssl_accept(suite) ->
    [];
close_before_ssl_accept(Config) when list(Config) ->
    process_flag(trap_exit, true),
    LPort = 3456,
    {ok, Host} = inet:gethostname(),
    {ok, {COpts, SOpts}} = ssl_test_MACHINE:mk_ssl_cert_opts(Config),
    Msg = lists:seq(0, 255),
    Self = self(),
    Params = "-pa " ++ filename:dirname(code:which(?MODULE)),
    Node = start_node(server, Params),
    CNode = start_node(client, Params),
    Server = spawn_link(Node, ?MODULE, tolerant_server,
			[Self, LPort, SOpts, Msg, 2]),
    Client = spawn_link(Node, ?MODULE, client,
			[Host, LPort, COpts, Msg, 1]),
    ok = receive
	     {Server, listening} ->
		 {ok, S} = gen_tcp:connect(Host, LPort, []),
		 gen_tcp:close(S),
		 Client ! {Server, listening},
		 ok;
	     E ->
		 io:format("bad receive (1) ~p\n", [E]),
		 E
	 end,
    receive
	{Server, done} ->
	    ok
    end,
    test_server:stop_node(Node),
    test_server:stop_node(CNode).

client(Host, LPort, COpts, Msg, N) ->
    ok = receive
	     {_Server, listening} ->
		 ok;
	     E ->
		 io:format("bad receive (2) ~p\n", [E]),
		 E
	 end,
    Opts = COpts ++ [{packet, raw}, {active, false}],
    app(),
    lists:foreach(fun(_) ->
			  {ok, S} = ssl:connect(Host, LPort, Opts),
			  ssl:send(S, Msg),
			  {ok, Msg} = ssl:recv(S, length(Msg)),
			  ssl:close(S)
		  end, lists:seq(1, N)).

echo_twice(doc) ->
    "Two clients sends 256 bytes to server, that receives them, sends them "
	"back, and closes. Client waits for close. Both have certs.";
echo_twice(suite) ->
    [];
echo_twice(Config) when list(Config) ->
    process_flag(trap_exit, true),
    LPort = 3456,
    {ok, Host} = inet:gethostname(),
    {ok, {COpts, SOpts}} = ssl_test_MACHINE:mk_ssl_cert_opts(Config),
    N = 2,
    Msg = lists:seq(0, 255),
    Self = self(),
    Params = "-pa " ++ filename:dirname(code:which(?MODULE)),
    Node = start_node(server, Params),
    CNode = start_node(client, Params),
    Server = spawn_link(Node, ?MODULE, server,
			[Self, LPort, SOpts, Msg, N]),
    Client = spawn_link(Node, ?MODULE, client,
			[Host, LPort, COpts, Msg, N]),
    ok = receive
	     {Server, listening} ->
		 Client ! {Server, listening},
		 ok;
	     E ->
		 io:format("bad receive (3) ~p\n", [E]),
		 E
	 end,
    receive
	{Server, done} ->
	    ok
    end,
    test_server:stop_node(Node),
    test_server:stop_node(CNode).

server(Client, Port, SOpts, Msg, N) ->
    app(),
    process_flag(trap_exit, true),
    Opts = SOpts ++ [{packet, raw}, {active, false}],
    {ok, LSock} = ssl:listen(Port, Opts),
    Client ! {self(), listening},
    server_loop(Client, LSock, Msg, N).

server_loop(Client, _, _, 0) ->
    Client ! {self(), done};
server_loop(Client, LSock, Msg, N) ->
    {ok, S} = ssl:transport_accept(LSock),
    ok = ssl:ssl_accept(S),
    %% P = ssl:controlling_process(S, Proxy),
    {ok, Msg} = ssl:recv(S, length(Msg)),
    ok = ssl:send(S, Msg),
    ok = ssl:close(S),
    server_loop(Client, LSock, Msg, N-1).

tolerant_server(Client, Port, SOpts, Msg, N) ->
    app(),
    process_flag(trap_exit, true),
    Opts = SOpts ++ [{packet, raw}, {active, false}],
    {ok, LSock} = ssl:listen(Port, Opts),
    Client ! {self(), listening},
    tolerant_server_loop(Client, LSock, Msg, N).

tolerant_server_loop(Client, _, _, 0) ->
    Client ! {self(), done};
tolerant_server_loop(Client, LSock, Msg, N) ->
    {ok, S} = ssl:transport_accept(LSock),
    case ssl:ssl_accept(S) of
	ok ->
	    %% P = ssl:controlling_process(S, Proxy),
	    {ok, Msg} = ssl:recv(S, length(Msg)),
	    ok = ssl:send(S, Msg),
	    ok = ssl:close(S);
	E ->
	    io:format("ssl_accept error: ~p\n", [E])
    end,
    tolerant_server_loop(Client, LSock, Msg, N-1).

app() ->
    crypto:start(),
    application:start(public_key),
    ssl:start().

start_node(Kind, Params) ->
    S = atom_to_list(?MODULE)++"_" ++ atom_to_list(Kind),
    {ok, Node} = test_server:start_node(list_to_atom(S), slave, [{args, Params}]),
    Node.

