aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-03-02 16:38:43 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-03-02 16:38:43 +0000
commitcbb6491d76c7aa81cdf5d3b3a81386129c5e2fce (patch)
treeefa0c55763b34cbc633bc494c2743d1b5d9aaff3 /libgo/go/net
parentff2f581b00ac6759f6366c16ef902c935163aa13 (diff)
downloadgcc-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')
-rw-r--r--libgo/go/net/dial.go2
-rw-r--r--libgo/go/net/dial_test.go53
-rw-r--r--libgo/go/net/fd.go246
-rw-r--r--libgo/go/net/fd_windows.go66
-rw-r--r--libgo/go/net/file.go2
-rw-r--r--libgo/go/net/http/cgi/host_test.go3
-rw-r--r--libgo/go/net/http/doc.go4
-rw-r--r--libgo/go/net/http/fcgi/child.go4
-rw-r--r--libgo/go/net/http/fs.go211
-rw-r--r--libgo/go/net/http/fs_test.go145
-rw-r--r--libgo/go/net/http/httptest/server.go4
-rw-r--r--libgo/go/net/http/httptest/server_test.go29
-rw-r--r--libgo/go/net/http/pprof/pprof.go22
-rw-r--r--libgo/go/net/http/response_test.go6
-rw-r--r--libgo/go/net/http/server.go74
-rw-r--r--libgo/go/net/http/sniff.go14
-rw-r--r--libgo/go/net/http/transport.go35
-rw-r--r--libgo/go/net/http/transport_test.go73
-rw-r--r--libgo/go/net/http/triv.go2
-rw-r--r--libgo/go/net/ipsock_posix.go15
-rw-r--r--libgo/go/net/net_test.go56
-rw-r--r--libgo/go/net/newpollserver.go6
-rw-r--r--libgo/go/net/sendfile_linux.go11
-rw-r--r--libgo/go/net/sendfile_windows.go6
-rw-r--r--libgo/go/net/server_test.go7
-rw-r--r--libgo/go/net/sock_bsd.go5
-rw-r--r--libgo/go/net/sock_linux.go5
-rw-r--r--libgo/go/net/sock_windows.go5
-rw-r--r--libgo/go/net/sockopt.go28
-rw-r--r--libgo/go/net/sockopt_bsd.go31
-rw-r--r--libgo/go/net/sockopt_linux.go19
-rw-r--r--libgo/go/net/sockopt_windows.go8
-rw-r--r--libgo/go/net/sockoptip.go72
-rw-r--r--libgo/go/net/sockoptip_bsd.go20
-rw-r--r--libgo/go/net/sockoptip_darwin.go24
-rw-r--r--libgo/go/net/sockoptip_freebsd.go24
-rw-r--r--libgo/go/net/sockoptip_linux.go42
-rw-r--r--libgo/go/net/sockoptip_openbsd.go24
-rw-r--r--libgo/go/net/sockoptip_windows.go12
-rw-r--r--libgo/go/net/tcpsock_posix.go30
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.