diff options
-rw-r--r-- | erts/doc/src/socket_usage.xml | 21 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_nif.c | 146 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.c | 29 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.h | 6 | ||||
-rw-r--r-- | erts/preloaded/ebin/socket.beam | bin | 66040 -> 66584 bytes | |||
-rw-r--r-- | erts/preloaded/src/socket.erl | 33 | ||||
-rw-r--r-- | lib/kernel/test/socket_SUITE.erl | 112 |
7 files changed, 342 insertions, 5 deletions
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml index 9785830637..23d0f319f1 100644 --- a/erts/doc/src/socket_usage.xml +++ b/erts/doc/src/socket_usage.xml @@ -81,6 +81,27 @@ <cell>yes</cell> <cell>none</cell> </row> + <row> + <cell>rcvbuf</cell> + <cell>default | pos_integer()</cell> + <cell>yes</cell> + <cell>yes</cell> + <cell>default only valid for set</cell> + </row> + <row> + <cell>rcvctrlbuf</cell> + <cell>default | pos_integer()</cell> + <cell>yes</cell> + <cell>yes</cell> + <cell>default only valid for set</cell> + </row> + <row> + <cell>sndctrlbuf</cell> + <cell>default | pos_integer()</cell> + <cell>yes</cell> + <cell>yes</cell> + <cell>default only valid for set</cell> + </row> </table> <p>Options for level <c>socket</c>: </p> diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index ccbcf63ece..876bed3135 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -501,6 +501,9 @@ typedef union { #define SOCKET_OPT_OTP_DEBUG 1 #define SOCKET_OPT_OTP_IOW 2 #define SOCKET_OPT_OTP_CTRL_PROC 3 +#define SOCKET_OPT_OTP_RCVBUF 4 +#define SOCKET_OPT_OTP_RCVCTRLBUF 6 +#define SOCKET_OPT_OTP_SNDCTRLBUF 7 #define SOCKET_OPT_SOCK_ACCEPTCONN 1 #define SOCKET_OPT_SOCK_BINDTODEVICE 3 @@ -1004,6 +1007,15 @@ static ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, static ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -1492,6 +1504,12 @@ static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -5001,6 +5019,18 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, result = nsetopt_otp_ctrl_proc(env, descP, eVal); break; + case SOCKET_OPT_OTP_RCVBUF: + result = nsetopt_otp_rcvbuf(env, descP, eVal); + break; + + case SOCKET_OPT_OTP_RCVCTRLBUF: + result = nsetopt_otp_rcvctrlbuf(env, descP, eVal); + break; + + case SOCKET_OPT_OTP_SNDCTRLBUF: + result = nsetopt_otp_sndctrlbuf(env, descP, eVal); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -5079,6 +5109,74 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, +/* nsetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option + */ +static +ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + size_t val; + char* xres; + + if ((xres = esock_decode_bufsz(env, + eVal, + SOCKET_RECV_BUFFER_SIZE_DEFAULT, &val)) != NULL) + return esock_make_error_str(env, xres); + + descP->rBufSz = val; + + return esock_atom_ok; +} + + + +/* nsetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option + */ +static +ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + size_t val; + char* xres; + + if ((xres = esock_decode_bufsz(env, + eVal, + SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT, + &val)) != NULL) + return esock_make_error_str(env, xres); + + descP->rCtrlSz = val; + + return esock_atom_ok; +} + + + +/* nsetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option + */ +static +ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + size_t val; + char* xres; + + if ((xres = esock_decode_bufsz(env, + eVal, + SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT, + &val)) != NULL) + return esock_make_error_str(env, xres); + + descP->wCtrlSz = val; + + return esock_atom_ok; +} + + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is and value as a binary). */ @@ -8169,6 +8267,18 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, result = ngetopt_otp_ctrl_proc(env, descP); break; + case SOCKET_OPT_OTP_RCVBUF: + result = ngetopt_otp_rcvbuf(env, descP); + break; + + case SOCKET_OPT_OTP_RCVCTRLBUF: + result = ngetopt_otp_rcvctrlbuf(env, descP); + break; + + case SOCKET_OPT_OTP_SNDCTRLBUF: + result = ngetopt_otp_sndctrlbuf(env, descP); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -8220,6 +8330,42 @@ ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, +/* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf options + */ +static +ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKI(env, descP->rBufSz); + + return esock_make_ok2(env, eVal); +} + + +/* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf options + */ +static +ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKI(env, descP->rCtrlSz); + + return esock_make_ok2(env, eVal); +} + + +/* ngetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf options + */ +static +ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKI(env, descP->wCtrlSz); + + return esock_make_ok2(env, eVal); +} + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is). In this case it will have the * format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()} diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 8bb725fb5b..ff50fd2384 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1261,6 +1261,35 @@ char* esock_decode_protocol(ErlNifEnv* env, +/* +++ esock_decode_bufsz +++ + * + * Decode an buffer size. The size of a buffer is: + * + * Sz > 0 => Use provided value + * Sz => Use provided default + * + */ +extern +char* esock_decode_bufsz(ErlNifEnv* env, + ERL_NIF_TERM eVal, + size_t defSz, + size_t* sz) +{ + int val; + + if (!GET_INT(env, eVal, &val)) + return ESOCK_STR_EINVAL; + + if (val > 0) + *sz = (size_t) val; + else + *sz = defSz; + + return NULL; +} + + + /* *** esock_decode_string *** * * Decode a string value. A successful decode results in an diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index a38453e238..1b5d003155 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -158,6 +158,12 @@ char* esock_encode_protocol(ErlNifEnv* env, ERL_NIF_TERM* eProtocol); extern +char* esock_decode_bufsz(ErlNifEnv* env, + ERL_NIF_TERM eVal, + size_t defSz, + size_t* sz); + +extern BOOLEAN_T esock_decode_string(ErlNifEnv* env, const ERL_NIF_TERM eString, char** stringP); diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differindex 9e6d9f4709..a0550990e3 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 652054457f..8093bad885 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -320,12 +320,15 @@ %% Should we just document it and leave it to the user? %% Or catch it in the encode functions? %% A setopt for a readonly option leads to einval? +%% Do we really need a sndbuf? -type otp_socket_option() :: debug | iow | controlling_process | rcvbuf | - sndbuf. + sndbuf | + rcvctrlbuf | + sndctrlbuf. %% Shall we have special treatment of linger?? %% read-only options: %% domain | protocol | type. @@ -645,6 +648,10 @@ -define(SOCKET_OPT_OTP_DEBUG, 1). -define(SOCKET_OPT_OTP_IOW, 2). -define(SOCKET_OPT_OTP_CTRL_PROC, 3). +-define(SOCKET_OPT_OTP_RCVBUF, 4). +%%-define(SOCKET_OPT_OTP_SNDBUF, 5). +-define(SOCKET_OPT_OTP_RCVCTRLBUF, 6). +-define(SOCKET_OPT_OTP_SNDCTRLBUF, 7). %% *** SOCKET (socket) options -define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). @@ -2136,9 +2143,9 @@ getopt(#socket{ref = SockRef}, Level, Key) -> end end catch - throw:T -> - T; - error:Reason -> + throw:E:_S -> + E; + error:Reason:_Stack -> {error, Reason} % Process more? end. @@ -2401,6 +2408,18 @@ enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) -> V; enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) -> V; +enc_setopt_value(otp, rcvbuf, V, _, _, _) when (V =:= default) -> + 0; +enc_setopt_value(otp, rcvbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> + V; +enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when (V =:= default) -> + 0; +enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> + V; +enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when (V =:= default) -> + 0; +enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> + V; enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -2871,6 +2890,12 @@ enc_sockopt_key(otp, iow, _, _, _, _) -> ?SOCKET_OPT_OTP_IOW; enc_sockopt_key(otp, controlling_process, _, _, _, _) -> ?SOCKET_OPT_OTP_CTRL_PROC; +enc_sockopt_key(otp, rcvbuf, _, _, _, _) -> + ?SOCKET_OPT_OTP_RCVBUF; +enc_sockopt_key(otp, rcvctrlbuf, _, _, _, _) -> + ?SOCKET_OPT_OTP_RCVCTRLBUF; +enc_sockopt_key(otp, sndctrlbuf, _, _, _, _) -> + ?SOCKET_OPT_OTP_SNDCTRLBUF; enc_sockopt_key(otp = L, Opt, _, _, _, _) -> not_supported({L, Opt}); diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 0255c8ef75..d4cd144168 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -38,6 +38,9 @@ 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, @@ -90,13 +93,15 @@ all() -> groups() -> [{api, [], api_cases()}, {api_basic, [], api_basic_cases()}, - {api_op_with_timeout, [], api_op_with_timeout_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} ]. @@ -110,6 +115,11 @@ api_basic_cases() -> api_b_sendmsg_and_recvmsg_tcp4 ]. +api_options_cases() -> + [ + api_opt_simple_otp_options + ]. + api_op_with_timeout_cases() -> [ api_to_connect_tcp4, @@ -433,6 +443,92 @@ api_b_send_and_recv_tcp(Domain, Send, Recv) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 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) -> @@ -1067,6 +1163,20 @@ sock_accept(LSock) -> 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. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |