aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/socket_usage.xml21
-rw-r--r--erts/emulator/nifs/common/socket_nif.c146
-rw-r--r--erts/emulator/nifs/common/socket_util.c29
-rw-r--r--erts/emulator/nifs/common/socket_util.h6
-rw-r--r--erts/preloaded/ebin/socket.beambin66040 -> 66584 bytes
-rw-r--r--erts/preloaded/src/socket.erl33
-rw-r--r--lib/kernel/test/socket_SUITE.erl112
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
index 9e6d9f4709..a0550990e3 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
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.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%