summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/socket.test160
-rw-r--r--unix/tclUnixSock.c75
2 files changed, 189 insertions, 46 deletions
diff --git a/tests/socket.test b/tests/socket.test
index a21bb8d..6b072c2 100644
--- a/tests/socket.test
+++ b/tests/socket.test
@@ -1759,7 +1759,7 @@ test socket-14.0.0 {[socket -async] when server only listens on IPv4} \
unset x
} -result ok
test socket-14.0.1 {[socket -async] when server only listens on IPv6} \
- -constraints [list socket supported_any localhost_v4] \
+ -constraints [list socket supported_any localhost_v6] \
-setup {
proc accept {s a p} {
global x
@@ -1962,13 +1962,13 @@ test socket-14.7.0 {pending [socket -async] and blocking [gets], server is IPv4}
set port [gets $fd]
} -body {
set sock [socket -async localhost $port]
- gets $sock
+ list [fconfigure $sock -error] [gets $sock] [fconfigure $sock -error]
} -cleanup {
# make sure the server exits
catch {socket 127.0.0.1 $port}
close $sock
close $fd
- } -result {ok}
+ } -result {{} ok {}}
test socket-14.7.1 {pending [socket -async] and blocking [gets], server is IPv6} \
-constraints {socket supported_inet supported_inet6} \
-setup {
@@ -1983,13 +1983,22 @@ test socket-14.7.1 {pending [socket -async] and blocking [gets], server is IPv6}
set port [gets $fd]
} -body {
set sock [socket -async localhost $port]
- gets $sock
+ list [fconfigure $sock -error] [gets $sock] [fconfigure $sock -error]
} -cleanup {
# make sure the server exits
catch {socket ::1 $port}
close $sock
close $fd
- } -result {ok}
+ } -result {{} ok {}}
+test socket-14.7.2 {pending [socket -async] and blocking [gets], no listener} \
+ -constraints {socket supported_inet supported_inet6} \
+ -body {
+ set sock [socket -async localhost [randport]]
+ catch {gets $sock} x
+ list $x [fconfigure $sock -error]
+ } -cleanup {
+ close $sock
+ } -match glob -result {{error reading "sock*": socket is not connected} {connection refused}}
test socket-14.8.0 {pending [socket -async] and nonblocking [gets], server is IPv4} \
-constraints {socket supported_inet supported_inet6} \
-setup {
@@ -2007,6 +2016,7 @@ test socket-14.8.0 {pending [socket -async] and nonblocking [gets], server is IP
fconfigure $sock -blocking 0
for {set i 0} {$i < 50} {incr i } {
if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break
+ update
after 200
}
set x
@@ -2033,6 +2043,7 @@ test socket-14.8.1 {pending [socket -async] and nonblocking [gets], server is IP
fconfigure $sock -blocking 0
for {set i 0} {$i < 50} {incr i } {
if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break
+ update
after 200
}
set x
@@ -2042,6 +2053,145 @@ test socket-14.8.1 {pending [socket -async] and nonblocking [gets], server is IP
close $sock
close $fd
} -result {ok}
+test socket-14.8.2 {pending [socket -async] and nonblocking [gets], no listener} \
+ -constraints {socket supported_inet supported_inet6} \
+ -body {
+ set sock [socket -async localhost [randport]]
+ fconfigure $sock -blocking 0
+ for {set i 0} {$i < 50} {incr i } {
+ if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break
+ update
+ after 200
+ }
+ fconfigure $sock -error
+ } -cleanup {
+ close $sock
+ } -match glob -result {connection refused}
+test socket-14.9.0 {pending [socket -async] and blocking [puts], server is IPv4} \
+ -constraints {socket supported_inet supported_inet6} \
+ -setup {
+ makeFile {
+ set server [socket -server accept -myaddr 127.0.0.1 0]
+ proc accept {s h p} {set ::x $s}
+ puts [lindex [fconfigure $server -sockname] 2]
+ flush stdout
+ vwait x
+ puts [gets $x]
+ } script
+ set fd [open |[list [interpreter] script] RDWR]
+ set port [gets $fd]
+ } -body {
+ set sock [socket -async localhost $port]
+ puts $sock ok
+ flush $sock
+ list [fconfigure $sock -error] [gets $fd]
+ } -cleanup {
+ # make sure the server exits
+ catch {socket 127.0.0.1 $port}
+ close $sock
+ close $fd
+ } -result {{} ok}
+test socket-14.9.1 {pending [socket -async] and blocking [puts], server is IPv6} \
+ -constraints {socket supported_inet supported_inet6} \
+ -setup {
+ makeFile {
+ set server [socket -server accept -myaddr ::1 0]
+ proc accept {s h p} {set ::x $s}
+ puts [lindex [fconfigure $server -sockname] 2]
+ flush stdout
+ vwait x
+ puts [gets $x]
+ } script
+ set fd [open |[list [interpreter] script] RDWR]
+ set port [gets $fd]
+ } -body {
+ set sock [socket -async localhost $port]
+ puts $sock ok
+ flush $sock
+ list [fconfigure $sock -error] [gets $fd]
+ } -cleanup {
+ # make sure the server exits
+ catch {socket ::1 $port}
+ close $sock
+ close $fd
+ } -result {{} ok}
+test socket-14.10.0 {pending [socket -async] and blocking [puts], server is IPv4} \
+ -constraints {socket supported_inet supported_inet6} \
+ -setup {
+ makeFile {
+ set server [socket -server accept -myaddr 127.0.0.1 0]
+ proc accept {s h p} {set ::x $s}
+ puts [lindex [fconfigure $server -sockname] 2]
+ flush stdout
+ vwait x
+ puts [gets $x]
+ } script
+ set fd [open |[list [interpreter] script] RDWR]
+ set port [gets $fd]
+ } -body {
+ set sock [socket -async localhost $port]
+ fconfigure $sock -blocking 0
+ puts $sock ok
+ flush $sock
+ fileevent $fd readable {set x 1}
+ vwait x
+ list [fconfigure $sock -error] [gets $fd]
+ } -cleanup {
+ # make sure the server exits
+ catch {socket 127.0.0.1 $port}
+ close $sock
+ close $fd
+ } -result {{} ok}
+test socket-14.10.1 {pending [socket -async] and blocking [puts], server is IPv6} \
+ -constraints {socket supported_inet supported_inet6} \
+ -setup {
+ makeFile {
+ set server [socket -server accept -myaddr ::1 0]
+ proc accept {s h p} {set ::x $s}
+ puts [lindex [fconfigure $server -sockname] 2]
+ flush stdout
+ vwait x
+ puts [gets $x]
+ } script
+ set fd [open |[list [interpreter] script] RDWR]
+ set port [gets $fd]
+ } -body {
+ set sock [socket -async localhost $port]
+ fconfigure $sock -blocking 0
+ puts $sock ok
+ flush $sock
+ fileevent $fd readable {set x 1}
+ vwait x
+ list [fconfigure $sock -error] [gets $fd]
+ } -cleanup {
+ # make sure the server exits
+ catch {socket ::1 $port}
+ close $sock
+ close $fd
+ } -result {{} ok}
+test socket-14.11.0 {pending [socket -async] and blocking [puts], no listener, no flush} \
+ -constraints {socket supported_inet supported_inet6} \
+ -body {
+ set sock [socket -async localhost [randport]]
+ fconfigure $sock -blocking 0
+ puts $sock ok
+ fileevent $sock writable {set x 1}
+ vwait x
+ close $sock
+ } -cleanup {
+ } -result {broken pipe} -returnCodes 1
+test socket-14.11.1 {pending [socket -async] and blocking [puts], no listener, flush} \
+ -constraints {socket supported_inet supported_inet6} \
+ -body {
+ set sock [socket -async localhost [randport]]
+ fconfigure $sock -blocking 0
+ puts $sock ok
+ flush $sock
+ fileevent $sock writable {set x 1}
+ vwait x
+ close $sock
+ } -cleanup {
+ } -result {broken pipe} -returnCodes 1
::tcltest::cleanupTests
flush stdout
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index 6e84ed5..8336bdb 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -73,7 +73,7 @@ struct TcpState {
struct addrinfo *myaddr; /* Iterator over myaddrlist. */
int filehandlers; /* Caches FileHandlers that get set up while
* an async socket is not yet connected. */
- int status; /* Cache status of async socket. */
+ int error; /* Cache SO_ERROR of async socket. */
int cachedBlocking; /* Cache blocking mode of async socket. */
};
@@ -417,7 +417,7 @@ WaitForConnect(
errno = 0;
state = TclUnixWaitForFile(statePtr->fds.fd,
TCL_WRITABLE | TCL_EXCEPTION, timeOut);
- if (state != 0) {
+ if (timeOut == -1 && state != 0) {
CreateClientSocket(NULL, statePtr);
}
if (statePtr->flags & TCP_ASYNC_CONNECT) {
@@ -522,6 +522,7 @@ TcpOutputProc(
return -1;
}
written = send(statePtr->fds.fd, buf, (size_t) toWrite, 0);
+
if (written > -1) {
return written;
}
@@ -752,24 +753,22 @@ TcpGetOptionProc(
if ((len > 1) && (optionName[1] == 'e') &&
(strncmp(optionName, "-error", len) == 0)) {
socklen_t optlen = sizeof(int);
- int err, ret;
- if (statePtr->status == 0) {
- ret = getsockopt(statePtr->fds.fd, SOL_SOCKET, SO_ERROR,
- (char *) &err, &optlen);
- if (statePtr->flags & TCP_ASYNC_CONNECT) {
- statePtr->status = err;
- }
- if (ret < 0) {
- err = errno;
- }
+ if (statePtr->flags & TCP_ASYNC_CONNECT) {
+ /* Suppress errors as long as we are not done */
+ errno = 0;
+ } else if (statePtr->error != 0) {
+ errno = statePtr->error;
+ statePtr->error = 0;
} else {
- err = statePtr->status;
- statePtr->status = 0;
+ int err;
+ getsockopt(statePtr->fds.fd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &optlen);
+ errno = err;
+ }
+ if (errno != 0) {
+ Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errno), -1);
}
- if (err != 0) {
- Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(err), -1);
- }
return TCL_OK;
}
@@ -982,7 +981,7 @@ CreateClientSocket(
{
socklen_t optlen;
int async_callback = (state->addr != NULL);
- int status;
+ int ret = -1, error;
int async = state->flags & TCP_ASYNC_CONNECT;
if (async_callback) {
@@ -991,8 +990,6 @@ CreateClientSocket(
for (state->addr = state->addrlist; state->addr != NULL;
state->addr = state->addr->ai_next) {
- status = -1;
-
for (state->myaddr = state->myaddrlist; state->myaddr != NULL;
state->myaddr = state->myaddr->ai_next) {
int reuseaddr;
@@ -1014,6 +1011,7 @@ CreateClientSocket(
if (state->fds.fd >= 0) {
close(state->fds.fd);
state->fds.fd = -1;
+ errno = 0;
}
state->fds.fd = socket(state->addr->ai_family, SOCK_STREAM, 0);
@@ -1035,19 +1033,18 @@ CreateClientSocket(
TclSockMinimumBuffers(INT2PTR(state->fds.fd), SOCKET_BUFSIZE);
if (async) {
- status = TclUnixSetBlockingMode(state->fds.fd,
- TCL_MODE_NONBLOCKING);
- if (status < 0) {
+ ret = TclUnixSetBlockingMode(state->fds.fd,TCL_MODE_NONBLOCKING);
+ if (ret < 0) {
continue;
- }
- }
+ }
+ }
reuseaddr = 1;
(void) setsockopt(state->fds.fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &reuseaddr, sizeof(reuseaddr));
- status = bind(state->fds.fd, state->myaddr->ai_addr,
+ ret = bind(state->fds.fd, state->myaddr->ai_addr,
state->myaddr->ai_addrlen);
- if (status < 0) {
+ if (ret < 0) {
continue;
}
@@ -1058,9 +1055,10 @@ CreateClientSocket(
* in being informed when the connect completes.
*/
- status = connect(state->fds.fd, state->addr->ai_addr,
- state->addr->ai_addrlen);
- if (status < 0 && errno == EINPROGRESS) {
+ ret = connect(state->fds.fd, state->addr->ai_addr,
+ state->addr->ai_addrlen);
+ error = errno;
+ if (ret < 0 && errno == EINPROGRESS) {
Tcl_CreateFileHandler(state->fds.fd,
TCL_WRITABLE|TCL_EXCEPTION, TcpAsyncCallback, state);
return TCL_OK;
@@ -1077,23 +1075,18 @@ CreateClientSocket(
optlen = sizeof(int);
- if (state->status == 0) {
- getsockopt(state->fds.fd, SOL_SOCKET, SO_ERROR,
- (char *) &status, &optlen);
- state->status = status;
- } else {
- status = state->status;
- state->status = 0;
- }
+ getsockopt(state->fds.fd, SOL_SOCKET, SO_ERROR,
+ (char *) &error, &optlen);
+ errno = error;
}
- if (status == 0) {
+ if (ret == 0 || errno == 0) {
goto out;
}
}
}
out:
-
+ state->error = errno;
CLEAR_BITS(state->flags, TCP_ASYNC_CONNECT);
if (async_callback) {
/*
@@ -1113,7 +1106,7 @@ out:
*/
Tcl_NotifyChannel(state->channel, TCL_WRITABLE);
- } else if (status != 0) {
+ } else if (ret != 0) {
/*
* Failure for either a synchronous connection, or an async one that
* failed before it could enter background mode, e.g. because an