summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2022-09-22 12:46:39 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2022-09-22 12:46:39 (GMT)
commitb246adbfbd4b44ebda9dc4d51430f00bdd677814 (patch)
tree1f5a96e1412a3652239d6c60e1b78826e527ee7d
parente0716f3a835d64a5e2b7cc722db53e7c20e2169c (diff)
parentc0ad0edfe03a4a4119f22763f04cae0bf8f94282 (diff)
downloadtcl-b246adbfbd4b44ebda9dc4d51430f00bdd677814.zip
tcl-b246adbfbd4b44ebda9dc4d51430f00bdd677814.tar.gz
tcl-b246adbfbd4b44ebda9dc4d51430f00bdd677814.tar.bz2
rebase to latest 8.7
-rw-r--r--doc/socket.n15
-rw-r--r--tests/ioCmd.test4
-rw-r--r--tests/socket.test2
-rw-r--r--unix/tclUnixSock.c135
-rw-r--r--win/tclWinSock.c76
5 files changed, 172 insertions, 60 deletions
diff --git a/doc/socket.n b/doc/socket.n
index 8836150..4506181 100644
--- a/doc/socket.n
+++ b/doc/socket.n
@@ -162,7 +162,8 @@ described below.
.SH "CONFIGURATION OPTIONS"
.PP
The \fBchan configure\fR command can be used to query several readonly
-configuration options for socket channels:
+configuration options for socket channels or in some cases to set
+alternative properties on socket channels:
.TP
\fB\-error\fR
.
@@ -204,6 +205,18 @@ list is identical to the address, its first element.
\fB\-connecting\fR
.
This option is not supported by server sockets. For client sockets, this option returns 1 if an asyncroneous connect is still in progress, 0 otherwise.
+.TP
+\fB\-keepalive\fR
+.
+This options sets or queries the TCP keepalive option on the socket as 1 if
+keepalive is turned on, 0 otherwise.
+.TP
+\fB\-nagle\fR
+.
+This options sets or queries the TCP nodelay option (aka the Nagle algorithm)
+When 1 the Nagle algorithm is turned on, 0 otherwise. Caution: the logic is
+reversed here, i.e. when the option is 0, the underlying system call asserts
+the TCP_NODELAY setting.
.PP
.SH "EXAMPLES"
.PP
diff --git a/tests/ioCmd.test b/tests/ioCmd.test
index dbca866..f911846 100644
--- a/tests/ioCmd.test
+++ b/tests/ioCmd.test
@@ -294,7 +294,7 @@ removeFile fconfigure.dummy
test iocmd-8.14 {fconfigure command} {
fconfigure stdin -buffers
} 4096
-test iocmd-8.15.1 {fconfigure command / tcp channel} -constraints {socket unixOrWin} -setup {
+test iocmd-8.15 {fconfigure command / tcp channel} -constraints {socket unixOrWin} -setup {
set srv [socket -server iocmdSRV -myaddr 127.0.0.1 0]
set port [lindex [fconfigure $srv -sockname] 2]
proc iocmdSRV {sock ip port} {close $sock}
@@ -306,7 +306,7 @@ test iocmd-8.15.1 {fconfigure command / tcp channel} -constraints {socket unixOr
close $srv
unset cli srv port
rename iocmdSRV {}
-} -returnCodes error -result [expectedOpts "-blah" {-connecting -peername -sockname}]
+} -returnCodes error -result [expectedOpts "-blah" {-connecting -keepalive -nagle -peername -sockname}]
test iocmd-8.16 {fconfigure command / tcp channel} -constraints socket -setup {
set srv [socket -server iocmdSRV -myaddr 127.0.0.1 0]
set port [lindex [fconfigure $srv -sockname] 2]
diff --git a/tests/socket.test b/tests/socket.test
index 4644e1d..7250cb8 100644
--- a/tests/socket.test
+++ b/tests/socket.test
@@ -1071,7 +1071,7 @@ test socket_$af-7.3 {testing socket specific options} -constraints [list socket
close $s
update
llength $l
-} -result 14
+} -result 18
test socket_$af-7.4 {testing socket specific options} -constraints [list socket supported_$af] -setup {
set timer [after 10000 "set x timed_out"]
set l ""
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index 217d5ce..7f22796 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -9,6 +9,7 @@
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
+#include <netinet/tcp.h>
#include "tclInt.h"
/*
@@ -139,6 +140,9 @@ static int TcpInputProc(void *instanceData, char *buf,
int toRead, int *errorCode);
static int TcpOutputProc(void *instanceData,
const char *buf, int toWrite, int *errorCode);
+static int TcpSetOptionProc(void *instanceData,
+ Tcl_Interp *interp, const char *optionName,
+ const char *value);
static void TcpThreadActionProc(void *instanceData, int action);
static void TcpWatchProc(void *instanceData, int mask);
static int WaitForConnect(TcpState *statePtr, int *errorCodePtr);
@@ -160,7 +164,7 @@ static const Tcl_ChannelType tcpChannelType = {
TcpInputProc, /* Input proc. */
TcpOutputProc, /* Output proc. */
NULL, /* Seek proc. */
- NULL, /* Set option proc. */
+ TcpSetOptionProc, /* Set option proc. */
TcpGetOptionProc, /* Get option proc. */
TcpWatchProc, /* Initialize notifier. */
TcpGetHandleProc, /* Get OS handles out of channel. */
@@ -808,6 +812,88 @@ TcpHostPortList(
/*
*----------------------------------------------------------------------
*
+ * TcpSetOptionProc --
+ *
+ * Sets TCP channel specific options.
+ *
+ * Results:
+ * None, unless an error happens.
+ *
+ * Side effects:
+ * Changes attributes of the socket at the system level.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TcpSetOptionProc(
+ void *instanceData, /* Socket state. */
+ Tcl_Interp *interp, /* For error reporting - can be NULL. */
+ const char *optionName, /* Name of the option to set. */
+ const char *value) /* New value for option. */
+{
+ TcpState *statePtr = (TcpState *)instanceData;
+ size_t len = 0;
+
+ if (optionName != NULL) {
+ len = strlen(optionName);
+ }
+
+ if ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0)) {
+ int val = 0, ret;
+
+ if (Tcl_GetBoolean(interp, value, &val) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#if defined(SO_KEEPALIVE)
+ ret = setsockopt(statePtr->fds.fd, SOL_SOCKET, SO_KEEPALIVE,
+ (const char *) &val, sizeof(int));
+#else
+ ret = -1;
+ Tcl_SetErrno(ENOTSUP);
+#endif
+ if (ret < 0) {
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't set socket option: %s",
+ Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+ if ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nagle", len) == 0)) {
+ int val = 0, ret;
+
+ if (Tcl_GetBoolean(interp, value, &val) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ val = !val; /* Nagle ain't nodelay */
+#if defined(SOL_TCP) && defined(TCP_NODELAY)
+ ret = setsockopt(statePtr->fds.fd, SOL_TCP, TCP_NODELAY,
+ (const char *) &val, sizeof(int));
+#else
+ ret = -1;
+ Tcl_SetErrno(ENOTSUP);
+#endif
+ if (ret < 0) {
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "couldn't set socket option: %s",
+ Tcl_PosixError(interp)));
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+ return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TcpGetOptionProc --
*
* Computes an option value for a TCP socket based channel, or a list of
@@ -828,7 +914,7 @@ TcpHostPortList(
static int
TcpGetOptionProc(
- void *instanceData, /* Socket state. */
+ void *instanceData, /* Socket state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Name of the option to retrieve the value
* for, or NULL to get all options and their
@@ -839,8 +925,6 @@ TcpGetOptionProc(
TcpState *statePtr = (TcpState *)instanceData;
size_t len = 0;
- WaitForConnect(statePtr, NULL);
-
if (optionName != NULL) {
len = strlen(optionName);
}
@@ -849,6 +933,7 @@ TcpGetOptionProc(
(strncmp(optionName, "-error", len) == 0)) {
socklen_t optlen = sizeof(int);
+ WaitForConnect(statePtr, NULL);
if (GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT)) {
/*
* Suppress errors as long as we are not done.
@@ -873,6 +958,7 @@ TcpGetOptionProc(
if ((len > 1) && (optionName[1] == 'c') &&
(strncmp(optionName, "-connecting", len) == 0)) {
+ WaitForConnect(statePtr, NULL);
Tcl_DStringAppend(dsPtr,
GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT) ? "1" : "0", TCL_INDEX_NONE);
return TCL_OK;
@@ -883,6 +969,7 @@ TcpGetOptionProc(
address peername;
socklen_t size = sizeof(peername);
+ WaitForConnect(statePtr, NULL);
if (GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT)) {
/*
* In async connect output an empty string
@@ -934,6 +1021,7 @@ TcpGetOptionProc(
socklen_t size;
int found = 0;
+ WaitForConnect(statePtr, NULL);
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-sockname");
Tcl_DStringStartSublist(dsPtr);
@@ -967,9 +1055,46 @@ TcpGetOptionProc(
}
}
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0))) {
+ socklen_t size;
+ int opt = 0;
+
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-keepalive");
+ }
+#if defined(SO_KEEPALIVE)
+ getsockopt(statePtr->fds.fd, SOL_SOCKET, SO_KEEPALIVE,
+ (char *) &opt, &size);
+#endif
+ Tcl_DStringAppendElement(dsPtr, opt ? "1" : "0");
+ if (len > 0) {
+ return TCL_OK;
+ }
+ }
+
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nagle", len) == 0))) {
+ socklen_t size;
+ int opt = 0;
+
+ if (len == 0) {
+ Tcl_DStringAppendElement(dsPtr, "-nagle");
+ }
+#if defined(SOL_TCP) && defined(TCP_NODELAY)
+ getsockopt(statePtr->fds.fd, SOL_TCP, TCP_NODELAY,
+ (char *) &opt, &size);
+#endif
+ opt = !opt; /* Nagle ain't nodelay */
+ Tcl_DStringAppendElement(dsPtr, opt ? "1" : "0");
+ if (len > 0) {
+ return TCL_OK;
+ }
+ }
+
if (len > 0) {
return Tcl_BadChannelOption(interp, optionName,
- "connecting peername sockname");
+ "connecting keepalive nagle peername sockname");
}
return TCL_OK;
diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index f1a6a5e..8d16b5c 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -55,13 +55,6 @@
#endif
/*
- * Support for control over sockets' KEEPALIVE and NODELAY behavior is
- * currently disabled.
- */
-
-#undef TCL_FEATURE_KEEPALIVE_NAGLE
-
-/*
* Helper macros to make parts of this file clearer. The macros do exactly
* what they say on the tin. :-) They also only ever refer to their arguments
* once, and so can be used without regard to side effects.
@@ -1178,14 +1171,15 @@ TcpSetOptionProc(
void *instanceData, /* Socket state. */
Tcl_Interp *interp, /* For error reporting - can be NULL. */
const char *optionName, /* Name of the option to set. */
- TCL_UNUSED(const char *) /*value*/) /* New value for option. */
+ const char *value) /* New value for option. */
{
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- TcpState *statePtr = instanceData;
+ TcpState *statePtr = (TcpState *)instanceData;
SOCKET sock;
-#else
- (void)instanceData;
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+ size_t len = 0;
+
+ if (optionName != NULL) {
+ len = strlen(optionName);
+ }
/*
* Check that WinSock is initialized; do not call it if not, to prevent
@@ -1201,20 +1195,17 @@ TcpSetOptionProc(
return TCL_ERROR;
}
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
-#error "TCL_FEATURE_KEEPALIVE_NAGLE not reviewed for whether to treat statePtr->sockets as single fd or list"
sock = statePtr->sockets->fd;
- if (!strcasecmp(optionName, "-keepalive")) {
- BOOL val = FALSE;
+ if ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0)) {
+ BOOL val;
int boolVar, rtn;
if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
return TCL_ERROR;
}
- if (boolVar) {
- val = TRUE;
- }
+ val = boolVar ? TRUE : FALSE;
rtn = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
(const char *) &val, sizeof(BOOL));
if (rtn != 0) {
@@ -1227,16 +1218,16 @@ TcpSetOptionProc(
return TCL_ERROR;
}
return TCL_OK;
- } else if (!strcasecmp(optionName, "-nagle")) {
- BOOL val = FALSE;
+ }
+ if ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nagle", len) == 0)) {
+ BOOL val;
int boolVar, rtn;
if (Tcl_GetBoolean(interp, value, &boolVar) != TCL_OK) {
return TCL_ERROR;
}
- if (!boolVar) {
- val = TRUE;
- }
+ val = boolVar ? FALSE : TRUE;
rtn = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(const char *) &val, sizeof(BOOL));
if (rtn != 0) {
@@ -1250,11 +1241,7 @@ TcpSetOptionProc(
}
return TCL_OK;
}
-
return Tcl_BadChannelOption(interp, optionName, "keepalive nagle");
-#else
- return Tcl_BadChannelOption(interp, optionName, "");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
}
/*
@@ -1528,54 +1515,43 @@ TcpGetOptionProc(
}
}
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- if (len == 0 || !strncmp(optionName, "-keepalive", len)) {
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'k') &&
+ (strncmp(optionName, "-keepalive", len) == 0))) {
int optlen;
BOOL opt = FALSE;
if (len == 0) {
+ sock = statePtr->sockets->fd;
Tcl_DStringAppendElement(dsPtr, "-keepalive");
}
optlen = sizeof(BOOL);
getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, &optlen);
- if (opt) {
- Tcl_DStringAppendElement(dsPtr, "1");
- } else {
- Tcl_DStringAppendElement(dsPtr, "0");
- }
+ Tcl_DStringAppendElement(dsPtr, opt ? "1" : "0");
if (len > 0) {
return TCL_OK;
}
}
- if (len == 0 || !strncmp(optionName, "-nagle", len)) {
+ if ((len == 0) || ((len > 1) && (optionName[1] == 'n') &&
+ (strncmp(optionName, "-nagle", len) == 0))) {
int optlen;
BOOL opt = FALSE;
if (len == 0) {
+ sock = statePtr->sockets->fd;
Tcl_DStringAppendElement(dsPtr, "-nagle");
}
optlen = sizeof(BOOL);
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, &optlen);
- if (opt) {
- Tcl_DStringAppendElement(dsPtr, "0");
- } else {
- Tcl_DStringAppendElement(dsPtr, "1");
- }
+ Tcl_DStringAppendElement(dsPtr, opt ? "0" : "1");
if (len > 0) {
return TCL_OK;
}
}
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
if (len > 0) {
-#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
- return Tcl_BadChannelOption(interp, optionName,
- "connecting peername sockname keepalive nagle");
-#else
return Tcl_BadChannelOption(interp, optionName,
- "connecting peername sockname");
-#endif /*TCL_FEATURE_KEEPALIVE_NAGLE*/
+ "connecting keepalive nagle peername sockname");
}
return TCL_OK;
@@ -1664,8 +1640,6 @@ TcpGetHandleProc(
*handlePtr = INT2PTR(statePtr->sockets->fd);
return TCL_OK;
}
-
-
/*
*----------------------------------------------------------------------