# Commands tested in this file: socket. # # This file contains a collection of tests for one or more of the Tcl built-in # commands. Sourcing this file into Tcl runs the tests and generates output # for errors. No output means no errors were found. # # Copyright © 1994-1996 Sun Microsystems, Inc. # Copyright © 1998-2000 Ajuba Solutions. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # Running socket tests with a remote server: # ------------------------------------------ # # Some tests in socket.test depend on the existence of a remote server to # which they connect. The remote server must be an instance of tcltest and it # must run the script found in the file "remote.tcl" in this directory. You # can start the remote server on any machine reachable from the machine on # which you want to run the socket tests, by issuing: # # tcltest remote.tcl -port 2048 # Or choose another port number. # # If the machine you are running the remote server on has several IP # interfaces, you can choose which interface the server listens on for # connections by specifying the -address command line flag, so: # # tcltest remote.tcl -address your.machine.com # # These options can also be set by environment variables. On Unix, you can # type these commands to the shell from which the remote server is started: # # shell% setenv serverPort 2048 # shell% setenv serverAddress your.machine.com # # and subsequently you can start the remote server with: # # tcltest remote.tcl # # to have it listen on port 2048 on the interface your.machine.com. # # When the server starts, it prints out a detailed message containing its # configuration information, and it will block until killed with a Ctrl-C. # Once the remote server exists, you can run the tests in socket.test with the # server by setting two Tcl variables: # # % set remoteServerIP # % set remoteServerPort 2048 # # These variables are also settable from the environment. On Unix, you can: # # shell% setenv remoteServerIP machine.where.server.runs # shell% senetv remoteServerPort 2048 # # The preamble of the socket.test file checks to see if the variables are set # either in Tcl or in the environment; if they are, it attempts to connect to # the server. If the connection is successful, the tests using the remote # server will be performed; otherwise, it will attempt to start the remote # server (via exec) on platforms that support this, on the local host, # listening at port 2048. If all fails, a message is printed and the tests # using the remote server are not performed. if {"::tcltest" ni [namespace children]} { package require tcltest 2.5 namespace import -force ::tcltest::* } ::tcltest::loadTestedCommands catch [list package require -exact tcl::test [info patchlevel]] ::tcltest::loadTestedCommands # A bad interaction between socket creation, macOS, and unattended CI # environments make this whole file impractical to run; too many weird hangs. if {[info exists ::env(MAC_CI)]} { return } testConstraint notWine [expr {![info exists ::env(CI_USING_WINE)]}] # Some tests require the Thread package or exec command testConstraint thread [expr {0 == [catch {package require Thread 2.7-}]}] testConstraint exec [llength [info commands exec]] testConstraint notWinCI [expr { $tcl_platform(platform) ne "windows" || ![info exists ::env(CI)]}] # Produce a random port number in the Dynamic/Private range # from 49152 through 65535. proc randport {} { # firstly try dynamic port via server-socket(0): set port 0x7fffffff catch { set port [lindex [fconfigure [set s [socket -server {} 0]] -sockname] 2] close $s } while {[catch { close [socket -server {} $port] } msg]} { if {[incr i] > 1000} {return -code error "too many iterations to get free random port: $msg"} # try random port: set port [expr {int(rand()*16383+49152)}] } return $port } # Check if testsocket testflags is available testConstraint testsocket_testflags [expr {![catch { set h [socket -async localhost [randport]] testsocket testflags $h 0 close $h }]}] # Test the latency of tcp connections over the loopback interface. Some OSes # (e.g. NetBSD) seem to use the Nagle algorithm and delayed ACKs, so it takes # up to 200ms for a packet sent to localhost to arrive. We're measuring this # here, so that OSes that don't have this problem can run the tests at full # speed. set server [socket -server {apply {{s a p} {set ::s1 $s}}} 0] set s2 [socket localhost [lindex [fconfigure $server -sockname] 2]] vwait s1; close $server fconfigure $s1 -buffering line fconfigure $s2 -buffering line set t1 [clock milliseconds] puts $s2 test1; gets $s1 puts $s2 test2; gets $s1 close $s1; close $s2 set t2 [clock milliseconds] set lat1 [expr {($t2-$t1)*2}]; # doubled as a safety margin # Test the latency of failed connection attempts over the loopback # interface. They can take more than a second under Windowos and requres # additional [after]s in some tests that are not needed on systems that fail # immediately. set t1 [clock milliseconds] catch {socket 127.0.0.1 [randport]} set t2 [clock milliseconds] set lat2 [expr {($t2-$t1)*3}] # Use the maximum of the two latency calculations, but at least 200ms set latency [expr {$lat1 > $lat2 ? $lat1 : $lat2}] set latency [expr {$latency > 200 ? $latency : 200}] unset t1 t2 s1 s2 lat1 lat2 server # If remoteServerIP or remoteServerPort are not set, check in the environment # variables for externally set values. # if {![info exists remoteServerIP]} { if {[info exists env(remoteServerIP)]} { set remoteServerIP $env(remoteServerIP) } } if {![info exists remoteServerPort]} { if {[info exists env(remoteServerPort)]} { set remoteServerPort $env(remoteServerPort) } else { if {[info exists remoteServerIP]} { set remoteServerPort 2048 } } } if 0 { # activate this to time the tests proc test {args} { set name [lindex $args 0] puts "[lindex [time {uplevel [linsert $args 0 tcltest::test]}] 0] @@@ $name" } } foreach {af localhost} { inet 127.0.0.1 inet6 ::1 } { # Check if the family is supported and set the constraint accordingly testConstraint supported_$af [expr {![catch {socket -server foo -myaddr $localhost 0} sock]}] catch {close $sock} } set sock [socket -server foo -myaddr localhost 0] set sockname [fconfigure $sock -sockname] close $sock testConstraint localhost_v4 [expr {"127.0.0.1" in $sockname}] testConstraint localhost_v6 [expr {"::1" in $sockname}] foreach {af localhost} { any 127.0.0.1 inet 127.0.0.1 inet6 ::1 } { if {![testConstraint supported_$af]} { continue } set ::tcl::unsupported::socketAF $af # # Check if we're supposed to do tests against the remote server # set doTestsWithRemoteServer 1 if {![info exists remoteServerIP]} { set remoteServerIP $localhost } if {($doTestsWithRemoteServer == 1) && (![info exists remoteServerPort])} { set remoteServerPort [randport] } # Attempt to connect to a remote server if one is already running. If it is # not running or for some other reason the connect fails, attempt to start the # remote server on the local host listening on port 2048. This is only done on # platforms that support exec (i.e. not on the Mac). On platforms that do not # support exec, the remote server must be started by the user before running # the tests. set remoteProcChan "" set commandSocket "" if {$doTestsWithRemoteServer} { catch {close $commandSocket} if {![catch { set commandSocket [socket $remoteServerIP $remoteServerPort] }]} then { fconfigure $commandSocket -translation crlf -buffering line } elseif {![testConstraint exec]} { set noRemoteTestReason "can't exec" set doTestsWithRemoteServer 0 } else { set remoteServerIP $localhost # Be *extra* careful in case this file is sourced from # a directory other than the current one... set remoteFile [file join [pwd] [file dirname [info script]] \ remote.tcl] if {![catch { set remoteProcChan [open "|[list \ [interpreter] $remoteFile -serverIsSilent \ -port $remoteServerPort -address $remoteServerIP]" w+] } msg]} then { gets $remoteProcChan if {[catch { set commandSocket [socket $remoteServerIP $remoteServerPort] } msg] == 0} then { fconfigure $commandSocket -translation crlf -buffering line } else { set noRemoteTestReason $msg set doTestsWithRemoteServer 0 } } else { set noRemoteTestReason "$msg [interpreter]" set doTestsWithRemoteServer 0 } } } # Some tests are run only if we are doing testing against a remote server. testConstraint doTestsWithRemoteServer $doTestsWithRemoteServer if {!$doTestsWithRemoteServer} { if {[string first s $::tcltest::verbose] >= 0} { puts "Skipping tests with remote server. See tests/socket.test for" puts "information on how to run remote server." puts "Reason for not doing remote tests: $noRemoteTestReason" } } # # If we do the tests, define a command to send a command to the remote server. # if {[testConstraint doTestsWithRemoteServer]} { proc sendCommand {c} { global commandSocket if {[eof $commandSocket]} { error "remote server disappeared" } if {[catch {puts $commandSocket $c} msg]} { error "remote server disappaered: $msg" } if {[catch {puts $commandSocket "--Marker--Marker--Marker--"} msg]} { error "remote server disappeared: $msg" } while {1} { set line [gets $commandSocket] if {[eof $commandSocket]} { error "remote server disappaered" } if {$line eq "--Marker--Marker--Marker--"} { lassign $result code info value return -code $code -errorinfo $info $value } append result $line "\n" } } } proc getPort sock { lindex [fconfigure $sock -sockname] 2 } # Some tests in this file are known to hang *occasionally* on OSX; stop the # worst offenders. testConstraint notOSX [expr {$::tcl_platform(os) ne "Darwin"}] # Here "Windows" means derived platforms as Cygwin or Msys2 too. testConstraint notWindows [expr {![regexp {^(Windows|MSYS|CYGWIN)} $::tcl_platform(os)]}] # ---------------------------------------------------------------------- test socket_$af-1.1 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server } -returnCodes error -result {no argument given for -server option} test socket_$af-1.2 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server foo } -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"} test socket_$af-1.3 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myaddr } -returnCodes error -result {no argument given for -myaddr option} test socket_$af-1.4 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myaddr $localhost } -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"} test socket_$af-1.5 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myport } -returnCodes error -result {no argument given for -myport option} test socket_$af-1.6 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myport xxxx } -returnCodes error -result {expected integer but got "xxxx"} test socket_$af-1.7 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -myport 2522 } -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"} test socket_$af-1.8 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -froboz } -returnCodes error -result {bad option "-froboz": must be -async, -backlog, -myaddr, -myport, -reuseaddr, -reuseport, or -server} test socket_$af-1.9 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server foo -myport 2521 3333 } -returnCodes error -result {option -myport is not valid for servers} test socket_$af-1.10 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket host 2528 -junk } -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"} test socket_$af-1.11 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server callback 2520 -- } -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"} test socket_$af-1.12 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket foo badport } -returnCodes error -result {expected integer but got "badport"} test socket_$af-1.13 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -async -server } -returnCodes error -result {cannot set -async option for server sockets} test socket_$af-1.14 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -server foo -async } -returnCodes error -result {cannot set -async option for server sockets} test socket_$af-1.15 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -reuseaddr yes 4242 } -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers} test socket_$af-1.16 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -reuseaddr no 4242 } -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers} test socket_$af-1.17 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -reuseaddr } -returnCodes error -result {no argument given for -reuseaddr option} test socket_$af-1.18 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -reuseport yes 4242 } -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers} test socket_$af-1.19 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -reuseport no 4242 } -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers} test socket_$af-1.20 {arg parsing for socket command} -constraints [list socket supported_$af] -body { socket -reuseport } -returnCodes error -result {no argument given for -reuseport option} set path(script) [makeFile {} script] test socket_$af-2.1 {tcp connection} -constraints [list socket supported_$af stdio] -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timed_out"] set f [socket -server accept 0] proc accept {file addr port} { global x set x done close $file } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f puts $x } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -body { # $x == "ready" at this point set sock [socket $localhost $listen] lappend x [gets $f] close $sock lappend x [gets $f] } -cleanup { close $f } -result {ready done {}} test socket_$af-2.2 {tcp connection with client port specified} -setup { set port [randport] file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timeout"] set f [socket -server accept 0] proc accept {file addr port} { global x puts "[gets $file] $port" close $file set x done } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -constraints [list socket supported_$af stdio] -body { # $x == "ready" at this point set sock [socket -myport $port $localhost $listen] puts $sock hello flush $sock lappend x [expr {[gets $f] eq "hello $port"}] close $sock return $x } -cleanup { catch {close [socket $localhost $listen]} close $f } -result {ready 1} test socket_$af-2.3 {tcp connection with client interface specified} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 2000 "set x done"] set f [socket -server accept 0] proc accept {file addr port} { global x puts "[gets $file] $addr" close $file set x done } puts [lindex [fconfigure $f -sockname] 2] puts ready vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f listen gets $f x } -constraints [list socket supported_$af stdio] -body { # $x == "ready" at this point set sock [socket -myaddr $localhost $localhost $listen] puts $sock hello flush $sock lappend x [gets $f] close $sock return $x } -cleanup { close $f } -result [list ready [list hello $localhost]] test socket_$af-2.4 {tcp connection with server interface specified} -setup { file delete $path(script) set f [open $path(script) w] puts $f [list set localhost $localhost] puts $f { set timer [after 2000 "set x done"] set f [socket -server accept -myaddr $localhost 0] proc accept {file addr port} { global x puts "[gets $file]" close $file set x done } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -constraints [list socket supported_$af stdio] -body { # $x == "ready" at this point set sock [socket $localhost $listen] puts $sock hello flush $sock lappend x [gets $f] close $sock return $x } -cleanup { close $f } -result {ready hello} test socket_$af-2.5 {tcp connection with redundant server port} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timeout"] set f [socket -server accept 0] proc accept {file addr port} { global x puts "[gets $file]" close $file set x done } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -constraints [list socket supported_$af stdio] -body { # $x == "ready" at this point set sock [socket $localhost $listen] puts $sock hello flush $sock lappend x [gets $f] close $sock return $x } -cleanup { close $f } -result {ready hello} test socket_$af-2.6 {tcp connection} -constraints [list socket supported_$af] -body { set status ok if {![catch {set sock [socket $localhost [randport]]}]} { if {![catch {gets $sock}]} { set status broken } close $sock } set status } -result ok test socket_$af-2.7 {echo server, one line} -constraints [list socket supported_$af stdio] -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timeout"] set f [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -translation lf -buffering line } proc echo {s} { set l [gets $s] if {[eof $s]} { global x close $s set x done } else { puts $s $l } } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f puts $x } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen } -body { set s [socket $localhost $listen] fconfigure $s -buffering line -translation lf puts $s "hello abcdefghijklmnop" set x [gets $s] close $s list $x [gets $f] } -cleanup { close $f } -result {{hello abcdefghijklmnop} done} removeFile script test socket_$af-2.8 {echo server, loop 50 times, single connection} -setup { set path(script) [makeFile { set f [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line } proc echo {s} { global i set l [gets $s] if {[eof $s]} { global x close $s set x done } else { incr i puts $s $l } } set i 0 puts ready puts [lindex [fconfigure $f -sockname] 2] set timer [after 20000 "set x done"] vwait x after cancel $timer close $f puts "done $i" } script] set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen } -constraints [list socket supported_$af stdio] -body { set s [socket $localhost $listen] fconfigure $s -buffering line catch { for {set x 0} {$x < 50} {incr x} { puts $s "hello abcdefghijklmnop" gets $s } } close $s catch {set x [gets $f]} return $x } -cleanup { close $f removeFile script } -result {done 50} set path(script) [makeFile {} script] test socket_$af-2.9 {socket conflict} -constraints [list socket supported_$af stdio] -body { set s [socket -server accept 0] file delete $path(script) set f [open $path(script) w] puts $f [list set ::tcl::unsupported::socketAF $::tcl::unsupported::socketAF] puts $f "socket -server accept [lindex [fconfigure $s -sockname] 2]" close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f after 100 close $f } -returnCodes error -cleanup { close $s } -match glob -result {couldn't open socket: address already in use*} test socket_$af-2.10 {close on accept, accepted socket lives} -setup { set done 0 set timer [after 20000 "set done timed_out"] } -constraints [list socket supported_$af] -body { set ss [socket -server accept 0] proc accept {s a p} { global ss close $ss fileevent $s readable "readit $s" fconfigure $s -trans lf } proc readit {s} { global done gets $s close $s set done 1 } set cs [socket $localhost [lindex [fconfigure $ss -sockname] 2]] puts $cs hello close $cs vwait done return $done } -cleanup { after cancel $timer } -result 1 test socket_$af-2.11 {detecting new data} -constraints [list socket supported_$af] -setup { proc accept {s a p} { global sock set sock $s } set s [socket -server accept 0] set sock "" } -body { set s2 [socket $localhost [lindex [fconfigure $s -sockname] 2]] vwait sock puts $s2 one flush $s2 after $latency {set x 1}; # Spurious failures in Travis CI, if we do [after idle] vwait x fconfigure $sock -blocking 0 set result a:[gets $sock] lappend result b:[gets $sock] fconfigure $sock -blocking 1 puts $s2 two flush $s2 after $latency {set x 1}; # NetBSD fails here if we do [after idle] vwait x fconfigure $sock -blocking 0 lappend result c:[gets $sock] } -cleanup { fconfigure $sock -blocking 1 close $s2 close $s close $sock } -result {a:one b: c:two} test socket_$af-2.12 {} [list socket stdio supported_$af] { file delete $path(script) set f [open $path(script) w] puts $f { set server [socket -server accept_client 0] puts [lindex [chan configure $server -sockname] 2] proc accept_client { client host port } { chan configure $client -blocking 0 -buffering line write_line $client } proc write_line client { if { [catch { chan puts $client [string repeat . 720000]}] } { puts [catch {chan close $client}] } else { puts signal1 after 0 write_line $client } } chan event stdin readable {set forever now} vwait forever exit } close $f set f [open "|[list [interpreter] $path(script)]" r+] gets $f port set sock [socket $localhost $port] chan event $sock readable [list read_lines $sock $f] proc read_lines { sock pipe } { gets $pipe chan close $sock chan event $pipe readable [list readpipe $pipe] } proc readpipe {pipe} { while {![string is integer [set ::done [gets $pipe]]]} {} } vwait ::done close $f set ::done } 0 test socket_$af-2.13 {Bug 1758a0b603} {socket stdio notWine} { file delete $path(script) set f [open $path(script) w] puts $f { set server [socket -server accept 0] puts [lindex [chan configure $server -sockname] 2] proc accept { client host port } { chan configure $client -blocking 0 -buffering line -buffersize 1 puts $client [string repeat . 720000] puts ready chan event $client writable [list setup $client] } proc setup client { chan event $client writable {set forever write} after 5 {set forever timeout} } vwait forever puts $forever } close $f set pipe [open |[list [interpreter] $path(script)] r] gets $pipe port set sock [socket $localhost $port] chan configure $sock -blocking 0 -buffering line chan event $sock readable [list read_lines $sock $pipe ] proc read_lines { sock pipe } { gets $pipe gets $sock line after idle [list stop $sock $pipe] chan event $sock readable {} } proc stop {sock pipe} { variable done close $sock set done [gets $pipe] } variable done vwait [namespace which -variable done] close $pipe set done } write test socket_$af-3.1 {socket conflict} -constraints [list socket supported_$af stdio] -setup { file delete $path(script) set f [open $path(script) w] puts $f [list set localhost $localhost] puts $f { set f [socket -server accept -myaddr $localhost 0] puts ready puts [lindex [fconfigure $f -sockname] 2] gets stdin close $f } close $f set f [open "|[list [interpreter] $path(script)]" r+] gets $f gets $f listen } -body { socket -server accept -myaddr $localhost $listen } -cleanup { puts $f bye close $f } -returnCodes error -result {couldn't open socket: address already in use} test socket_$af-3.2 {server with several clients} -setup { file delete $path(script) set f [open $path(script) w] puts $f [list set localhost $localhost] puts $f { set t1 [after 30000 "set x timed_out"] set t2 [after 31000 "set x timed_out"] set t3 [after 32000 "set x timed_out"] set counter 0 set s [socket -server accept -myaddr $localhost 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line } proc echo {s} { global x set l [gets $s] if {[eof $s]} { close $s set x done } else { puts $s $l } } puts ready puts [lindex [fconfigure $s -sockname] 2] vwait x after cancel $t1 vwait x after cancel $t2 vwait x after cancel $t3 close $s puts $x } close $f set f [open "|[list [interpreter] $path(script)]" r+] set x [gets $f] gets $f listen } -constraints [list socket supported_$af stdio] -body { # $x == "ready" here set s1 [socket $localhost $listen] fconfigure $s1 -buffering line set s2 [socket $localhost $listen] fconfigure $s2 -buffering line set s3 [socket $localhost $listen] fconfigure $s3 -buffering line for {set i 0} {$i < 100} {incr i} { puts $s1 hello,s1 gets $s1 puts $s2 hello,s2 gets $s2 puts $s3 hello,s3 gets $s3 } close $s1 close $s2 close $s3 lappend x [gets $f] } -cleanup { close $f } -result {ready done} test socket_$af-4.1 {server with several clients} -setup { file delete $path(script) set f [open $path(script) w] puts $f [list set localhost $localhost] puts $f { set port [gets stdin] set s [socket $localhost $port] fconfigure $s -buffering line for {set i 0} {$i < 100} {incr i} { puts $s hello gets $s } close $s puts bye gets stdin } close $f set p1 [open "|[list [interpreter] $path(script)]" r+] fconfigure $p1 -buffering line set p2 [open "|[list [interpreter] $path(script)]" r+] fconfigure $p2 -buffering line set p3 [open "|[list [interpreter] $path(script)]" r+] fconfigure $p3 -buffering line } -constraints [list socket supported_$af stdio] -body { proc accept {s a p} { fconfigure $s -buffering line fileevent $s readable [list echo $s] } proc echo {s} { global x set l [gets $s] if {[eof $s]} { close $s set x done } else { puts $s $l } } set t1 [after 30000 "set x timed_out"] set t2 [after 31000 "set x timed_out"] set t3 [after 32000 "set x timed_out"] set s [socket -server accept -myaddr $localhost 0] set listen [lindex [fconfigure $s -sockname] 2] puts $p1 $listen puts $p2 $listen puts $p3 $listen vwait x vwait x vwait x after cancel $t1 after cancel $t2 after cancel $t3 close $s set l "" lappend l [list p1 [gets $p1] $x] lappend l [list p2 [gets $p2] $x] lappend l [list p3 [gets $p3] $x] } -cleanup { puts $p1 bye puts $p2 bye puts $p3 bye close $p1 close $p2 close $p3 } -result {{p1 bye done} {p2 bye done} {p3 bye done}} test socket_$af-4.2 {byte order problems, socket numbers, htons} -body { close [socket -server dodo -myaddr $localhost 0x3000] return ok } -constraints [list socket supported_$af] -result ok test socket_$af-5.1 {byte order problems, socket numbers, htons} -body { if {![catch {socket -server dodo 0x1} msg]} { close $msg return {htons problem, should be disallowed, are you running as SU?} } return {couldn't open socket: not owner} } -constraints [list socket supported_$af unix notRoot notOSX notWindows] -result {couldn't open socket: not owner} test socket_$af-5.2 {byte order problems, socket numbers, htons} -body { if {![catch {socket -server dodo 0x10000} msg]} { close $msg return {port resolution problem, should be disallowed} } return {couldn't open socket: port number too high} } -constraints [list socket supported_$af] -result {couldn't open socket: port number too high} test socket_$af-5.3 {byte order problems, socket numbers, htons} -body { if {![catch {socket -server dodo 21} msg]} { close $msg return {htons problem, should be disallowed, are you running as SU?} } return {couldn't open socket: not owner} } -constraints [list socket supported_$af unix notRoot notOSX notWindows] -result {couldn't open socket: not owner} test socket_$af-6.1 {accept callback error} -constraints [list socket supported_$af stdio] -setup { proc myHandler {msg options} { variable x $msg } set handler [interp bgerror {}] interp bgerror {} [namespace which myHandler] file delete $path(script) } -body { set f [open $path(script) w] puts $f [list set localhost $localhost] puts $f { gets stdin port socket $localhost $port } close $f set f [open "|[list [interpreter] $path(script)]" r+] proc accept {s a p} {expr {10 / 0}} set s [socket -server accept -myaddr $localhost 0] puts $f [lindex [fconfigure $s -sockname] 2] close $f set timer [after 10000 "set x timed_out"] vwait x after cancel $timer close $s return $x } -cleanup { interp bgerror {} $handler } -result {divide by zero} test socket_$af-6.2 { readable fileevent on server socket } -setup { set sock [socket -server dummy 0] } -constraints [list socket supported_$af] -body { fileevent $sock readable dummy } -cleanup { close $sock } -returnCodes 1 -result "channel is not readable" test socket_$af-6.3 {writable fileevent on server socket} -setup { set sock [socket -server dummy 0] } -constraints [list socket supported_$af] -body { fileevent $sock writable dummy } -cleanup { close $sock } -returnCodes 1 -result "channel is not writable" test socket_$af-7.1 {testing socket specific options} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set ss [socket -server accept 0] proc accept args { global x set x done } puts ready puts [lindex [fconfigure $ss -sockname] 2] set timer [after 10000 "set x timed_out"] vwait x after cancel $timer } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen set l "" } -constraints [list socket supported_$af stdio] -body { set s [socket $localhost $listen] set p [fconfigure $s -peername] close $s lappend l [string compare [lindex $p 0] $localhost] lappend l [string compare [lindex $p 2] $listen] lappend l [llength $p] } -cleanup { close $f } -result {0 0 3} test socket_$af-7.2 {testing socket specific options} -setup { file delete $path(script) set f [open $path(script) w] puts $f [list set ::tcl::unsupported::socketAF $::tcl::unsupported::socketAF] puts $f { set ss [socket -server accept 0] proc accept args { global x set x done } puts ready puts [lindex [fconfigure $ss -sockname] 2] set timer [after 10000 "set x timed_out"] vwait x after cancel $timer } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen } -constraints [list socket supported_$af stdio] -body { set s [socket $localhost $listen] set p [fconfigure $s -sockname] close $s list [llength $p] \ [regexp {^(127\.0\.0\.1|0\.0\.0\.0|::1)$} [lindex $p 0]] \ [expr {[lindex $p 2] == $listen}] } -cleanup { close $f } -result {3 1 0} test socket_$af-7.3 {testing socket specific options} -constraints [list socket supported_$af] -body { set s [socket -server accept -myaddr $localhost 0] set l [fconfigure $s] close $s update llength $l } -result 20 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 "" } -body { set s [socket -server accept -myaddr $localhost 0] proc accept {s a p} { global x set x [fconfigure $s -sockname] close $s } set listen [lindex [fconfigure $s -sockname] 2] set s1 [socket $localhost $listen] vwait x lappend l [expr {[lindex $x 2] == $listen}] [llength $x] } -cleanup { after cancel $timer close $s close $s1 } -result {1 3} test socket_$af-7.5 {testing socket specific options} -setup { set timer [after 10000 "set x timed_out"] set l "" } -constraints [list socket supported_$af unixOrWin] -body { set s [socket -server accept 0] proc accept {s a p} { global x set x [fconfigure $s -sockname] close $s } set listen [lindex [fconfigure $s -sockname] 2] set s1 [socket $localhost $listen] vwait x lappend l [lindex $x 0] [expr {[lindex $x 2] == $listen}] [llength $x] } -cleanup { after cancel $timer close $s close $s1 } -result [list $localhost 1 3] test socket_$af-8.1 {testing -async flag on sockets} -constraints [list socket supported_$af] -body { # NOTE: This test may fail on some Solaris 2.4 systems. If it does, check # that you have these patches installed (using showrev -p): # # 101907-05, 101925-02, 101945-14, 101959-03, 101969-05, 101973-03, # 101977-03, 101981-02, 101985-01, 102001-03, 102003-01, 102007-01, # 102011-02, 102024-01, 102039-01, 102044-01, 102048-01, 102062-03, # 102066-04, 102070-01, 102105-01, 102153-03, 102216-01, 102232-01, # 101878-03, 101879-01, 101880-03, 101933-01, 101950-01, 102030-01, # 102057-08, 102140-01, 101920-02, 101921-09, 101922-07, 101923-03 # # If after installing these patches you are still experiencing a problem, # please email jyl@eng.sun.com. We have not observed this failure on # Solaris 2.5, so another option (instead of installing these patches) is # to upgrade to Solaris 2.5. set s [socket -server accept -myaddr $localhost 0] proc accept {s a p} { global x puts $s bye close $s set x done } set s1 [socket -async $localhost [lindex [fconfigure $s -sockname] 2]] vwait x gets $s1 } -cleanup { close $s close $s1 } -result bye test socket_$af-9.1 {testing spurious events} -constraints [list socket supported_$af] -setup { set len 0 set spurious 0 set done 0 set timer [after 10000 "set done timed_out"] } -body { proc readlittle {s} { global spurious done len set l [read $s 1] if {[string length $l] == 0} { if {![eof $s]} { incr spurious } else { close $s set done 1 } } else { incr len [string length $l] } } proc accept {s a p} { fconfigure $s -buffering none -blocking off fileevent $s readable [list readlittle $s] } set s [socket -server accept -myaddr $localhost 0] set c [socket $localhost [lindex [fconfigure $s -sockname] 2]] puts -nonewline $c 01234567890123456789012345678901234567890123456789 close $c vwait done close $s list $spurious $len } -cleanup { after cancel $timer } -result {0 50} test socket_$af-9.2 {testing async write, fileevents, flush on close} -constraints [list socket supported_$af] -setup { set firstblock "" for {set i 0} {$i < 5} {incr i} {set firstblock "a$firstblock$firstblock"} set secondblock "" for {set i 0} {$i < 16} {incr i} { set secondblock "b$secondblock$secondblock" } set timer [after 10000 "set done timed_out"] set l [socket -server accept -myaddr $localhost 0] proc accept {s a p} { fconfigure $s -blocking 0 -translation lf -buffersize 16384 \ -buffering line fileevent $s readable "readable $s" } proc readable {s} { set l [gets $s] fileevent $s readable {} after idle respond $s } proc respond {s} { global firstblock puts -nonewline $s $firstblock after idle writedata $s } proc writedata {s} { global secondblock puts -nonewline $s $secondblock close $s } } -body { set s [socket $localhost [lindex [fconfigure $l -sockname] 2]] fconfigure $s -blocking 0 -trans lf -buffering line set count 0 puts $s hello proc readit {s} { global count done set l [read $s] incr count [string length $l] if {[eof $s]} { close $s set done 1 } } fileevent $s readable "readit $s" vwait done return $count } -cleanup { close $l after cancel $timer } -result 65566 test socket_$af-9.3 {testing EOF stickyness} -constraints [list socket supported_$af] -setup { set count 0 set done false proc write_then_close {s} { puts $s bye close $s } proc accept {s a p} { fconfigure $s -buffering line -translation lf fileevent $s writable "write_then_close $s" } set s [socket -server accept -myaddr $localhost 0] } -body { proc count_to_eof {s} { global count done set l [gets $s] if {[eof $s]} { incr count if {$count > 9} { close $s set done true set count {eof is sticky} } } } proc timerproc {s} { global done count set done true set count {timer went off, eof is not sticky} close $s } set c [socket $localhost [lindex [fconfigure $s -sockname] 2]] fconfigure $c -blocking off -buffering line -translation lf fileevent $c readable "count_to_eof $c" set timer [after 1000 timerproc $c] vwait done return $count } -cleanup { close $s after cancel $timer } -result {eof is sticky} removeFile script test socket_$af-10.1 {testing socket accept callback error handling} \ -constraints [list socket supported_$af] -setup { variable goterror 0 proc myHandler {msg options} { variable goterror 1 } set handler [interp bgerror {}] interp bgerror {} [namespace which myHandler] } -body { set s [socket -server accept -myaddr $localhost 0] proc accept {s a p} {close $s; error} set c [socket $localhost [lindex [fconfigure $s -sockname] 2]] vwait goterror close $s close $c return $goterror } -cleanup { interp bgerror {} $handler } -result 1 test socket_$af-11.1 {tcp connection} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { puts $s done close $s } getPort $server }] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set s [socket $remoteServerIP $port] gets $s } -cleanup { close $s sendCommand {close $server} } -result done test socket_$af-11.2 {client specifies its port} -setup { set lport [randport] set rport [sendCommand { set server [socket -server accept 0] proc accept {s a p} { puts $s $p close $s } getPort $server }] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set s [socket -myport $lport $remoteServerIP $rport] set r [gets $s] expr {$r==$lport ? "ok" : "broken: $r != $port"} } -cleanup { close $s sendCommand {close $server} } -result ok test socket_$af-11.3 {trying to connect, no server} -body { set status ok if {![catch {set s [socket $remoteServerIp [randport]]}]} { if {![catch {gets $s}]} { set status broken } close $s } return $status } -constraints [list socket supported_$af doTestsWithRemoteServer] -result ok test socket_$af-11.4 {remote echo, one line} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line -translation crlf } proc echo {s} { set l [gets $s] if {[eof $s]} { close $s } else { puts $s $l } } getPort $server }] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set f [socket $remoteServerIP $port] fconfigure $f -translation crlf -buffering line puts $f hello gets $f } -cleanup { catch {close $f} sendCommand {close $server} } -result hello test socket_$af-11.5 {remote echo, 50 lines} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line -translation crlf } proc echo {s} { set l [gets $s] if {[eof $s]} { close $s } else { puts $s $l } } getPort $server }] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set f [socket $remoteServerIP $port] fconfigure $f -translation crlf -buffering line for {set cnt 0} {$cnt < 50} {incr cnt} { puts $f "hello, $cnt" if {[gets $f] != "hello, $cnt"} { break } } return $cnt } -cleanup { close $f sendCommand {close $server} } -result 50 test socket_$af-11.6 {socket conflict} -setup { set s1 [socket -server accept -myaddr $localhost 0] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set s2 [socket -server accept -myaddr $localhost [getPort $s1]] list [getPort $s2] [close $s2] } -cleanup { close $s1 } -returnCodes error -result {couldn't open socket: address already in use} test socket_$af-11.7 {server with several clients} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fconfigure $s -buffering line fileevent $s readable [list echo $s] } proc echo {s} { set l [gets $s] if {[eof $s]} { close $s } else { puts $s $l } } getPort $server }] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set s1 [socket $remoteServerIP $port] fconfigure $s1 -buffering line set s2 [socket $remoteServerIP $port] fconfigure $s2 -buffering line set s3 [socket $remoteServerIP $port] fconfigure $s3 -buffering line for {set i 0} {$i < 100} {incr i} { puts $s1 hello,s1 gets $s1 puts $s2 hello,s2 gets $s2 puts $s3 hello,s3 gets $s3 } return $i } -cleanup { close $s1 close $s2 close $s3 sendCommand {close $server} } -result 100 test socket_$af-11.8 {client with several servers} -setup { lassign [sendCommand { set s1 [socket -server "accept server1" 0] set s2 [socket -server "accept server2" 0] set s3 [socket -server "accept server3" 0] proc accept {mp s a p} { puts $s $mp close $s } list [getPort $s1] [getPort $s2] [getPort $s3] }] p1 p2 p3 } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set s1 [socket $remoteServerIP $p1] set s2 [socket $remoteServerIP $p2] set s3 [socket $remoteServerIP $p3] list [gets $s1] [gets $s1] [eof $s1] [gets $s2] [gets $s2] [eof $s2] \ [gets $s3] [gets $s3] [eof $s3] } -cleanup { close $s1 close $s2 close $s3 sendCommand { close $s1 close $s2 close $s3 } } -result {server1 {} 1 server2 {} 1 server3 {} 1} test socket_$af-11.9 {accept callback error} -constraints [list socket supported_$af doTestsWithRemoteServer] -setup { proc myHandler {msg options} { variable x $msg } set handler [interp bgerror {}] interp bgerror {} [namespace which myHandler] set timer [after 10000 "set x timed_out"] } -body { set s [socket -server accept 0] proc accept {s a p} {expr {10 / 0}} sendCommand "set port [getPort $s]" if {[catch { sendCommand { set peername [fconfigure $callerSocket -peername] set s [socket [lindex $peername 0] $port] close $s } } msg]} then { close $s error $msg } vwait x return $x } -cleanup { close $s after cancel $timer interp bgerror {} $handler } -result {divide by zero} test socket_$af-11.10 {testing socket specific options} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} {close $s} getPort $server }] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { set s [socket $remoteServerIP $port] set p [fconfigure $s -peername] set n [fconfigure $s -sockname] list [expr {[lindex $p 2] == $port}] [llength $p] [llength $n] } -cleanup { close $s sendCommand {close $server} } -result {1 3 3} test socket_$af-11.11 {testing spurious events} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fconfigure $s -translation "auto lf" after idle writesome $s } proc writesome {s} { for {set i 0} {$i < 100} {incr i} { puts $s "line $i from remote server" } close $s } getPort $server }] set len 0 set spurious 0 set done 0 set timer [after 40000 "set done timed_out"] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { proc readlittle {s} { global spurious done len set l [read $s 1] if {[string length $l] == 0} { if {![eof $s]} { incr spurious } else { close $s set done 1 } } else { incr len [string length $l] } } set c [socket $remoteServerIP $port] fileevent $c readable "readlittle $c" vwait done list $spurious $len $done } -cleanup { after cancel $timer sendCommand {close $server} } -result {0 2690 1} test socket_$af-11.12 {testing EOF stickyness} -constraints [list socket supported_$af doTestsWithRemoteServer notWine] -setup { set counter 0 set done 0 set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { after idle close $s } getPort $server }] proc timed_out {} { global c done set done {timed_out, EOF is not sticky} close $c } set after_id [after 1000 timed_out] } -body { proc count_up {s} { global counter done set l [gets $s] if {[eof $s]} { incr counter if {$counter > 9} { set done {EOF is sticky} close $s } } } set c [socket $remoteServerIP $port] fileevent $c readable [list count_up $c] vwait done return $done } -cleanup { after cancel $after_id sendCommand {close $server} } -result {EOF is sticky} test socket_$af-11.13 {testing async write, async flush, async close} -setup { set port [sendCommand { set firstblock "" for {set i 0} {$i < 5} {incr i} { set firstblock "a$firstblock$firstblock" } set secondblock "" for {set i 0} {$i < 16} {incr i} { set secondblock "b$secondblock$secondblock" } set l [socket -server accept 0] proc accept {s a p} { fconfigure $s -blocking 0 -translation lf -buffersize 16384 \ -buffering line fileevent $s readable "readable $s" } proc readable {s} { set l [gets $s] fileevent $s readable {} after idle respond $s } proc respond {s} { global firstblock puts -nonewline $s $firstblock after idle writedata $s } proc writedata {s} { global secondblock puts -nonewline $s $secondblock close $s } getPort $l }] set timer [after 10000 "set done timed_out"] } -constraints [list socket supported_$af doTestsWithRemoteServer] -body { proc readit {s} { global count done set l [read $s] incr count [string length $l] if {[eof $s]} { close $s set done 1 } } set s [socket $remoteServerIP $port] fconfigure $s -blocking 0 -trans lf -buffering line set count 0 puts $s hello fileevent $s readable "readit $s" vwait done return $count } -cleanup { after cancel $timer sendCommand {close $l} } -result 65566 set path(script1) [makeFile {} script1] set path(script2) [makeFile {} script2] test socket_$af-12.1 {testing inheritance of server sockets} -setup { file delete $path(script1) file delete $path(script2) # Script1 is just a 10 second delay. If the server socket is inherited, it # will be held open for 10 seconds set f [open $path(script1) w] puts $f { fileevent stdin readable exit after 10000 exit vwait forever } close $f # Script2 creates the server socket, launches script1, and exits. # The server socket will now be closed unless script1 inherited it. set f [open $path(script2) w] puts $f [list set tcltest [interpreter]] puts $f [list set delay $path(script1)] puts $f [list set localhost $localhost] puts $f { set f [socket -server accept -myaddr $localhost 0] proc accept { file addr port } { close $file } exec $tcltest $delay & puts [lindex [fconfigure $f -sockname] 2] close $f exit } close $f } -constraints [list socket supported_$af stdio exec] -body { # Launch script2 and wait 5 seconds ### exec [interpreter] script2 & set p [open "|[list [interpreter] $path(script2)]" r] # If we can still connect to the server, the socket got inherited. if {[catch {close [socket $localhost $listen]}]} { return {server socket was not inherited} } else { return {server socket was inherited} } } -cleanup { catch {close $p} } -result {server socket was not inherited} test socket_$af-12.2 {testing inheritance of client sockets} -setup { file delete $path(script1) file delete $path(script2) # Script1 is just a 20 second delay. If the server socket is inherited, it # will be held open for 20 seconds set f [open $path(script1) w] puts $f { fileevent stdin readable exit after 20000 exit vwait forever } close $f # Script2 opens the client socket and writes to it. It then launches # script1 and exits. If the child process inherited the client socket, the # socket will still be open. set f [open $path(script2) w] puts $f [list set tcltest [interpreter]] puts $f [list set delay $path(script1)] puts $f [list set localhost $localhost] puts $f { gets stdin port set f [socket $localhost $port] exec $tcltest $delay & puts $f testing flush $f exit } close $f # If the socket doesn't hit end-of-file in 10 seconds, the script1 process # must have inherited the client. set timeout 0 set after [after 10000 {set x "client socket was inherited"}] } -constraints [list socket supported_$af stdio exec] -body { # Create the server socket set server [socket -server accept -myaddr $localhost 0] proc accept { file host port } { # When the client connects, establish the read handler global server close $server fileevent $file readable [list getdata $file] fconfigure $file -buffering line -blocking 0 set ::f $file } proc getdata { file } { # Read handler on the accepted socket. global x set status [catch {read $file} data] if {$status != 0} { set x "read failed, error was $data" } elseif {$data ne ""} { } elseif {[fblocked $file]} { } elseif {[eof $file]} { set x "client socket was not inherited" } else { set x "impossible case" } } # Launch the script2 process ### exec [interpreter] script2 & set p [open "|[list [interpreter] $path(script2)]" w] puts $p [lindex [fconfigure $server -sockname] 2] ; flush $p vwait x return $x } -cleanup { fconfigure $f -blocking 1 close $f after cancel $after close $p } -result {client socket was not inherited} test socket_$af-12.3 {testing inheritance of accepted sockets} -setup { file delete $path(script1) file delete $path(script2) set f [open $path(script1) w] puts $f { fileevent stdin readable exit after 10000 exit vwait forever } close $f set f [open $path(script2) w] puts $f [list set tcltest [interpreter]] puts $f [list set delay $path(script1)] puts $f [list set localhost $localhost] puts $f { set server [socket -server accept -myaddr $localhost 0] proc accept { file host port } { global tcltest delay puts $file {test data on socket} exec $tcltest $delay & after idle exit } puts stdout [lindex [fconfigure $server -sockname] 2] vwait forever } close $f } -constraints [list socket supported_$af stdio exec] -body { # Launch the script2 process and connect to it. See how long the socket # stays open ## exec [interpreter] script2 & set p [open "|[list [interpreter] $path(script2)]" r] gets $p listen set f [socket $localhost $listen] fconfigure $f -buffering full -blocking 0 fileevent $f readable [list getdata $f] # If the socket is still open after 5 seconds, the script1 process must # have inherited the accepted socket. set failed 0 set after [after 5000 [list set x "accepted socket was inherited"]] proc getdata { file } { # Read handler on the client socket. global x global failed set status [catch {read $file} data] if {$status != 0} { set x "read failed, error was $data" } elseif {[string compare {} $data]} { } elseif {[fblocked $file]} { } elseif {[eof $file]} { set x "accepted socket was not inherited" } else { set x "impossible case" } return } vwait x set x } -cleanup { fconfigure $f -blocking 1 close $f after cancel $after close $p } -result {accepted socket was not inherited} test socket_$af-13.1 {Testing use of shared socket between two threads} -body { # create a thread set serverthread [thread::create -preserved [string map [list @localhost@ $localhost] { set f [socket -server accept -myaddr @localhost@ 0] set listen [lindex [fconfigure $f -sockname] 2] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line } proc echo {s} { global i set l [gets $s] if {[eof $s]} { global x close $s set x done } else { incr i puts $s $l } } set i 0 vwait x close $f }]] set port [thread::send $serverthread {set listen}] set s [socket $localhost $port] fconfigure $s -buffering line catch { puts $s "hello" gets $s result } close $s thread::release $serverthread append result " " [llength [thread::names]] } -result {hello 1} -constraints [list socket supported_$af thread] proc transf_test {{testmode transfer} {maxIter 1000} {maxTime 10000}} { try { set ::count 0 set ::testmode $testmode set port 0 set srvsock {} # if binding on port 0 is not possible (system related, blocked on ISPs etc): if {[catch {close [socket -async $::localhost $port]}]} { # simplest server on random port (immediatelly closing a connect): set port [randport] set srvsock [socket -server {apply {{ch args} {close $ch}}} -myaddr $::localhost $port] # socket on windows has some issues yet (e. g. bug [b6d0d8cc2c]), so we simply decrease iteration count (to 1/4): if {$::tcl_platform(platform) eq "windows" && $maxIter > 50} { set ::count [expr {$maxIter / 4 * 3 - 1}]; # bypass 3/4 iterations } } tcltest::DebugPuts 2 "== test \[$::localhost\]:$port $testmode ==" set ::parent [thread::id] # helper thread creating async connection and initiating transfer (detach) to parent: set ::helper [thread::create] thread::send -async $::helper [list \ lassign [list $::parent $::localhost $port $testmode] \ ::parent ::localhost ::port ::testmode ] thread::send -async $::helper { set ::helper [thread::id] proc iteration {args} { set fd [socket -async $::localhost $::port] if {"helper-writable" in $::testmode} {;# to test both sides during connect fileevent $fd writable [list apply {{fd} { if {[thread::id] ne $::helper} { thread::send -async $::parent {set ::count "ERROR: invalid thread, $::helper is expecting"} close $fd return } }} $fd] };# thread::detach $fd thread::send -async $::parent [list transf_parent $fd {*}$args] } iteration first } # parent proc commiting transfer attempt (attach) and checking acquire was successful: proc transf_parent {fd args} { tcltest::DebugPuts 2 "** trma / $::count ** $args **" thread::attach $fd if {"parent-close" in $::testmode} {;# to test close during connect set ::count $::count close $fd return };# fileevent $fd writable [list apply {{fd} { if {[thread::id] ne $::parent} { thread::send -async $::parent {set ::count "ERROR: invalid thread, $::parent is expecting"} close $fd return } set ::count $::count close $fd }} $fd] } # repeat maxIter times (up to maxTime ms as timeout): set tout [after $maxTime {set ::count "TIMEOUT"}] while 1 { vwait ::count if {![string is integer $::count]} { # if timeout just skip (test was successful until now): if {$::count eq "TIMEOUT"} {::tcltest::Skip "timing issue"} break } if {[incr ::count] >= $maxIter} break tcltest::DebugPuts 2 "** iter / $::count **" thread::send -async $::helper [list iteration nr $::count] } update set ::count } finally { catch {after cancel $tout} if {$srvsock ne {}} {close $srvsock} if {[info exists ::helper]} {thread::release -wait $::helper} tcltest::DebugPuts 2 "== stop / $::count ==" unset -nocomplain ::count ::testmode ::parent ::helper } } test socket_$af-13.2.tr1 {Testing socket transfer between threads during async connect} -body { transf_test {transfer} 1000 } -result 1000 -constraints [list socket supported_$af thread] test socket_$af-13.2.tr2 {Testing socket transfer between threads during async connect} -body { transf_test {transfer helper-writable} 100 } -result 100 -constraints [list socket supported_$af thread] test socket_$af-13.2.cl1 {Testing socket transfer between threads during async connect} -body { transf_test {parent-close} 100 } -result 100 -constraints [list socket supported_$af thread] test socket_$af-13.2.cl2 {Testing socket transfer between threads during async connect} -body { transf_test {parent-close helper-writable} 100 } -result 100 -constraints [list socket supported_$af thread] catch {rename transf_parent {}} rename transf_test {} # ---------------------------------------------------------------------- removeFile script1 removeFile script2 # cleanup if {$remoteProcChan ne ""} { catch {sendCommand exit} } catch {close $commandSocket} catch {close $remoteProcChan} } unset ::tcl::unsupported::socketAF test socket-14.0.0 {[socket -async] when server only listens on IPv4} \ -constraints {socket supported_inet localhost_v4} \ -setup { proc accept {s a p} { global x puts $s bye close $s set x ok } set server [socket -server accept -myaddr 127.0.0.1 0] set port [lindex [fconfigure $server -sockname] 2] } -body { set client [socket -async localhost $port] set after [after $latency {set x [fconfigure $client -error]}] vwait x set x } -cleanup { after cancel $after close $server close $client unset x } -result ok test socket-14.0.1 {[socket -async] when server only listens on IPv6} \ -constraints {socket supported_inet6 localhost_v6} \ -setup { proc accept {s a p} { global x puts $s bye close $s set x ok } set server [socket -server accept -myaddr ::1 0] set port [lindex [fconfigure $server -sockname] 2] } -body { set client [socket -async localhost $port] set after [after $latency {set x [fconfigure $client -error]}] vwait x set x } -cleanup { after cancel $after close $server close $client unset x } -result ok test socket-14.1 {[socket -async] fileevent while still connecting} \ -constraints {socket} \ -setup { proc accept {s a p} { global x puts $s bye close $s lappend x ok } set server [socket -server accept -myaddr localhost 0] set port [lindex [fconfigure $server -sockname] 2] set x "" } -body { set client [socket -async localhost $port] fileevent $client writable { lappend x [fconfigure $client -error] fileevent $client writable {} } set after [after $latency {lappend x timeout}] while {[llength $x] < 2 && "timeout" ni $x} { vwait x } lsort $x; # we only want to see both events, the order doesn't matter } -cleanup { after cancel $after close $server close $client unset x } -result {{} ok} test socket-14.2 {[socket -async] fileevent connection refused} \ -constraints {socket} \ -body { set client [socket -async localhost [randport]] fileevent $client writable {set x ok} set after [after $latency {set x timeout}] vwait x after cancel $after lappend x [fconfigure $client -error] } -cleanup { after cancel $after close $client unset x after client } -result {ok {connection refused}} test socket-14.3 {[socket -async] when server only listens on IPv6} \ -constraints {socket supported_inet6 localhost_v6} \ -setup { proc accept {s a p} { global x puts $s bye close $s set x ok } set server [socket -server accept -myaddr ::1 0] set port [lindex [fconfigure $server -sockname] 2] } -body { set client [socket -async localhost $port] set after [after $latency {set x [fconfigure $client -error]}] vwait x set x } -cleanup { after cancel $after close $server close $client unset x } -result ok test socket-14.4 {[socket -async] and both, readdable and writable fileevents} \ -constraints {socket} \ -setup { proc accept {s a p} { puts $s bye close $s } set server [socket -server accept -myaddr localhost 0] set port [lindex [fconfigure $server -sockname] 2] set x "" } -body { set client [socket -async localhost $port] fileevent $client writable { lappend x [fconfigure $client -error] fileevent $client writable {} } fileevent $client readable {lappend x [gets $client]} set after [after $latency {lappend x timeout}] while {[llength $x] < 2 && "timeout" ni $x} { vwait x } lsort $x } -cleanup { after cancel $after close $client close $server unset x } -result {{} bye} # FIXME: we should also have an IPv6 counterpart of this test socket-14.5 {[socket -async] which fails before any connect() can be made} \ -constraints {socket supported_inet notWine} \ -body { # address from rfc5737 socket -async -myaddr 192.0.2.42 127.0.0.1 [randport] } \ -returnCodes 1 \ -result {couldn't open socket: cannot assign requested address} test socket-14.6.0 {[socket -async] with no event loop and server listening on IPv4} \ -constraints {socket supported_inet localhost_v4} \ -setup { proc accept {s a p} { global x puts $s bye close $s set x ok } set server [socket -server accept -myaddr 127.0.0.1 0] set port [lindex [fconfigure $server -sockname] 2] set x "" } \ -body { set client [socket -async localhost $port] for {set i 0} {$i < 50} {incr i } { update if {$x ne ""} { lappend x [gets $client] break } after 100 } set x } \ -cleanup { close $server close $client unset x } \ -result {ok bye} test socket-14.6.1 {[socket -async] with no event loop and server listening on IPv6} \ -constraints {socket supported_inet6 localhost_v6} \ -setup { proc accept {s a p} { global x puts $s bye close $s set x ok } set server [socket -server accept -myaddr ::1 0] set port [lindex [fconfigure $server -sockname] 2] set x "" } \ -body { set client [socket -async localhost $port] for {set i 0} {$i < 50} {incr i } { update if {$x ne ""} { lappend x [gets $client] break } after 100 } set x } \ -cleanup { close $server close $client unset x } \ -result {ok bye} test socket-14.7.0 {pending [socket -async] and blocking [gets], server is IPv4} \ -constraints {socket supported_inet localhost_v4} \ -setup { makeFile { fileevent stdin readable exit set server [socket -server accept -myaddr 127.0.0.1 0] proc accept {s h p} {puts $s ok; close $s; set ::x 1} puts [lindex [fconfigure $server -sockname] 2] flush stdout vwait x } script set fd [open |[list [interpreter] script] RDWR] set port [gets $fd] } -body { set sock [socket -async localhost $port] list [fconfigure $sock -error] [gets $sock] [fconfigure $sock -error] } -cleanup { close $fd close $sock removeFile script } -result {{} ok {}} test socket-14.7.1 {pending [socket -async] and blocking [gets], server is IPv6} \ -constraints {socket supported_inet6 localhost_v6} \ -setup { makeFile { fileevent stdin readable exit set server [socket -server accept -myaddr ::1 0] proc accept {s h p} {puts $s ok; close $s; set ::x 1} puts [lindex [fconfigure $server -sockname] 2] flush stdout vwait x } script set fd [open |[list [interpreter] script] RDWR] set port [gets $fd] } -body { set sock [socket -async localhost $port] list [fconfigure $sock -error] [gets $sock] [fconfigure $sock -error] } -cleanup { close $fd close $sock removeFile script } -result {{} ok {}} test socket-14.7.2 {pending [socket -async] and blocking [gets], no listener} \ -constraints {socket} \ -body { set sock [socket -async localhost [randport]] catch {gets $sock} x list $x [fconfigure $sock -error] [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 localhost_v4} \ -setup { makeFile { fileevent stdin readable exit set server [socket -server accept -myaddr 127.0.0.1 0] proc accept {s h p} {puts $s ok; close $s; set ::x 1} puts [lindex [fconfigure $server -sockname] 2] flush stdout vwait x } script set fd [open |[list [interpreter] script] RDWR] set port [gets $fd] } -body { set sock [socket -async localhost $port] fconfigure $sock -blocking 0 for {set i 0} {$i < 50} {incr i } { if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break after 200 } set x } -cleanup { close $fd close $sock removeFile script } -result {ok} test socket-14.8.1 {pending [socket -async] and nonblocking [gets], server is IPv6} \ -constraints {socket supported_inet6 localhost_v6} \ -setup { makeFile { fileevent stdin readable exit set server [socket -server accept -myaddr ::1 0] proc accept {s h p} {puts $s ok; close $s; set ::x 1} puts [lindex [fconfigure $server -sockname] 2] flush stdout vwait x } script set fd [open |[list [interpreter] script] RDWR] set port [gets $fd] } -body { set sock [socket -async localhost $port] fconfigure $sock -blocking 0 for {set i 0} {$i < 50} {incr i } { if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break after 200 } set x } -cleanup { close $fd close $sock removeFile script } -result {ok} test socket-14.8.2 {pending [socket -async] and nonblocking [gets], no listener} \ -constraints {socket} \ -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 after 200 } list $x [fconfigure $sock -error] [fconfigure $sock -error] } -cleanup { close $sock } -match glob -result {{error reading "sock*": socket is not connected} {connection refused} {}} test socket-14.9.0 {pending [socket -async] and blocking [puts], server is IPv4} \ -constraints {socket supported_inet localhost_v4} \ -setup { makeFile { fileevent stdin readable exit 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 { close $fd close $sock removeFile script } -result {{} ok} test socket-14.9.1 {pending [socket -async] and blocking [puts], server is IPv6} \ -constraints {socket supported_inet6 localhost_v6} \ -setup { makeFile { fileevent stdin readable exit 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 { close $fd close $sock removeFile script } -result {{} ok} test socket-14.10.0 {pending [socket -async] and nonblocking [puts], server is IPv4} \ -constraints {socket supported_inet localhost_v4} \ -setup { makeFile { fileevent stdin readable exit 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 { close $fd close $sock removeFile script } -result {{} ok} test socket-14.10.1 {pending [socket -async] and nonblocking [puts], server is IPv6} \ -constraints {socket supported_inet6 localhost_v6} \ -setup { makeFile { fileevent stdin readable exit 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 { close $fd close $sock removeFile script } -result {{} ok} test socket-14.11.0 {pending [socket -async] and nonblocking [puts], no listener, no flush} \ -constraints {socket notWinCI} \ -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 { catch {close $sock} unset x } -result {socket is not connected} -returnCodes 1 test socket-14.11.1 {pending [socket -async] and nonblocking [puts], no listener, flush} \ -constraints {socket testsocket_testflags} \ -body { set sock [socket -async localhost [randport]] # Set the socket in async test mode. # The async connect will not be continued on the following fconfigure # and puts/flush. Thus, the connect will fail after them. testsocket testflags $sock 1 fconfigure $sock -blocking 0 puts $sock ok flush $sock testsocket testflags $sock 0 fileevent $sock writable {set x 1} vwait x close $sock } -cleanup { catch {close $sock} catch {unset x} } -result {socket is not connected} -returnCodes 1 test socket-14.12 {[socket -async] background progress triggered by [fconfigure -error]} \ -constraints {socket} \ -body { set s [socket -async localhost [randport]] for {set i 0} {$i < 50} {incr i} { set x [fconfigure $s -error] if {$x != ""} break after 200 } set x } -cleanup { close $s unset x s } -result {connection refused} test socket-14.13 {testing writable event when quick failure} \ -constraints {socket win supported_inet notWine} \ -body { # Test for bug 336441ed59 where a quick background fail was ignored # Test only for windows as socket -async 255.255.255.255 fails # directly on unix # The following connect should fail very quickly set a1 [after 2000 {set x timeout}] set s [socket -async 255.255.255.255 43434] fileevent $s writable {set x writable} vwait x set x } -cleanup { catch {close $s} after cancel $a1 } -result writable test socket-14.14 {testing fileevent readable on failed async socket connect} \ -constraints {socket} -body { # Test for bug 581937ab1e set a1 [after 5000 {set x timeout}] # This connect should fail set s [socket -async localhost [randport]] fileevent $s readable {set x readable} vwait x set x } -cleanup { catch {close $s} after cancel $a1 } -result readable test socket-14.15 {blocking read on async socket should not trigger event handlers} \ -constraints socket -body { set s [socket -async localhost [randport]] set x ok fileevent $s writable {set x fail} catch {read $s} close $s set x } -result ok # v4 and v6 is required to prevent that the async connect does not terminate # before the fconfigure command. There is always an additional ip to try. test socket-14.16 {empty -peername while [socket -async] connecting} \ -constraints {socket localhost_v4 localhost_v6} \ -body { set client [socket -async localhost [randport]] fconfigure $client -peername } -cleanup { catch {close $client} } -result {} # v4 and v6 is required to prevent that the async connect does not terminate # before the fconfigure command. There is always an additional ip to try. test socket-14.17 {empty -sockname while [socket -async] connecting} \ -constraints {socket localhost_v4 localhost_v6} \ -body { set client [socket -async localhost [randport]] fconfigure $client -sockname } -cleanup { catch {close $client} } -result {} # test for bug c6ed4acfd8: running async socket connect with other connect # established will block tcl as it goes in an infinite loop in vwait test socket-14.18 {bug c6ed4acfd8: running async socket connect made other connect block} \ -constraints {socket} \ -body { proc accept {channel address port} {} set port [randport] set ssock [socket -server accept $port] set csock1 [socket -async localhost [randport]] set csock2 [socket localhost $port] after 1000 {set done ok} vwait done } -cleanup { catch {close $ssock} catch {close $csock1} catch {close $csock2} } -result {} test socket-14.19 {tip 456 -- introduce the -reuseport option} \ -constraints {socket notWine} \ -body { proc accept {channel address port} {} set port [randport] set ssock1 [socket -server accept -reuseport yes $port] set ssock2 [socket -server accept -reuseport yes $port] return ok } -cleanup { catch {close $ssock1} catch {close $ssock2} } -result ok set num 0 set x {localhost {socket} 127.0.0.1 {supported_inet} ::1 {supported_inet6}} set resultok {-result "sock*" -match glob} set resulterr { -result {couldn't open socket: connection refused} -returnCodes 1 } foreach {servip sc} $x { foreach {cliip cc} $x { set constraints socket lappend constraints $sc $cc set result $resulterr switch -- [lsort -unique [list $servip $cliip]] { localhost - 127.0.0.1 - ::1 { set result $resultok } {127.0.0.1 localhost} { if {[testConstraint localhost_v4]} { set result $resultok } } {::1 localhost} { if {[testConstraint localhost_v6]} { set result $resultok } } } test socket-15.1.$num "Connect to $servip from $cliip" \ -constraints $constraints -setup { set server [socket -server accept -myaddr $servip 0] proc accept {s h p} { close $s } set port [lindex [fconfigure $server -sockname] 2] } -body { set s [socket $cliip $port] } -cleanup { close $server catch {close $s} } {*}$result incr num } } ::tcltest::cleanupTests flush stdout return # Local Variables: # mode: tcl # fill-column: 78 # End: