diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-03-02 16:38:43 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-03-02 16:38:43 +0000 |
commit | cbb6491d76c7aa81cdf5d3b3a81386129c5e2fce (patch) | |
tree | efa0c55763b34cbc633bc494c2743d1b5d9aaff3 /libgo/go/net | |
parent | ff2f581b00ac6759f6366c16ef902c935163aa13 (diff) | |
download | gcc-cbb6491d76c7aa81cdf5d3b3a81386129c5e2fce.zip gcc-cbb6491d76c7aa81cdf5d3b3a81386129c5e2fce.tar.gz gcc-cbb6491d76c7aa81cdf5d3b3a81386129c5e2fce.tar.bz2 |
libgo: Update to weekly.2012-02-14 release.
From-SVN: r184798
Diffstat (limited to 'libgo/go/net')
40 files changed, 1041 insertions, 404 deletions
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index 5d596bc..61b8911 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/net/dial.go @@ -185,7 +185,7 @@ func Listen(net, laddr string) (Listener, error) { if a != nil { la = a.(*TCPAddr) } - return ListenTCP(afnet, la) + return ListenTCP(net, la) case "unix", "unixpacket": var la *UnixAddr if a != nil { diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go index de35ec9..e5a797e 100644 --- a/libgo/go/net/dial_test.go +++ b/libgo/go/net/dial_test.go @@ -27,8 +27,7 @@ func TestDialTimeout(t *testing.T) { errc := make(chan error) - const SOMAXCONN = 0x80 // copied from syscall, but not always available - const numConns = SOMAXCONN + 10 + numConns := listenerBacklog + 10 // TODO(bradfitz): It's hard to test this in a portable // way. This is unforunate, but works for now. @@ -43,7 +42,7 @@ func TestDialTimeout(t *testing.T) { errc <- err }() } - case "darwin", "windows": + case "darwin": // At least OS X 10.7 seems to accept any number of // connections, ignoring listen's backlog, so resort // to connecting to a hopefully-dead 127/8 address. @@ -54,8 +53,10 @@ func TestDialTimeout(t *testing.T) { }() default: // TODO(bradfitz): - // OpenBSD may have a reject route to 10/8. - // FreeBSD likely works, but is untested. + // OpenBSD may have a reject route to 127/8 except 127.0.0.1/32 + // by default. FreeBSD likely works, but is untested. + // TODO(rsc): + // The timeout never happens on Windows. Why? Issue 3016. t.Logf("skipping test on %q; untested.", runtime.GOOS) return } @@ -85,3 +86,45 @@ func TestDialTimeout(t *testing.T) { } } } + +func TestSelfConnect(t *testing.T) { + if runtime.GOOS == "windows" { + // TODO(brainman): do not know why it hangs. + t.Logf("skipping known-broken test on windows") + return + } + // Test that Dial does not honor self-connects. + // See the comment in DialTCP. + + // Find a port that would be used as a local address. + l, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + c, err := Dial("tcp", l.Addr().String()) + if err != nil { + t.Fatal(err) + } + addr := c.LocalAddr().String() + c.Close() + l.Close() + + // Try to connect to that address repeatedly. + n := 100000 + if testing.Short() { + n = 1000 + } + switch runtime.GOOS { + case "darwin", "freebsd", "openbsd", "windows": + // Non-Linux systems take a long time to figure + // out that there is nothing listening on localhost. + n = 100 + } + for i := 0; i < n; i++ { + c, err := Dial("tcp", addr) + if err == nil { + c.Close() + t.Errorf("#%d: Dial %q succeeded", i, addr) + } + } +} diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go index 2352d22..bf0a387 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -7,6 +7,7 @@ package net import ( + "errors" "io" "os" "sync" @@ -17,8 +18,11 @@ import ( // Network file descriptor. type netFD struct { // locking/lifetime of sysfd - sysmu sync.Mutex - sysref int + sysmu sync.Mutex + sysref int + + // must lock both sysmu and pollserver to write + // can lock either to read closing bool // immutable until Close @@ -27,8 +31,8 @@ type netFD struct { sotype int isConnected bool sysfile *os.File - cr chan bool - cw chan bool + cr chan error + cw chan error net string laddr Addr raddr Addr @@ -86,20 +90,15 @@ type pollServer struct { deadline int64 // next deadline (nsec since 1970) } -func (s *pollServer) AddFD(fd *netFD, mode int) { +func (s *pollServer) AddFD(fd *netFD, mode int) error { + s.Lock() intfd := fd.sysfd - if intfd < 0 { + if intfd < 0 || fd.closing { // fd closed underfoot - if mode == 'r' { - fd.cr <- true - } else { - fd.cw <- true - } - return + s.Unlock() + return errClosing } - s.Lock() - var t int64 key := intfd << 1 if mode == 'r' { @@ -124,12 +123,28 @@ func (s *pollServer) AddFD(fd *netFD, mode int) { if wake { doWakeup = true } - s.Unlock() if doWakeup { s.Wakeup() } + return nil +} + +// Evict evicts fd from the pending list, unblocking +// any I/O running on fd. The caller must have locked +// pollserver. +func (s *pollServer) Evict(fd *netFD) { + if s.pending[fd.sysfd<<1] == fd { + s.WakeFD(fd, 'r', errClosing) + s.poll.DelFD(fd.sysfd, 'r') + delete(s.pending, fd.sysfd<<1) + } + if s.pending[fd.sysfd<<1|1] == fd { + s.WakeFD(fd, 'w', errClosing) + s.poll.DelFD(fd.sysfd, 'w') + delete(s.pending, fd.sysfd<<1|1) + } } var wakeupbuf [1]byte @@ -149,16 +164,16 @@ func (s *pollServer) LookupFD(fd int, mode int) *netFD { return netfd } -func (s *pollServer) WakeFD(fd *netFD, mode int) { +func (s *pollServer) WakeFD(fd *netFD, mode int, err error) { if mode == 'r' { for fd.ncr > 0 { fd.ncr-- - fd.cr <- true + fd.cr <- err } } else { for fd.ncw > 0 { fd.ncw-- - fd.cw <- true + fd.cw <- err } } } @@ -196,7 +211,7 @@ func (s *pollServer) CheckDeadlines() { s.poll.DelFD(fd.sysfd, mode) fd.wdeadline = -1 } - s.WakeFD(fd, mode) + s.WakeFD(fd, mode, nil) } else if next_deadline == 0 || t < next_deadline { next_deadline = t } @@ -228,7 +243,7 @@ func (s *pollServer) Run() { s.CheckDeadlines() continue } - if fd == s.pr.Fd() { + if fd == int(s.pr.Fd()) { // Drain our wakeup pipe (we could loop here, // but it's unlikely that there are more than // len(scratch) wakeup calls). @@ -240,19 +255,25 @@ func (s *pollServer) Run() { print("pollServer: unexpected wakeup for fd=", fd, " mode=", string(mode), "\n") continue } - s.WakeFD(netfd, mode) + s.WakeFD(netfd, mode, nil) } } } -func (s *pollServer) WaitRead(fd *netFD) { - s.AddFD(fd, 'r') - <-fd.cr +func (s *pollServer) WaitRead(fd *netFD) error { + err := s.AddFD(fd, 'r') + if err == nil { + err = <-fd.cr + } + return err } -func (s *pollServer) WaitWrite(fd *netFD) { - s.AddFD(fd, 'w') - <-fd.cw +func (s *pollServer) WaitWrite(fd *netFD) error { + err := s.AddFD(fd, 'w') + if err == nil { + err = <-fd.cw + } + return err } // Network FD methods. @@ -280,8 +301,8 @@ func newFD(fd, family, sotype int, net string) (*netFD, error) { sotype: sotype, net: net, } - netfd.cr = make(chan bool, 1) - netfd.cw = make(chan bool, 1) + netfd.cr = make(chan error, 1) + netfd.cw = make(chan error, 1) return netfd, nil } @@ -295,13 +316,15 @@ func (fd *netFD) setAddr(laddr, raddr Addr) { if raddr != nil { rs = raddr.String() } - fd.sysfile = os.NewFile(fd.sysfd, fd.net+":"+ls+"->"+rs) + fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net+":"+ls+"->"+rs) } func (fd *netFD) connect(ra syscall.Sockaddr) error { err := syscall.Connect(fd.sysfd, ra) if err == syscall.EINPROGRESS { - pollserver.WaitWrite(fd) + if err = pollserver.WaitWrite(fd); err != nil { + return err + } var e int e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) if err != nil { @@ -314,24 +337,37 @@ func (fd *netFD) connect(ra syscall.Sockaddr) error { return err } +var errClosing = errors.New("use of closed network connection") + // Add a reference to this fd. -func (fd *netFD) incref() { +// If closing==true, pollserver must be locked; mark the fd as closing. +// Returns an error if the fd cannot be used. +func (fd *netFD) incref(closing bool) error { + if fd == nil { + return errClosing + } fd.sysmu.Lock() + if fd.closing { + fd.sysmu.Unlock() + return errClosing + } fd.sysref++ + if closing { + fd.closing = true + } fd.sysmu.Unlock() + return nil } // Remove a reference to this FD and close if we've been asked to do so (and // there are no references left. func (fd *netFD) decref() { + if fd == nil { + return + } fd.sysmu.Lock() fd.sysref-- - if fd.closing && fd.sysref == 0 && fd.sysfd >= 0 { - // In case the user has set linger, switch to blocking mode so - // the close blocks. As long as this doesn't happen often, we - // can handle the extra OS processes. Otherwise we'll need to - // use the pollserver for Close too. Sigh. - syscall.SetNonblock(fd.sysfd, false) + if fd.closing && fd.sysref == 0 && fd.sysfile != nil { fd.sysfile.Close() fd.sysfile = nil fd.sysfd = -1 @@ -340,21 +376,26 @@ func (fd *netFD) decref() { } func (fd *netFD) Close() error { - if fd == nil || fd.sysfile == nil { - return os.EINVAL - } - - fd.incref() - syscall.Shutdown(fd.sysfd, syscall.SHUT_RDWR) - fd.closing = true + pollserver.Lock() // needed for both fd.incref(true) and pollserver.Evict + defer pollserver.Unlock() + if err := fd.incref(true); err != nil { + return err + } + // Unblock any I/O. Once it all unblocks and returns, + // so that it cannot be referring to fd.sysfd anymore, + // the final decref will close fd.sysfd. This should happen + // fairly quickly, since all the I/O is non-blocking, and any + // attempts to block in the pollserver will return errClosing. + pollserver.Evict(fd) fd.decref() return nil } func (fd *netFD) shutdown(how int) error { - if fd == nil || fd.sysfile == nil { - return os.EINVAL + if err := fd.incref(false); err != nil { + return err } + defer fd.decref() err := syscall.Shutdown(fd.sysfd, how) if err != nil { return &OpError{"shutdown", fd.net, fd.laddr, err} @@ -371,24 +412,21 @@ func (fd *netFD) CloseWrite() error { } func (fd *netFD) Read(p []byte) (n int, err error) { - if fd == nil { - return 0, os.EINVAL - } fd.rio.Lock() defer fd.rio.Unlock() - fd.incref() - defer fd.decref() - if fd.sysfile == nil { - return 0, os.EINVAL + if err := fd.incref(false); err != nil { + return 0, err } + defer fd.decref() for { - n, err = syscall.Read(fd.sysfile.Fd(), p) + n, err = syscall.Read(int(fd.sysfd), p) if err == syscall.EAGAIN { + err = errTimeout if fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + if err = pollserver.WaitRead(fd); err == nil { + continue + } } - err = errTimeout } if err != nil { n = 0 @@ -404,49 +442,49 @@ func (fd *netFD) Read(p []byte) (n int, err error) { } func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { - if fd == nil || fd.sysfile == nil { - return 0, nil, os.EINVAL - } fd.rio.Lock() defer fd.rio.Unlock() - fd.incref() + if err := fd.incref(false); err != nil { + return 0, nil, err + } defer fd.decref() for { n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) if err == syscall.EAGAIN { + err = errTimeout if fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + if err = pollserver.WaitRead(fd); err == nil { + continue + } } - err = errTimeout } if err != nil { n = 0 } break } - if err != nil { + if err != nil && err != io.EOF { err = &OpError{"read", fd.net, fd.laddr, err} } return } func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { - if fd == nil || fd.sysfile == nil { - return 0, 0, 0, nil, os.EINVAL - } fd.rio.Lock() defer fd.rio.Unlock() - fd.incref() + if err := fd.incref(false); err != nil { + return 0, 0, 0, nil, err + } defer fd.decref() for { n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) if err == syscall.EAGAIN { + err = errTimeout if fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + if err = pollserver.WaitRead(fd); err == nil { + continue + } } - err = errTimeout } if err == nil && n == 0 { err = io.EOF @@ -461,12 +499,11 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S } func (fd *netFD) Write(p []byte) (int, error) { - if fd == nil { - return 0, os.EINVAL - } fd.wio.Lock() defer fd.wio.Unlock() - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() if fd.sysfile == nil { return 0, os.EINVAL @@ -476,7 +513,7 @@ func (fd *netFD) Write(p []byte) (int, error) { nn := 0 for { var n int - n, err = syscall.Write(fd.sysfile.Fd(), p[nn:]) + n, err = syscall.Write(int(fd.sysfd), p[nn:]) if n > 0 { nn += n } @@ -484,11 +521,12 @@ func (fd *netFD) Write(p []byte) (int, error) { break } if err == syscall.EAGAIN { + err = errTimeout if fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue + if err = pollserver.WaitWrite(fd); err == nil { + continue + } } - err = errTimeout } if err != nil { n = 0 @@ -506,21 +544,21 @@ func (fd *netFD) Write(p []byte) (int, error) { } func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { - if fd == nil || fd.sysfile == nil { - return 0, os.EINVAL - } fd.wio.Lock() defer fd.wio.Unlock() - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() for { err = syscall.Sendto(fd.sysfd, p, 0, sa) if err == syscall.EAGAIN { + err = errTimeout if fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue + if err = pollserver.WaitWrite(fd); err == nil { + continue + } } - err = errTimeout } break } @@ -533,21 +571,21 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { } func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { - if fd == nil || fd.sysfile == nil { - return 0, 0, os.EINVAL - } fd.wio.Lock() defer fd.wio.Unlock() - fd.incref() + if err := fd.incref(false); err != nil { + return 0, 0, err + } defer fd.decref() for { err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) if err == syscall.EAGAIN { + err = errTimeout if fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue + if err = pollserver.WaitWrite(fd); err == nil { + continue + } } - err = errTimeout } break } @@ -561,11 +599,9 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob } func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err error) { - if fd == nil || fd.sysfile == nil { - return nil, os.EINVAL + if err := fd.incref(false); err != nil { + return nil, err } - - fd.incref() defer fd.decref() // See ../syscall/exec.go for description of ForkLock. @@ -574,19 +610,17 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e var s int var rsa syscall.Sockaddr for { - if fd.closing { - return nil, os.EINVAL - } syscall.ForkLock.RLock() s, rsa, err = syscall.Accept(fd.sysfd) if err != nil { syscall.ForkLock.RUnlock() if err == syscall.EAGAIN { + err = errTimeout if fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + if err = pollserver.WaitRead(fd); err == nil { + continue + } } - err = errTimeout } return nil, &OpError{"accept", fd.net, fd.laddr, err} } @@ -615,7 +649,7 @@ func (fd *netFD) dup() (f *os.File, err error) { return nil, &OpError{"setnonblock", fd.net, fd.laddr, err} } - return os.NewFile(ns, fd.sysfile.Name()), nil + return os.NewFile(uintptr(ns), fd.sysfile.Name()), nil } func closesocket(s int) error { diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index 78168eb..efd846e 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -5,6 +5,7 @@ package net import ( + "errors" "io" "os" "runtime" @@ -272,11 +273,27 @@ func (fd *netFD) connect(ra syscall.Sockaddr) error { return syscall.Connect(fd.sysfd, ra) } +var errClosing = errors.New("use of closed network connection") + // Add a reference to this fd. -func (fd *netFD) incref() { +// If closing==true, mark the fd as closing. +// Returns an error if the fd cannot be used. +func (fd *netFD) incref(closing bool) error { + if fd == nil { + return errClosing + } fd.sysmu.Lock() + if fd.closing { + fd.sysmu.Unlock() + return errClosing + } fd.sysref++ + if closing { + fd.closing = true + } + closing = fd.closing fd.sysmu.Unlock() + return nil } // Remove a reference to this FD and close if we've been asked to do so (and @@ -284,7 +301,17 @@ func (fd *netFD) incref() { func (fd *netFD) decref() { fd.sysmu.Lock() fd.sysref-- - if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle { + // NOTE(rsc): On Unix we check fd.sysref == 0 here before closing, + // but on Windows we have no way to wake up the blocked I/O other + // than closing the socket (or calling Shutdown, which breaks other + // programs that might have a reference to the socket). So there is + // a small race here that we might close fd.sysfd and then some other + // goroutine might start a read of fd.sysfd (having read it before we + // write InvalidHandle to it), which might refer to some other file + // if the specific handle value gets reused. I think handle values on + // Windows are not reused as aggressively as file descriptors on Unix, + // so this might be tolerable. + if fd.closing && fd.sysfd != syscall.InvalidHandle { // In case the user has set linger, switch to blocking mode so // the close blocks. As long as this doesn't happen often, we // can handle the extra OS processes. Otherwise we'll need to @@ -299,13 +326,9 @@ func (fd *netFD) decref() { } func (fd *netFD) Close() error { - if fd == nil || fd.sysfd == syscall.InvalidHandle { - return os.EINVAL + if err := fd.incref(true); err != nil { + return err } - - fd.incref() - syscall.Shutdown(fd.sysfd, syscall.SHUT_RDWR) - fd.closing = true fd.decref() return nil } @@ -350,7 +373,9 @@ func (fd *netFD) Read(buf []byte) (int, error) { } fd.rio.Lock() defer fd.rio.Unlock() - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL @@ -390,11 +415,10 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { } fd.rio.Lock() defer fd.rio.Unlock() - fd.incref() - defer fd.decref() - if fd.sysfd == syscall.InvalidHandle { - return 0, nil, os.EINVAL + if err := fd.incref(false); err != nil { + return 0, nil, err } + defer fd.decref() var o readFromOp o.Init(fd, buf, 'r') o.rsan = int32(unsafe.Sizeof(o.rsa)) @@ -427,11 +451,10 @@ func (fd *netFD) Write(buf []byte) (int, error) { } fd.wio.Lock() defer fd.wio.Unlock() - fd.incref() - defer fd.decref() - if fd.sysfd == syscall.InvalidHandle { - return 0, os.EINVAL + if err := fd.incref(false); err != nil { + return 0, err } + defer fd.decref() var o writeOp o.Init(fd, buf, 'w') return iosrv.ExecIO(&o, fd.wdeadline) @@ -462,7 +485,9 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { } fd.wio.Lock() defer fd.wio.Unlock() - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL @@ -493,10 +518,9 @@ func (o *acceptOp) Name() string { } func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { - if fd == nil || fd.sysfd == syscall.InvalidHandle { - return nil, os.EINVAL + if err := fd.incref(false); err != nil { + return nil, err } - fd.incref() defer fd.decref() // Get new socket. diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go index 901b856..f9546dc 100644 --- a/libgo/go/net/file.go +++ b/libgo/go/net/file.go @@ -12,7 +12,7 @@ import ( ) func newFileFD(f *os.File) (*netFD, error) { - fd, err := syscall.Dup(f.Fd()) + fd, err := syscall.Dup(int(f.Fd())) if err != nil { return nil, os.NewSyscallError("dup", err) } diff --git a/libgo/go/net/http/cgi/host_test.go b/libgo/go/net/http/cgi/host_test.go index b8dbdb4..fec35b7 100644 --- a/libgo/go/net/http/cgi/host_test.go +++ b/libgo/go/net/http/cgi/host_test.go @@ -19,6 +19,7 @@ import ( "runtime" "strconv" "strings" + "syscall" "testing" "time" ) @@ -355,7 +356,7 @@ func TestCopyError(t *testing.T) { if err != nil { return false } - return p.Signal(os.UnixSignal(0)) == nil + return p.Signal(syscall.Signal(0)) == nil } if !childRunning() { diff --git a/libgo/go/net/http/doc.go b/libgo/go/net/http/doc.go index 8962ed3..b6ae8b8 100644 --- a/libgo/go/net/http/doc.go +++ b/libgo/go/net/http/doc.go @@ -12,7 +12,7 @@ Get, Head, Post, and PostForm make HTTP requests: resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) ... resp, err := http.PostForm("http://example.com/form", - url.Values{"key": {"Value"}, "id": {"123"}}) + url.Values{"key": {"Value"}, "id": {"123"}}) The client must close the response body when finished with it: @@ -60,7 +60,7 @@ Handle and HandleFunc add handlers to DefaultServeMux: http.Handle("/foo", fooHandler) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.RawPath)) + fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.ListenAndServe(":8080", nil)) diff --git a/libgo/go/net/http/fcgi/child.go b/libgo/go/net/http/fcgi/child.go index c94b9a7..c8b9a33 100644 --- a/libgo/go/net/http/fcgi/child.go +++ b/libgo/go/net/http/fcgi/child.go @@ -243,9 +243,9 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) { } // Serve accepts incoming FastCGI connections on the listener l, creating a new -// service thread for each. The service threads read requests and then call handler +// goroutine for each. The goroutine reads requests and then calls handler // to reply to them. -// If l is nil, Serve accepts connections on stdin. +// If l is nil, Serve accepts connections from os.Stdin. // If handler is nil, http.DefaultServeMux is used. func Serve(l net.Listener, handler http.Handler) error { if l == nil { diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go index 1392ca6..f35dd32 100644 --- a/libgo/go/net/http/fs.go +++ b/libgo/go/net/http/fs.go @@ -17,7 +17,6 @@ import ( "strconv" "strings" "time" - "unicode/utf8" ) // A Dir implements http.FileSystem using the native file @@ -58,32 +57,6 @@ type File interface { Seek(offset int64, whence int) (int64, error) } -// Heuristic: b is text if it is valid UTF-8 and doesn't -// contain any unprintable ASCII or Unicode characters. -func isText(b []byte) bool { - for len(b) > 0 && utf8.FullRune(b) { - rune, size := utf8.DecodeRune(b) - if size == 1 && rune == utf8.RuneError { - // decoding error - return false - } - if 0x7F <= rune && rune <= 0x9F { - return false - } - if rune < ' ' { - switch rune { - case '\n', '\r', '\t': - // okay - default: - // binary garbage - return false - } - } - b = b[size:] - } - return true -} - func dirList(w ResponseWriter, f File) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprintf(w, "<pre>\n") @@ -104,6 +77,126 @@ func dirList(w ResponseWriter, f File) { fmt.Fprintf(w, "</pre>\n") } +// ServeContent replies to the request using the content in the +// provided ReadSeeker. The main benefit of ServeContent over io.Copy +// is that it handles Range requests properly, sets the MIME type, and +// handles If-Modified-Since requests. +// +// If the response's Content-Type header is not set, ServeContent +// first tries to deduce the type from name's file extension and, +// if that fails, falls back to reading the first block of the content +// and passing it to DetectContentType. +// The name is otherwise unused; in particular it can be empty and is +// never sent in the response. +// +// If modtime is not the zero time, ServeContent includes it in a +// Last-Modified header in the response. If the request includes an +// If-Modified-Since header, ServeContent uses modtime to decide +// whether the content needs to be sent at all. +// +// The content's Seek method must work: ServeContent uses +// a seek to the end of the content to determine its size. +// +// Note that *os.File implements the io.ReadSeeker interface. +func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) { + size, err := content.Seek(0, os.SEEK_END) + if err != nil { + Error(w, "seeker can't seek", StatusInternalServerError) + return + } + _, err = content.Seek(0, os.SEEK_SET) + if err != nil { + Error(w, "seeker can't seek", StatusInternalServerError) + return + } + serveContent(w, req, name, modtime, size, content) +} + +// if name is empty, filename is unknown. (used for mime type, before sniffing) +// if modtime.IsZero(), modtime is unknown. +// content must be seeked to the beginning of the file. +func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, size int64, content io.ReadSeeker) { + if checkLastModified(w, r, modtime) { + return + } + + code := StatusOK + + // If Content-Type isn't set, use the file's extension to find it. + if w.Header().Get("Content-Type") == "" { + ctype := mime.TypeByExtension(filepath.Ext(name)) + if ctype == "" { + // read a chunk to decide between utf-8 text and binary + var buf [1024]byte + n, _ := io.ReadFull(content, buf[:]) + b := buf[:n] + ctype = DetectContentType(b) + _, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file + if err != nil { + Error(w, "seeker can't seek", StatusInternalServerError) + return + } + } + w.Header().Set("Content-Type", ctype) + } + + // handle Content-Range header. + // TODO(adg): handle multiple ranges + sendSize := size + if size >= 0 { + ranges, err := parseRange(r.Header.Get("Range"), size) + if err == nil && len(ranges) > 1 { + err = errors.New("multiple ranges not supported") + } + if err != nil { + Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) + return + } + if len(ranges) == 1 { + ra := ranges[0] + if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil { + Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) + return + } + sendSize = ra.length + code = StatusPartialContent + w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, size)) + } + + w.Header().Set("Accept-Ranges", "bytes") + if w.Header().Get("Content-Encoding") == "" { + w.Header().Set("Content-Length", strconv.FormatInt(sendSize, 10)) + } + } + + w.WriteHeader(code) + + if r.Method != "HEAD" { + if sendSize == -1 { + io.Copy(w, content) + } else { + io.CopyN(w, content, sendSize) + } + } +} + +// modtime is the modification time of the resource to be served, or IsZero(). +// return value is whether this request is now complete. +func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool { + if modtime.IsZero() { + return false + } + + // The Date-Modified header truncates sub-second precision, so + // use mtime < t+1s instead of mtime <= t to check for unmodified. + if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) { + w.WriteHeader(StatusNotModified) + return true + } + w.Header().Set("Last-Modified", modtime.UTC().Format(TimeFormat)) + return false +} + // name is '/'-separated, not filepath.Separator. func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) { const indexPage = "/index.html" @@ -148,14 +241,11 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } } - if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && !d.ModTime().After(t) { - w.WriteHeader(StatusNotModified) - return - } - w.Header().Set("Last-Modified", d.ModTime().UTC().Format(TimeFormat)) - // use contents of index.html for directory, if present if d.IsDir() { + if checkLastModified(w, r, d.ModTime()) { + return + } index := name + indexPage ff, err := fs.Open(index) if err == nil { @@ -174,60 +264,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec return } - // serve file - size := d.Size() - code := StatusOK - - // If Content-Type isn't set, use the file's extension to find it. - if w.Header().Get("Content-Type") == "" { - ctype := mime.TypeByExtension(filepath.Ext(name)) - if ctype == "" { - // read a chunk to decide between utf-8 text and binary - var buf [1024]byte - n, _ := io.ReadFull(f, buf[:]) - b := buf[:n] - if isText(b) { - ctype = "text/plain; charset=utf-8" - } else { - // generic binary - ctype = "application/octet-stream" - } - f.Seek(0, os.SEEK_SET) // rewind to output whole file - } - w.Header().Set("Content-Type", ctype) - } - - // handle Content-Range header. - // TODO(adg): handle multiple ranges - ranges, err := parseRange(r.Header.Get("Range"), size) - if err == nil && len(ranges) > 1 { - err = errors.New("multiple ranges not supported") - } - if err != nil { - Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) - return - } - if len(ranges) == 1 { - ra := ranges[0] - if _, err := f.Seek(ra.start, os.SEEK_SET); err != nil { - Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) - return - } - size = ra.length - code = StatusPartialContent - w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size())) - } - - w.Header().Set("Accept-Ranges", "bytes") - if w.Header().Get("Content-Encoding") == "" { - w.Header().Set("Content-Length", strconv.FormatInt(size, 10)) - } - - w.WriteHeader(code) - - if r.Method != "HEAD" { - io.CopyN(w, f, size) - } + serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f) } // localRedirect gives a Moved Permanently response. diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go index feea920..143617e 100644 --- a/libgo/go/net/http/fs_test.go +++ b/libgo/go/net/http/fs_test.go @@ -5,15 +5,22 @@ package http_test import ( + "bytes" "fmt" + "io" "io/ioutil" + "net" . "net/http" "net/http/httptest" "net/url" "os" + "os/exec" "path/filepath" + "regexp" + "runtime" "strings" "testing" + "time" ) const ( @@ -56,18 +63,18 @@ func TestServeFile(t *testing.T) { req.Method = "GET" // straight GET - _, body := getBody(t, req) + _, body := getBody(t, "straight get", req) if !equal(body, file) { t.Fatalf("body mismatch: got %q, want %q", body, file) } // Range tests - for _, rt := range ServeFileRangeTests { + for i, rt := range ServeFileRangeTests { req.Header.Set("Range", "bytes="+rt.r) if rt.r == "" { req.Header["Range"] = nil } - r, body := getBody(t, req) + r, body := getBody(t, fmt.Sprintf("test %d", i), req) if r.StatusCode != rt.code { t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code) } @@ -298,7 +305,6 @@ func TestServeIndexHtml(t *testing.T) { if err != nil { t.Fatal(err) } - defer res.Body.Close() b, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal("reading Body:", err) @@ -306,21 +312,146 @@ func TestServeIndexHtml(t *testing.T) { if s := string(b); s != want { t.Errorf("for path %q got %q, want %q", path, s, want) } + res.Body.Close() + } +} + +func TestServeContent(t *testing.T) { + type req struct { + name string + modtime time.Time + content io.ReadSeeker + } + ch := make(chan req, 1) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + p := <-ch + ServeContent(w, r, p.name, p.modtime, p.content) + })) + defer ts.Close() + + css, err := os.Open("testdata/style.css") + if err != nil { + t.Fatal(err) + } + defer css.Close() + + ch <- req{"style.css", time.Time{}, css} + res, err := Get(ts.URL) + if err != nil { + t.Fatal(err) + } + if g, e := res.Header.Get("Content-Type"), "text/css; charset=utf-8"; g != e { + t.Errorf("style.css: content type = %q, want %q", g, e) + } + if g := res.Header.Get("Last-Modified"); g != "" { + t.Errorf("want empty Last-Modified; got %q", g) + } + + fi, err := css.Stat() + if err != nil { + t.Fatal(err) + } + ch <- req{"style.html", fi.ModTime(), css} + res, err = Get(ts.URL) + if err != nil { + t.Fatal(err) + } + if g, e := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != e { + t.Errorf("style.html: content type = %q, want %q", g, e) + } + if g := res.Header.Get("Last-Modified"); g == "" { + t.Errorf("want non-empty last-modified") } } -func getBody(t *testing.T, req Request) (*Response, []byte) { +// verifies that sendfile is being used on Linux +func TestLinuxSendfile(t *testing.T) { + if runtime.GOOS != "linux" { + t.Logf("skipping; linux-only test") + return + } + _, err := exec.LookPath("strace") + if err != nil { + t.Logf("skipping; strace not found in path") + return + } + + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + lnf, err := ln.(*net.TCPListener).File() + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + var buf bytes.Buffer + child := exec.Command("strace", "-f", os.Args[0], "-test.run=TestLinuxSendfileChild") + child.ExtraFiles = append(child.ExtraFiles, lnf) + child.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) + child.Stdout = &buf + child.Stderr = &buf + err = child.Start() + if err != nil { + t.Logf("skipping; failed to start straced child: %v", err) + return + } + + _, err = Get(fmt.Sprintf("http://%s/", ln.Addr())) + if err != nil { + t.Errorf("http client error: %v", err) + return + } + + // Force child to exit cleanly. + Get(fmt.Sprintf("http://%s/quit", ln.Addr())) + child.Wait() + + rx := regexp.MustCompile(`sendfile(64)?\(\d+,\s*\d+,\s*NULL,\s*\d+\)\s*=\s*\d+\s*\n`) + rxResume := regexp.MustCompile(`<\.\.\. sendfile(64)? resumed> \)\s*=\s*\d+\s*\n`) + out := buf.String() + if !rx.MatchString(out) && !rxResume.MatchString(out) { + t.Errorf("no sendfile system call found in:\n%s", out) + } +} + +func getBody(t *testing.T, testName string, req Request) (*Response, []byte) { r, err := DefaultClient.Do(&req) if err != nil { - t.Fatal(req.URL.String(), "send:", err) + t.Fatalf("%s: for URL %q, send error: %v", testName, req.URL.String(), err) } b, err := ioutil.ReadAll(r.Body) if err != nil { - t.Fatal("reading Body:", err) + t.Fatalf("%s: for URL %q, reading body: %v", testName, req.URL.String(), err) } return r, b } +// TestLinuxSendfileChild isn't a real test. It's used as a helper process +// for TestLinuxSendfile. +func TestLinuxSendfileChild(*testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + defer os.Exit(0) + fd3 := os.NewFile(3, "ephemeral-port-listener") + ln, err := net.FileListener(fd3) + if err != nil { + panic(err) + } + mux := NewServeMux() + mux.Handle("/", FileServer(Dir("testdata"))) + mux.HandleFunc("/quit", func(ResponseWriter, *Request) { + os.Exit(0) + }) + s := &Server{Handler: mux} + err = s.Serve(ln) + if err != nil { + panic(err) + } +} + func equal(a, b []byte) bool { if len(a) != len(b) { return false diff --git a/libgo/go/net/http/httptest/server.go b/libgo/go/net/http/httptest/server.go index 5b02e14..8d911f7 100644 --- a/libgo/go/net/http/httptest/server.go +++ b/libgo/go/net/http/httptest/server.go @@ -61,7 +61,7 @@ func newLocalListener() net.Listener { // When debugging a particular http server-based test, // this flag lets you run -// gotest -run=BrokenTest -httptest.serve=127.0.0.1:8000 +// go test -run=BrokenTest -httptest.serve=127.0.0.1:8000 // to start the broken server so you can interact with it manually. var serve = flag.String("httptest.serve", "", "if non-empty, httptest.NewServer serves on this address and blocks") @@ -95,7 +95,7 @@ func (s *Server) Start() { s.URL = "http://" + s.Listener.Addr().String() go s.Config.Serve(s.Listener) if *serve != "" { - fmt.Println(os.Stderr, "httptest: serving on", s.URL) + fmt.Fprintln(os.Stderr, "httptest: serving on", s.URL) select {} } } diff --git a/libgo/go/net/http/httptest/server_test.go b/libgo/go/net/http/httptest/server_test.go new file mode 100644 index 0000000..500a9f0 --- /dev/null +++ b/libgo/go/net/http/httptest/server_test.go @@ -0,0 +1,29 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package httptest + +import ( + "io/ioutil" + "net/http" + "testing" +) + +func TestServer(t *testing.T) { + ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("hello")) + })) + defer ts.Close() + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + got, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if string(got) != "hello" { + t.Errorf("got %q, want hello", string(got)) + } +} diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go index 21eac47..0fe41b7 100644 --- a/libgo/go/net/http/pprof/pprof.go +++ b/libgo/go/net/http/pprof/pprof.go @@ -12,15 +12,23 @@ // The handled paths all begin with /debug/pprof/. // // To use pprof, link this package into your program: -// import _ "http/pprof" +// import _ "net/http/pprof" // // Then use the pprof tool to look at the heap profile: // -// pprof http://localhost:6060/debug/pprof/heap +// go tool pprof http://localhost:6060/debug/pprof/heap // // Or to look at a 30-second CPU profile: // -// pprof http://localhost:6060/debug/pprof/profile +// go tool pprof http://localhost:6060/debug/pprof/profile +// +// Or to look at the thread creation profile: +// +// go tool pprof http://localhost:6060/debug/pprof/thread +// +// For a study of the facility in action, visit +// +// http://blog.golang.org/2011/06/profiling-go-programs.html // package pprof @@ -43,6 +51,7 @@ func init() { http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile)) http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap)) http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol)) + http.Handle("/debug/pprof/thread", http.HandlerFunc(Thread)) } // Cmdline responds with the running program's @@ -60,6 +69,13 @@ func Heap(w http.ResponseWriter, r *http.Request) { pprof.WriteHeapProfile(w) } +// Thread responds with the pprof-formatted thread creation profile. +// The package initialization registers it as /debug/pprof/thread. +func Thread(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + pprof.WriteThreadProfile(w) +} + // Profile responds with the pprof-formatted cpu profile. // The package initialization registers it as /debug/pprof/profile. func Profile(w http.ResponseWriter, r *http.Request) { diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go index e5d0169..165ec36 100644 --- a/libgo/go/net/http/response_test.go +++ b/libgo/go/net/http/response_test.go @@ -321,9 +321,7 @@ func TestReadResponseCloseInMiddle(t *testing.T) { } if test.compressed { buf.WriteString("Content-Encoding: gzip\r\n") - var err error - wr, err = gzip.NewWriter(wr) - checkErr(err, "gzip.NewWriter") + wr = gzip.NewWriter(wr) } buf.WriteString("\r\n") @@ -337,7 +335,7 @@ func TestReadResponseCloseInMiddle(t *testing.T) { wr.Write(chunk) } if test.compressed { - err := wr.(*gzip.Compressor).Close() + err := wr.(*gzip.Writer).Close() checkErr(err, "compressor close") } if test.chunked { diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index 288539b..e715c73 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -59,7 +59,9 @@ type ResponseWriter interface { // Write writes the data to the connection as part of an HTTP reply. // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) - // before writing the data. + // before writing the data. If the Header does not contain a + // Content-Type line, Write adds a Content-Type set to the result of passing + // the initial 512 bytes of written data to DetectContentType. Write([]byte) (int, error) // WriteHeader sends an HTTP response header with status code. @@ -833,11 +835,17 @@ func RedirectHandler(url string, code int) Handler { // redirecting any request containing . or .. elements to an // equivalent .- and ..-free URL. type ServeMux struct { - m map[string]Handler + mu sync.RWMutex + m map[string]muxEntry +} + +type muxEntry struct { + explicit bool + h Handler } // NewServeMux allocates and returns a new ServeMux. -func NewServeMux() *ServeMux { return &ServeMux{make(map[string]Handler)} } +func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = NewServeMux() @@ -883,12 +891,28 @@ func (mux *ServeMux) match(path string) Handler { } if h == nil || len(k) > n { n = len(k) - h = v + h = v.h } } return h } +// handler returns the handler to use for the request r. +func (mux *ServeMux) handler(r *Request) Handler { + mux.mu.RLock() + defer mux.mu.RUnlock() + + // Host-specific pattern takes precedence over generic ones + h := mux.match(r.Host + r.URL.Path) + if h == nil { + h = mux.match(r.URL.Path) + } + if h == nil { + h = NotFoundHandler() + } + return h +} + // ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { @@ -898,30 +922,33 @@ func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { w.WriteHeader(StatusMovedPermanently) return } - // Host-specific pattern takes precedence over generic ones - h := mux.match(r.Host + r.URL.Path) - if h == nil { - h = mux.match(r.URL.Path) - } - if h == nil { - h = NotFoundHandler() - } - h.ServeHTTP(w, r) + mux.handler(r).ServeHTTP(w, r) } // Handle registers the handler for the given pattern. +// If a handler already exists for pattern, Handle panics. func (mux *ServeMux) Handle(pattern string, handler Handler) { + mux.mu.Lock() + defer mux.mu.Unlock() + if pattern == "" { panic("http: invalid pattern " + pattern) } + if handler == nil { + panic("http: nil handler") + } + if mux.m[pattern].explicit { + panic("http: multiple registrations for " + pattern) + } - mux.m[pattern] = handler + mux.m[pattern] = muxEntry{explicit: true, h: handler} // Helpful behavior: - // If pattern is /tree/, insert permanent redirect for /tree. + // If pattern is /tree/, insert an implicit permanent redirect for /tree. + // It can be overridden by an explicit registration. n := len(pattern) - if n > 0 && pattern[n-1] == '/' { - mux.m[pattern[0:n-1]] = RedirectHandler(pattern, StatusMovedPermanently) + if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { + mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(pattern, StatusMovedPermanently)} } } @@ -980,15 +1007,26 @@ func (srv *Server) ListenAndServe() error { // then call srv.Handler to reply to them. func (srv *Server) Serve(l net.Listener) error { defer l.Close() + var tempDelay time.Duration // how long to sleep on accept failure for { rw, e := l.Accept() if e != nil { if ne, ok := e.(net.Error); ok && ne.Temporary() { - log.Printf("http: Accept error: %v", e) + if tempDelay == 0 { + tempDelay = 5 * time.Millisecond + } else { + tempDelay *= 2 + } + if max := 1 * time.Second; tempDelay > max { + tempDelay = max + } + log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay) + time.Sleep(tempDelay) continue } return e } + tempDelay = 0 if srv.ReadTimeout != 0 { rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) } diff --git a/libgo/go/net/http/sniff.go b/libgo/go/net/http/sniff.go index c1c78e2..68f519b 100644 --- a/libgo/go/net/http/sniff.go +++ b/libgo/go/net/http/sniff.go @@ -9,15 +9,15 @@ import ( "encoding/binary" ) -// Content-type sniffing algorithm. -// References in this file refer to this draft specification: -// http://mimesniff.spec.whatwg.org/ - -// The algorithm prefers to use sniffLen bytes to make its decision. +// The algorithm uses at most sniffLen bytes to make its decision. const sniffLen = 512 -// DetectContentType returns the sniffed Content-Type string -// for the given data. This function always returns a valid MIME type. +// DetectContentType implements the algorithm described +// at http://mimesniff.spec.whatwg.org/ to determine the +// Content-Type of the given data. It considers at most the +// first 512 bytes of data. DetectContentType always returns +// a valid MIME type: if it cannot determine a more specific one, it +// returns "application/octet-stream". func DetectContentType(data []byte) string { if len(data) > sniffLen { data = data[:sniffLen] diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 693215e..3e48aba 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -85,16 +85,16 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) { if !useProxy(canonicalAddr(req.URL)) { return nil, nil } - proxyURL, err := url.ParseRequest(proxy) + proxyURL, err := url.Parse(proxy) if err != nil { - return nil, errors.New("invalid proxy address") - } - if proxyURL.Host == "" { - proxyURL, err = url.ParseRequest("http://" + proxy) - if err != nil { - return nil, errors.New("invalid proxy address") + if u, err := url.Parse("http://" + proxy); err == nil { + proxyURL = u + err = nil } } + if err != nil { + return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) + } return proxyURL, nil } @@ -235,15 +235,19 @@ func (cm *connectMethod) proxyAuth() string { return "" } -func (t *Transport) putIdleConn(pconn *persistConn) { +// putIdleConn adds pconn to the list of idle persistent connections awaiting +// a new request. +// If pconn is no longer needed or not in a good state, putIdleConn +// returns false. +func (t *Transport) putIdleConn(pconn *persistConn) bool { t.lk.Lock() defer t.lk.Unlock() if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 { pconn.close() - return + return false } if pconn.isBroken() { - return + return false } key := pconn.cacheKey max := t.MaxIdleConnsPerHost @@ -252,9 +256,10 @@ func (t *Transport) putIdleConn(pconn *persistConn) { } if len(t.idleConn[key]) >= max { pconn.close() - return + return false } t.idleConn[key] = append(t.idleConn[key], pconn) + return true } func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) { @@ -565,7 +570,9 @@ func (pc *persistConn) readLoop() { lastbody = resp.Body waitForBodyRead = make(chan bool) resp.Body.(*bodyEOFSignal).fn = func() { - pc.t.putIdleConn(pc) + if !pc.t.putIdleConn(pc) { + alive = false + } waitForBodyRead <- true } } else { @@ -578,7 +585,9 @@ func (pc *persistConn) readLoop() { // read it (even though it'll just be 0, EOF). lastbody = nil - pc.t.putIdleConn(pc) + if !pc.t.putIdleConn(pc) { + alive = false + } } } diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index caf81d6..a36571a 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -16,6 +16,7 @@ import ( . "net/http" "net/http/httptest" "net/url" + "runtime" "strconv" "strings" "testing" @@ -441,11 +442,7 @@ func TestRoundTripGzip(t *testing.T) { } if accept == "gzip" { rw.Header().Set("Content-Encoding", "gzip") - gz, err := gzip.NewWriter(rw) - if err != nil { - t.Errorf("gzip NewWriter: %v", err) - return - } + gz := gzip.NewWriter(rw) gz.Write([]byte(responseBody)) gz.Close() } else { @@ -512,7 +509,7 @@ func TestTransportGzip(t *testing.T) { rw.Header().Set("Content-Length", strconv.Itoa(buf.Len())) }() } - gz, _ := gzip.NewWriter(w) + gz := gzip.NewWriter(w) gz.Write([]byte(testString)) if req.FormValue("body") == "large" { io.CopyN(gz, rand.Reader, nRandBytes) @@ -636,6 +633,70 @@ func TestTransportGzipRecursive(t *testing.T) { } } +// tests that persistent goroutine connections shut down when no longer desired. +func TestTransportPersistConnLeak(t *testing.T) { + gotReqCh := make(chan bool) + unblockCh := make(chan bool) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + gotReqCh <- true + <-unblockCh + w.Header().Set("Content-Length", "0") + w.WriteHeader(204) + })) + defer ts.Close() + + tr := &Transport{} + c := &Client{Transport: tr} + + n0 := runtime.Goroutines() + + const numReq = 25 + didReqCh := make(chan bool) + for i := 0; i < numReq; i++ { + go func() { + res, err := c.Get(ts.URL) + didReqCh <- true + if err != nil { + t.Errorf("client fetch error: %v", err) + return + } + res.Body.Close() + }() + } + + // Wait for all goroutines to be stuck in the Handler. + for i := 0; i < numReq; i++ { + <-gotReqCh + } + + nhigh := runtime.Goroutines() + + // Tell all handlers to unblock and reply. + for i := 0; i < numReq; i++ { + unblockCh <- true + } + + // Wait for all HTTP clients to be done. + for i := 0; i < numReq; i++ { + <-didReqCh + } + + tr.CloseIdleConnections() + time.Sleep(100 * time.Millisecond) + runtime.GC() + runtime.GC() // even more. + nfinal := runtime.Goroutines() + + growth := nfinal - n0 + + // We expect 0 or 1 extra goroutine, empirically. Allow up to 5. + // Previously we were leaking one per numReq. + t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth) + if int(growth) > 5 { + t.Error("too many new goroutines") + } +} + type fooProto struct{} func (fooProto) RoundTrip(req *Request) (*Response, error) { diff --git a/libgo/go/net/http/triv.go b/libgo/go/net/http/triv.go index 994fc0e..c88a0fb 100644 --- a/libgo/go/net/http/triv.go +++ b/libgo/go/net/http/triv.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + package main import ( diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index 3a059f5..4da18a5 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -53,13 +53,13 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { } // favoriteAddrFamily returns the appropriate address family to -// the given net, raddr, laddr and mode. At first it figures +// the given net, laddr, raddr and mode. At first it figures // address family out from the net. If mode indicates "listen" // and laddr.(type).IP is nil, it assumes that the user wants to // make a passive connection with wildcard address family, both // INET and INET6, and wildcard address. Otherwise guess: if the // addresses are IPv4 then returns INET, or else returns INET6. -func favoriteAddrFamily(net string, raddr, laddr sockaddr, mode string) int { +func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) int { switch net[len(net)-1] { case '4': return syscall.AF_INET @@ -68,17 +68,20 @@ func favoriteAddrFamily(net string, raddr, laddr sockaddr, mode string) int { } if mode == "listen" { + // Note that OpenBSD allows neither "net.inet6.ip6.v6only" + // change nor IPPROTO_IPV6 level IPV6_V6ONLY socket option + // setting. switch a := laddr.(type) { case *TCPAddr: - if a.IP == nil && supportsIPv6 { + if a.IP == nil && supportsIPv6 && supportsIPv4map { return syscall.AF_INET6 } case *UDPAddr: - if a.IP == nil && supportsIPv6 { + if a.IP == nil && supportsIPv6 && supportsIPv4map { return syscall.AF_INET6 } case *IPAddr: - if a.IP == nil && supportsIPv6 { + if a.IP == nil && supportsIPv6 && supportsIPv4map { return syscall.AF_INET6 } } @@ -104,7 +107,7 @@ type sockaddr interface { func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { var oserr error var la, ra syscall.Sockaddr - family := favoriteAddrFamily(net, raddr, laddr, mode) + family := favoriteAddrFamily(net, laddr, raddr, mode) if laddr != nil { if la, oserr = laddr.sockaddr(family); oserr != nil { goto Error diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go index 1bc9977..69d7ac8 100644 --- a/libgo/go/net/net_test.go +++ b/libgo/go/net/net_test.go @@ -10,6 +10,7 @@ import ( "regexp" "runtime" "testing" + "time" ) var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors") @@ -173,3 +174,58 @@ func TestShutdown(t *testing.T) { t.Errorf("read = %q, want \"response\"", got) } } + +func TestTCPListenClose(t *testing.T) { + l, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Listen failed: %v", err) + } + + done := make(chan bool, 1) + go func() { + time.Sleep(100 * time.Millisecond) + l.Close() + }() + go func() { + _, err = l.Accept() + if err == nil { + t.Error("Accept succeeded") + } else { + t.Logf("Accept timeout error: %s (any error is fine)", err) + } + done <- true + }() + select { + case <-done: + case <-time.After(2 * time.Second): + t.Fatal("timeout waiting for TCP close") + } +} + +func TestUDPListenClose(t *testing.T) { + l, err := ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Listen failed: %v", err) + } + + buf := make([]byte, 1000) + done := make(chan bool, 1) + go func() { + time.Sleep(100 * time.Millisecond) + l.Close() + }() + go func() { + _, _, err = l.ReadFrom(buf) + if err == nil { + t.Error("ReadFrom succeeded") + } else { + t.Logf("ReadFrom timeout error: %s (any error is fine)", err) + } + done <- true + }() + select { + case <-done: + case <-time.After(2 * time.Second): + t.Fatal("timeout waiting for UDP close") + } +} diff --git a/libgo/go/net/newpollserver.go b/libgo/go/net/newpollserver.go index 06bc24c..d34bb51 100644 --- a/libgo/go/net/newpollserver.go +++ b/libgo/go/net/newpollserver.go @@ -18,16 +18,16 @@ func newPollServer() (s *pollServer, err error) { if s.pr, s.pw, err = os.Pipe(); err != nil { return nil, err } - if err = syscall.SetNonblock(s.pr.Fd(), true); err != nil { + if err = syscall.SetNonblock(int(s.pr.Fd()), true); err != nil { goto Errno } - if err = syscall.SetNonblock(s.pw.Fd(), true); err != nil { + if err = syscall.SetNonblock(int(s.pw.Fd()), true); err != nil { goto Errno } if s.poll, err = newpollster(); err != nil { goto Error } - if _, err = s.poll.AddFD(s.pr.Fd(), 'r', true); err != nil { + if _, err = s.poll.AddFD(int(s.pr.Fd()), 'r', true); err != nil { s.poll.Close() goto Error } diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go index 7f51519..a0d5303 100644 --- a/libgo/go/net/sendfile_linux.go +++ b/libgo/go/net/sendfile_linux.go @@ -38,11 +38,13 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { c.wio.Lock() defer c.wio.Unlock() - c.incref() + if err := c.incref(false); err != nil { + return 0, err, true + } defer c.decref() dst := c.sysfd - src := f.Fd() + src := int(f.Fd()) for remain > 0 { n := maxSendfileSize if int64(n) > remain { @@ -57,8 +59,9 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { break } if err1 == syscall.EAGAIN && c.wdeadline >= 0 { - pollserver.WaitWrite(c) - continue + if err1 = pollserver.WaitWrite(c); err1 == nil { + continue + } } if err1 != nil { // This includes syscall.ENOSYS (no kernel diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go index ee7ff8b..f5a6d88 100644 --- a/libgo/go/net/sendfile_windows.go +++ b/libgo/go/net/sendfile_windows.go @@ -50,13 +50,15 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { c.wio.Lock() defer c.wio.Unlock() - c.incref() + if err := c.incref(false); err != nil { + return 0, err, true + } defer c.decref() var o sendfileOp o.Init(c, 'w') o.n = uint32(n) - o.src = f.Fd() + o.src = syscall.Handle(f.Fd()) done, err := iosrv.ExecIO(&o, 0) if err != nil { return 0, err, false diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index b0b546b..5569149 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -83,7 +83,7 @@ func connect(t *testing.T, network, addr string, isEmpty bool) { } // Send explicit ending for unixpacket. - // Older Linux kernels do stop reads on close. + // Older Linux kernels do not stop reads on close. if network == "unixpacket" { fd.Write([]byte("END")) } @@ -115,16 +115,13 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) { } func TestTCPServer(t *testing.T) { - if runtime.GOOS != "openbsd" { - doTest(t, "tcp", "", "127.0.0.1") - } + doTest(t, "tcp", "", "127.0.0.1") doTest(t, "tcp", "0.0.0.0", "127.0.0.1") doTest(t, "tcp", "127.0.0.1", "127.0.0.1") doTest(t, "tcp4", "", "127.0.0.1") doTest(t, "tcp4", "0.0.0.0", "127.0.0.1") doTest(t, "tcp4", "127.0.0.1", "127.0.0.1") if supportsIPv6 { - doTest(t, "tcp", "", "[::1]") doTest(t, "tcp", "[::]", "[::1]") doTest(t, "tcp", "[::1]", "[::1]") doTest(t, "tcp6", "", "[::1]") diff --git a/libgo/go/net/sock_bsd.go b/libgo/go/net/sock_bsd.go index 7c693a2..2607b04 100644 --- a/libgo/go/net/sock_bsd.go +++ b/libgo/go/net/sock_bsd.go @@ -38,6 +38,11 @@ func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockadd return la, nil } switch v := a.(type) { + case *TCPAddr, *UnixAddr: + err := setDefaultListenerSockopts(s) + if err != nil { + return nil, err + } case *UDPAddr: if v.IP.IsMulticast() { err := setDefaultMulticastSockopts(s) diff --git a/libgo/go/net/sock_linux.go b/libgo/go/net/sock_linux.go index 0743843..e509d93 100644 --- a/libgo/go/net/sock_linux.go +++ b/libgo/go/net/sock_linux.go @@ -32,6 +32,11 @@ func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockadd return la, nil } switch v := a.(type) { + case *TCPAddr, *UnixAddr: + err := setDefaultListenerSockopts(s) + if err != nil { + return nil, err + } case *UDPAddr: if v.IP.IsMulticast() { err := setDefaultMulticastSockopts(s) diff --git a/libgo/go/net/sock_windows.go b/libgo/go/net/sock_windows.go index 434122c..cce6181 100644 --- a/libgo/go/net/sock_windows.go +++ b/libgo/go/net/sock_windows.go @@ -19,6 +19,11 @@ func listenerSockaddr(s syscall.Handle, f int, la syscall.Sockaddr, toAddr func( return la, nil } switch v := a.(type) { + case *TCPAddr, *UnixAddr: + err := setDefaultListenerSockopts(s) + if err != nil { + return nil, err + } case *UDPAddr: if v.IP.IsMulticast() { err := setDefaultMulticastSockopts(s) diff --git a/libgo/go/net/sockopt.go b/libgo/go/net/sockopt.go index b5b75a2..0a051d7 100644 --- a/libgo/go/net/sockopt.go +++ b/libgo/go/net/sockopt.go @@ -105,13 +105,17 @@ done: } func setReadBuffer(fd *netFD, bytes int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)) } func setWriteBuffer(fd *netFD, bytes int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)) } @@ -142,25 +146,33 @@ func setDeadline(fd *netFD, t time.Time) error { } func setReuseAddr(fd *netFD, reuse bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse))) } func setDontRoute(fd *netFD, dontroute bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute))) } func setKeepAlive(fd *netFD, keepalive bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))) } func setNoDelay(fd *netFD, noDelay bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))) } @@ -174,7 +186,9 @@ func setLinger(fd *netFD, sec int) error { l.Onoff = 0 l.Linger = 0 } - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l)) } diff --git a/libgo/go/net/sockopt_bsd.go b/libgo/go/net/sockopt_bsd.go index bc76465..79e0e57 100644 --- a/libgo/go/net/sockopt_bsd.go +++ b/libgo/go/net/sockopt_bsd.go @@ -20,31 +20,20 @@ func setDefaultSockopts(s, f, t int) error { // Note that some operating systems never admit this option. syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) } - - if f == syscall.AF_UNIX || - (f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM { - // Allow reuse of recently-used addresses. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - - // Allow reuse of recently-used ports. - // This option is supported only in descendants of 4.4BSD, - // to make an effective multicast application and an application - // that requires quick draw possible. - err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - } - // Allow broadcast. err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) if err != nil { return os.NewSyscallError("setsockopt", err) } + return nil +} +func setDefaultListenerSockopts(s int) error { + // Allow reuse of recently-used addresses. + err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } return nil } @@ -55,6 +44,10 @@ func setDefaultMulticastSockopts(s int) error { if err != nil { return os.NewSyscallError("setsockopt", err) } + // Allow reuse of recently-used ports. + // This option is supported only in descendants of 4.4BSD, + // to make an effective multicast application that requires + // quick draw possible. err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1) if err != nil { return os.NewSyscallError("setsockopt", err) diff --git a/libgo/go/net/sockopt_linux.go b/libgo/go/net/sockopt_linux.go index 67c1dc8..7509c29 100644 --- a/libgo/go/net/sockopt_linux.go +++ b/libgo/go/net/sockopt_linux.go @@ -18,23 +18,20 @@ func setDefaultSockopts(s, f, t int) error { // Note that some operating systems never admit this option. syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) } - - if f == syscall.AF_UNIX || - (f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM { - // Allow reuse of recently-used addresses. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - - } - // Allow broadcast. err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) if err != nil { return os.NewSyscallError("setsockopt", err) } + return nil +} +func setDefaultListenerSockopts(s int) error { + // Allow reuse of recently-used addresses. + err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } return nil } diff --git a/libgo/go/net/sockopt_windows.go b/libgo/go/net/sockopt_windows.go index 842bccc..b18af67 100644 --- a/libgo/go/net/sockopt_windows.go +++ b/libgo/go/net/sockopt_windows.go @@ -18,16 +18,18 @@ func setDefaultSockopts(s syscall.Handle, f, t int) error { // Note that some operating systems never admit this option. syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) } + // Allow broadcast. + syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) + return nil +} +func setDefaultListenerSockopts(s syscall.Handle) error { // Windows will reuse recently-used addresses by default. // SO_REUSEADDR should not be used here, as it allows // a socket to forcibly bind to a port in use by another socket. // This could lead to a non-deterministic behavior, where // connection requests over the port cannot be guaranteed // to be handled by the correct socket. - - // Allow broadcast. - syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) return nil } diff --git a/libgo/go/net/sockoptip.go b/libgo/go/net/sockoptip.go index 90b6f75..1fcad40 100644 --- a/libgo/go/net/sockoptip.go +++ b/libgo/go/net/sockoptip.go @@ -14,17 +14,21 @@ import ( ) func ipv4TOS(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS) if err != nil { - return -1, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv4TOS(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS, v) if err != nil { @@ -34,17 +38,21 @@ func setIPv4TOS(fd *netFD, v int) error { } func ipv4TTL(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL) if err != nil { - return -1, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv4TTL(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL, v) if err != nil { @@ -58,7 +66,9 @@ func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { if err := setIPv4MreqToInterface(mreq, ifi); err != nil { return err } - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) } @@ -68,23 +78,29 @@ func leaveIPv4Group(fd *netFD, ifi *Interface, ip IP) error { if err := setIPv4MreqToInterface(mreq, ifi); err != nil { return err } - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) } func ipv6HopLimit(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS) if err != nil { - return -1, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv6HopLimit(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, v) if err != nil { @@ -94,7 +110,9 @@ func setIPv6HopLimit(fd *netFD, v int) error { } func ipv6MulticastInterface(fd *netFD) (*Interface, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return nil, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF) if err != nil { @@ -115,7 +133,9 @@ func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { if ifi != nil { v = ifi.Index } - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v) if err != nil { @@ -125,17 +145,21 @@ func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { } func ipv6MulticastHopLimit(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS) if err != nil { - return -1, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv6MulticastHopLimit(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS, v) if err != nil { @@ -145,7 +169,9 @@ func setIPv6MulticastHopLimit(fd *netFD, v int) error { } func ipv6MulticastLoopback(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP) if err != nil { @@ -155,7 +181,9 @@ func ipv6MulticastLoopback(fd *netFD) (bool, error) { } func setIPv6MulticastLoopback(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v)) if err != nil { @@ -170,7 +198,9 @@ func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { if ifi != nil { mreq.Interface = uint32(ifi.Index) } - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)) } @@ -181,7 +211,9 @@ func leaveIPv6Group(fd *netFD, ifi *Interface, ip IP) error { if ifi != nil { mreq.Interface = uint32(ifi.Index) } - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq)) } diff --git a/libgo/go/net/sockoptip_bsd.go b/libgo/go/net/sockoptip_bsd.go index 5f7dff2..19e2b14 100644 --- a/libgo/go/net/sockoptip_bsd.go +++ b/libgo/go/net/sockoptip_bsd.go @@ -14,17 +14,21 @@ import ( ) func ipv4MulticastTTL(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL) if err != nil { - return -1, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil } func setIPv4MulticastTTL(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, byte(v)) if err != nil { @@ -34,17 +38,21 @@ func setIPv4MulticastTTL(fd *netFD, v int) error { } func ipv6TrafficClass(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS) if err != nil { - return -1, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv6TrafficClass(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v) if err != nil { diff --git a/libgo/go/net/sockoptip_darwin.go b/libgo/go/net/sockoptip_darwin.go index dedfd6f..52b237c 100644 --- a/libgo/go/net/sockoptip_darwin.go +++ b/libgo/go/net/sockoptip_darwin.go @@ -12,7 +12,9 @@ import ( ) func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return nil, err + } defer fd.decref() a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) if err != nil { @@ -28,7 +30,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } var x [4]byte copy(x[:], ip.To4()) - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x) if err != nil { @@ -38,7 +42,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } func ipv4MulticastLoopback(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) if err != nil { @@ -48,7 +54,9 @@ func ipv4MulticastLoopback(fd *netFD) (bool, error) { } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) if err != nil { @@ -58,7 +66,9 @@ func setIPv4MulticastLoopback(fd *netFD, v bool) error { } func ipv4ReceiveInterface(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF) if err != nil { @@ -68,7 +78,9 @@ func ipv4ReceiveInterface(fd *netFD) (bool, error) { } func setIPv4ReceiveInterface(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v)) if err != nil { diff --git a/libgo/go/net/sockoptip_freebsd.go b/libgo/go/net/sockoptip_freebsd.go index 55f7b1a..4a3bc2e 100644 --- a/libgo/go/net/sockoptip_freebsd.go +++ b/libgo/go/net/sockoptip_freebsd.go @@ -12,7 +12,9 @@ import ( ) func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return nil, err + } defer fd.decref() mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) if err != nil { @@ -30,7 +32,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { v = int32(ifi.Index) } mreq := &syscall.IPMreqn{Ifindex: v} - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq) if err != nil { @@ -40,7 +44,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } func ipv4MulticastLoopback(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) if err != nil { @@ -50,7 +56,9 @@ func ipv4MulticastLoopback(fd *netFD) (bool, error) { } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) if err != nil { @@ -60,7 +68,9 @@ func setIPv4MulticastLoopback(fd *netFD, v bool) error { } func ipv4ReceiveInterface(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF) if err != nil { @@ -70,7 +80,9 @@ func ipv4ReceiveInterface(fd *netFD) (bool, error) { } func setIPv4ReceiveInterface(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v)) if err != nil { diff --git a/libgo/go/net/sockoptip_linux.go b/libgo/go/net/sockoptip_linux.go index 360f8de..169718f 100644 --- a/libgo/go/net/sockoptip_linux.go +++ b/libgo/go/net/sockoptip_linux.go @@ -12,7 +12,9 @@ import ( ) func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return nil, err + } defer fd.decref() mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) if err != nil { @@ -30,7 +32,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { v = int32(ifi.Index) } mreq := &syscall.IPMreqn{Ifindex: v} - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq) if err != nil { @@ -40,7 +44,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } func ipv4MulticastTTL(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL) if err != nil { @@ -50,7 +56,9 @@ func ipv4MulticastTTL(fd *netFD) (int, error) { } func setIPv4MulticastTTL(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v) if err != nil { @@ -60,7 +68,9 @@ func setIPv4MulticastTTL(fd *netFD, v int) error { } func ipv4MulticastLoopback(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) if err != nil { @@ -70,7 +80,9 @@ func ipv4MulticastLoopback(fd *netFD) (bool, error) { } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) if err != nil { @@ -80,7 +92,9 @@ func setIPv4MulticastLoopback(fd *netFD, v bool) error { } func ipv4ReceiveInterface(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO) if err != nil { @@ -90,7 +104,9 @@ func ipv4ReceiveInterface(fd *netFD) (bool, error) { } func setIPv4ReceiveInterface(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO, boolint(v)) if err != nil { @@ -100,17 +116,21 @@ func setIPv4ReceiveInterface(fd *netFD, v bool) error { } func ipv6TrafficClass(fd *netFD) (int, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return 0, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS) if err != nil { - return -1, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv6TrafficClass(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v) if err != nil { diff --git a/libgo/go/net/sockoptip_openbsd.go b/libgo/go/net/sockoptip_openbsd.go index 89b8e45..f3e42f1 100644 --- a/libgo/go/net/sockoptip_openbsd.go +++ b/libgo/go/net/sockoptip_openbsd.go @@ -12,7 +12,9 @@ import ( ) func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return nil, err + } defer fd.decref() a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) if err != nil { @@ -28,7 +30,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } var x [4]byte copy(x[:], ip.To4()) - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x) if err != nil { @@ -38,7 +42,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } func ipv4MulticastLoopback(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) if err != nil { @@ -48,7 +54,9 @@ func ipv4MulticastLoopback(fd *netFD) (bool, error) { } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))) if err != nil { @@ -58,7 +66,9 @@ func setIPv4MulticastLoopback(fd *netFD, v bool) error { } func ipv4ReceiveInterface(fd *netFD) (bool, error) { - fd.incref() + if err := fd.incref(false); err != nil { + return false, err + } defer fd.decref() v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF) if err != nil { @@ -68,7 +78,9 @@ func ipv4ReceiveInterface(fd *netFD) (bool, error) { } func setIPv4ReceiveInterface(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v)) if err != nil { diff --git a/libgo/go/net/sockoptip_windows.go b/libgo/go/net/sockoptip_windows.go index a8a9d1c..b9db333 100644 --- a/libgo/go/net/sockoptip_windows.go +++ b/libgo/go/net/sockoptip_windows.go @@ -23,7 +23,9 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } var x [4]byte copy(x[:], ip.To4()) - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x) if err != nil { @@ -38,7 +40,9 @@ func ipv4MulticastTTL(fd *netFD) (int, error) { } func setIPv4MulticastTTL(fd *netFD, v int) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v) if err != nil { @@ -54,7 +58,9 @@ func ipv4MulticastLoopback(fd *netFD) (bool, error) { } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - fd.incref() + if err := fd.incref(false); err != nil { + return err + } defer fd.decref() err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) if err != nil { diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index 51a5d6f..200ce91 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -227,13 +227,43 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { if raddr == nil { return nil, &OpError{"dial", net, nil, errMissingAddress} } + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + + // TCP has a rarely used mechanism called a 'simultaneous connection' in + // which Dial("tcp", addr1, addr2) run on the machine at addr1 can + // connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine + // at addr2, without either machine executing Listen. If laddr == nil, + // it means we want the kernel to pick an appropriate originating local + // address. Some Linux kernels cycle blindly through a fixed range of + // local ports, regardless of destination port. If a kernel happens to + // pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"), + // then the Dial will succeed, having simultaneously connected to itself. + // This can only happen when we are letting the kernel pick a port (laddr == nil) + // and when there is no listener for the destination address. + // It's hard to argue this is anything other than a kernel bug. If we + // see this happen, rather than expose the buggy effect to users, we + // close the fd and try again. If it happens twice more, we relent and + // use the result. See also: + // http://golang.org/issue/2690 + // http://stackoverflow.com/questions/4949858/ + for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ { + fd.Close() + fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + } + if err != nil { return nil, err } return newTCPConn(fd), nil } +func selfConnect(fd *netFD) bool { + l := fd.laddr.(*TCPAddr) + r := fd.raddr.(*TCPAddr) + return l.Port == r.Port && l.IP.Equal(r.IP) +} + // TCPListener is a TCP network listener. // Clients should typically use variables of type Listener // instead of assuming TCP. |