%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2018-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(socket_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). %% Suite exports -export([suite/0, all/0, groups/0]). -export([init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2]). %% Test cases -export([ %% API Basic api_b_open_and_close_udp4/1, api_b_open_and_close_tcp4/1, api_b_sendto_and_recvfrom_udp4/1, api_b_sendmsg_and_recvmsg_udp4/1, api_b_send_and_recv_tcp4/1, api_b_sendmsg_and_recvmsg_tcp4/1, %% API Options api_opt_simple_otp_options/1, %% API Operation Timeout api_to_connect_tcp4/1, api_to_connect_tcp6/1, api_to_accept_tcp4/1, api_to_accept_tcp6/1, api_to_send_tcp4/1, api_to_send_tcp6/1, api_to_sendapi_to_udp4/1, api_to_sendapi_to_udp6/1, api_to_sendmsg_tcp4/1, api_to_sendmsg_tcp6/1, api_to_recv_udp4/1, api_to_recv_udp6/1, api_to_recv_tcp4/1, api_to_recv_tcp6/1, api_to_recvfrom_udp4/1, api_to_recvfrom_udp6/1, api_to_recvmsg_udp4/1, api_to_recvmsg_udp6/1, api_to_recvmsg_tcp4/1, api_to_recvmsg_tcp6/1 %% Tickets ]). %% Internal exports %% -export([]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(BASIC_REQ, <<"hejsan">>). -define(BASIC_REP, <<"hoppsan">>). -define(FAIL(R), exit(R)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. all() -> [ {group, api} %% {group, tickets} ]. groups() -> [{api, [], api_cases()}, {api_basic, [], api_basic_cases()}, {api_op_with_timeout, [], api_op_with_timeout_cases()}, {api_options, [], api_options_cases()} %% {tickets, [], ticket_cases()} ]. api_cases() -> [ {group, api_basic}, {group, api_options}, {group, api_op_with_timeout} ]. api_basic_cases() -> [ api_b_open_and_close_udp4, api_b_open_and_close_tcp4, api_b_sendto_and_recvfrom_udp4, api_b_sendmsg_and_recvmsg_udp4, api_b_send_and_recv_tcp4, api_b_sendmsg_and_recvmsg_tcp4 ]. api_options_cases() -> [ api_opt_simple_otp_options ]. api_op_with_timeout_cases() -> [ api_to_connect_tcp4, api_to_connect_tcp6, api_to_accept_tcp4, api_to_accept_tcp6, api_to_send_tcp4, api_to_send_tcp6, api_to_sendapi_to_udp4, api_to_sendapi_to_udp6, api_to_sendmsg_tcp4, api_to_sendmsg_tcp6, api_to_recv_udp4, api_to_recv_udp6, api_to_recv_tcp4, api_to_recv_tcp6, api_to_recvfrom_udp4, api_to_recvfrom_udp6, api_to_recvmsg_udp4, api_to_recvmsg_udp6, api_to_recvmsg_tcp4, api_to_recvmsg_tcp6 ]. %% ticket_cases() -> %% []. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config) -> Config. end_per_suite(_) -> ok. init_per_testcase(_TC, Config) -> Config. end_per_testcase(_TC, Config) -> Config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically open (create) and close an IPv4 UDP (dgram) socket. %% With some extra checks... api_b_open_and_close_udp4(suite) -> []; api_b_open_and_close_udp4(doc) -> []; api_b_open_and_close_udp4(_Config) when is_list(_Config) -> tc_begin(api_b_open_and_close_udp4), ok = api_b_open_and_close(inet, dgram, udp), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically open (create) and close an IPv4 TCP (stream) socket. %% With some extra checks... api_b_open_and_close_tcp4(suite) -> []; api_b_open_and_close_tcp4(doc) -> []; api_b_open_and_close_tcp4(_Config) when is_list(_Config) -> tc_begin(api_b_open_and_close_tcp4), ok = api_b_open_and_close(inet, stream, tcp), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_b_open_and_close(Domain, Type, Proto) -> Socket = case socket:open(Domain, Type, Proto) of {ok, S} -> S; {error, Reason} -> ?FAIL({open, Reason}) end, {ok, Domain} = socket:getopt(Socket, socket, domain), {ok, Type} = socket:getopt(Socket, socket, type), {ok, Proto} = socket:getopt(Socket, socket, protocol), Self = self(), {ok, Self} = socket:getopt(Socket, otp, controlling_process), ok = socket:close(Socket), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically send and receive on an IPv4 UDP (dgram) socket using %% sendto and recvfrom.. api_b_sendto_and_recvfrom_udp4(suite) -> []; api_b_sendto_and_recvfrom_udp4(doc) -> []; api_b_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> tc_begin(api_b_sendto_and_recvfrom_udp4), Send = fun(Sock, Data, Dest) -> socket:sendto(Sock, Data, Dest) end, Recv = fun(Sock) -> socket:recvfrom(Sock) end, ok = api_b_send_and_recv_udp(inet, Send, Recv), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically send and receive on an IPv4 UDP (dgram) socket %% using sendmsg and recvmsg. api_b_sendmsg_and_recvmsg_udp4(suite) -> []; api_b_sendmsg_and_recvmsg_udp4(doc) -> []; api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> tc_begin(api_b_sendmsg_and_recvmsg_udp4), Send = fun(Sock, Data, Dest) -> %% CMsgHdr = #{level => ip, type => tos, data => reliability}, %% CMsgHdrs = [CMsgHdr], MsgHdr = #{addr => Dest, %% ctrl => CMsgHdrs, iov => [Data]}, socket:sendmsg(Sock, MsgHdr) end, Recv = fun(Sock) -> case socket:recvmsg(Sock) of {ok, #{addr := Source, iov := [Data]}} -> {ok, {Source, Data}}; {error, _} = ERROR -> ERROR end end, ok = api_b_send_and_recv_udp(inet, Send, Recv), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_b_send_and_recv_udp(Domain, Send, Recv) -> SockSrc = sock_open(Domain, dgram, udp), LAddr = which_local_addr(Domain), LSA = #{family => Domain, addr => LAddr}, sock_bind(SockSrc, LSA), SockDst = sock_open(Domain, dgram, udp), sock_bind(SockDst, LSA), Dst = sock_sockname(SockDst), ok = Send(SockSrc, ?BASIC_REQ, Dst), {ok, {Src, ?BASIC_REQ}} = Recv(SockDst), ok = Send(SockDst, ?BASIC_REP, Src), {ok, {Dst, ?BASIC_REP}} = Recv(SockSrc), socket:close(SockSrc), socket:close(SockDst), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically send and receive using the "common" functions (send and recv) %% on an IPv4 TCP (stream) socket. api_b_send_and_recv_tcp4(suite) -> []; api_b_send_and_recv_tcp4(doc) -> []; api_b_send_and_recv_tcp4(_Config) when is_list(_Config) -> tc_begin(api_b_send_and_recv_tcp4), Send = fun(Sock, Data) -> socket:send(Sock, Data) end, Recv = fun(Sock) -> socket:recv(Sock) end, ok = api_b_send_and_recv_tcp(inet, Send, Recv), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Basically send and receive using the msg functions (sendmsg and recvmsg) %% on an IPv4 TCP (stream) socket. api_b_sendmsg_and_recvmsg_tcp4(suite) -> []; api_b_sendmsg_and_recvmsg_tcp4(doc) -> []; api_b_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> tc_begin(api_b_sendmsg_and_recvmsg_tcp4), Send = fun(Sock, Data) -> MsgHdr = #{iov => [Data]}, socket:sendmsg(Sock, MsgHdr) end, Recv = fun(Sock) -> case socket:recvmsg(Sock) of {ok, #{addr := undefined, iov := [Data]}} -> {ok, Data}; {error, _} = ERROR -> ERROR end end, ok = api_b_send_and_recv_tcp(inet, Send, Recv), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_b_send_and_recv_tcp(Domain, Send, Recv) -> process_flag(trap_exit, true), LAddr = which_local_addr(Domain), LSA = #{family => Domain, addr => LAddr}, Starter = self(), ServerFun = fun() -> %% Create the listen socket ServerLSock = case socket:open(Domain, stream, tcp) of {ok, S1} -> S1; {error, ServerOR} -> ?FAIL({server, open, ServerOR}) end, %% And bind it to the local address SP = case socket:bind(ServerLSock, LSA) of {ok, P} -> P; {error, ServerBR} -> ?FAIL({server, bind, ServerBR}) end, %% Listen for connecting clients case socket:listen(ServerLSock) of ok -> ok; {error, ServerLR} -> ?FAIL({server, listen, ServerLR}) end, %% We are ready Starter ! {self(), {ok, SP}}, %% Accept connections ServerSock = case socket:accept(ServerLSock) of {ok, Sock} -> Sock; {error, ServerAR} -> ?FAIL({server, accept, ServerAR}) end, %% Wait for a message case Recv(ServerSock) of {ok, ?BASIC_REQ} -> ok; {error, ServerRR} -> ?FAIL({server, recv, ServerRR}) end, %% Send the reply case Send(ServerSock, ?BASIC_REP) of ok -> ok; {error, ServerSR} -> ?FAIL({server, send, ServerSR}) end, %% Close the sockets socket:close(ServerSock), socket:close(ServerLSock), %% We are done exit(normal) end, Server = spawn_link(ServerFun), ServerPort = receive {Server, {ok, P}} -> P; {'EXIT', Server, ServerStartReason} -> ?FAIL({server, start, ServerStartReason}) end, ClientSock = case socket:open(Domain, stream, tcp) of {ok, S2} -> S2; {error, ClientOR} -> ?FAIL({client, open, ClientOR}) end, case socket:bind(ClientSock, LSA) of {ok, _} -> ok; {error, ClientBR} -> ?FAIL({client, bind, ClientBR}) end, case socket:connect(ClientSock, LSA#{port => ServerPort}) of ok -> ok; {error, ClientCR} -> ?FAIL({client, connect, ClientCR}) end, case Send(ClientSock, ?BASIC_REQ) of ok -> ok; {error, ClientSR} -> ?FAIL({client, send, ClientSR}) end, case Recv(ClientSock) of {ok, ?BASIC_REP} -> ok; {ok, Msg} -> ?FAIL({client, recv, {unexpected, Msg}}) end, receive {'EXIT', Server, normal} -> ok; {'EXIT', Server, ServerStopReason} -> ?FAIL({server, stop, ServerStopReason}) end, socket:close(ClientSock), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Perform some simple getopt and setopt with the level = otp options api_opt_simple_otp_options(suite) -> []; api_opt_simple_otp_options(doc) -> []; api_opt_simple_otp_options(_Config) when is_list(_Config) -> tc_begin(api_opt_simple_otp_options), p("Create sockets"), S1 = sock_open(inet, stream, tcp), S2 = sock_open(inet, dgram, udp), Get = fun(S, Key) -> socket:getopt(S, otp, Key) end, Set = fun(S, Key, Val) -> socket:setopt(S, otp, Key, Val) end, p("Create dummy process"), Pid = spawn_link(fun() -> receive die -> exit(normal) end end), F = fun(Sock) -> p("Test IOW"), {ok, IOW} = Get(Sock, iow), NotIOW = not IOW, ok = Set(Sock, iow, NotIOW), {ok, NotIOW} = Get(Sock, iow), p("Test rcvbuf"), {ok, RcvBuf} = Get(Sock, rcvbuf), RcvBuf2 = RcvBuf*2, ok = Set(Sock, rcvbuf, RcvBuf2), {ok, RcvBuf2} = Get(Sock, rcvbuf), ok = Set(Sock, rcvbuf, default), {ok, RcvBuf} = Get(Sock, rcvbuf), p("Test rcvctrlbuf"), {ok, RcvCtrlBuf} = Get(Sock, rcvctrlbuf), RcvCtrlBuf2 = RcvCtrlBuf*2, ok = Set(Sock, rcvctrlbuf, RcvCtrlBuf2), {ok, RcvCtrlBuf2} = Get(Sock, rcvctrlbuf), ok = Set(Sock, rcvctrlbuf, default), {ok, RcvCtrlBuf} = Get(Sock, rcvctrlbuf), p("Test sndctrlbuf"), {ok, SndCtrlBuf} = Get(Sock, sndctrlbuf), SndCtrlBuf2 = SndCtrlBuf*2, ok = Set(Sock, sndctrlbuf, SndCtrlBuf2), {ok, SndCtrlBuf2} = Get(Sock, sndctrlbuf), ok = Set(Sock, sndctrlbuf, default), {ok, RcvCtrlBuf} = Get(Sock, sndctrlbuf), p("Test controlling-process"), Self = self(), {ok, Self} = Get(Sock, controlling_process), ok = Set(Sock, controlling_process, Pid), {ok, Pid} = Get(Sock, controlling_process) end, p("Test stream/tcp "), F(S1), p("Test dgram/udp "), F(S2), p("kill dummy process"), %% This will also close its sockets (S1 and S2), %% This should really be tested explicitly... Pid ! die, %% p("close sockets"), %% sock_close(S1), %% sock_close(S2), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the connect timeout option %% on an IPv4 TCP (stream) socket. api_to_connect_tcp4(suite) -> []; api_to_connect_tcp4(doc) -> []; api_to_connect_tcp4(_Config) when is_list(_Config) -> tc_begin(api_to_connect_tcp4), ok = api_to_connect_tcp(inet), tc_end(). %% not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the connect timeout option %% on an IPv6 TCP (stream) socket. api_to_connect_tcp6(suite) -> []; api_to_connect_tcp6(doc) -> []; api_to_connect_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_connect_tcp6), %% ok = api_to_connect_tcp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_to_connect_tcp(Domain) -> process_flag(trap_exit, true), p("init"), Client = self(), LocalAddr = which_local_addr(Domain), LocalSA = #{family => Domain, addr => LocalAddr}, ServerName = f("~s:server", [get_tc_name()]), Server = spawn_link(fun() -> set_tc_name(ServerName), p("open"), LSock = sock_open(Domain, stream, tcp), p("bind"), ServerLPort = sock_bind(LSock, LocalSA), p("listen on ~w", [ServerLPort]), sock_listen(LSock, 1), p("inform client"), Client ! {self(), ServerLPort}, p("await termination command"), receive die -> p("terminating"), exit(normal) end end), p("await server port"), ServerLPort = receive {Server, Port} -> Port end, p("open(s)"), CSock1 = sock_open(Domain, stream, tcp), CSock2 = sock_open(Domain, stream, tcp), CSock3 = sock_open(Domain, stream, tcp), p("bind(s)"), _ClientPort1 = sock_bind(CSock1, LocalSA), _ClientPort2 = sock_bind(CSock2, LocalSA), _ClientPort3 = sock_bind(CSock3, LocalSA), ServerSA = LocalSA#{port => ServerLPort}, api_to_connect_tcp_await_timeout([CSock1, CSock2, CSock3], ServerSA), p("terminate server"), Server ! die, receive {'EXIT', Server, _} -> p("server terminated"), ok end, ok. api_to_connect_tcp_await_timeout(Socks, ServerSA) -> api_to_connect_tcp_await_timeout(Socks, ServerSA, 1). api_to_connect_tcp_await_timeout([], _ServerSA, _ID) -> ?FAIL(unexpected_success); api_to_connect_tcp_await_timeout([Sock|Socks], ServerSA, ID) -> p("~w: try connect", [ID]), case socket:connect(Sock, ServerSA, 5000) of {error, timeout} -> p("expected timeout (~w)", [ID]), ok; {error, Reason} -> p("failed connecting: ~p", [Reason]), ?FAIL({recv, Reason}); ok -> p("unexpected success (~w) - try next", [ID]), api_to_connect_tcp_await_timeout(Socks, ServerSA, ID+1) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the accept timeout option %% on an IPv4 TCP (stream) socket. api_to_accept_tcp4(suite) -> []; api_to_accept_tcp4(doc) -> []; api_to_accept_tcp4(_Config) when is_list(_Config) -> %% tc_begin(api_to_accept_tcp4), %% ok = api_to_accept_tcp(inet), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the accept timeout option %% on an IPv6 TCP (stream) socket. api_to_accept_tcp6(suite) -> []; api_to_accept_tcp6(doc) -> []; api_to_accept_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_accept_tcp6), %% ok = api_to_accept_tcp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the send timeout option %% on an IPv4 TCP (stream) socket. api_to_send_tcp4(suite) -> []; api_to_send_tcp4(doc) -> []; api_to_send_tcp4(_Config) when is_list(_Config) -> %% tc_begin(api_to_send_tcp4), %% ok = api_to_send_tcp(inet), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the send timeout option %% on an IPv6 TCP (stream) socket. api_to_send_tcp6(suite) -> []; api_to_send_tcp6(doc) -> []; api_to_send_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_send_tcp6), %% ok = api_to_send_tcp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the sendto timeout option %% on an IPv4 UDP (dgram) socket. api_to_sendapi_to_udp4(suite) -> []; api_to_sendapi_to_udp4(doc) -> []; api_to_sendapi_to_udp4(_Config) when is_list(_Config) -> %% tc_begin(api_to_sendapi_to_udp4), %% ok = api_to_sendapi_to_udp(inet), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the sendto timeout option %% on an IPv6 UDP (dgram) socket. api_to_sendapi_to_udp6(suite) -> []; api_to_sendapi_to_udp6(doc) -> []; api_to_sendapi_to_udp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_sendapi_to_udp6), %% ok = api_to_sendapi_to_udp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the sendmsg timeout option %% on an IPv4 TCP (stream) socket. api_to_sendmsg_tcp4(suite) -> []; api_to_sendmsg_tcp4(doc) -> []; api_to_sendmsg_tcp4(_Config) when is_list(_Config) -> %% tc_begin(api_to_sendmsg_tcp4), %% ok = api_to_sendmsg_tcp(inet), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the sendmsg timeout option %% on an IPv6 TCP (stream) socket. api_to_sendmsg_tcp6(suite) -> []; api_to_sendmsg_tcp6(doc) -> []; api_to_sendmsg_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_sendmsg_tcp6), %% ok = api_to_sendmsg_tcp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recv timeout option %% on an IPv4 UDP (dgram) socket. To test this we must connect %% the socket. api_to_recv_udp4(suite) -> []; api_to_recv_udp4(doc) -> []; api_to_recv_udp4(_Config) when is_list(_Config) -> %% tc_begin(api_to_recv_udp4), %% ok = api_to_recv_udp(inet), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recv timeout option %% on an IPv6 UDP (dgram) socket. To test this we must connect %% the socket. api_to_recv_udp6(suite) -> []; api_to_recv_udp6(doc) -> []; api_to_recv_udp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recv_udp6), %% ok = api_to_recv_udp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recv timeout option %% on an IPv4 TCP (stream) socket. api_to_recv_tcp4(suite) -> []; api_to_recv_tcp4(doc) -> []; api_to_recv_tcp4(_Config) when is_list(_Config) -> tc_begin(api_to_recv_tcp4), ok = api_to_recv_tcp(inet), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recv timeout option %% on an IPv6 TCP (stream) socket. api_to_recv_tcp6(suite) -> []; api_to_recv_tcp6(doc) -> []; api_to_recv_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recv_tcp6), %% ok = api_to_recv_tcp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_to_recv_tcp(Domain) -> process_flag(trap_exit, true), p("server -> open"), LSock = sock_open(Domain, stream, tcp), LocalAddr = which_local_addr(Domain), LocalSA = #{family => Domain, addr => LocalAddr}, p("server -> bind"), ServerLPort = sock_bind(LSock, LocalSA), p("server(~w) -> listen", [ServerLPort]), sock_listen(LSock), ClientName = f("~s:client", [get_tc_name()]), Client = spawn_link(fun() -> set_tc_name(ClientName), p("open"), CSock = sock_open(Domain, stream, tcp), p("bind"), ClientPort = sock_bind(CSock, LocalSA), p("[~w] connect to ~w", [ClientPort, ServerLPort]), sock_connect(CSock, LocalSA#{port => ServerLPort}), p("await termination command"), receive die -> p("terminating"), exit(normal) end end), p("server -> accept on ~w", [ServerLPort]), Sock = sock_accept(LSock), p("server -> recv"), %% The zero (0) represents "give me everything you have" case socket:recv(Sock, 0, 5000) of {error, timeout} -> p("server -> expected timeout"), ok; {ok, _Data} -> ?FAIL(unexpected_success); {error, Reason} -> ?FAIL({recv, Reason}) end, Client ! die, receive {'EXIT', Client, _} -> ok end, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recvfrom timeout option %% on an IPv4 UDP (dgram) socket. api_to_recvfrom_udp4(suite) -> []; api_to_recvfrom_udp4(doc) -> []; api_to_recvfrom_udp4(_Config) when is_list(_Config) -> tc_begin(api_to_recvfrom_udp4), ok = api_to_recvfrom_udp(inet), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recvfrom timeout option %% on an IPv6 UDP (dgram) socket. api_to_recvfrom_udp6(suite) -> []; api_to_recvfrom_udp6(doc) -> []; api_to_recvfrom_udp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recvfrom_udp6), %% ok = api_to_recvfrom_udp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_to_recvfrom_udp(Domain) -> process_flag(trap_exit, true), p("init"), LocalAddr = which_local_addr(Domain), LocalSA = #{family => Domain, addr => LocalAddr}, p("open"), Sock = sock_open(Domain, dgram, udp), p("bind"), _Port = sock_bind(Sock, LocalSA), p("recv"), case socket:recvfrom(Sock, 0, 5000) of {error, timeout} -> p("expected timeout"), ok; {ok, _SrcData} -> ?FAIL(unexpected_success); {error, Reason} -> ?FAIL({recv, Reason}) end, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recvmsg timeout option %% on an IPv4 UDP (dgram) socket. api_to_recvmsg_udp4(suite) -> []; api_to_recvmsg_udp4(doc) -> []; api_to_recvmsg_udp4(_Config) when is_list(_Config) -> %% not_yet_implemented(). tc_begin(api_to_recvmsg_udp4), ok = api_to_recvmsg_udp(inet), tc_end(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recvmsg timeout option %% on an IPv6 UDP (dgram) socket. api_to_recvmsg_udp6(suite) -> []; api_to_recvmsg_udp6(doc) -> []; api_to_recvmsg_udp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recvmsg_udp6), %% ok = api_to_recvmsg_udp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_to_recvmsg_udp(Domain) -> process_flag(trap_exit, true), p("init"), LocalAddr = which_local_addr(Domain), LocalSA = #{family => Domain, addr => LocalAddr}, p("open"), Sock = sock_open(Domain, dgram, udp), p("bind"), _Port = sock_bind(Sock, LocalSA), p("recv"), case socket:recvmsg(Sock, 5000) of {error, timeout} -> p("expected timeout"), ok; {ok, _MsgHdr} -> ?FAIL(unexpected_success); {error, Reason} -> ?FAIL({recv, Reason}) end, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recvmsg timeout option %% on an IPv4 TCP (stream) socket. api_to_recvmsg_tcp4(suite) -> []; api_to_recvmsg_tcp4(doc) -> []; api_to_recvmsg_tcp4(_Config) when is_list(_Config) -> tc_begin(api_to_recvmsg_tcp4), ok = api_to_recvmsg_tcp(inet), tc_end(). %% not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test the recvmsg timeout option %% on an IPv6 TCP (stream) socket. api_to_recvmsg_tcp6(suite) -> []; api_to_recvmsg_tcp6(doc) -> []; api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> %% tc_begin(api_to_recvmsg_tcp6), %% ok = api_to_recvmsg_tcp(inet6), %% tc_end(). not_yet_implemented(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% api_to_recvmsg_tcp(Domain) -> process_flag(trap_exit, true), p("server -> open"), LSock = sock_open(Domain, stream, tcp), LocalAddr = which_local_addr(Domain), LocalSA = #{family => Domain, addr => LocalAddr}, p("server -> bind"), ServerLPort = sock_bind(LSock, LocalSA), p("server(~w) -> listen", [ServerLPort]), sock_listen(LSock), ClientName = f("~s:client", [get_tc_name()]), Client = spawn_link(fun() -> set_tc_name(ClientName), p("open"), CSock = sock_open(Domain, stream, tcp), p("bind"), ClientPort = sock_bind(CSock, LocalSA), p("[~w] connect to ~w", [ClientPort, ServerLPort]), sock_connect(CSock, LocalSA#{port => ServerLPort}), p("await termination command"), receive die -> p("terminating"), exit(normal) end end), p("server -> accept on ~w", [ServerLPort]), Sock = sock_accept(LSock), p("server -> recv"), %% The zero (0) represents "give me everything you have" case socket:recvmsg(Sock, 5000) of {error, timeout} -> p("server -> expected timeout"), ok; {ok, _Data} -> ?FAIL(unexpected_success); {error, Reason} -> ?FAIL({recv, Reason}) end, Client ! die, receive {'EXIT', Client, _} -> ok end, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This gets the local address (not 127.0...) %% We should really implement this using the (new) net module, %% but until that gets the necessary functionality... which_local_addr(Domain) -> case inet:getifaddrs() of {ok, IFL} -> which_addr(Domain, IFL); {error, Reason} -> ?FAIL({inet, getifaddrs, Reason}) end. which_addr(_Domain, []) -> ?FAIL(no_address); which_addr(Domain, [{Name, IFO}|_IFL]) when (Name =/= "lo") -> which_addr2(Domain, IFO); which_addr(Domain, [_|IFL]) -> which_addr(Domain, IFL). which_addr2(_Domain, []) -> ?FAIL(no_address); which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 4) -> Addr; which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 8) -> Addr; which_addr2(Domain, [_|IFO]) -> which_addr2(Domain, IFO). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sock_open(Domain, Type, Proto) -> try socket:open(Domain, Type, Proto) of {ok, Socket} -> Socket; {error, Reason} -> ?FAIL({open, Reason}) catch C:E:S -> ?FAIL({open, C, E, S}) end. sock_bind(Sock, SockAddr) -> try socket:bind(Sock, SockAddr) of {ok, Port} -> Port; {error, Reason} -> p("sock_bind -> error: ~p", [Reason]), ?FAIL({bind, Reason}) catch C:E:S -> p("sock_bind -> failed: ~p, ~p, ~p", [C, E, S]), ?FAIL({bind, C, E, S}) end. sock_connect(Sock, SockAddr) -> try socket:connect(Sock, SockAddr) of ok -> ok; {error, Reason} -> ?FAIL({connect, Reason}) catch C:E:S -> ?FAIL({connect, C, E, S}) end. sock_sockname(Sock) -> try socket:sockname(Sock) of {ok, SockAddr} -> SockAddr; {error, Reason} -> ?FAIL({sockname, Reason}) catch C:E:S -> ?FAIL({sockname, C, E, S}) end. sock_listen(Sock) -> sock_listen2(fun() -> socket:listen(Sock) end). sock_listen(Sock, BackLog) -> sock_listen2(fun() -> socket:listen(Sock, BackLog) end). sock_listen2(Listen) -> try Listen() of ok -> ok; {error, Reason} -> ?FAIL({listen, Reason}) catch C:E:S -> ?FAIL({listen, C, E, S}) end. sock_accept(LSock) -> try socket:accept(LSock) of {ok, Sock} -> Sock; {error, Reason} -> p("sock_accept -> error: ~p", [Reason]), ?FAIL({accept, Reason}) catch C:E:S -> p("sock_accept -> failed: ~p, ~p, ~p", [C, E, S]), ?FAIL({accept, C, E, S}) end. sock_close(Sock) -> try socket:close(Sock) of ok -> ok; {error, Reason} -> p("sock_close -> error: ~p", [Reason]), ?FAIL({close, Reason}) catch C:E:S -> p("sock_close -> failed: ~p, ~p, ~p", [C, E, S]), ?FAIL({close, C, E, S}) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% not_yet_implemented() -> {skip, "not yet implemented"}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% set_tc_name(N) when is_atom(N) -> set_tc_name(atom_to_list(N)); set_tc_name(N) when is_list(N) -> put(tc_name, N). get_tc_name() -> get(tc_name). tc_begin(TC) -> set_tc_name(TC), p("begin ***"). tc_end() -> p("done ***"), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% f(F, A) -> lists:flatten(io_lib:format(F, A)). p(F) -> p(F, []). p(F, A) -> TcName = case get(tc_name) of undefined -> ""; Name when is_list(Name) -> Name end, i("*** ~s[~p] " ++ F, [TcName,self()|A]). %% i(F) -> %% i(F, []). i(F, A) -> io:format(user, F ++ "~n", A).