1# Commands tested in this file: socket.
2#
3# This file contains a collection of tests for one or more of the Tcl
4# built-in commands.  Sourcing this file into Tcl runs the tests and
5# generates output for errors.  No output means no errors were found.
6#
7# Copyright (c) 1994-1996 Sun Microsystems, Inc.
8# Copyright (c) 1998-2000 Ajuba Solutions.
9#
10# See the file "license.terms" for information on usage and redistribution
11# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12
13# Running socket tests with a remote server:
14# ------------------------------------------
15#
16# Some tests in socket.test depend on the existence of a remote server to
17# which they connect. The remote server must be an instance of tcltest and it
18# must run the script found in the file "remote.tcl" in this directory. You
19# can start the remote server on any machine reachable from the machine on
20# which you want to run the socket tests, by issuing:
21#
22#     tcltest remote.tcl -port 2048	# Or choose another port number.
23#
24# If the machine you are running the remote server on has several IP
25# interfaces, you can choose which interface the server listens on for
26# connections by specifying the -address command line flag, so:
27#
28#     tcltest remote.tcl -address your.machine.com
29#
30# These options can also be set by environment variables. On Unix, you can
31# type these commands to the shell from which the remote server is started:
32#
33#     shell% setenv serverPort 2048
34#     shell% setenv serverAddress your.machine.com
35#
36# and subsequently you can start the remote server with:
37#
38#     tcltest remote.tcl
39#
40# to have it listen on port 2048 on the interface your.machine.com.
41#
42# When the server starts, it prints out a detailed message containing its
43# configuration information, and it will block until killed with a Ctrl-C.
44# Once the remote server exists, you can run the tests in socket.test with
45# the server by setting two Tcl variables:
46#
47#     % set remoteServerIP <name or address of machine on which server runs>
48#     % set remoteServerPort 2048
49#
50# These variables are also settable from the environment. On Unix, you can:
51#
52#     shell% setenv remoteServerIP machine.where.server.runs
53#     shell% senetv remoteServerPort 2048
54#
55# The preamble of the socket.test file checks to see if the variables are set
56# either in Tcl or in the environment; if they are, it attempts to connect to
57# the server. If the connection is successful, the tests using the remote
58# server will be performed; otherwise, it will attempt to start the remote
59# server (via exec) on platforms that support this, on the local host,
60# listening at port 2048. If all fails, a message is printed and the tests
61# using the remote server are not performed.
62
63package require tcltest 2
64namespace import -force ::tcltest::*
65
66# Some tests require the testthread and exec commands
67testConstraint testthread [llength [info commands testthread]]
68testConstraint exec [llength [info commands exec]]
69
70# Produce a random port number in the Dynamic/Private range
71# from 49152 through 65535.
72proc randport {} { expr {int(rand()*16383+49152)} }
73
74# If remoteServerIP or remoteServerPort are not set, check in the
75# environment variables for externally set values.
76#
77
78if {![info exists remoteServerIP]} {
79    if {[info exists env(remoteServerIP)]} {
80	set remoteServerIP $env(remoteServerIP)
81    }
82}
83if {![info exists remoteServerPort]} {
84    if {[info exists env(remoteServerIP)]} {
85	set remoteServerPort $env(remoteServerPort)
86    } else {
87        if {[info exists remoteServerIP]} {
88	    set remoteServerPort 2048
89        }
90    }
91}
92
93#
94# Check if we're supposed to do tests against the remote server
95#
96
97set doTestsWithRemoteServer 1
98if {![info exists remoteServerIP]} {
99    set remoteServerIP 127.0.0.1
100}
101if {($doTestsWithRemoteServer == 1) && (![info exists remoteServerPort])} {
102    set remoteServerPort 2048
103}
104
105# Attempt to connect to a remote server if one is already running. If it
106# is not running or for some other reason the connect fails, attempt to
107# start the remote server on the local host listening on port 2048. This
108# is only done on platforms that support exec (i.e. not on the Mac). On
109# platforms that do not support exec, the remote server must be started
110# by the user before running the tests.
111
112set remoteProcChan ""
113set commandSocket ""
114if {$doTestsWithRemoteServer} {
115    catch {close $commandSocket}
116    if {![catch {
117	set commandSocket [socket $remoteServerIP $remoteServerPort]
118    }]} then {
119	fconfigure $commandSocket -translation crlf -buffering line
120    } elseif {![testConstraint exec]} {
121	set noRemoteTestReason "can't exec"
122	set doTestsWithRemoteServer 0
123    } else {
124	set remoteServerIP 127.0.0.1
125	# Be *extra* careful in case this file is sourced from
126	# a directory other than the current one...
127	set remoteFile [file join [pwd] [file dirname [info script]] \
128		remote.tcl]
129	if {![catch {
130	    set remoteProcChan [open "|[list \
131		    [interpreter] $remoteFile -serverIsSilent \
132		    -port $remoteServerPort -address $remoteServerIP]" w+]
133	} msg]} then {
134	    after 1000
135	    if {[catch {
136		set commandSocket [socket $remoteServerIP $remoteServerPort]
137	    } msg] == 0} then {
138		fconfigure $commandSocket -translation crlf -buffering line
139	    } else {
140		set noRemoteTestReason $msg
141		set doTestsWithRemoteServer 0
142	    }
143	} else {
144	    set noRemoteTestReason "$msg [interpreter]"
145	    set doTestsWithRemoteServer 0
146	}
147    }
148}
149
150# Some tests are run only if we are doing testing against a remote server.
151testConstraint doTestsWithRemoteServer $doTestsWithRemoteServer
152if {!$doTestsWithRemoteServer} {
153    if {[string first s $::tcltest::verbose] != -1} {
154    	puts "Skipping tests with remote server. See tests/socket.test for"
155	puts "information on how to run remote server."
156	puts "Reason for not doing remote tests: $noRemoteTestReason"
157    }
158}
159
160#
161# If we do the tests, define a command to send a command to the
162# remote server.
163#
164
165if {[testConstraint doTestsWithRemoteServer]} {
166    proc sendCommand {c} {
167	global commandSocket
168
169	if {[eof $commandSocket]} {
170	    error "remote server disappeared"
171	}
172	if {[catch {puts $commandSocket $c} msg]} {
173	    error "remote server disappaered: $msg"
174	}
175	if {[catch {puts $commandSocket "--Marker--Marker--Marker--"} msg]} {
176	    error "remote server disappeared: $msg"
177	}
178
179	set resp ""
180	while {1} {
181	    set line [gets $commandSocket]
182	    if {[eof $commandSocket]} {
183		error "remote server disappaered"
184	    }
185	    if {[string compare $line "--Marker--Marker--Marker--"] == 0} {
186		if {[string compare [lindex $resp 0] error] == 0} {
187		    error [lindex $resp 1]
188		} else {
189		    return [lindex $resp 1]
190		}
191	    } else {
192		append resp $line "\n"
193	    }
194	}
195    }
196}
197
198test socket-1.1 {arg parsing for socket command} {socket} {
199    list [catch {socket -server} msg] $msg
200} {1 {no argument given for -server option}}
201test socket-1.2 {arg parsing for socket command} {socket} {
202    list [catch {socket -server foo} msg] $msg
203} {1 {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"}}
204test socket-1.3 {arg parsing for socket command} {socket} {
205    list [catch {socket -myaddr} msg] $msg
206} {1 {no argument given for -myaddr option}}
207test socket-1.4 {arg parsing for socket command} {socket} {
208    list [catch {socket -myaddr 127.0.0.1} msg] $msg
209} {1 {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"}}
210test socket-1.5 {arg parsing for socket command} {socket} {
211    list [catch {socket -myport} msg] $msg
212} {1 {no argument given for -myport option}}
213test socket-1.6 {arg parsing for socket command} {socket} {
214    list [catch {socket -myport xxxx} msg] $msg
215} {1 {expected integer but got "xxxx"}}
216test socket-1.7 {arg parsing for socket command} {socket} {
217    list [catch {socket -myport 2522} msg] $msg
218} {1 {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"}}
219test socket-1.8 {arg parsing for socket command} {socket} {
220    list [catch {socket -froboz} msg] $msg
221} {1 {bad option "-froboz": must be -async, -myaddr, -myport, or -server}}
222test socket-1.9 {arg parsing for socket command} {socket} {
223    list [catch {socket -server foo -myport 2521 3333} msg] $msg
224} {1 {option -myport is not valid for servers}}
225test socket-1.10 {arg parsing for socket command} {socket} {
226    list [catch {socket host 2528 -junk} msg] $msg
227} {1 {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"}}
228test socket-1.11 {arg parsing for socket command} {socket} {
229    list [catch {socket -server callback 2520 --} msg] $msg
230} {1 {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"}}
231test socket-1.12 {arg parsing for socket command} {socket} {
232    list [catch {socket foo badport} msg] $msg
233} {1 {expected integer but got "badport"}}
234test socket-1.13 {arg parsing for socket command} {socket} {
235list [catch {socket -async -server} msg] $msg
236} {1 {cannot set -async option for server sockets}}
237test socket-1.14 {arg parsing for socket command} {socket} {
238list [catch {socket -server foo -async} msg] $msg
239} {1 {cannot set -async option for server sockets}}
240
241set path(script) [makeFile {} script]
242
243test socket-2.1 {tcp connection} {socket stdio} {
244    file delete $path(script)
245    set f [open $path(script) w]
246    puts $f {
247	set timer [after 10000 "set x timed_out"]
248	set f [socket -server accept 0]
249	proc accept {file addr port} {
250	    global x
251	    set x done
252            close $file
253	}
254	puts ready
255	puts [lindex [fconfigure $f -sockname] 2]
256	vwait x
257	after cancel $timer
258	close $f
259	puts $x
260    }
261    close $f
262    set f [open "|[list [interpreter] $path(script)]" r]
263    gets $f x
264    gets $f listen
265    if {[catch {socket 127.0.0.1 $listen} msg]} {
266        set x $msg
267    } else {
268        lappend x [gets $f]
269        close $msg
270    }
271    lappend x [gets $f]
272    close $f
273    set x
274} {ready done {}}
275
276if [info exists port] {
277    incr port
278} else {
279    set port [expr 2048 + [pid]%1024]
280}
281test socket-2.2 {tcp connection with client port specified} {socket stdio} {
282    file delete $path(script)
283    set f [open $path(script) w]
284    puts $f {
285	set timer [after 10000 "set x timeout"]
286        set f [socket -server accept 0]
287	proc accept {file addr port} {
288            global x
289            puts "[gets $file] $port"
290            close $file
291            set x done
292	}
293	puts ready
294	puts [lindex [fconfigure $f -sockname] 2]
295	vwait x
296	after cancel $timer
297	close $f
298    }
299    close $f
300    set f [open "|[list [interpreter] $path(script)]" r]
301    gets $f x
302    gets $f listen
303    global port
304    if {[catch {socket -myport $port 127.0.0.1 $listen} sock]} {
305        set x $sock
306	close [socket 127.0.0.1 $listen]
307	puts stderr $sock
308    } else {
309        puts $sock hello
310	flush $sock
311        lappend x [gets $f]
312        close $sock
313    }
314    close $f
315    set x
316} [list ready "hello $port"]
317test socket-2.3 {tcp connection with client interface specified} {socket stdio} {
318    file delete $path(script)
319    set f [open $path(script) w]
320    puts $f {
321	set timer [after 2000 "set x done"]
322        set f [socket  -server accept 2830]
323	proc accept {file addr port} {
324            global x
325            puts "[gets $file] $addr"
326            close $file
327            set x done
328	}
329	puts ready
330	vwait x
331	after cancel $timer
332	close $f
333    }
334    close $f
335    set f [open "|[list [interpreter] $path(script)]" r]
336    gets $f x
337    if {[catch {socket -myaddr 127.0.0.1 127.0.0.1 2830} sock]} {
338        set x $sock
339    } else {
340        puts $sock hello
341	flush $sock
342        lappend x [gets $f]
343        close $sock
344    }
345    close $f
346    set x
347} {ready {hello 127.0.0.1}}
348test socket-2.4 {tcp connection with server interface specified} {socket stdio} {
349    file delete $path(script)
350    set f [open $path(script) w]
351    puts $f {
352	set timer [after 2000 "set x done"]
353        set f [socket -server accept -myaddr 127.0.0.1 0]
354	proc accept {file addr port} {
355            global x
356            puts "[gets $file]"
357            close $file
358            set x done
359	}
360	puts ready
361	puts [lindex [fconfigure $f -sockname] 2]
362	vwait x
363	after cancel $timer
364	close $f
365    }
366    close $f
367    set f [open "|[list [interpreter] $path(script)]" r]
368    gets $f x
369    gets $f listen
370    if {[catch {socket 127.0.0.1 $listen} sock]} {
371        set x $sock
372    } else {
373        puts $sock hello
374	flush $sock
375        lappend x [gets $f]
376        close $sock
377    }
378    close $f
379    set x
380} {ready hello}
381test socket-2.5 {tcp connection with redundant server port} {socket stdio} {
382    file delete $path(script)
383    set f [open $path(script) w]
384    puts $f {
385	set timer [after 10000 "set x timeout"]
386        set f [socket -server accept 0]
387	proc accept {file addr port} {
388            global x
389            puts "[gets $file]"
390            close $file
391            set x done
392	}
393	puts ready
394	puts [lindex [fconfigure $f -sockname] 2]
395	vwait x
396	after cancel $timer
397	close $f
398    }
399    close $f
400    set f [open "|[list [interpreter] $path(script)]" r]
401    gets $f x
402    gets $f listen
403    if {[catch {socket 127.0.0.1 $listen} sock]} {
404        set x $sock
405    } else {
406        puts $sock hello
407	flush $sock
408        lappend x [gets $f]
409        close $sock
410    }
411    close $f
412    set x
413} {ready hello}
414test socket-2.6 {tcp connection} {socket} {
415    set status ok
416    if {![catch {set sock [socket 127.0.0.1 2833]}]} {
417	if {![catch {gets $sock}]} {
418	    set status broken
419	}
420	close $sock
421    }
422    set status
423} ok
424test socket-2.7 {echo server, one line} {socket stdio} {
425    file delete $path(script)
426    set f [open $path(script) w]
427    puts $f {
428	set timer [after 10000 "set x timeout"]
429	set f [socket -server accept 0]
430	proc accept {s a p} {
431            fileevent $s readable [list echo $s]
432	    fconfigure $s -translation lf -buffering line
433        }
434	proc echo {s} {
435	     set l [gets $s]
436             if {[eof $s]} {
437                 global x
438                 close $s
439                 set x done
440             } else {
441                 puts $s $l
442             }
443	}
444	puts ready
445	puts [lindex [fconfigure $f -sockname] 2]
446	vwait x
447	after cancel $timer
448	close $f
449	puts $x
450    }
451    close $f
452    set f [open "|[list [interpreter] $path(script)]" r]
453    gets $f
454    gets $f listen
455    set s [socket 127.0.0.1 $listen]
456    fconfigure $s -buffering line -translation lf
457    puts $s "hello abcdefghijklmnop"
458    after 1000
459    set x [gets $s]
460    close $s
461    set y [gets $f]
462    close $f
463    list $x $y
464} {{hello abcdefghijklmnop} done}
465removeFile script
466test socket-2.8 {echo server, loop 50 times, single connection} -constraints {socket stdio} -setup {
467    set path(script) [makeFile {
468	set f [socket -server accept 0]
469	proc accept {s a p} {
470            fileevent $s readable [list echo $s]
471            fconfigure $s -buffering line
472        }
473	proc echo {s} {
474	     global i
475             set l [gets $s]
476             if {[eof $s]} {
477                 global x
478                 close $s
479                 set x done
480             } else {
481	         incr i
482                 puts $s $l
483             }
484	}
485	set i 0
486	puts ready
487	puts [lindex [fconfigure $f -sockname] 2]
488	set timer [after 20000 "set x done"]
489	vwait x
490	after cancel $timer
491	close $f
492	puts "done $i"
493    } script]
494} -body {
495    set f [open "|[list [interpreter] $path(script)]" r]
496    gets $f
497    gets $f listen
498    set s [socket 127.0.0.1 $listen]
499    fconfigure $s -buffering line
500    catch {
501	for {set x 0} {$x < 50} {incr x} {
502	    puts $s "hello abcdefghijklmnop"
503	    gets $s
504	}
505    }
506    close $s
507    catch {set x [gets $f]}
508    close $f
509    set x
510} -cleanup {
511    removeFile script
512} -result {done 50}
513set path(script) [makeFile {} script]
514test socket-2.9 {socket conflict} {socket stdio} {
515    set s [socket -server accept 0]
516    file delete $path(script)
517    set f [open $path(script) w]
518    puts -nonewline $f "socket -server accept [lindex [fconfigure $s -sockname] 2]"
519    close $f
520    set f [open "|[list [interpreter] $path(script)]" r]
521    gets $f
522    after 100
523    set x [list [catch {close $f} msg]]
524    regsub "\n.*$" $msg {} msg ; # cut part of the error message containing the port number
525    lappend x $msg
526    close $s
527    set x
528} {1 {couldn't open socket: address already in use}}
529test socket-2.10 {close on accept, accepted socket lives} {socket} {
530    set done 0
531    set timer [after 20000 "set done timed_out"]
532    set ss [socket -server accept 0]
533    proc accept {s a p} {
534	global ss
535	close $ss
536	fileevent $s readable "readit $s"
537	fconfigure $s -trans lf
538    }
539    proc readit {s} {
540	global done
541	gets $s
542	close $s
543	set done 1
544    }
545    set cs [socket [info hostname] [lindex [fconfigure $ss -sockname] 2]]
546    puts $cs hello
547    close $cs
548    vwait done
549    after cancel $timer
550    set done
551} 1
552test socket-2.11 {detecting new data} {socket} {
553    proc accept {s a p} {
554	global sock
555	set sock $s
556    }
557
558    set s [socket -server accept 0]
559    set sock ""
560    set s2 [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]]
561    vwait sock
562    puts $s2 one
563    flush $s2
564    after 500
565    fconfigure $sock -blocking 0
566    set result a:[gets $sock]
567    lappend result b:[gets $sock]
568    fconfigure $sock -blocking 1
569    puts $s2 two
570    flush $s2
571    after 500
572    fconfigure $sock -blocking 0
573    lappend result c:[gets $sock]
574    fconfigure $sock -blocking 1
575    close $s2
576    close $s
577    close $sock
578    set result
579} {a:one b: c:two}
580test socket-2.12 {} {socket stdio} {
581    file delete $path(script)
582    set f [open $path(script) w]
583    puts $f {
584	set server [socket -server accept_client 0]
585	puts [lindex [chan configure $server -sockname] 2]
586	proc accept_client { client host port } {
587	    chan configure $client -blocking  0 -buffering line
588	    write_line $client
589	}
590	proc write_line client {
591	    if { [catch { chan puts $client [string repeat . 720000]}] } {
592		puts [catch {chan close $client}]
593	    } else {
594		puts signal1
595		after 0 write_line $client
596	    }
597	}
598	chan event stdin readable {set forever now}
599	vwait forever
600	exit
601    }
602    close $f
603    set f [open "|[list [interpreter] $path(script)]" r+]
604    gets $f port
605    set sock [socket 127.0.0.1 $port]
606    chan event $sock readable [list read_lines $sock $f]
607    proc read_lines { sock pipe } {
608	gets $pipe
609	chan close $sock
610	chan event $pipe readable [list readpipe $pipe]
611    }
612    proc readpipe {pipe} {
613	while {![string is integer [set ::done [gets $pipe]]]} {}
614    }
615    vwait ::done
616    close $f
617    set ::done
618} 0
619test socket-2.13 {Bug 1758a0b603} {socket stdio} {
620    file delete $path(script)
621    set f [open $path(script) w]
622    puts $f {
623	set server [socket -server accept 0]
624	puts [lindex [chan configure $server -sockname] 2]
625	proc accept { client host port } {
626	    chan configure $client -blocking  0 -buffering line -buffersize 1
627	    puts $client [string repeat . 720000]
628	    puts ready
629	    chan event $client writable [list setup $client]
630	}
631	proc setup client {
632	    chan event $client writable {set forever write}
633	    after 5 {set forever timeout}
634	}
635	vwait forever
636	puts $forever
637    }
638    close $f
639    set pipe [open |[list [interpreter] $path(script)] r]
640    gets $pipe port
641    set sock [socket localhost $port]
642    chan configure $sock -blocking  0 -buffering line
643    chan event $sock readable [list read_lines $sock $pipe ]
644    proc read_lines { sock pipe } {
645	gets $pipe
646	gets $sock line
647	after idle [list stop $sock $pipe]
648	chan event $sock readable {}
649    }
650    proc stop {sock pipe} {
651	variable done
652	close $sock
653	set done [gets $pipe]
654    }
655    variable done
656    vwait [namespace which -variable done]
657    close $pipe
658    set done
659} write
660
661test socket-3.1 {socket conflict} {socket stdio} {
662    file delete $path(script)
663    set f [open $path(script) w]
664    puts $f {
665	set f [socket -server accept -myaddr 127.0.0.1 0]
666	puts ready
667	puts [lindex [fconfigure $f -sockname] 2]
668	gets stdin
669	close $f
670    }
671    close $f
672    set f [open "|[list [interpreter] $path(script)]" r+]
673    gets $f
674    gets $f listen
675    set x [list [catch {socket -server accept -myaddr 127.0.0.1 $listen} msg] \
676		$msg]
677    puts $f bye
678    close $f
679    set x
680} {1 {couldn't open socket: address already in use}}
681test socket-3.2 {server with several clients} {socket stdio} {
682    file delete $path(script)
683    set f [open $path(script) w]
684    puts $f {
685	set t1 [after 30000 "set x timed_out"]
686	set t2 [after 31000 "set x timed_out"]
687	set t3 [after 32000 "set x timed_out"]
688	set counter 0
689	set s [socket -server accept -myaddr 127.0.0.1 0]
690	proc accept {s a p} {
691	    fileevent $s readable [list echo $s]
692	    fconfigure $s -buffering line
693	}
694	proc echo {s} {
695	     global x
696             set l [gets $s]
697             if {[eof $s]} {
698                 close $s
699                 set x done
700             } else {
701                 puts $s $l
702             }
703	}
704	puts ready
705	puts [lindex [fconfigure $s -sockname] 2]
706	vwait x
707	after cancel $t1
708	vwait x
709	after cancel $t2
710	vwait x
711	after cancel $t3
712	close $s
713	puts $x
714    }
715    close $f
716    set f [open "|[list [interpreter] $path(script)]" r+]
717    set x [gets $f]
718    gets $f listen
719    set s1 [socket 127.0.0.1 $listen]
720    fconfigure $s1 -buffering line
721    set s2 [socket 127.0.0.1 $listen]
722    fconfigure $s2 -buffering line
723    set s3 [socket 127.0.0.1 $listen]
724    fconfigure $s3 -buffering line
725    for {set i 0} {$i < 100} {incr i} {
726	puts $s1 hello,s1
727	gets $s1
728	puts $s2 hello,s2
729	gets $s2
730	puts $s3 hello,s3
731	gets $s3
732    }
733    close $s1
734    close $s2
735    close $s3
736    lappend x [gets $f]
737    close $f
738    set x
739} {ready done}
740
741test socket-4.1 {server with several clients} {socket stdio} {
742    file delete $path(script)
743    set f [open $path(script) w]
744    puts $f {
745	set port [gets stdin]
746	set s [socket 127.0.0.1 $port]
747	fconfigure $s -buffering line
748	for {set i 0} {$i < 100} {incr i} {
749	    puts $s hello
750	    gets $s
751	}
752	close $s
753	puts bye
754	gets stdin
755    }
756    close $f
757    set p1 [open "|[list [interpreter] $path(script)]" r+]
758    fconfigure $p1 -buffering line
759    set p2 [open "|[list [interpreter] $path(script)]" r+]
760    fconfigure $p2 -buffering line
761    set p3 [open "|[list [interpreter] $path(script)]" r+]
762    fconfigure $p3 -buffering line
763    proc accept {s a p} {
764	fconfigure $s -buffering line
765	fileevent $s readable [list echo $s]
766    }
767    proc echo {s} {
768	global x
769        set l [gets $s]
770        if {[eof $s]} {
771            close $s
772            set x done
773        } else {
774            puts $s $l
775        }
776    }
777    set t1 [after 30000 "set x timed_out"]
778    set t2 [after 31000 "set x timed_out"]
779    set t3 [after 32000 "set x timed_out"]
780    set s [socket -server accept -myaddr 127.0.0.1 0]
781    set listen [lindex [fconfigure $s -sockname] 2]
782    puts $p1 $listen
783    puts $p2 $listen
784    puts $p3 $listen
785    vwait x
786    vwait x
787    vwait x
788    after cancel $t1
789    after cancel $t2
790    after cancel $t3
791    close $s
792    set l ""
793    lappend l [list p1 [gets $p1] $x]
794    lappend l [list p2 [gets $p2] $x]
795    lappend l [list p3 [gets $p3] $x]
796    puts $p1 bye
797    puts $p2 bye
798    puts $p3 bye
799    close $p1
800    close $p2
801    close $p3
802    set l
803} {{p1 bye done} {p2 bye done} {p3 bye done}}
804test socket-4.2 {byte order problems, socket numbers, htons} {socket} {
805    set x ok
806    if {[catch {socket -server dodo -myaddr 127.0.0.1 0x3000} msg]} {
807	set x $msg
808    } else {
809	close $msg
810    }
811    set x
812} ok
813
814test socket-5.1 {byte order problems, socket numbers, htons} \
815	{socket unix notRoot} {
816    set x {couldn't open socket: not owner}
817    if {![catch {socket -server dodo 0x1} msg]} {
818        set x {htons problem, should be disallowed, are you running as SU?}
819	close $msg
820    }
821    set x
822} {couldn't open socket: not owner}
823test socket-5.2 {byte order problems, socket numbers, htons} {socket} {
824    set x {couldn't open socket: port number too high}
825    if {![catch {socket -server dodo 0x10000} msg]} {
826	set x {port resolution problem, should be disallowed}
827	close $msg
828    }
829    set x
830} {couldn't open socket: port number too high}
831test socket-5.3 {byte order problems, socket numbers, htons} \
832	{socket unix notRoot} {
833    set x {couldn't open socket: not owner}
834    if {![catch {socket -server dodo 21} msg]} {
835	set x {htons problem, should be disallowed, are you running as SU?}
836	close $msg
837    }
838    set x
839} {couldn't open socket: not owner}
840
841test socket-6.1 {accept callback error} -constraints {socket stdio} -setup {
842    proc myHandler {msg options} {
843	variable x $msg
844    }
845    set handler [interp bgerror {}]
846    interp bgerror {} [namespace which myHandler]
847    file delete $path(script)
848} -body {
849    set f [open $path(script) w]
850    puts $f {
851	gets stdin port
852	socket 127.0.0.1 $port
853    }
854    close $f
855    set f [open "|[list [interpreter] $path(script)]" r+]
856    proc accept {s a p} {expr 10 / 0}
857    set s [socket -server accept -myaddr 127.0.0.1 0]
858    puts $f [lindex [fconfigure $s -sockname] 2]
859    close $f
860    set timer [after 10000 "set x timed_out"]
861    vwait x
862    after cancel $timer
863    close $s
864    set x
865} -cleanup {
866    interp bgerror {} $handler
867} -result {divide by zero}
868
869test socket-7.1 {testing socket specific options} {socket stdio} {
870    file delete $path(script)
871    set f [open $path(script) w]
872    puts $f {
873	set ss [socket -server accept 0]
874	proc accept args {
875	    global x
876	    set x done
877	}
878	puts ready
879	puts [lindex [fconfigure $ss -sockname] 2]
880	set timer [after 10000 "set x timed_out"]
881	vwait x
882	after cancel $timer
883    }
884    close $f
885    set f [open "|[list [interpreter] $path(script)]" r]
886    gets $f
887    gets $f listen
888    set s [socket 127.0.0.1 $listen]
889    set p [fconfigure $s -peername]
890    close $s
891    close $f
892    set l ""
893    lappend l [string compare [lindex $p 0] 127.0.0.1]
894    lappend l [string compare [lindex $p 2] $listen]
895    lappend l [llength $p]
896} {0 0 3}
897test socket-7.2 {testing socket specific options} {socket stdio} {
898    file delete $path(script)
899    set f [open $path(script) w]
900    puts $f {
901	set ss [socket -server accept 2821]
902	proc accept args {
903	    global x
904	    set x done
905	}
906	puts ready
907	puts [lindex [fconfigure $ss -sockname] 2]
908	set timer [after 10000 "set x timed_out"]
909	vwait x
910	after cancel $timer
911    }
912    close $f
913    set f [open "|[list [interpreter] $path(script)]" r]
914    gets $f
915    gets $f listen
916    set s [socket 127.0.0.1 $listen]
917    set p [fconfigure $s -sockname]
918    close $s
919    close $f
920    list [llength $p] \
921	    [regexp {^(127\.0\.0\.1|0\.0\.0\.0)$} [lindex $p 0]] \
922	    [expr {[lindex $p 2] == $listen}]
923} {3 1 0}
924test socket-7.3 {testing socket specific options} {socket} {
925    set s [socket -server accept -myaddr 127.0.0.1 0]
926    set l [fconfigure $s]
927    close $s
928    update
929    llength $l
930} 14
931test socket-7.4 {testing socket specific options} {socket} {
932    set s [socket -server accept -myaddr 127.0.0.1 0]
933    proc accept {s a p} {
934	global x
935	set x [fconfigure $s -sockname]
936	close $s
937    }
938    set listen [lindex [fconfigure $s -sockname] 2]
939    set s1 [socket 127.0.0.1 $listen]
940    set timer [after 10000 "set x timed_out"]
941    vwait x
942    after cancel $timer
943    close $s
944    close $s1
945    set l ""
946    lappend l [expr {[lindex $x 2] == $listen}] [llength $x]
947} {1 3}
948test socket-7.5 {testing socket specific options} {socket unixOrPc} {
949    set s [socket -server accept 0]
950    proc accept {s a p} {
951	global x
952	set x [fconfigure $s -sockname]
953	close $s
954    }
955    set listen [lindex [fconfigure $s -sockname] 2]
956    set s1 [socket 127.0.0.1 $listen]
957    set timer [after 10000 "set x timed_out"]
958    vwait x
959    after cancel $timer
960    close $s
961    close $s1
962    set l ""
963    lappend l [lindex $x 0] [expr {[lindex $x 2] == $listen}] [llength $x]
964} {127.0.0.1 1 3}
965
966test socket-8.1 {testing -async flag on sockets} {socket} {
967    # NOTE: This test may fail on some Solaris 2.4 systems. If it does,
968    # check that you have these patches installed (using showrev -p):
969    #
970    # 101907-05, 101925-02, 101945-14, 101959-03, 101969-05, 101973-03,
971    # 101977-03, 101981-02, 101985-01, 102001-03, 102003-01, 102007-01,
972    # 102011-02, 102024-01, 102039-01, 102044-01, 102048-01, 102062-03,
973    # 102066-04, 102070-01, 102105-01, 102153-03, 102216-01, 102232-01,
974    # 101878-03, 101879-01, 101880-03, 101933-01, 101950-01, 102030-01,
975    # 102057-08, 102140-01, 101920-02, 101921-09, 101922-07, 101923-03
976    #
977    # If after installing these patches you are still experiencing a
978    # problem, please email jyl@eng.sun.com. We have not observed this
979    # failure on Solaris 2.5, so another option (instead of installing
980    # these patches) is to upgrade to Solaris 2.5.
981    set s [socket -server accept -myaddr 127.0.0.1 0]
982    proc accept {s a p} {
983	global x
984	puts $s bye
985	close $s
986	set x done
987    }
988    set s1 [socket -async 127.0.0.1 [lindex [fconfigure $s -sockname] 2]]
989    vwait x
990    set z [gets $s1]
991    close $s
992    close $s1
993    set z
994} bye
995
996test socket-9.1 {testing spurious events} {socket} {
997    set len 0
998    set spurious 0
999    set done 0
1000    proc readlittle {s} {
1001	global spurious done len
1002	set l [read $s 1]
1003	if {[string length $l] == 0} {
1004	    if {![eof $s]} {
1005		incr spurious
1006	    } else {
1007		close $s
1008		set done 1
1009	    }
1010	} else {
1011	    incr len [string length $l]
1012	}
1013    }
1014    proc accept {s a p} {
1015	fconfigure $s -buffering none -blocking off
1016	fileevent $s readable [list readlittle $s]
1017    }
1018    set s [socket -server accept -myaddr 127.0.0.1 0]
1019    set c [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]]
1020    puts -nonewline $c 01234567890123456789012345678901234567890123456789
1021    close $c
1022    set timer [after 10000 "set done timed_out"]
1023    vwait done
1024    after cancel $timer
1025    close $s
1026    list $spurious $len
1027} {0 50}
1028test socket-9.2 {testing async write, fileevents, flush on close} {socket} {
1029    set firstblock ""
1030    for {set i 0} {$i < 5} {incr i} {set firstblock "a$firstblock$firstblock"}
1031    set secondblock ""
1032    for {set i 0} {$i < 16} {incr i} {
1033	set secondblock "b$secondblock$secondblock"
1034    }
1035    set l [socket -server accept -myaddr 127.0.0.1 0]
1036    proc accept {s a p} {
1037	fconfigure $s -blocking 0 -translation lf -buffersize 16384 \
1038		-buffering line
1039	fileevent $s readable "readable $s"
1040    }
1041    proc readable {s} {
1042	set l [gets $s]
1043	fileevent $s readable {}
1044	after 1000 respond $s
1045    }
1046    proc respond {s} {
1047	global firstblock
1048	puts -nonewline $s $firstblock
1049	after 1000 writedata $s
1050    }
1051    proc writedata {s} {
1052	global secondblock
1053	puts -nonewline $s $secondblock
1054	close $s
1055    }
1056    set s [socket 127.0.0.1 [lindex [fconfigure $l -sockname] 2]]
1057    fconfigure $s -blocking 0 -trans lf -buffering line
1058    set count 0
1059    puts $s hello
1060    proc readit {s} {
1061	global count done
1062	set l [read $s]
1063	incr count [string length $l]
1064	if {[eof $s]} {
1065	    close $s
1066	    set done 1
1067	}
1068    }
1069    fileevent $s readable "readit $s"
1070    set timer [after 10000 "set done timed_out"]
1071    vwait done
1072    after cancel $timer
1073    close $l
1074    set count
1075} 65566
1076test socket-9.3 {testing EOF stickyness} {socket} {
1077    proc count_to_eof {s} {
1078	global count done timer
1079	set l [gets $s]
1080	if {[eof $s]} {
1081	    incr count
1082	    if {$count > 9} {
1083		close $s
1084		set done true
1085		set count {eof is sticky}
1086		after cancel $timer
1087	    }
1088	}
1089    }
1090    proc timerproc {} {
1091	global done count c
1092	set done true
1093	set count {timer went off, eof is not sticky}
1094	close $c
1095    }
1096    set count 0
1097    set done false
1098    proc write_then_close {s} {
1099	puts $s bye
1100	close $s
1101    }
1102    proc accept {s a p} {
1103	fconfigure $s -buffering line -translation lf
1104	fileevent $s writable "write_then_close $s"
1105    }
1106    set s [socket -server accept -myaddr 127.0.0.1 0]
1107    set c [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]]
1108    fconfigure $c -blocking off -buffering line -translation lf
1109    fileevent $c readable "count_to_eof $c"
1110    set timer [after 1000 timerproc]
1111    vwait done
1112    close $s
1113    set count
1114} {eof is sticky}
1115
1116removeFile script
1117
1118test socket-10.1 {testing socket accept callback error handling} -constraints {
1119    socket
1120} -setup {
1121    variable goterror 0
1122    proc myHandler {msg options} {
1123	variable goterror 1
1124    }
1125    set handler [interp bgerror {}]
1126    interp bgerror {} [namespace which myHandler]
1127} -body {
1128    set s [socket -server accept -myaddr 127.0.0.1 0]
1129    proc accept {s a p} {close $s; error}
1130    set c [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]]
1131    vwait goterror
1132    close $s
1133    close $c
1134    set goterror
1135} -cleanup {
1136    interp bgerror {} $handler
1137} -result 1
1138
1139test socket-11.1 {tcp connection} {socket doTestsWithRemoteServer} {
1140    sendCommand {
1141	set socket9_1_test_server [socket -server accept 2834]
1142	proc accept {s a p} {
1143	    puts $s done
1144	    close $s
1145	}
1146    }
1147    set s [socket $remoteServerIP 2834]
1148    set r [gets $s]
1149    close $s
1150    sendCommand {close $socket9_1_test_server}
1151    set r
1152} done
1153test socket-11.2 {client specifies its port} {socket doTestsWithRemoteServer} {
1154    if {[info exists port]} {
1155	incr port
1156    } else {
1157	set port [expr 2048 + [pid]%1024]
1158    }
1159    sendCommand {
1160	set socket9_2_test_server [socket -server accept 2835]
1161	proc accept {s a p} {
1162	    puts $s $p
1163	    close $s
1164	}
1165    }
1166    set s [socket -myport $port $remoteServerIP 2835]
1167    set r [gets $s]
1168    close $s
1169    sendCommand {close $socket9_2_test_server}
1170    if {$r == $port} {
1171	set result ok
1172    } else {
1173	set result broken
1174    }
1175    set result
1176} ok
1177test socket-11.3 {trying to connect, no server} {socket doTestsWithRemoteServer} {
1178    set status ok
1179    if {![catch {set s [socket $remoteServerIp 2836]}]} {
1180	if {![catch {gets $s}]} {
1181	    set status broken
1182	}
1183	close $s
1184    }
1185    set status
1186} ok
1187test socket-11.4 {remote echo, one line} {socket doTestsWithRemoteServer} {
1188    sendCommand {
1189	set socket10_6_test_server [socket -server accept 2836]
1190	proc accept {s a p} {
1191	    fileevent $s readable [list echo $s]
1192	    fconfigure $s -buffering line -translation crlf
1193	}
1194	proc echo {s} {
1195	    set l [gets $s]
1196	    if {[eof $s]} {
1197		close $s
1198	    } else {
1199		puts $s $l
1200	    }
1201	}
1202    }
1203    set f [socket $remoteServerIP 2836]
1204    fconfigure $f -translation crlf -buffering line
1205    puts $f hello
1206    set r [gets $f]
1207    close $f
1208    sendCommand {close $socket10_6_test_server}
1209    set r
1210} hello
1211test socket-11.5 {remote echo, 50 lines} {socket doTestsWithRemoteServer} {
1212    sendCommand {
1213	set socket10_7_test_server [socket -server accept 2836]
1214	proc accept {s a p} {
1215	    fileevent $s readable [list echo $s]
1216	    fconfigure $s -buffering line -translation crlf
1217	}
1218	proc echo {s} {
1219	    set l [gets $s]
1220	    if {[eof $s]} {
1221		close $s
1222	    } else {
1223		puts $s $l
1224	    }
1225	}
1226    }
1227    set f [socket $remoteServerIP 2836]
1228    fconfigure $f -translation crlf -buffering line
1229    for {set cnt 0} {$cnt < 50} {incr cnt} {
1230	puts $f "hello, $cnt"
1231	if {[string compare [gets $f] "hello, $cnt"] != 0} {
1232	    break
1233	}
1234    }
1235    close $f
1236    sendCommand {close $socket10_7_test_server}
1237    set cnt
1238} 50
1239test socket-11.6 {socket conflict} {socket doTestsWithRemoteServer} {
1240    set s1 [socket -server accept -myaddr 127.0.0.1 2836]
1241    if {[catch {set s2 [socket -server accept -myaddr 127.0.0.1 2836]} msg]} {
1242	set result [list 1 $msg]
1243    } else {
1244	set result [list 0 [lindex [fconfigure $s2 -sockname] 2]]
1245	close $s2
1246    }
1247    close $s1
1248    set result
1249} {1 {couldn't open socket: address already in use}}
1250test socket-11.7 {server with several clients} {socket doTestsWithRemoteServer} {
1251    sendCommand {
1252	set socket10_9_test_server [socket -server accept 2836]
1253	proc accept {s a p} {
1254	    fconfigure $s -buffering line
1255	    fileevent $s readable [list echo $s]
1256	}
1257	proc echo {s} {
1258	    set l [gets $s]
1259	    if {[eof $s]} {
1260		close $s
1261	    } else {
1262		puts $s $l
1263	    }
1264	}
1265    }
1266    set s1 [socket $remoteServerIP 2836]
1267    fconfigure $s1 -buffering line
1268    set s2 [socket $remoteServerIP 2836]
1269    fconfigure $s2 -buffering line
1270    set s3 [socket $remoteServerIP 2836]
1271    fconfigure $s3 -buffering line
1272    for {set i 0} {$i < 100} {incr i} {
1273	puts $s1 hello,s1
1274	gets $s1
1275	puts $s2 hello,s2
1276	gets $s2
1277	puts $s3 hello,s3
1278	gets $s3
1279    }
1280    close $s1
1281    close $s2
1282    close $s3
1283    sendCommand {close $socket10_9_test_server}
1284    set i
1285} 100
1286test socket-11.8 {client with several servers} {socket doTestsWithRemoteServer} {
1287    sendCommand {
1288	set s1 [socket -server "accept 4003" 4003]
1289	set s2 [socket -server "accept 4004" 4004]
1290	set s3 [socket -server "accept 4005" 4005]
1291	proc accept {mp s a p} {
1292	    puts $s $mp
1293	    close $s
1294	}
1295    }
1296    set s1 [socket $remoteServerIP 4003]
1297    set s2 [socket $remoteServerIP 4004]
1298    set s3 [socket $remoteServerIP 4005]
1299    set l ""
1300    lappend l [gets $s1] [gets $s1] [eof $s1] [gets $s2] [gets $s2] [eof $s2] \
1301	[gets $s3] [gets $s3] [eof $s3]
1302    close $s1
1303    close $s2
1304    close $s3
1305    sendCommand {
1306	close $s1
1307	close $s2
1308	close $s3
1309    }
1310    set l
1311} {4003 {} 1 4004 {} 1 4005 {} 1}
1312test socket-11.9 {accept callback error} -constraints {
1313    socket doTestsWithRemoteServer
1314} -setup {
1315    proc myHandler {msg options} {
1316	variable x $msg
1317    }
1318    set handler [interp bgerror {}]
1319    interp bgerror {} [namespace which myHandler]
1320} -body {
1321    set s [socket -server accept 2836]
1322    proc accept {s a p} {expr 10 / 0}
1323    if {[catch {sendCommand {
1324	    set peername [fconfigure $callerSocket -peername]
1325	    set s [socket [lindex $peername 0] 2836]
1326	    close $s
1327    	 }} msg]} {
1328	close $s
1329	error $msg
1330    }
1331    set timer [after 10000 "set x timed_out"]
1332    vwait x
1333    after cancel $timer
1334    close $s
1335    set x
1336} -cleanup {
1337    interp bgerror {} $handler
1338} -result {divide by zero}
1339test socket-11.10 {testing socket specific options} {socket doTestsWithRemoteServer} {
1340    sendCommand {
1341	set socket10_12_test_server [socket -server accept 2836]
1342	proc accept {s a p} {close $s}
1343    }
1344    set s [socket $remoteServerIP 2836]
1345    set p [fconfigure $s -peername]
1346    set n [fconfigure $s -sockname]
1347    set l ""
1348    lappend l [lindex $p 2] [llength $p] [llength $p]
1349    close $s
1350    sendCommand {close $socket10_12_test_server}
1351    set l
1352} {2836 3 3}
1353test socket-11.11 {testing spurious events} {socket doTestsWithRemoteServer} {
1354    sendCommand {
1355	set socket10_13_test_server [socket -server accept 2836]
1356	proc accept {s a p} {
1357	    fconfigure $s -translation "auto lf"
1358	    after 100 writesome $s
1359	}
1360	proc writesome {s} {
1361	    for {set i 0} {$i < 100} {incr i} {
1362		puts $s "line $i from remote server"
1363	    }
1364	    close $s
1365	}
1366    }
1367    set len 0
1368    set spurious 0
1369    set done 0
1370    proc readlittle {s} {
1371	global spurious done len
1372	set l [read $s 1]
1373	if {[string length $l] == 0} {
1374	    if {![eof $s]} {
1375		incr spurious
1376	    } else {
1377		close $s
1378		set done 1
1379	    }
1380	} else {
1381	    incr len [string length $l]
1382	}
1383    }
1384    set c [socket $remoteServerIP 2836]
1385    fileevent $c readable "readlittle $c"
1386    set timer [after 40000 "set done timed_out"]
1387    vwait done
1388    after cancel $timer
1389    sendCommand {close $socket10_13_test_server}
1390    list $spurious $len $done
1391} {0 2690 1}
1392test socket-11.12 {testing EOF stickyness} {socket doTestsWithRemoteServer} {
1393    set counter 0
1394    set done 0
1395    proc count_up {s} {
1396	global counter done after_id
1397	set l [gets $s]
1398	if {[eof $s]} {
1399	    incr counter
1400	    if {$counter > 9} {
1401		set done {EOF is sticky}
1402		after cancel $after_id
1403		close $s
1404	    }
1405	}
1406    }
1407    proc timed_out {} {
1408	global c done
1409	set done {timed_out, EOF is not sticky}
1410	close $c
1411    }
1412    sendCommand {
1413	set socket10_14_test_server [socket -server accept 2836]
1414	proc accept {s a p} {
1415	    after 100 close $s
1416	}
1417    }
1418    set c [socket $remoteServerIP 2836]
1419    fileevent $c readable [list count_up $c]
1420    set after_id [after 1000 timed_out]
1421    vwait done
1422    sendCommand {close $socket10_14_test_server}
1423    set done
1424} {EOF is sticky}
1425test socket-11.13 {testing async write, async flush, async close} \
1426	{socket doTestsWithRemoteServer} {
1427    proc readit {s} {
1428	global count done
1429	set l [read $s]
1430	incr count [string length $l]
1431	if {[eof $s]} {
1432	    close $s
1433	    set done 1
1434	}
1435    }
1436    sendCommand {
1437	set firstblock ""
1438	for {set i 0} {$i < 5} {incr i} {
1439		set firstblock "a$firstblock$firstblock"
1440	}
1441	set secondblock ""
1442	for {set i 0} {$i < 16} {incr i} {
1443	    set secondblock "b$secondblock$secondblock"
1444	}
1445	set l [socket -server accept 2845]
1446	proc accept {s a p} {
1447	    fconfigure $s -blocking 0 -translation lf -buffersize 16384 \
1448		-buffering line
1449	    fileevent $s readable "readable $s"
1450	}
1451	proc readable {s} {
1452	    set l [gets $s]
1453	    fileevent $s readable {}
1454	    after 1000 respond $s
1455	}
1456	proc respond {s} {
1457	    global firstblock
1458	    puts -nonewline $s $firstblock
1459	    after 1000 writedata $s
1460	}
1461	proc writedata {s} {
1462	    global secondblock
1463	    puts -nonewline $s $secondblock
1464	    close $s
1465	}
1466    }
1467    set s [socket $remoteServerIP 2845]
1468    fconfigure $s -blocking 0 -trans lf -buffering line
1469    set count 0
1470    puts $s hello
1471    fileevent $s readable "readit $s"
1472    set timer [after 10000 "set done timed_out"]
1473    vwait done
1474    after cancel $timer
1475    sendCommand {close $l}
1476    set count
1477} 65566
1478
1479set path(script1) [makeFile {} script1]
1480set path(script2) [makeFile {} script2]
1481
1482test socket-12.1 {testing inheritance of server sockets} {socket stdio exec} {
1483    file delete $path(script1)
1484    file delete $path(script2)
1485
1486    # Script1 is just a 10 second delay.  If the server socket
1487    # is inherited, it will be held open for 10 seconds
1488
1489    set f [open $path(script1) w]
1490    puts $f {
1491	after 10000 exit
1492	vwait forever
1493    }
1494    close $f
1495
1496    # Script2 creates the server socket, launches script1,
1497    # waits a second, and exits.  The server socket will now
1498    # be closed unless script1 inherited it.
1499
1500    set f [open $path(script2) w]
1501    puts $f [list set tcltest [interpreter]]
1502    puts -nonewline $f {
1503	set f [socket -server accept -myaddr 127.0.0.1 0]
1504	puts [lindex [fconfigure $f -sockname] 2]
1505	proc accept { file addr port } {
1506	    close $file
1507	}
1508	exec $tcltest }
1509    puts $f [list $path(script1) &]
1510    puts $f {
1511	close $f
1512	after 1000 exit
1513	vwait forever
1514    }
1515    close $f
1516
1517    # Launch script2 and wait 5 seconds
1518
1519    ### exec [interpreter] script2 &
1520    set p [open "|[list [interpreter] $path(script2)]" r]
1521    gets $p listen
1522
1523    after 5000 { set ok_to_proceed 1 }
1524    vwait ok_to_proceed
1525
1526    # If we can still connect to the server, the socket got inherited.
1527
1528    if {[catch {socket 127.0.0.1 $listen} msg]} {
1529	set x {server socket was not inherited}
1530    } else {
1531	close $msg
1532	set x {server socket was inherited}
1533    }
1534
1535    close $p
1536    set x
1537} {server socket was not inherited}
1538test socket-12.2 {testing inheritance of client sockets} {socket stdio exec} {
1539    file delete $path(script1)
1540    file delete $path(script2)
1541
1542    # Script1 is just a 20 second delay.  If the server socket
1543    # is inherited, it will be held open for 10 seconds
1544
1545    set f [open $path(script1) w]
1546    puts $f {
1547	after 20000 exit
1548	vwait forever
1549    }
1550    close $f
1551
1552    # Script2 opens the client socket and writes to it.  It then
1553    # launches script1 and exits.  If the child process inherited the
1554    # client socket, the socket will still be open.
1555
1556    set f [open $path(script2) w]
1557    puts $f [list set tcltest [interpreter]]
1558    puts -nonewline $f {
1559        gets stdin port
1560	set f [socket 127.0.0.1 $port]
1561        exec $tcltest }
1562    puts $f [list $path(script1) &]
1563    puts $f {
1564	puts $f testing
1565	flush $f
1566	after 1000 exit
1567	vwait forever
1568    }
1569    close $f
1570
1571    # Create the server socket
1572
1573    set server [socket -server accept -myaddr 127.0.0.1 0]
1574    proc accept { file host port } {
1575	# When the client connects, establish the read handler
1576	global server
1577	close $server
1578	fileevent $file readable [list getdata $file]
1579	fconfigure $file -buffering line -blocking 0
1580	return
1581    }
1582    proc getdata { file } {
1583	# Read handler on the accepted socket.
1584	global x
1585	global failed
1586	set status [catch {read $file} data]
1587	if {$status != 0} {
1588	    set x {read failed, error was $data}
1589	    catch { close $file }
1590	} elseif {[string compare {} $data]} {
1591	} elseif {[fblocked $file]} {
1592	} elseif {[eof $file]} {
1593	    if {$failed} {
1594		set x {client socket was inherited}
1595	    } else {
1596		set x {client socket was not inherited}
1597	    }
1598	    catch { close $file }
1599	} else {
1600	    set x {impossible case}
1601	    catch { close $file }
1602	}
1603	return
1604    }
1605
1606    # If the socket doesn't hit end-of-file in 10 seconds, the
1607    # script1 process must have inherited the client.
1608
1609    set failed 0
1610    after 10000 [list set failed 1]
1611
1612    # Launch the script2 process
1613    ### exec [interpreter] script2 &
1614
1615    set p [open "|[list [interpreter] $path(script2)]" w]
1616    puts $p [lindex [fconfigure $server -sockname] 2] ; flush $p
1617
1618    vwait x
1619    if {!$failed} {
1620	vwait failed
1621    }
1622    close $p
1623    set x
1624} {client socket was not inherited}
1625test socket-12.3 {testing inheritance of accepted sockets} {socket stdio exec} {
1626    file delete $path(script1)
1627    file delete $path(script2)
1628
1629    set f [open $path(script1) w]
1630    puts $f {
1631	after 10000 exit
1632	vwait forever
1633    }
1634    close $f
1635
1636    set f [open $path(script2) w]
1637    puts $f [list set tcltest [interpreter]]
1638    puts -nonewline $f {
1639	set server [socket -server accept -myaddr 127.0.0.1 0]
1640	puts stdout [lindex [fconfigure $server -sockname] 2]
1641	proc accept { file host port } }
1642    puts $f \{
1643    puts -nonewline $f {
1644	    global tcltest
1645	    puts $file {test data on socket}
1646	    exec $tcltest }
1647    puts $f [list $path(script1) &]
1648    puts $f {
1649	    after 1000 exit
1650	}
1651    puts $f \}
1652    puts $f {
1653	vwait forever
1654    }
1655    close $f
1656
1657    # Launch the script2 process and connect to it.  See how long
1658    # the socket stays open
1659
1660    ## exec [interpreter] script2 &
1661    set p [open "|[list [interpreter] $path(script2)]" r]
1662    gets $p listen
1663
1664    after 1000 set ok_to_proceed 1
1665    vwait ok_to_proceed
1666
1667    set f [socket 127.0.0.1 $listen]
1668    fconfigure $f -buffering full -blocking 0
1669    fileevent $f readable [list getdata $f]
1670
1671    # If the socket is still open after 5 seconds, the script1 process
1672    # must have inherited the accepted socket.
1673
1674    set failed 0
1675    after 5000 set failed 1
1676
1677    proc getdata { file } {
1678	# Read handler on the client socket.
1679	global x
1680	global failed
1681	set status [catch {read $file} data]
1682	if {$status != 0} {
1683	    set x {read failed, error was $data}
1684	    catch { close $file }
1685	} elseif {[string compare {} $data]} {
1686	} elseif {[fblocked $file]} {
1687	} elseif {[eof $file]} {
1688	    if {$failed} {
1689		set x {accepted socket was inherited}
1690	    } else {
1691		set x {accepted socket was not inherited}
1692	    }
1693	    catch { close $file }
1694	} else {
1695	    set x {impossible case}
1696	    catch { close $file }
1697	}
1698	return
1699    }
1700
1701    vwait x
1702
1703    close $p
1704    set x
1705} {accepted socket was not inherited}
1706
1707test socket-13.1 {Testing use of shared socket between two threads} \
1708        -constraints {socket testthread} -setup {
1709    threadReap
1710    set path(script) [makeFile {
1711        set f [socket -server accept -myaddr 127.0.0.1 0]
1712        set listen [lindex [fconfigure $f -sockname] 2]
1713        proc accept {s a p} {
1714            fileevent $s readable [list echo $s]
1715            fconfigure $s -buffering line
1716        }
1717        proc echo {s} {
1718             global i
1719             set l [gets $s]
1720             if {[eof $s]} {
1721                 global x
1722                 close $s
1723                 set x done
1724             } else {
1725                 incr i
1726                 puts $s $l
1727             }
1728        }
1729        set i 0
1730        vwait x
1731        close $f
1732        # thread cleans itself up.
1733        testthread exit
1734    } script]
1735} -body {
1736    # create a thread
1737    set serverthread [testthread create [list source $path(script) ] ]
1738    update
1739    set port [testthread send $serverthread {set listen}]
1740    update
1741
1742    after 1000
1743    set s [socket 127.0.0.1 $port]
1744    fconfigure $s -buffering line
1745
1746    catch {
1747        puts $s "hello"
1748        gets $s result
1749    }
1750    close $s
1751    update
1752
1753    after 2000
1754    lappend result [threadReap]
1755} -cleanup {
1756    removeFile script
1757} -result {hello 1}
1758
1759removeFile script1
1760removeFile script2
1761
1762# cleanup
1763if {[string match sock* $commandSocket] == 1} {
1764   puts $commandSocket exit
1765   flush $commandSocket
1766}
1767catch {close $commandSocket}
1768catch {close $remoteProcChan}
1769test socket-14.13 {testing writable event when quick failure} -constraints {socket win supported_inet} -body {
1770    # Test for bug 336441ed59 where a quick background fail was ignored
1771
1772    # Test only for windows as socket -async 255.255.255.255 fails
1773    # directly on unix
1774
1775    # The following connect should fail very quickly
1776    set a1 [after 2000 {set x timeout}]
1777    set s [socket -async 255.255.255.255 43434]
1778    fileevent $s writable {set x writable}
1779    vwait x
1780    set x
1781} -cleanup {
1782    catch {close $s}
1783    after cancel $a1
1784} -result writable
1785
1786test socket-14.14 {testing fileevent readable on failed async socket connect} -constraints [list socket] -body {
1787    # Test for bug 581937ab1e
1788
1789    set a1 [after 5000 {set x timeout}]
1790    # This connect should fail
1791    set s [socket -async localhost [randport]]
1792    fileevent $s readable {set x readable}
1793    vwait x
1794    set x
1795} -cleanup {
1796    catch {close $s}
1797    after cancel $a1
1798} -result readable
1799
1800::tcltest::cleanupTests
1801flush stdout
1802return
1803