aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-11-21 07:03:38 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-11-21 07:03:38 +0000
commitfabcaa8df3d6eb852b87821ef090d31d222870b7 (patch)
tree72455aea0286937aa08cc141e5efc800e4626577 /libgo/go/net
parenta51fb17f48428e7cfc96a72a9f9f87901363bb6b (diff)
downloadgcc-fabcaa8df3d6eb852b87821ef090d31d222870b7.zip
gcc-fabcaa8df3d6eb852b87821ef090d31d222870b7.tar.gz
gcc-fabcaa8df3d6eb852b87821ef090d31d222870b7.tar.bz2
libgo: Update to current version of master library.
From-SVN: r193688
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/conn_test.go103
-rw-r--r--libgo/go/net/dial.go77
-rw-r--r--libgo/go/net/dial_test.go80
-rw-r--r--libgo/go/net/dialgoogle_test.go7
-rw-r--r--libgo/go/net/dnsclient.go7
-rw-r--r--libgo/go/net/dnsmsg.go2
-rw-r--r--libgo/go/net/fd_plan9.go115
-rw-r--r--libgo/go/net/fd_unix.go51
-rw-r--r--libgo/go/net/fd_unix_test.go60
-rw-r--r--libgo/go/net/fd_windows.go183
-rw-r--r--libgo/go/net/http/cgi/testdata/test.cgi63
-rw-r--r--libgo/go/net/http/chunked.go13
-rw-r--r--libgo/go/net/http/client.go50
-rw-r--r--libgo/go/net/http/client_test.go12
-rw-r--r--libgo/go/net/http/cookie.go2
-rw-r--r--libgo/go/net/http/cookie_test.go2
-rw-r--r--libgo/go/net/http/fs_test.go2
-rw-r--r--libgo/go/net/http/httputil/chunked.go13
-rw-r--r--libgo/go/net/http/jar.go17
-rw-r--r--libgo/go/net/http/proxy_test.go2
-rw-r--r--libgo/go/net/http/request_test.go2
-rw-r--r--libgo/go/net/http/serve_test.go10
-rw-r--r--libgo/go/net/http/server.go40
-rw-r--r--libgo/go/net/http/transfer.go13
-rw-r--r--libgo/go/net/http/transfer_test.go37
-rw-r--r--libgo/go/net/http/transport.go29
-rw-r--r--libgo/go/net/ipraw_test.go32
-rw-r--r--libgo/go/net/iprawsock.go14
-rw-r--r--libgo/go/net/iprawsock_plan9.go4
-rw-r--r--libgo/go/net/iprawsock_posix.go13
-rw-r--r--libgo/go/net/ipsock.go13
-rw-r--r--libgo/go/net/ipsock_plan9.go197
-rw-r--r--libgo/go/net/ipsock_posix.go9
-rw-r--r--libgo/go/net/lookup.go46
-rw-r--r--libgo/go/net/lookup_plan9.go15
-rw-r--r--libgo/go/net/lookup_test.go14
-rw-r--r--libgo/go/net/lookup_unix.go13
-rw-r--r--libgo/go/net/lookup_windows.go50
-rw-r--r--libgo/go/net/multicast_posix_test.go23
-rw-r--r--libgo/go/net/net.go101
-rw-r--r--libgo/go/net/net_posix.go110
-rw-r--r--libgo/go/net/net_test.go39
-rw-r--r--libgo/go/net/packetconn_test.go161
-rw-r--r--libgo/go/net/protoconn_test.go372
-rw-r--r--libgo/go/net/rpc/client.go2
-rw-r--r--libgo/go/net/rpc/server.go67
-rw-r--r--libgo/go/net/rpc/server_test.go38
-rw-r--r--libgo/go/net/sendfile_windows.go4
-rw-r--r--libgo/go/net/sock_posix.go26
-rw-r--r--libgo/go/net/tcp_test.go30
-rw-r--r--libgo/go/net/tcpsock.go8
-rw-r--r--libgo/go/net/tcpsock_plan9.go127
-rw-r--r--libgo/go/net/tcpsock_posix.go60
-rw-r--r--libgo/go/net/textproto/reader.go85
-rw-r--r--libgo/go/net/textproto/reader_test.go83
-rw-r--r--libgo/go/net/timeout_test.go111
-rw-r--r--libgo/go/net/udp_test.go30
-rw-r--r--libgo/go/net/udpsock.go11
-rw-r--r--libgo/go/net/udpsock_plan9.go44
-rw-r--r--libgo/go/net/udpsock_posix.go17
-rw-r--r--libgo/go/net/unicast_posix_test.go6
-rw-r--r--libgo/go/net/unixsock_plan9.go4
-rw-r--r--libgo/go/net/unixsock_posix.go18
63 files changed, 2306 insertions, 683 deletions
diff --git a/libgo/go/net/conn_test.go b/libgo/go/net/conn_test.go
new file mode 100644
index 0000000..037ce80
--- /dev/null
+++ b/libgo/go/net/conn_test.go
@@ -0,0 +1,103 @@
+// 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 net_test
+
+import (
+ "net"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+)
+
+var connTests = []struct {
+ net string
+ addr string
+}{
+ {"tcp", "127.0.0.1:0"},
+ {"unix", "/tmp/gotest.net"},
+ {"unixpacket", "/tmp/gotest.net"},
+}
+
+func TestConnAndListener(t *testing.T) {
+ for _, tt := range connTests {
+ switch tt.net {
+ case "unix", "unixpacket":
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ continue
+ }
+ if tt.net == "unixpacket" && runtime.GOOS != "linux" {
+ continue
+ }
+ os.Remove(tt.addr)
+ }
+
+ ln, err := net.Listen(tt.net, tt.addr)
+ if err != nil {
+ t.Errorf("net.Listen failed: %v", err)
+ return
+ }
+ ln.Addr()
+ defer ln.Close()
+
+ done := make(chan int)
+ go transponder(t, ln, done)
+
+ c, err := net.Dial(tt.net, ln.Addr().String())
+ if err != nil {
+ t.Errorf("net.Dial failed: %v", err)
+ return
+ }
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c.Close()
+
+ if _, err := c.Write([]byte("CONN TEST")); err != nil {
+ t.Errorf("net.Conn.Write failed: %v", err)
+ return
+ }
+ rb := make([]byte, 128)
+ if _, err := c.Read(rb); err != nil {
+ t.Errorf("net.Conn.Read failed: %v", err)
+ }
+
+ <-done
+ switch tt.net {
+ case "unix", "unixpacket":
+ os.Remove(tt.addr)
+ }
+ }
+}
+
+func transponder(t *testing.T, ln net.Listener, done chan<- int) {
+ defer func() { done <- 1 }()
+
+ c, err := ln.Accept()
+ if err != nil {
+ t.Errorf("net.Listener.Accept failed: %v", err)
+ return
+ }
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c.Close()
+
+ b := make([]byte, 128)
+ n, err := c.Read(b)
+ if err != nil {
+ t.Errorf("net.Conn.Read failed: %v", err)
+ return
+ }
+ if _, err := c.Write(b[:n]); err != nil {
+ t.Errorf("net.Conn.Write failed: %v", err)
+ return
+ }
+}
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go
index 752f81b..a85e3c6 100644
--- a/libgo/go/net/dial.go
+++ b/libgo/go/net/dial.go
@@ -5,6 +5,7 @@
package net
import (
+ "runtime"
"time"
)
@@ -36,7 +37,7 @@ func parseDialNetwork(net string) (afnet string, proto int, err error) {
return "", 0, UnknownNetworkError(net)
}
-func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
+func resolveNetAddr(op, net, addr string, deadline time.Time) (afnet string, a Addr, err error) {
afnet, _, err = parseDialNetwork(net)
if err != nil {
return "", nil, &OpError{op, net, nil, err}
@@ -44,25 +45,25 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
if op == "dial" && addr == "" {
return "", nil, &OpError{op, net, nil, errMissingAddress}
}
+ a, err = resolveAfnetAddr(afnet, addr, deadline)
+ return
+}
+
+func resolveAfnetAddr(afnet, addr string, deadline time.Time) (Addr, error) {
+ if addr == "" {
+ return nil, nil
+ }
switch afnet {
case "tcp", "tcp4", "tcp6":
- if addr != "" {
- a, err = ResolveTCPAddr(afnet, addr)
- }
+ return resolveTCPAddr(afnet, addr, deadline)
case "udp", "udp4", "udp6":
- if addr != "" {
- a, err = ResolveUDPAddr(afnet, addr)
- }
+ return resolveUDPAddr(afnet, addr, deadline)
case "ip", "ip4", "ip6":
- if addr != "" {
- a, err = ResolveIPAddr(afnet, addr)
- }
+ return resolveIPAddr(afnet, addr, deadline)
case "unix", "unixgram", "unixpacket":
- if addr != "" {
- a, err = ResolveUnixAddr(afnet, addr)
- }
+ return ResolveUnixAddr(afnet, addr)
}
- return
+ return nil, nil
}
// Dial connects to the address addr on the network net.
@@ -89,23 +90,23 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
// Dial("ip6:ospf", "::1")
//
func Dial(net, addr string) (Conn, error) {
- _, addri, err := resolveNetAddr("dial", net, addr)
+ _, addri, err := resolveNetAddr("dial", net, addr, noDeadline)
if err != nil {
return nil, err
}
- return dialAddr(net, addr, addri)
+ return dialAddr(net, addr, addri, noDeadline)
}
-func dialAddr(net, addr string, addri Addr) (c Conn, err error) {
+func dialAddr(net, addr string, addri Addr, deadline time.Time) (c Conn, err error) {
switch ra := addri.(type) {
case *TCPAddr:
- c, err = DialTCP(net, nil, ra)
+ c, err = dialTCP(net, nil, ra, deadline)
case *UDPAddr:
- c, err = DialUDP(net, nil, ra)
+ c, err = dialUDP(net, nil, ra, deadline)
case *IPAddr:
- c, err = DialIP(net, nil, ra)
+ c, err = dialIP(net, nil, ra, deadline)
case *UnixAddr:
- c, err = DialUnix(net, nil, ra)
+ c, err = dialUnix(net, nil, ra, deadline)
default:
err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)}
}
@@ -115,13 +116,31 @@ func dialAddr(net, addr string, addri Addr) (c Conn, err error) {
return
}
+const useDialTimeoutRace = runtime.GOOS == "windows" || runtime.GOOS == "plan9"
+
// DialTimeout acts like Dial but takes a timeout.
// The timeout includes name resolution, if required.
func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
- // TODO(bradfitz): the timeout should be pushed down into the
- // net package's event loop, so on timeout to dead hosts we
- // don't have a goroutine sticking around for the default of
- // ~3 minutes.
+ if useDialTimeoutRace {
+ // On windows and plan9, use the relatively inefficient
+ // goroutine-racing implementation of DialTimeout that
+ // doesn't push down deadlines to the pollster.
+ // TODO: remove this once those are implemented.
+ return dialTimeoutRace(net, addr, timeout)
+ }
+ deadline := time.Now().Add(timeout)
+ _, addri, err := resolveNetAddr("dial", net, addr, deadline)
+ if err != nil {
+ return nil, err
+ }
+ return dialAddr(net, addr, addri, deadline)
+}
+
+// dialTimeoutRace is the old implementation of DialTimeout, still used
+// on operating systems where the deadline hasn't been pushed down
+// into the pollserver.
+// TODO: fix this on Windows and plan9.
+func dialTimeoutRace(net, addr string, timeout time.Duration) (Conn, error) {
t := time.NewTimer(timeout)
defer t.Stop()
type pair struct {
@@ -131,13 +150,13 @@ func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
ch := make(chan pair, 1)
resolvedAddr := make(chan Addr, 1)
go func() {
- _, addri, err := resolveNetAddr("dial", net, addr)
+ _, addri, err := resolveNetAddr("dial", net, addr, noDeadline)
if err != nil {
ch <- pair{nil, err}
return
}
resolvedAddr <- addri // in case we need it for OpError
- c, err := dialAddr(net, addr, addri)
+ c, err := dialAddr(net, addr, addri, noDeadline)
ch <- pair{c, err}
}()
select {
@@ -175,7 +194,7 @@ func (a stringAddr) String() string { return a.addr }
// The network string net must be a stream-oriented network:
// "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
func Listen(net, laddr string) (Listener, error) {
- afnet, a, err := resolveNetAddr("listen", net, laddr)
+ afnet, a, err := resolveNetAddr("listen", net, laddr, noDeadline)
if err != nil {
return nil, err
}
@@ -200,7 +219,7 @@ func Listen(net, laddr string) (Listener, error) {
// The network string net must be a packet-oriented network:
// "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram".
func ListenPacket(net, addr string) (PacketConn, error) {
- afnet, a, err := resolveNetAddr("listen", net, addr)
+ afnet, a, err := resolveNetAddr("listen", net, addr, noDeadline)
if err != nil {
return nil, err
}
diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go
index abd6b7f..865dd62 100644
--- a/libgo/go/net/dial_test.go
+++ b/libgo/go/net/dial_test.go
@@ -7,6 +7,8 @@ package net
import (
"flag"
"fmt"
+ "io"
+ "os"
"regexp"
"runtime"
"testing"
@@ -55,7 +57,7 @@ func TestDialTimeout(t *testing.T) {
// on our 386 builder, this Dial succeeds, connecting
// to an IIS web server somewhere. The data center
// or VM or firewall must be stealing the TCP connection.
- //
+ //
// IANA Service Name and Transport Protocol Port Number Registry
// <http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml>
go func() {
@@ -222,3 +224,79 @@ func TestDialError(t *testing.T) {
}
}
}
+
+func TestDialTimeoutFDLeak(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ // TODO(bradfitz): test on other platforms
+ t.Logf("skipping test on %s", runtime.GOOS)
+ return
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+
+ type connErr struct {
+ conn Conn
+ err error
+ }
+ dials := listenerBacklog + 100
+ maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows?
+ resc := make(chan connErr)
+ for i := 0; i < dials; i++ {
+ go func() {
+ conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond)
+ resc <- connErr{conn, err}
+ }()
+ }
+
+ var firstErr string
+ var ngood int
+ var toClose []io.Closer
+ for i := 0; i < dials; i++ {
+ ce := <-resc
+ if ce.err == nil {
+ ngood++
+ if ngood > maxGoodConnect {
+ t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect)
+ }
+ toClose = append(toClose, ce.conn)
+ continue
+ }
+ err := ce.err
+ if firstErr == "" {
+ firstErr = err.Error()
+ } else if err.Error() != firstErr {
+ t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err)
+ }
+ }
+ for _, c := range toClose {
+ c.Close()
+ }
+ for i := 0; i < 100; i++ {
+ if got := numFD(); got < dials {
+ // Test passes.
+ return
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ if got := numFD(); got >= dials {
+ t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials)
+ }
+}
+
+func numFD() int {
+ if runtime.GOOS == "linux" {
+ f, err := os.Open("/proc/self/fd")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ names, err := f.Readdirnames(0)
+ if err != nil {
+ panic(err)
+ }
+ return len(names)
+ }
+ // All tests using this should be skipped anyway, but:
+ panic("numFDs not implemented on " + runtime.GOOS)
+}
diff --git a/libgo/go/net/dialgoogle_test.go b/libgo/go/net/dialgoogle_test.go
index 426e2ff..dd3c4ba 100644
--- a/libgo/go/net/dialgoogle_test.go
+++ b/libgo/go/net/dialgoogle_test.go
@@ -116,7 +116,12 @@ func TestDialGoogleIPv6(t *testing.T) {
return
}
// Only run tcp6 if the kernel will take it.
- if !*testIPv6 || !supportsIPv6 {
+ if !supportsIPv6 {
+ t.Logf("skipping test; ipv6 is not supported")
+ return
+ }
+ if !*testIPv6 {
+ t.Logf("test disabled; use -ipv6 to enable")
return
}
diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go
index e69cb31..76b1926 100644
--- a/libgo/go/net/dnsclient.go
+++ b/libgo/go/net/dnsclient.go
@@ -183,7 +183,7 @@ func (s byPriorityWeight) Less(i, j int) bool {
}
// shuffleByWeight shuffles SRV records by weight using the algorithm
-// described in RFC 2782.
+// described in RFC 2782.
func (addrs byPriorityWeight) shuffleByWeight() {
sum := 0
for _, addr := range addrs {
@@ -244,3 +244,8 @@ func (s byPref) sort() {
}
sort.Sort(s)
}
+
+// An NS represents a single DNS NS record.
+type NS struct {
+ Host string
+}
diff --git a/libgo/go/net/dnsmsg.go b/libgo/go/net/dnsmsg.go
index b6ebe11..161afb2 100644
--- a/libgo/go/net/dnsmsg.go
+++ b/libgo/go/net/dnsmsg.go
@@ -618,7 +618,7 @@ func printStruct(any dnsStruct) string {
s += name + "="
switch tag {
case "ipv4":
- i := val.(uint32)
+ i := *val.(*uint32)
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
case "ipv6":
i := val.([]byte)
diff --git a/libgo/go/net/fd_plan9.go b/libgo/go/net/fd_plan9.go
new file mode 100644
index 0000000..6d7ab38
--- /dev/null
+++ b/libgo/go/net/fd_plan9.go
@@ -0,0 +1,115 @@
+// Copyright 2009 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 net
+
+import (
+ "io"
+ "os"
+ "syscall"
+ "time"
+)
+
+// Network file descritor.
+type netFD struct {
+ proto, name, dir string
+ ctl, data *os.File
+ laddr, raddr Addr
+}
+
+var canCancelIO = true // used for testing current package
+
+func sysInit() {
+}
+
+func newFD(proto, name string, ctl *os.File, laddr, raddr Addr) *netFD {
+ return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, nil, laddr, raddr}
+}
+
+func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil }
+
+func (fd *netFD) Read(b []byte) (n int, err error) {
+ if !fd.ok() {
+ return 0, syscall.EINVAL
+ }
+ if fd.data == nil {
+ fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0)
+ if err != nil {
+ return 0, err
+ }
+ }
+ n, err = fd.data.Read(b)
+ if fd.proto == "udp" && err == io.EOF {
+ n = 0
+ err = nil
+ }
+ return
+}
+
+func (fd *netFD) Write(b []byte) (n int, err error) {
+ if !fd.ok() {
+ return 0, syscall.EINVAL
+ }
+ if fd.data == nil {
+ fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0)
+ if err != nil {
+ return 0, err
+ }
+ }
+ return fd.data.Write(b)
+}
+
+func (fd *netFD) CloseRead() error {
+ if !fd.ok() {
+ return syscall.EINVAL
+ }
+ return syscall.EPLAN9
+}
+
+func (fd *netFD) CloseWrite() error {
+ if !fd.ok() {
+ return syscall.EINVAL
+ }
+ return syscall.EPLAN9
+}
+
+func (fd *netFD) Close() error {
+ if !fd.ok() {
+ return syscall.EINVAL
+ }
+ err := fd.ctl.Close()
+ if err != nil {
+ return err
+ }
+ if fd.data != nil {
+ err = fd.data.Close()
+ }
+ fd.ctl = nil
+ fd.data = nil
+ return err
+}
+
+func (fd *netFD) dup() (*os.File, error) {
+ return nil, syscall.EPLAN9
+}
+
+func setDeadline(fd *netFD, t time.Time) error {
+ return syscall.EPLAN9
+}
+
+func setReadDeadline(fd *netFD, t time.Time) error {
+ return syscall.EPLAN9
+}
+
+func setWriteDeadline(fd *netFD, t time.Time) error {
+ return syscall.EPLAN9
+}
+
+func setReadBuffer(fd *netFD, bytes int) error {
+ return syscall.EPLAN9
+}
+
+func setWriteBuffer(fd *netFD, bytes int) error {
+ return syscall.EPLAN9
+}
diff --git a/libgo/go/net/fd_unix.go b/libgo/go/net/fd_unix.go
index c55f336..096ad41 100644
--- a/libgo/go/net/fd_unix.go
+++ b/libgo/go/net/fd_unix.go
@@ -7,7 +7,6 @@
package net
import (
- "errors"
"io"
"os"
"runtime"
@@ -97,15 +96,11 @@ func (s *pollServer) AddFD(fd *netFD, mode int) error {
}
wake, err := s.poll.AddFD(intfd, mode, false)
+ s.Unlock()
if err != nil {
- panic("pollServer AddFD " + err.Error())
- }
- if wake {
- doWakeup = true
+ return &OpError{"addfd", fd.net, fd.laddr, err}
}
- s.Unlock()
-
- if doWakeup {
+ if wake || doWakeup {
s.Wakeup()
}
return nil
@@ -167,7 +162,7 @@ func (s *pollServer) CheckDeadlines() {
// TODO(rsc): This will need to be handled more efficiently,
// probably with a heap indexed by wakeup time.
- var next_deadline int64
+ var nextDeadline int64
for key, fd := range s.pending {
var t int64
var mode int
@@ -192,12 +187,12 @@ func (s *pollServer) CheckDeadlines() {
fd.wdeadline = -1
}
s.WakeFD(fd, mode, nil)
- } else if next_deadline == 0 || t < next_deadline {
- next_deadline = t
+ } else if nextDeadline == 0 || t < nextDeadline {
+ nextDeadline = t
}
}
}
- s.deadline = next_deadline
+ s.deadline = nextDeadline
}
func (s *pollServer) Run() {
@@ -265,7 +260,9 @@ var pollMaxN int
var pollservers []*pollServer
var startServersOnce []func()
-func init() {
+var canCancelIO = true // used for testing current package
+
+func sysInit() {
pollMaxN = runtime.NumCPU()
if pollMaxN > 8 {
pollMaxN = 8 // No improvement then.
@@ -316,22 +313,30 @@ func newFD(fd, family, sotype int, net string) (*netFD, error) {
func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.laddr = laddr
fd.raddr = raddr
+ fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net)
+}
+
+func (fd *netFD) name() string {
var ls, rs string
- if laddr != nil {
- ls = laddr.String()
+ if fd.laddr != nil {
+ ls = fd.laddr.String()
}
- if raddr != nil {
- rs = raddr.String()
+ if fd.raddr != nil {
+ rs = fd.raddr.String()
}
- fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net+":"+ls+"->"+rs)
+ return fd.net + ":" + ls + "->" + rs
}
func (fd *netFD) connect(ra syscall.Sockaddr) error {
err := syscall.Connect(fd.sysfd, ra)
+ hadTimeout := fd.wdeadline > 0
if err == syscall.EINPROGRESS {
if err = fd.pollServer.WaitWrite(fd); err != nil {
return err
}
+ if hadTimeout && fd.wdeadline < 0 {
+ return errTimeout
+ }
var e int
e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
@@ -344,15 +349,10 @@ 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.
// 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()
@@ -369,9 +369,6 @@ func (fd *netFD) incref(closing bool) error {
// 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.sysfile != nil {
@@ -664,7 +661,7 @@ func (fd *netFD) dup() (f *os.File, err error) {
return nil, &OpError{"setnonblock", fd.net, fd.laddr, err}
}
- return os.NewFile(uintptr(ns), fd.sysfile.Name()), nil
+ return os.NewFile(uintptr(ns), fd.name()), nil
}
func closesocket(s int) error {
diff --git a/libgo/go/net/fd_unix_test.go b/libgo/go/net/fd_unix_test.go
new file mode 100644
index 0000000..d1eb573
--- /dev/null
+++ b/libgo/go/net/fd_unix_test.go
@@ -0,0 +1,60 @@
+// 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.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package net
+
+import (
+ "testing"
+)
+
+// Issue 3590. netFd.AddFD should return an error
+// from the underlying pollster rather than panicing.
+func TestAddFDReturnsError(t *testing.T) {
+ l, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer l.Close()
+
+ go func() {
+ for {
+ c, err := l.Accept()
+ if err != nil {
+ return
+ }
+ defer c.Close()
+ }
+ }()
+
+ c, err := Dial("tcp", l.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ // replace c's pollServer with a closed version.
+ ps, err := newPollServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ ps.poll.Close()
+ c.(*TCPConn).conn.fd.pollServer = ps
+
+ var b [1]byte
+ _, err = c.Read(b[:])
+ if err, ok := err.(*OpError); ok {
+ if err.Op == "addfd" {
+ return
+ }
+ if err, ok := err.Err.(*OpError); ok {
+ // the err is sometimes wrapped by another OpError
+ if err.Op == "addfd" {
+ return
+ }
+ }
+ }
+ t.Error(err)
+}
diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go
index 4ae7839..5338def 100644
--- a/libgo/go/net/fd_windows.go
+++ b/libgo/go/net/fd_windows.go
@@ -17,19 +17,35 @@ import (
var initErr error
-func init() {
+// CancelIo Windows API cancels all outstanding IO for a particular
+// socket on current thread. To overcome that limitation, we run
+// special goroutine, locked to OS single thread, that both starts
+// and cancels IO. It means, there are 2 unavoidable thread switches
+// for every IO.
+// Some newer versions of Windows has new CancelIoEx API, that does
+// not have that limitation and can be used from any thread. This
+// package uses CancelIoEx API, if present, otherwise it fallback
+// to CancelIo.
+
+var canCancelIO bool // determines if CancelIoEx API is present
+
+func sysInit() {
var d syscall.WSAData
e := syscall.WSAStartup(uint32(0x202), &d)
if e != nil {
initErr = os.NewSyscallError("WSAStartup", e)
}
+ canCancelIO = syscall.LoadCancelIoEx() == nil
+ if syscall.LoadGetAddrInfo() == nil {
+ lookupIP = newLookupIP
+ }
}
func closesocket(s syscall.Handle) error {
return syscall.Closesocket(s)
}
-// Interface for all io operations.
+// Interface for all IO operations.
type anOpIface interface {
Op() *anOp
Name() string
@@ -42,7 +58,7 @@ type ioResult struct {
err error
}
-// anOp implements functionality common to all io operations.
+// anOp implements functionality common to all IO operations.
type anOp struct {
// Used by IOCP interface, it must be first field
// of the struct, as our code rely on it.
@@ -75,7 +91,7 @@ func (o *anOp) Op() *anOp {
return o
}
-// bufOp is used by io operations that read / write
+// bufOp is used by IO operations that read / write
// data from / to client buffer.
type bufOp struct {
anOp
@@ -92,7 +108,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte, mode int) {
}
}
-// resultSrv will retrieve all io completion results from
+// resultSrv will retrieve all IO completion results from
// iocp and send them to the correspondent waiting client
// goroutine via channel supplied in the request.
type resultSrv struct {
@@ -107,7 +123,7 @@ func (s *resultSrv) Run() {
r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
switch {
case r.err == nil:
- // Dequeued successfully completed io packet.
+ // Dequeued successfully completed IO packet.
case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil:
// Wait has timed out (should not happen now, but might be used in the future).
panic("GetQueuedCompletionStatus timed out")
@@ -115,22 +131,23 @@ func (s *resultSrv) Run() {
// Failed to dequeue anything -> report the error.
panic("GetQueuedCompletionStatus failed " + r.err.Error())
default:
- // Dequeued failed io packet.
+ // Dequeued failed IO packet.
}
(*anOp)(unsafe.Pointer(o)).resultc <- r
}
}
-// ioSrv executes net io requests.
+// ioSrv executes net IO requests.
type ioSrv struct {
- submchan chan anOpIface // submit io requests
- canchan chan anOpIface // cancel io requests
+ submchan chan anOpIface // submit IO requests
+ canchan chan anOpIface // cancel IO requests
}
-// ProcessRemoteIO will execute submit io requests on behalf
+// ProcessRemoteIO will execute submit IO requests on behalf
// of other goroutines, all on a single os thread, so it can
// cancel them later. Results of all operations will be sent
// back to their requesters via channel supplied in request.
+// It is used only when the CancelIoEx API is unavailable.
func (s *ioSrv) ProcessRemoteIO() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -144,20 +161,21 @@ func (s *ioSrv) ProcessRemoteIO() {
}
}
-// ExecIO executes a single io operation. It either executes it
-// inline, or, if a deadline is employed, passes the request onto
+// ExecIO executes a single IO operation oi. It submits and cancels
+// IO in the current thread for systems where Windows CancelIoEx API
+// is available. Alternatively, it passes the request onto
// a special goroutine and waits for completion or cancels request.
// deadline is unix nanos.
func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
var err error
o := oi.Op()
- if deadline != 0 {
+ if canCancelIO {
+ err = oi.Submit()
+ } else {
// Send request to a special dedicated thread,
- // so it can stop the io with CancelIO later.
+ // so it can stop the IO with CancelIO later.
s.submchan <- oi
err = <-o.errnoc
- } else {
- err = oi.Submit()
}
switch err {
case nil:
@@ -168,27 +186,50 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
default:
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err}
}
- // Wait for our request to complete.
- var r ioResult
+ // Setup timer, if deadline is given.
+ var timer <-chan time.Time
if deadline != 0 {
dt := deadline - time.Now().UnixNano()
if dt < 1 {
dt = 1
}
- timer := time.NewTimer(time.Duration(dt) * time.Nanosecond)
- defer timer.Stop()
- select {
- case r = <-o.resultc:
- case <-timer.C:
+ t := time.NewTimer(time.Duration(dt) * time.Nanosecond)
+ defer t.Stop()
+ timer = t.C
+ }
+ // Wait for our request to complete.
+ var r ioResult
+ var cancelled, timeout bool
+ select {
+ case r = <-o.resultc:
+ case <-timer:
+ cancelled = true
+ timeout = true
+ case <-o.fd.closec:
+ cancelled = true
+ }
+ if cancelled {
+ // Cancel it.
+ if canCancelIO {
+ err := syscall.CancelIoEx(syscall.Handle(o.Op().fd.sysfd), &o.o)
+ // Assuming ERROR_NOT_FOUND is returned, if IO is completed.
+ if err != nil && err != syscall.ERROR_NOT_FOUND {
+ // TODO(brainman): maybe do something else, but panic.
+ panic(err)
+ }
+ } else {
s.canchan <- oi
<-o.errnoc
- r = <-o.resultc
- if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
- r.err = syscall.EWOULDBLOCK
- }
}
- } else {
+ // Wait for IO to be canceled or complete successfully.
r = <-o.resultc
+ if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
+ if timeout {
+ r.err = errTimeout
+ } else {
+ r.err = errClosing
+ }
+ }
}
if r.err != nil {
err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err}
@@ -211,9 +252,13 @@ func startServer() {
go resultsrv.Run()
iosrv = new(ioSrv)
- iosrv.submchan = make(chan anOpIface)
- iosrv.canchan = make(chan anOpIface)
- go iosrv.ProcessRemoteIO()
+ if !canCancelIO {
+ // Only CancelIo API is available. Lets start special goroutine
+ // locked to an OS thread, that both starts and cancels IO.
+ iosrv.submchan = make(chan anOpIface)
+ iosrv.canchan = make(chan anOpIface)
+ go iosrv.ProcessRemoteIO()
+ }
}
// Network file descriptor.
@@ -233,6 +278,7 @@ type netFD struct {
raddr Addr
resultc [2]chan ioResult // read/write completion results
errnoc [2]chan error // read/write submit or cancel operation errors
+ closec chan bool // used by Close to cancel pending IO
// owned by client
rdeadline int64
@@ -247,6 +293,7 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
family: family,
sotype: sotype,
net: net,
+ closec: make(chan bool),
}
runtime.SetFinalizer(netfd, (*netFD).Close)
return netfd
@@ -273,8 +320,6 @@ 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.
// If closing==true, mark the fd as closing.
// Returns an error if the fd cannot be used.
@@ -299,24 +344,12 @@ func (fd *netFD) incref(closing bool) error {
// 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--
- // 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
- // use the resultsrv for Close too. Sigh.
- syscall.SetNonblock(fd.sysfd, false)
+ if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle {
closesocket(fd.sysfd)
fd.sysfd = syscall.InvalidHandle
// no need for a finalizer anymore
@@ -329,14 +362,22 @@ func (fd *netFD) Close() error {
if err := fd.incref(true); err != nil {
return err
}
- fd.decref()
+ defer fd.decref()
+ // unblock pending reader and writer
+ close(fd.closec)
+ // wait for both reader and writer to exit
+ fd.rio.Lock()
+ defer fd.rio.Unlock()
+ fd.wio.Lock()
+ defer fd.wio.Unlock()
return nil
}
func (fd *netFD) shutdown(how int) error {
- if fd == nil || fd.sysfd == syscall.InvalidHandle {
- return syscall.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}
@@ -368,18 +409,12 @@ func (o *readOp) Name() string {
}
func (fd *netFD) Read(buf []byte) (int, error) {
- if fd == nil {
- return 0, syscall.EINVAL
- }
- fd.rio.Lock()
- defer fd.rio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
- if fd.sysfd == syscall.InvalidHandle {
- return 0, syscall.EINVAL
- }
+ fd.rio.Lock()
+ defer fd.rio.Unlock()
var o readOp
o.Init(fd, buf, 'r')
n, err := iosrv.ExecIO(&o, fd.rdeadline)
@@ -407,18 +442,15 @@ func (o *readFromOp) Name() string {
}
func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
- if fd == nil {
- return 0, nil, syscall.EINVAL
- }
if len(buf) == 0 {
return 0, nil, nil
}
- fd.rio.Lock()
- defer fd.rio.Unlock()
if err := fd.incref(false); err != nil {
return 0, nil, err
}
defer fd.decref()
+ fd.rio.Lock()
+ defer fd.rio.Unlock()
var o readFromOp
o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa))
@@ -446,15 +478,12 @@ func (o *writeOp) Name() string {
}
func (fd *netFD) Write(buf []byte) (int, error) {
- if fd == nil {
- return 0, syscall.EINVAL
- }
- fd.wio.Lock()
- defer fd.wio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
+ fd.wio.Lock()
+ defer fd.wio.Unlock()
var o writeOp
o.Init(fd, buf, 'w')
return iosrv.ExecIO(&o, fd.wdeadline)
@@ -477,21 +506,15 @@ func (o *writeToOp) Name() string {
}
func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
- if fd == nil {
- return 0, syscall.EINVAL
- }
if len(buf) == 0 {
return 0, nil
}
- fd.wio.Lock()
- defer fd.wio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
- if fd.sysfd == syscall.InvalidHandle {
- return 0, syscall.EINVAL
- }
+ fd.wio.Lock()
+ defer fd.wio.Unlock()
var o writeToOp
o.Init(fd, buf, 'w')
o.sa = sa
@@ -544,7 +567,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
var o acceptOp
o.Init(fd, 'r')
o.newsock = s
- _, err = iosrv.ExecIO(&o, 0)
+ _, err = iosrv.ExecIO(&o, fd.rdeadline)
if err != nil {
closesocket(s)
return nil, err
diff --git a/libgo/go/net/http/cgi/testdata/test.cgi b/libgo/go/net/http/cgi/testdata/test.cgi
index b46b133..dcefe4e 100644
--- a/libgo/go/net/http/cgi/testdata/test.cgi
+++ b/libgo/go/net/http/cgi/testdata/test.cgi
@@ -8,6 +8,25 @@
use strict;
use Cwd;
+binmode STDOUT;
+
+sub on_windows {
+ return $^O eq 'MSWin32' || $^O eq 'msys';
+}
+
+# normalize_windows_path normalizes the various Windows Perl path
+# formats into Go's format.
+sub normalize_windows_path {
+ my $dir = shift;
+ return $dir unless on_windows();
+ $dir =~ s!^[a-z]:!uc($&)!e;
+ if ($dir =~ s!^/([a-zA-Z])/!!) {
+ $dir = uc($1) . ":\\$dir";
+ }
+ $dir =~ s!/!\\!g;
+ return $dir;
+}
+
my $q = MiniCGI->new;
my $params = $q->Vars;
@@ -20,7 +39,7 @@ my $NL = "\r\n";
$NL = "\n" if $params->{mode} eq "NL";
my $p = sub {
- print "$_[0]$NL";
+ print "$_[0]$NL";
};
# With carriage returns
@@ -39,29 +58,18 @@ if ($params->{"bigresponse"}) {
print "test=Hello CGI\n";
foreach my $k (sort keys %$params) {
- print "param-$k=$params->{$k}\n";
+ print "param-$k=$params->{$k}\n";
}
foreach my $k (sort keys %ENV) {
- my $clean_env = $ENV{$k};
- $clean_env =~ s/[\n\r]//g;
- print "env-$k=$clean_env\n";
+ my $clean_env = $ENV{$k};
+ $clean_env =~ s/[\n\r]//g;
+ print "env-$k=$clean_env\n";
}
-# NOTE: don't call getcwd() for windows.
-# msys return /c/go/src/... not C:\go\...
-my $dir;
-if ($^O eq 'MSWin32' || $^O eq 'msys') {
- my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
- $cmd =~ s!\\!/!g;
- $dir = `$cmd /c cd`;
- chomp $dir;
-} else {
- $dir = getcwd();
-}
+my $dir = normalize_windows_path(getcwd());
print "cwd=$dir\n";
-
# A minimal version of CGI.pm, for people without the perl-modules
# package installed. (CGI.pm used to be part of the Perl core, but
# some distros now bundle perl-base and perl-modules separately...)
@@ -94,3 +102,24 @@ sub _urldecode {
$v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
return $v;
}
+
+package Tests;
+
+sub test_normalize_windows_paths {
+ my @tests = (
+ {in => "C:\\foo\\bar", want => "C:\\foo\\bar"},
+ {in => "C:/foo/bar", want => "C:\\foo\\bar"},
+ {in => "c:/foo/bar", want => "C:\\foo\\bar"},
+ {in => "/c/foo/bar", want => "C:\\foo\\bar"},
+ );
+ foreach my $tt (@tests) {
+ my $got = ::normalize_windows_path($tt->{in});
+ unless ($got eq $tt->{want}) {
+ die "For path $tt->{in}, normalize = $got; want $tt->{want}\n";
+ }
+ }
+}
+
+BEGIN {
+ test_normalize_windows_paths() if ::on_windows();
+}
diff --git a/libgo/go/net/http/chunked.go b/libgo/go/net/http/chunked.go
index 60a478f..7cf39cf 100644
--- a/libgo/go/net/http/chunked.go
+++ b/libgo/go/net/http/chunked.go
@@ -13,6 +13,7 @@ import (
"bufio"
"bytes"
"errors"
+ "fmt"
"io"
"strconv"
)
@@ -22,7 +23,7 @@ const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
var ErrLineTooLong = errors.New("header line too long")
// newChunkedReader returns a new chunkedReader that translates the data read from r
-// out of HTTP "chunked" format before returning it.
+// out of HTTP "chunked" format before returning it.
// The chunkedReader returns io.EOF when the final 0-length chunk is read.
//
// newChunkedReader is not needed by normal applications. The http package
@@ -39,6 +40,7 @@ type chunkedReader struct {
r *bufio.Reader
n uint64 // unread bytes in chunk
err error
+ buf [2]byte
}
func (cr *chunkedReader) beginChunk() {
@@ -74,9 +76,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
cr.n -= uint64(n)
if cr.n == 0 && cr.err == nil {
// end of chunk (CRLF)
- b := make([]byte, 2)
- if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
- if b[0] != '\r' || b[1] != '\n' {
+ if _, cr.err = io.ReadFull(cr.r, cr.buf[:]); cr.err == nil {
+ if cr.buf[0] != '\r' || cr.buf[1] != '\n' {
cr.err = errors.New("malformed chunked encoding")
}
}
@@ -147,9 +148,7 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
return 0, nil
}
- head := strconv.FormatInt(int64(len(data)), 16) + "\r\n"
-
- if _, err = io.WriteString(cw.Wire, head); err != nil {
+ if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil {
return 0, err
}
if n, err = cw.Wire.Write(data); err != nil {
diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go
index 02891db..2f957d2 100644
--- a/libgo/go/net/http/client.go
+++ b/libgo/go/net/http/client.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// HTTP client. See RFC 2616.
-//
+//
// This is the high-level Client interface.
// The low-level implementation is in transport.go.
@@ -44,8 +44,8 @@ type Client struct {
// which is to stop after 10 consecutive requests.
CheckRedirect func(req *Request, via []*Request) error
- // Jar specifies the cookie jar.
- // If Jar is nil, cookies are not sent in requests and ignored
+ // Jar specifies the cookie jar.
+ // If Jar is nil, cookies are not sent in requests and ignored
// in responses.
Jar CookieJar
}
@@ -87,6 +87,22 @@ type readClose struct {
io.Closer
}
+func (c *Client) send(req *Request) (*Response, error) {
+ if c.Jar != nil {
+ for _, cookie := range c.Jar.Cookies(req.URL) {
+ req.AddCookie(cookie)
+ }
+ }
+ resp, err := send(req, c.Transport)
+ if err != nil {
+ return nil, err
+ }
+ if c.Jar != nil {
+ c.Jar.SetCookies(req.URL, resp.Cookies())
+ }
+ return resp, err
+}
+
// Do sends an HTTP request and returns an HTTP response, following
// policy (e.g. redirects, cookies, auth) as configured on the client.
//
@@ -106,7 +122,7 @@ func (c *Client) Do(req *Request) (resp *Response, err error) {
if req.Method == "GET" || req.Method == "HEAD" {
return c.doFollowingRedirects(req)
}
- return send(req, c.Transport)
+ return c.send(req)
}
// send issues an HTTP request.
@@ -215,11 +231,6 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
return nil, errors.New("http: nil Request.URL")
}
- jar := c.Jar
- if jar == nil {
- jar = blackHoleJar{}
- }
-
req := ireq
urlStr := "" // next relative or absolute URL to fetch (after first request)
redirectFailed := false
@@ -247,16 +258,10 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
}
}
- for _, cookie := range jar.Cookies(req.URL) {
- req.AddCookie(cookie)
- }
urlStr = req.URL.String()
- if resp, err = send(req, c.Transport); err != nil {
+ if resp, err = c.send(req); err != nil {
break
}
- if c := resp.Cookies(); len(c) > 0 {
- jar.SetCookies(req.URL, c)
- }
if shouldRedirect(resp.StatusCode) {
resp.Body.Close()
@@ -316,16 +321,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon
return nil, err
}
req.Header.Set("Content-Type", bodyType)
- if c.Jar != nil {
- for _, cookie := range c.Jar.Cookies(req.URL) {
- req.AddCookie(cookie)
- }
- }
- resp, err = send(req, c.Transport)
- if err == nil && c.Jar != nil {
- c.Jar.SetCookies(req.URL, resp.Cookies())
- }
- return
+ return c.send(req)
}
// PostForm issues a POST to the specified URL, with data's keys and
@@ -339,7 +335,7 @@ func PostForm(url string, data url.Values) (resp *Response, err error) {
return DefaultClient.PostForm(url, data)
}
-// PostForm issues a POST to the specified URL,
+// PostForm issues a POST to the specified URL,
// with data's keys and values urlencoded as the request body.
//
// When err is nil, resp always contains a non-nil resp.Body.
diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go
index c61b17d..9a45b14 100644
--- a/libgo/go/net/http/client_test.go
+++ b/libgo/go/net/http/client_test.go
@@ -219,6 +219,9 @@ func TestRedirects(t *testing.T) {
return checkErr
}}
res, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get error: %v", err)
+ }
finalUrl := res.Request.URL.String()
if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
t.Errorf("with custom client, expected error %q, got %q", e, g)
@@ -285,6 +288,10 @@ func TestClientSendsCookieFromJar(t *testing.T) {
req, _ := NewRequest("GET", us, nil)
client.Do(req) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+ req, _ = NewRequest("POST", us, nil)
+ client.Do(req) // Note: doesn't hit network
+ matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
}
// Just enough correctness for our redirect tests. Uses the URL.Host as the
@@ -331,7 +338,10 @@ func TestRedirectCookiesJar(t *testing.T) {
c.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
u, _ := url.Parse(ts.URL)
c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
- resp, _ := c.Get(ts.URL)
+ resp, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
matchReturnedCookies(t, expectedCookies, resp.Cookies())
}
diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go
index 43f519d..155b092 100644
--- a/libgo/go/net/http/cookie.go
+++ b/libgo/go/net/http/cookie.go
@@ -26,7 +26,7 @@ type Cookie struct {
Expires time.Time
RawExpires string
- // MaxAge=0 means no 'Max-Age' attribute specified.
+ // MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
// MaxAge>0 means Max-Age attribute present and given in seconds
MaxAge int
diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go
index 1e9186a..f84f739 100644
--- a/libgo/go/net/http/cookie_test.go
+++ b/libgo/go/net/http/cookie_test.go
@@ -217,7 +217,7 @@ var readCookiesTests = []struct {
func TestReadCookies(t *testing.T) {
for i, tt := range readCookiesTests {
- for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
+ for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
c := readCookies(tt.Header, tt.Filter)
if !reflect.DeepEqual(c, tt.Cookies) {
t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go
index 09d5cfa..59e30a1 100644
--- a/libgo/go/net/http/fs_test.go
+++ b/libgo/go/net/http/fs_test.go
@@ -648,6 +648,8 @@ func TestServeContent(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+ io.Copy(ioutil.Discard, res.Body)
+ res.Body.Close()
if res.StatusCode != tt.wantStatus {
t.Errorf("test %q: status = %d; want %d", testName, res.StatusCode, tt.wantStatus)
}
diff --git a/libgo/go/net/http/httputil/chunked.go b/libgo/go/net/http/httputil/chunked.go
index 29eaf34..26daee5 100644
--- a/libgo/go/net/http/httputil/chunked.go
+++ b/libgo/go/net/http/httputil/chunked.go
@@ -15,6 +15,7 @@ import (
"bufio"
"bytes"
"errors"
+ "fmt"
"io"
"strconv"
)
@@ -24,7 +25,7 @@ const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
var ErrLineTooLong = errors.New("header line too long")
// NewChunkedReader returns a new chunkedReader that translates the data read from r
-// out of HTTP "chunked" format before returning it.
+// out of HTTP "chunked" format before returning it.
// The chunkedReader returns io.EOF when the final 0-length chunk is read.
//
// NewChunkedReader is not needed by normal applications. The http package
@@ -41,6 +42,7 @@ type chunkedReader struct {
r *bufio.Reader
n uint64 // unread bytes in chunk
err error
+ buf [2]byte
}
func (cr *chunkedReader) beginChunk() {
@@ -76,9 +78,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
cr.n -= uint64(n)
if cr.n == 0 && cr.err == nil {
// end of chunk (CRLF)
- b := make([]byte, 2)
- if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
- if b[0] != '\r' || b[1] != '\n' {
+ if _, cr.err = io.ReadFull(cr.r, cr.buf[:]); cr.err == nil {
+ if cr.buf[0] != '\r' || cr.buf[1] != '\n' {
cr.err = errors.New("malformed chunked encoding")
}
}
@@ -149,9 +150,7 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
return 0, nil
}
- head := strconv.FormatInt(int64(len(data)), 16) + "\r\n"
-
- if _, err = io.WriteString(cw.Wire, head); err != nil {
+ if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil {
return 0, err
}
if n, err = cw.Wire.Write(data); err != nil {
diff --git a/libgo/go/net/http/jar.go b/libgo/go/net/http/jar.go
index 2c2caa2..35eee68 100644
--- a/libgo/go/net/http/jar.go
+++ b/libgo/go/net/http/jar.go
@@ -8,23 +8,18 @@ import (
"net/url"
)
-// A CookieJar manages storage and use of cookies in HTTP requests.
+// A CookieJar manages storage and use of cookies in HTTP requests.
//
// Implementations of CookieJar must be safe for concurrent use by multiple
// goroutines.
type CookieJar interface {
- // SetCookies handles the receipt of the cookies in a reply for the
- // given URL. It may or may not choose to save the cookies, depending
- // on the jar's policy and implementation.
+ // SetCookies handles the receipt of the cookies in a reply for the
+ // given URL. It may or may not choose to save the cookies, depending
+ // on the jar's policy and implementation.
SetCookies(u *url.URL, cookies []*Cookie)
// Cookies returns the cookies to send in a request for the given URL.
- // It is up to the implementation to honor the standard cookie use
- // restrictions such as in RFC 6265.
+ // It is up to the implementation to honor the standard cookie use
+ // restrictions such as in RFC 6265.
Cookies(u *url.URL) []*Cookie
}
-
-type blackHoleJar struct{}
-
-func (blackHoleJar) SetCookies(u *url.URL, cookies []*Cookie) {}
-func (blackHoleJar) Cookies(u *url.URL) []*Cookie { return nil }
diff --git a/libgo/go/net/http/proxy_test.go b/libgo/go/net/http/proxy_test.go
index 5ecffaf..86db976 100644
--- a/libgo/go/net/http/proxy_test.go
+++ b/libgo/go/net/http/proxy_test.go
@@ -25,7 +25,7 @@ var UseProxyTests = []struct {
{"[::2]", true}, // not a loopback address
{"barbaz.net", false}, // match as .barbaz.net
- {"foobar.com", false}, // have a port but match
+ {"foobar.com", false}, // have a port but match
{"foofoobar.com", true}, // not match as a part of foobar.com
{"baz.com", true}, // not match as a part of barbaz.com
{"localhost.net", true}, // not match as suffix of address
diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go
index db7419b..c0b738c 100644
--- a/libgo/go/net/http/request_test.go
+++ b/libgo/go/net/http/request_test.go
@@ -161,7 +161,7 @@ func TestSetBasicAuth(t *testing.T) {
}
func TestMultipartRequest(t *testing.T) {
- // Test that we can read the values and files of a
+ // Test that we can read the values and files of a
// multipart request with FormValue and FormFile,
// and that ParseMultipartForm can be called multiple times.
req := newTestMultipartRequest(t)
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index 71b7b3f..355efb2 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -314,7 +314,7 @@ func TestServerTimeouts(t *testing.T) {
l.Close()
}
-// TestIdentityResponse verifies that a handler can unset
+// TestIdentityResponse verifies that a handler can unset
func TestIdentityResponse(t *testing.T) {
handler := HandlerFunc(func(rw ResponseWriter, req *Request) {
rw.Header().Set("Content-Length", "3")
@@ -1063,7 +1063,7 @@ type countReader struct {
func (cr countReader) Read(p []byte) (n int, err error) {
n, err = cr.r.Read(p)
- *cr.n += int64(n)
+ atomic.AddInt64(cr.n, int64(n))
return
}
@@ -1081,8 +1081,8 @@ func TestRequestBodyLimit(t *testing.T) {
}))
defer ts.Close()
- nWritten := int64(0)
- req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), &nWritten}, limit*200))
+ nWritten := new(int64)
+ req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), nWritten}, limit*200))
// Send the POST, but don't care it succeeds or not. The
// remote side is going to reply and then close the TCP
@@ -1095,7 +1095,7 @@ func TestRequestBodyLimit(t *testing.T) {
// the remote side hung up on us before we wrote too much.
_, _ = DefaultClient.Do(req)
- if nWritten > limit*100 {
+ if atomic.LoadInt64(nWritten) > limit*100 {
t.Errorf("handler restricted the request body to %d bytes, but client managed to write %d",
limit, nWritten)
}
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index ee57e01..719cecf 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -579,13 +579,27 @@ func (c *conn) close() {
}
}
-// closeWrite flushes any outstanding data and sends a FIN packet (if client
-// is connected via TCP), signalling that we're done.
-func (c *conn) closeWrite() {
+// rstAvoidanceDelay is the amount of time we sleep after closing the
+// write side of a TCP connection before closing the entire socket.
+// By sleeping, we increase the chances that the client sees our FIN
+// and processes its final data before they process the subsequent RST
+// from closing a connection with known unread data.
+// This RST seems to occur mostly on BSD systems. (And Windows?)
+// This timeout is somewhat arbitrary (~latency around the planet).
+const rstAvoidanceDelay = 500 * time.Millisecond
+
+// closeWrite flushes any outstanding data and sends a FIN packet (if
+// client is connected via TCP), signalling that we're done. We then
+// pause for a bit, hoping the client processes it before `any
+// subsequent RST.
+//
+// See http://golang.org/issue/3595
+func (c *conn) closeWriteAndWait() {
c.finalFlush()
if tcp, ok := c.rwc.(*net.TCPConn); ok {
tcp.CloseWrite()
}
+ time.Sleep(rstAvoidanceDelay)
}
// Serve a new connection.
@@ -618,20 +632,21 @@ func (c *conn) serve() {
for {
w, err := c.readRequest()
if err != nil {
- msg := "400 Bad Request"
if err == errTooLarge {
// Their HTTP client may or may not be
// able to read this if we're
// responding to them and hanging up
// while they're still writing their
// request. Undefined behavior.
- msg = "413 Request Entity Too Large"
+ io.WriteString(c.rwc, "HTTP/1.1 413 Request Entity Too Large\r\n\r\n")
+ c.closeWriteAndWait()
+ break
} else if err == io.EOF {
break // Don't reply
} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
break // Don't reply
}
- fmt.Fprintf(c.rwc, "HTTP/1.1 %s\r\n\r\n", msg)
+ io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\n\r\n")
break
}
@@ -685,18 +700,7 @@ func (c *conn) serve() {
w.finishRequest()
if w.closeAfterReply {
if w.requestBodyLimitHit {
- // Flush our response and send a FIN packet and wait a bit
- // before closing the connection, so the client has a chance
- // to read our response before they possibly get a RST from
- // our TCP stack from ignoring their unread body.
- // See http://golang.org/issue/3595
- c.closeWrite()
- // Now wait a bit for our machine to send the FIN and the client's
- // machine's HTTP client to read the request before we close
- // the connection, which might send a RST (on BSDs, at least).
- // 250ms is somewhat arbitrary (~latency around half the planet),
- // but this doesn't need to be a full second probably.
- time.Sleep(250 * time.Millisecond)
+ c.closeWriteAndWait()
}
break
}
diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go
index 1fc1e63..9833ddd 100644
--- a/libgo/go/net/http/transfer.go
+++ b/libgo/go/net/http/transfer.go
@@ -567,14 +567,22 @@ func seeUpcomingDoubleCRLF(r *bufio.Reader) bool {
return false
}
+var errTrailerEOF = errors.New("http: unexpected EOF reading trailer")
+
func (b *body) readTrailer() error {
// The common case, since nobody uses trailers.
- buf, _ := b.r.Peek(2)
+ buf, err := b.r.Peek(2)
if bytes.Equal(buf, singleCRLF) {
b.r.ReadByte()
b.r.ReadByte()
return nil
}
+ if len(buf) < 2 {
+ return errTrailerEOF
+ }
+ if err != nil {
+ return err
+ }
// Make sure there's a header terminator coming up, to prevent
// a DoS with an unbounded size Trailer. It's not easy to
@@ -590,6 +598,9 @@ func (b *body) readTrailer() error {
hdr, err := textproto.NewReader(b.r).ReadMIMEHeader()
if err != nil {
+ if err == io.EOF {
+ return errTrailerEOF
+ }
return err
}
switch rr := b.hdr.(type) {
diff --git a/libgo/go/net/http/transfer_test.go b/libgo/go/net/http/transfer_test.go
new file mode 100644
index 0000000..8627a37
--- /dev/null
+++ b/libgo/go/net/http/transfer_test.go
@@ -0,0 +1,37 @@
+// 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 http
+
+import (
+ "bufio"
+ "strings"
+ "testing"
+)
+
+func TestBodyReadBadTrailer(t *testing.T) {
+ b := &body{
+ Reader: strings.NewReader("foobar"),
+ hdr: true, // force reading the trailer
+ r: bufio.NewReader(strings.NewReader("")),
+ }
+ buf := make([]byte, 7)
+ n, err := b.Read(buf[:3])
+ got := string(buf[:n])
+ if got != "foo" || err != nil {
+ t.Fatalf(`first Read = %d (%q), %v; want 3 ("foo")`, n, got, err)
+ }
+
+ n, err = b.Read(buf[:])
+ got = string(buf[:n])
+ if got != "bar" || err != nil {
+ t.Fatalf(`second Read = %d (%q), %v; want 3 ("bar")`, n, got, err)
+ }
+
+ n, err = b.Read(buf[:])
+ got = string(buf[:n])
+ if err == nil {
+ t.Errorf("final Read was successful (%q), expected error from trailer read", got)
+ }
+}
diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go
index 651f3ce..38ea6f7 100644
--- a/libgo/go/net/http/transport.go
+++ b/libgo/go/net/http/transport.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// HTTP client implementation. See RFC 2616.
-//
+//
// This is the low-level Transport implementation of RoundTripper.
// The high-level interface is in client.go.
@@ -24,6 +24,7 @@ import (
"os"
"strings"
"sync"
+ "sync/atomic"
"time"
)
@@ -604,6 +605,9 @@ func (pc *persistConn) readLoop() {
alive = false
}
+ // TODO(bradfitz): this hasBody conflicts with the defition
+ // above which excludes HEAD requests. Is this one
+ // incomplete?
hasBody := resp != nil && resp.ContentLength != 0
var waitForBodyRead chan bool
if hasBody {
@@ -706,7 +710,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
// requested it.
requestedGzip := false
if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" {
- // Request gzip only, not deflate. Deflate is ambiguous and
+ // Request gzip only, not deflate. Deflate is ambiguous and
// not as universally supported anyway.
// See: http://www.gzip.org/zlib/zlib_faq.html#faq38
requestedGzip = true
@@ -805,24 +809,19 @@ func canonicalAddr(url *url.URL) string {
return addr
}
-func responseIsKeepAlive(res *Response) bool {
- // TODO: implement. for now just always shutting down the connection.
- return false
-}
-
// bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most
// once, right before the final Read() or Close() call returns, but after
// EOF has been seen.
type bodyEOFSignal struct {
body io.ReadCloser
fn func()
- isClosed bool
+ isClosed uint32 // atomic bool, non-zero if true
once sync.Once
}
func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
n, err = es.body.Read(p)
- if es.isClosed && n > 0 {
+ if es.closed() && n > 0 {
panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725")
}
if err == io.EOF {
@@ -832,10 +831,10 @@ func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
}
func (es *bodyEOFSignal) Close() (err error) {
- if es.isClosed {
+ if !es.setClosed() {
+ // already closed
return nil
}
- es.isClosed = true
err = es.body.Close()
if err == nil {
es.condfn()
@@ -849,6 +848,14 @@ func (es *bodyEOFSignal) condfn() {
}
}
+func (es *bodyEOFSignal) closed() bool {
+ return atomic.LoadUint32(&es.isClosed) != 0
+}
+
+func (es *bodyEOFSignal) setClosed() bool {
+ return atomic.CompareAndSwapUint32(&es.isClosed, 0, 1)
+}
+
type readFirstCloseBoth struct {
io.ReadCloser
io.Closer
diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go
index d37272c..43b02ae 100644
--- a/libgo/go/net/ipraw_test.go
+++ b/libgo/go/net/ipraw_test.go
@@ -21,7 +21,7 @@ var icmpTests = []struct {
ipv6 bool // test with underlying AF_INET6 socket
}{
{"ip4:icmp", "", "127.0.0.1", false},
- {"ip6:icmp", "", "::1", true},
+ {"ip6:ipv6-icmp", "", "::1", true},
}
func TestICMP(t *testing.T) {
@@ -206,3 +206,33 @@ func parseICMPEchoReply(b []byte) (id, seqnum int) {
seqnum = int(b[6])<<8 | int(b[7])
return
}
+
+var ipConnLocalNameTests = []struct {
+ net string
+ laddr *IPAddr
+}{
+ {"ip4:icmp", &IPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"ip4:icmp", &IPAddr{}},
+ {"ip4:icmp", nil},
+}
+
+func TestIPConnLocalName(t *testing.T) {
+ if os.Getuid() != 0 {
+ t.Logf("skipping test; must be root")
+ return
+ }
+
+ for _, tt := range ipConnLocalNameTests {
+ c, err := ListenIP(tt.net, tt.laddr)
+ if err != nil {
+ t.Errorf("ListenIP failed: %v", err)
+ return
+ }
+ defer c.Close()
+ la := c.LocalAddr()
+ if la == nil {
+ t.Error("IPConn.LocalAddr failed")
+ return
+ }
+ }
+}
diff --git a/libgo/go/net/iprawsock.go b/libgo/go/net/iprawsock.go
index ae21b3c..d7bffc6 100644
--- a/libgo/go/net/iprawsock.go
+++ b/libgo/go/net/iprawsock.go
@@ -6,6 +6,10 @@
package net
+import (
+ "time"
+)
+
// IPAddr represents the address of an IP end point.
type IPAddr struct {
IP IP
@@ -26,7 +30,11 @@ func (a *IPAddr) String() string {
// "ip", "ip4" or "ip6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]".
func ResolveIPAddr(net, addr string) (*IPAddr, error) {
- ip, err := hostToIP(net, addr)
+ return resolveIPAddr(net, addr, noDeadline)
+}
+
+func resolveIPAddr(net, addr string, deadline time.Time) (*IPAddr, error) {
+ ip, err := hostToIP(net, addr, deadline)
if err != nil {
return nil, err
}
@@ -34,7 +42,7 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
}
// Convert "host" into IP address.
-func hostToIP(net, host string) (ip IP, err error) {
+func hostToIP(net, host string, deadline time.Time) (ip IP, err error) {
var addr IP
// Try as an IP address.
addr = ParseIP(host)
@@ -47,7 +55,7 @@ func hostToIP(net, host string) (ip IP, err error) {
filter = ipv6only
}
// Not an IP address. Try as a DNS name.
- addrs, err1 := LookupHost(host)
+ addrs, err1 := lookupHostDeadline(host, deadline)
if err1 != nil {
err = err1
goto Error
diff --git a/libgo/go/net/iprawsock_plan9.go b/libgo/go/net/iprawsock_plan9.go
index 6de2ee3..e77c547 100644
--- a/libgo/go/net/iprawsock_plan9.go
+++ b/libgo/go/net/iprawsock_plan9.go
@@ -130,6 +130,10 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
// netProto, which must be "ip", "ip4", or "ip6" followed by a colon
// and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
+ return dialIP(netProto, laddr, raddr, noDeadline)
+}
+
+func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go
index d0f0b56..00e87cf 100644
--- a/libgo/go/net/iprawsock_posix.go
+++ b/libgo/go/net/iprawsock_posix.go
@@ -10,6 +10,7 @@ package net
import (
"syscall"
+ "time"
)
func sockaddrToIP(sa syscall.Sockaddr) Addr {
@@ -163,6 +164,10 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
// DialIP connects to the remote address raddr on the network protocol netProto,
// which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
+ return dialIP(netProto, laddr, raddr, noDeadline)
+}
+
+func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
net, proto, err := parseDialNetwork(netProto)
if err != nil {
return nil, err
@@ -170,12 +175,12 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, UnknownNetworkError(netProto)
}
if raddr == nil {
return nil, &OpError{"dial", netProto, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
if err != nil {
return nil, err
}
@@ -194,9 +199,9 @@ func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, UnknownNetworkError(netProto)
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
if err != nil {
return nil, err
}
diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go
index 84547c7..9d48e8c 100644
--- a/libgo/go/net/ipsock.go
+++ b/libgo/go/net/ipsock.go
@@ -6,7 +6,14 @@
package net
-var supportsIPv6, supportsIPv4map = probeIPv6Stack()
+import "time"
+
+var supportsIPv6, supportsIPv4map bool
+
+func init() {
+ sysInit()
+ supportsIPv6, supportsIPv4map = probeIPv6Stack()
+}
func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
if filter == nil {
@@ -98,7 +105,7 @@ func JoinHostPort(host, port string) string {
}
// Convert "host:port" into IP address and port.
-func hostPortToIP(net, hostport string) (ip IP, iport int, err error) {
+func hostPortToIP(net, hostport string, deadline time.Time) (ip IP, iport int, err error) {
host, port, err := SplitHostPort(hostport)
if err != nil {
return nil, 0, err
@@ -117,7 +124,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err error) {
filter = ipv6only
}
// Not an IP address. Try as a DNS name.
- addrs, err := LookupHost(host)
+ addrs, err := lookupHostDeadline(host, deadline)
if err != nil {
return nil, 0, err
}
diff --git a/libgo/go/net/ipsock_plan9.go b/libgo/go/net/ipsock_plan9.go
index 7cc2d71..138c3b4 100644
--- a/libgo/go/net/ipsock_plan9.go
+++ b/libgo/go/net/ipsock_plan9.go
@@ -8,10 +8,7 @@ package net
import (
"errors"
- "io"
"os"
- "syscall"
- "time"
)
// /sys/include/ape/sys/socket.h:/SOMAXCONN
@@ -71,120 +68,6 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) {
return addr, nil
}
-type plan9Conn struct {
- proto, name, dir string
- ctl, data *os.File
- laddr, raddr Addr
-}
-
-func newPlan9Conn(proto, name string, ctl *os.File, laddr, raddr Addr) *plan9Conn {
- return &plan9Conn{proto, name, "/net/" + proto + "/" + name, ctl, nil, laddr, raddr}
-}
-
-func (c *plan9Conn) ok() bool { return c != nil && c.ctl != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *plan9Conn) Read(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
- if err != nil {
- return 0, err
- }
- }
- n, err = c.data.Read(b)
- if c.proto == "udp" && err == io.EOF {
- n = 0
- err = nil
- }
- return
-}
-
-// Write implements the Conn Write method.
-func (c *plan9Conn) Write(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
- if err != nil {
- return 0, err
- }
- }
- return c.data.Write(b)
-}
-
-// Close closes the connection.
-func (c *plan9Conn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- err := c.ctl.Close()
- if err != nil {
- return err
- }
- if c.data != nil {
- err = c.data.Close()
- }
- c.ctl = nil
- c.data = nil
- return err
-}
-
-// LocalAddr returns the local network address.
-func (c *plan9Conn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.laddr
-}
-
-// RemoteAddr returns the remote network address.
-func (c *plan9Conn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *plan9Conn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *plan9Conn) SetReadDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *plan9Conn) SetWriteDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetReadBuffer sets the size of the operating system's receive
-// buffer associated with the connection.
-func (c *plan9Conn) SetReadBuffer(bytes int) error {
- return syscall.EPLAN9
-}
-
-// SetWriteBuffer sets the size of the operating system's transmit
-// buffer associated with the connection.
-func (c *plan9Conn) SetWriteBuffer(bytes int) error {
- return syscall.EPLAN9
-}
-
-// File returns a copy of the underlying os.File, set to blocking
-// mode. It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *plan9Conn) File() (f *os.File, err error) {
- return nil, syscall.EPLAN9
-}
-
func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
var (
ip IP
@@ -221,114 +104,72 @@ func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string,
return f, dest, proto, string(buf[:n]), nil
}
-func dialPlan9(net string, laddr, raddr Addr) (c *plan9Conn, err error) {
+func dialPlan9(net string, laddr, raddr Addr) (*netFD, error) {
f, dest, proto, name, err := startPlan9(net, raddr)
if err != nil {
- return
+ return nil, err
}
_, err = f.WriteString("connect " + dest)
if err != nil {
f.Close()
- return
+ return nil, err
}
laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
if err != nil {
f.Close()
- return
+ return nil, err
}
raddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/remote")
if err != nil {
f.Close()
- return
+ return nil, err
}
- return newPlan9Conn(proto, name, f, laddr, raddr), nil
-}
-
-type plan9Listener struct {
- proto, name, dir string
- ctl *os.File
- laddr Addr
+ return newFD(proto, name, f, laddr, raddr), nil
}
-func listenPlan9(net string, laddr Addr) (l *plan9Listener, err error) {
+func listenPlan9(net string, laddr Addr) (*netFD, error) {
f, dest, proto, name, err := startPlan9(net, laddr)
if err != nil {
- return
+ return nil, err
}
_, err = f.WriteString("announce " + dest)
if err != nil {
f.Close()
- return
+ return nil, err
}
laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
if err != nil {
f.Close()
- return
+ return nil, err
}
- l = new(plan9Listener)
- l.proto = proto
- l.name = name
- l.dir = "/net/" + proto + "/" + name
- l.ctl = f
- l.laddr = laddr
- return l, nil
+ return &netFD{proto: proto, name: name, dir: "/net/" + proto + "/" + name, ctl: f, laddr: laddr}, nil
}
-func (l *plan9Listener) plan9Conn() *plan9Conn {
- return newPlan9Conn(l.proto, l.name, l.ctl, l.laddr, nil)
+func (l *netFD) netFD() *netFD {
+ return newFD(l.proto, l.name, l.ctl, l.laddr, nil)
}
-func (l *plan9Listener) acceptPlan9() (c *plan9Conn, err error) {
+func (l *netFD) acceptPlan9() (*netFD, error) {
f, err := os.Open(l.dir + "/listen")
if err != nil {
- return
+ return nil, err
}
var buf [16]byte
n, err := f.Read(buf[:])
if err != nil {
f.Close()
- return
+ return nil, err
}
name := string(buf[:n])
laddr, err := readPlan9Addr(l.proto, l.dir+"/local")
if err != nil {
f.Close()
- return
+ return nil, err
}
raddr, err := readPlan9Addr(l.proto, l.dir+"/remote")
if err != nil {
f.Close()
- return
- }
- return newPlan9Conn(l.proto, name, f, laddr, raddr), nil
-}
-
-func (l *plan9Listener) Accept() (c Conn, err error) {
- c1, err := l.acceptPlan9()
- if err != nil {
- return
+ return nil, err
}
- return c1, nil
-}
-
-func (l *plan9Listener) Close() error {
- if l == nil || l.ctl == nil {
- return syscall.EINVAL
- }
- return l.ctl.Close()
-}
-
-func (l *plan9Listener) Addr() Addr { return l.laddr }
-
-// SetDeadline sets the deadline associated with the listener.
-// A zero time value disables the deadline.
-func (l *plan9Listener) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// File returns a copy of the underlying os.File, set to blocking
-// mode. It is the caller's responsibility to close f when finished.
-// Closing l does not affect f, and closing f does not affect l.
-func (l *plan9Listener) File() (f *os.File, err error) {
- return nil, syscall.EPLAN9
+ return newFD(l.proto, name, f, laddr, raddr), nil
}
diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go
index 1718892..87a2288 100644
--- a/libgo/go/net/ipsock_posix.go
+++ b/libgo/go/net/ipsock_posix.go
@@ -6,7 +6,10 @@
package net
-import "syscall"
+import (
+ "syscall"
+ "time"
+)
// Should we try to use the IPv4 socket interface if we're
// only dealing with IPv4 sockets? As long as the host system
@@ -125,7 +128,7 @@ type sockaddr interface {
sockaddr(family int) (syscall.Sockaddr, error)
}
-func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
var la, ra syscall.Sockaddr
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
if laddr != nil {
@@ -138,7 +141,7 @@ func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode s
goto Error
}
}
- fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, toAddr)
+ fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, deadline, toAddr)
if err != nil {
goto Error
}
diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go
index 3a44e52..bec93ec 100644
--- a/libgo/go/net/lookup.go
+++ b/libgo/go/net/lookup.go
@@ -4,12 +4,53 @@
package net
+import (
+ "time"
+)
+
// LookupHost looks up the given host using the local resolver.
// It returns an array of that host's addresses.
func LookupHost(host string) (addrs []string, err error) {
return lookupHost(host)
}
+func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err error) {
+ if deadline.IsZero() {
+ return lookupHost(host)
+ }
+
+ // TODO(bradfitz): consider pushing the deadline down into the
+ // name resolution functions. But that involves fixing it for
+ // the native Go resolver, cgo, Windows, etc.
+ //
+ // In the meantime, just use a goroutine. Most users affected
+ // by http://golang.org/issue/2631 are due to TCP connections
+ // to unresponsive hosts, not DNS.
+ timeout := deadline.Sub(time.Now())
+ if timeout <= 0 {
+ err = errTimeout
+ return
+ }
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ type res struct {
+ addrs []string
+ err error
+ }
+ resc := make(chan res, 1)
+ go func() {
+ a, err := lookupHost(host)
+ resc <- res{a, err}
+ }()
+ select {
+ case <-t.C:
+ err = errTimeout
+ case r := <-resc:
+ addrs, err = r.addrs, r.err
+ }
+ return
+}
+
// LookupIP looks up host using the local resolver.
// It returns an array of that host's IPv4 and IPv6 addresses.
func LookupIP(host string) (addrs []IP, err error) {
@@ -47,6 +88,11 @@ func LookupMX(name string) (mx []*MX, err error) {
return lookupMX(name)
}
+// LookupNS returns the DNS NS records for the given domain name.
+func LookupNS(name string) (ns []*NS, err error) {
+ return lookupNS(name)
+}
+
// LookupTXT returns the DNS TXT records for the given domain name.
func LookupTXT(name string) (txt []string, err error) {
return lookupTXT(name)
diff --git a/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go
index 2c69830..ae7cf79 100644
--- a/libgo/go/net/lookup_plan9.go
+++ b/libgo/go/net/lookup_plan9.go
@@ -201,6 +201,21 @@ func lookupMX(name string) (mx []*MX, err error) {
return
}
+func lookupNS(name string) (ns []*NS, err error) {
+ lines, err := queryDNS(name, "ns")
+ if err != nil {
+ return
+ }
+ for _, line := range lines {
+ f := getFields(line)
+ if len(f) < 4 {
+ continue
+ }
+ ns = append(ns, &NS{f[3]})
+ }
+ return
+}
+
func lookupTXT(name string) (txt []string, err error) {
lines, err := queryDNS(name, "txt")
if err != nil {
diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go
index 84f089e..990ade9 100644
--- a/libgo/go/net/lookup_test.go
+++ b/libgo/go/net/lookup_test.go
@@ -52,6 +52,20 @@ func TestGmailMX(t *testing.T) {
}
}
+func TestGmailNS(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Logf("skipping test to avoid external network")
+ return
+ }
+ ns, err := LookupNS("gmail.com")
+ if err != nil {
+ t.Errorf("failed: %s", err)
+ }
+ if len(ns) == 0 {
+ t.Errorf("no results")
+ }
+}
+
func TestGmailTXT(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
diff --git a/libgo/go/net/lookup_unix.go b/libgo/go/net/lookup_unix.go
index d500a12..fa98eed 100644
--- a/libgo/go/net/lookup_unix.go
+++ b/libgo/go/net/lookup_unix.go
@@ -119,6 +119,19 @@ func lookupMX(name string) (mx []*MX, err error) {
return
}
+func lookupNS(name string) (ns []*NS, err error) {
+ _, records, err := lookup(name, dnsTypeNS)
+ if err != nil {
+ return
+ }
+ ns = make([]*NS, len(records))
+ for i, r := range records {
+ r := r.(*dnsRR_NS)
+ ns[i] = &NS{r.Ns}
+ }
+ return
+}
+
func lookupTXT(name string) (txt []string, err error) {
_, records, err := lookup(name, dnsTypeTXT)
if err != nil {
diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go
index 99783e9..390fe7f 100644
--- a/libgo/go/net/lookup_windows.go
+++ b/libgo/go/net/lookup_windows.go
@@ -40,7 +40,9 @@ func lookupHost(name string) (addrs []string, err error) {
return
}
-func lookupIP(name string) (addrs []IP, err error) {
+var lookupIP = oldLookupIP
+
+func oldLookupIP(name string) (addrs []IP, err error) {
hostentLock.Lock()
defer hostentLock.Unlock()
h, err := syscall.GetHostByName(name)
@@ -56,7 +58,36 @@ func lookupIP(name string) (addrs []IP, err error) {
}
addrs = addrs[0:i]
default: // TODO(vcc): Implement non IPv4 address lookups.
- return nil, os.NewSyscallError("LookupHost", syscall.EWINDOWS)
+ return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
+ }
+ return addrs, nil
+}
+
+func newLookupIP(name string) (addrs []IP, err error) {
+ hints := syscall.AddrinfoW{
+ Family: syscall.AF_UNSPEC,
+ Socktype: syscall.SOCK_STREAM,
+ Protocol: syscall.IPPROTO_IP,
+ }
+ var result *syscall.AddrinfoW
+ e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
+ if e != nil {
+ return nil, os.NewSyscallError("GetAddrInfoW", e)
+ }
+ defer syscall.FreeAddrInfoW(result)
+ addrs = make([]IP, 0, 5)
+ for ; result != nil; result = result.Next {
+ addr := unsafe.Pointer(result.Addr)
+ switch result.Family {
+ case syscall.AF_INET:
+ a := (*syscall.RawSockaddrInet4)(addr).Addr
+ addrs = append(addrs, IPv4(a[0], a[1], a[2], a[3]))
+ case syscall.AF_INET6:
+ a := (*syscall.RawSockaddrInet6)(addr).Addr
+ addrs = append(addrs, IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]})
+ default:
+ return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
+ }
}
return addrs, nil
}
@@ -129,6 +160,21 @@ func lookupMX(name string) (mx []*MX, err error) {
return mx, nil
}
+func lookupNS(name string) (ns []*NS, err error) {
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
+ if e != nil {
+ return nil, os.NewSyscallError("LookupNS", e)
+ }
+ defer syscall.DnsRecordListFree(r, 1)
+ ns = make([]*NS, 0, 10)
+ for p := r; p != nil && p.Type == syscall.DNS_TYPE_NS; p = p.Next {
+ v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
+ ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
+ }
+ return ns, nil
+}
+
func lookupTXT(name string) (txt []string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
diff --git a/libgo/go/net/multicast_posix_test.go b/libgo/go/net/multicast_posix_test.go
index 3767a6b..d4a8a35 100644
--- a/libgo/go/net/multicast_posix_test.go
+++ b/libgo/go/net/multicast_posix_test.go
@@ -118,16 +118,29 @@ func TestSimpleMulticastListener(t *testing.T) {
func checkMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
if !multicastRIBContains(t, gaddr.IP) {
- t.Fatalf("%q not found in RIB", gaddr.String())
+ t.Errorf("%q not found in RIB", gaddr.String())
+ return
+ }
+ la := c.LocalAddr()
+ if la == nil {
+ t.Error("LocalAddr failed")
+ return
}
- if c.LocalAddr().String() != gaddr.String() {
- t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String())
+ if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
}
}
func checkSimpleMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
- if c.LocalAddr().String() != gaddr.String() {
- t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String())
+ la := c.LocalAddr()
+ if la == nil {
+ t.Error("LocalAddr failed")
+ return
+ }
+ if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
}
}
diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go
index 9ebcdbe..feb92a2 100644
--- a/libgo/go/net/net.go
+++ b/libgo/go/net/net.go
@@ -44,6 +44,8 @@ package net
import (
"errors"
+ "os"
+ "syscall"
"time"
)
@@ -103,6 +105,101 @@ type Conn interface {
SetWriteDeadline(t time.Time) error
}
+type conn struct {
+ fd *netFD
+}
+
+func (c *conn) ok() bool { return c != nil && c.fd != nil }
+
+// Implementation of the Conn interface.
+
+// Read implements the Conn Read method.
+func (c *conn) Read(b []byte) (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ return c.fd.Read(b)
+}
+
+// Write implements the Conn Write method.
+func (c *conn) Write(b []byte) (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ return c.fd.Write(b)
+}
+
+// Close closes the connection.
+func (c *conn) Close() error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.fd.Close()
+}
+
+// LocalAddr returns the local network address.
+func (c *conn) LocalAddr() Addr {
+ if !c.ok() {
+ return nil
+ }
+ return c.fd.laddr
+}
+
+// RemoteAddr returns the remote network address.
+func (c *conn) RemoteAddr() Addr {
+ if !c.ok() {
+ return nil
+ }
+ return c.fd.raddr
+}
+
+// SetDeadline implements the Conn SetDeadline method.
+func (c *conn) SetDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setDeadline(c.fd, t)
+}
+
+// SetReadDeadline implements the Conn SetReadDeadline method.
+func (c *conn) SetReadDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setReadDeadline(c.fd, t)
+}
+
+// SetWriteDeadline implements the Conn SetWriteDeadline method.
+func (c *conn) SetWriteDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setWriteDeadline(c.fd, t)
+}
+
+// SetReadBuffer sets the size of the operating system's
+// receive buffer associated with the connection.
+func (c *conn) SetReadBuffer(bytes int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setReadBuffer(c.fd, bytes)
+}
+
+// SetWriteBuffer sets the size of the operating system's
+// transmit buffer associated with the connection.
+func (c *conn) SetWriteBuffer(bytes int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setWriteBuffer(c.fd, bytes)
+}
+
+// File returns a copy of the underlying os.File, set to blocking mode.
+// It is the caller's responsibility to close f when finished.
+// Closing c does not affect f, and closing f does not affect c.
+func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }
+
// An Error represents a network error.
type Error interface {
error
@@ -204,6 +301,8 @@ func (e *OpError) Temporary() bool {
return ok && t.Temporary()
}
+var noDeadline = time.Time{}
+
type timeout interface {
Timeout() bool
}
@@ -221,6 +320,8 @@ func (e *timeoutError) Temporary() bool { return true }
var errTimeout error = &timeoutError{}
+var errClosing = errors.New("use of closed network connection")
+
type AddrError struct {
Err string
Addr string
diff --git a/libgo/go/net/net_posix.go b/libgo/go/net/net_posix.go
deleted file mode 100644
index 3bcc54f..0000000
--- a/libgo/go/net/net_posix.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// 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.
-
-// +build darwin freebsd linux netbsd openbsd windows
-
-// Base posix socket functions.
-
-package net
-
-import (
- "os"
- "syscall"
- "time"
-)
-
-type conn struct {
- fd *netFD
-}
-
-func (c *conn) ok() bool { return c != nil && c.fd != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *conn) Read(b []byte) (int, error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Read(b)
-}
-
-// Write implements the Conn Write method.
-func (c *conn) Write(b []byte) (int, error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// LocalAddr returns the local network address.
-func (c *conn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address.
-func (c *conn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *conn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *conn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *conn) SetWriteDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteDeadline(c.fd, t)
-}
-
-// SetReadBuffer sets the size of the operating system's
-// receive buffer associated with the connection.
-func (c *conn) SetReadBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadBuffer(c.fd, bytes)
-}
-
-// SetWriteBuffer sets the size of the operating system's
-// transmit buffer associated with the connection.
-func (c *conn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
-
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }
-
-// Close closes the connection.
-func (c *conn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go
index 623a788..a4e8dcd 100644
--- a/libgo/go/net/net_test.go
+++ b/libgo/go/net/net_test.go
@@ -174,3 +174,42 @@ func TestUDPListenClose(t *testing.T) {
t.Fatal("timeout waiting for UDP close")
}
}
+
+func TestTCPClose(t *testing.T) {
+ l, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer l.Close()
+
+ read := func(r io.Reader) error {
+ var m [1]byte
+ _, err := r.Read(m[:])
+ return err
+ }
+
+ go func() {
+ c, err := Dial("tcp", l.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ go read(c)
+
+ time.Sleep(10 * time.Millisecond)
+ c.Close()
+ }()
+
+ c, err := l.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ for err == nil {
+ err = read(c)
+ }
+ if err != nil && err != io.EOF {
+ t.Fatal(err)
+ }
+}
diff --git a/libgo/go/net/packetconn_test.go b/libgo/go/net/packetconn_test.go
new file mode 100644
index 0000000..5075baa
--- /dev/null
+++ b/libgo/go/net/packetconn_test.go
@@ -0,0 +1,161 @@
+// 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 net_test
+
+import (
+ "net"
+ "os"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+var packetConnTests = []struct {
+ net string
+ addr1 string
+ addr2 string
+}{
+ {"udp", "127.0.0.1:0", "127.0.0.1:0"},
+ {"ip:icmp", "127.0.0.1", "127.0.0.1"},
+ {"unixgram", "/tmp/gotest.net1", "/tmp/gotest.net2"},
+}
+
+func TestPacketConn(t *testing.T) {
+ for _, tt := range packetConnTests {
+ var wb []byte
+ netstr := strings.Split(tt.net, ":")
+ switch netstr[0] {
+ case "udp":
+ wb = []byte("UDP PACKETCONN TEST")
+ case "ip":
+ switch runtime.GOOS {
+ case "plan9":
+ continue
+ }
+ if os.Getuid() != 0 {
+ continue
+ }
+ id := os.Getpid() & 0xffff
+ wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST "))
+ case "unixgram":
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ continue
+ }
+ os.Remove(tt.addr1)
+ os.Remove(tt.addr2)
+ wb = []byte("UNIXGRAM PACKETCONN TEST")
+ default:
+ continue
+ }
+
+ c1, err := net.ListenPacket(tt.net, tt.addr1)
+ if err != nil {
+ t.Fatalf("net.ListenPacket failed: %v", err)
+ }
+ c1.LocalAddr()
+ c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c1.Close()
+
+ c2, err := net.ListenPacket(tt.net, tt.addr2)
+ if err != nil {
+ t.Fatalf("net.ListenPacket failed: %v", err)
+ }
+ c2.LocalAddr()
+ c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c2.Close()
+
+ if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil {
+ t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
+ }
+ rb2 := make([]byte, 128)
+ if _, _, err := c2.ReadFrom(rb2); err != nil {
+ t.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
+ }
+ if _, err := c2.WriteTo(wb, c1.LocalAddr()); err != nil {
+ t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
+ }
+ rb1 := make([]byte, 128)
+ if _, _, err := c1.ReadFrom(rb1); err != nil {
+ t.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
+ }
+
+ switch netstr[0] {
+ case "unixgram":
+ os.Remove(tt.addr1)
+ os.Remove(tt.addr2)
+ }
+ }
+}
+
+func TestConnAndPacketConn(t *testing.T) {
+ for _, tt := range packetConnTests {
+ var wb []byte
+ netstr := strings.Split(tt.net, ":")
+ switch netstr[0] {
+ case "udp":
+ wb = []byte("UDP PACKETCONN TEST")
+ case "ip":
+ switch runtime.GOOS {
+ case "plan9":
+ continue
+ }
+ if os.Getuid() != 0 {
+ continue
+ }
+ id := os.Getpid() & 0xffff
+ wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST"))
+ default:
+ continue
+ }
+
+ c1, err := net.ListenPacket(tt.net, tt.addr1)
+ if err != nil {
+ t.Fatalf("net.ListenPacket failed: %v", err)
+ }
+ c1.LocalAddr()
+ c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c1.Close()
+
+ c2, err := net.Dial(tt.net, c1.LocalAddr().String())
+ if err != nil {
+ t.Fatalf("net.Dial failed: %v", err)
+ }
+ c2.LocalAddr()
+ c2.RemoteAddr()
+ c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c2.Close()
+
+ if _, err := c2.Write(wb); err != nil {
+ t.Fatalf("net.Conn.Write failed: %v", err)
+ }
+ rb1 := make([]byte, 128)
+ if _, _, err := c1.ReadFrom(rb1); err != nil {
+ t.Fatalf("net.PacetConn.ReadFrom failed: %v", err)
+ }
+ var dst net.Addr
+ if netstr[0] == "ip" {
+ dst = &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}
+ } else {
+ dst = c2.LocalAddr()
+ }
+ if _, err := c1.WriteTo(wb, dst); err != nil {
+ t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
+ }
+ rb2 := make([]byte, 128)
+ if _, err := c2.Read(rb2); err != nil {
+ t.Fatalf("net.Conn.Read failed: %v", err)
+ }
+ }
+}
diff --git a/libgo/go/net/protoconn_test.go b/libgo/go/net/protoconn_test.go
new file mode 100644
index 0000000..f249372
--- /dev/null
+++ b/libgo/go/net/protoconn_test.go
@@ -0,0 +1,372 @@
+// 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 net_test
+
+import (
+ "bytes"
+ "net"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+)
+
+var condErrorf = func() func(*testing.T, string, ...interface{}) {
+ // A few APIs are not implemented yet on both Plan 9 and Windows.
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ return (*testing.T).Logf
+ }
+ return (*testing.T).Errorf
+}()
+
+func TestTCPListenerSpecificMethods(t *testing.T) {
+ la, err := net.ResolveTCPAddr("tcp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.ResolveTCPAddr failed: %v", err)
+ }
+ ln, err := net.ListenTCP("tcp4", la)
+ if err != nil {
+ t.Fatalf("net.ListenTCP failed: %v", err)
+ }
+ ln.Addr()
+ ln.SetDeadline(time.Now().Add(30 * time.Nanosecond))
+ defer ln.Close()
+
+ if c, err := ln.Accept(); err != nil {
+ if !err.(net.Error).Timeout() {
+ t.Errorf("net.TCPListener.Accept failed: %v", err)
+ return
+ }
+ } else {
+ c.Close()
+ }
+ if c, err := ln.AcceptTCP(); err != nil {
+ if !err.(net.Error).Timeout() {
+ t.Errorf("net.TCPListener.AcceptTCP failed: %v", err)
+ return
+ }
+ } else {
+ c.Close()
+ }
+
+ if f, err := ln.File(); err != nil {
+ condErrorf(t, "net.TCPListener.File failed: %v", err)
+ return
+ } else {
+ f.Close()
+ }
+}
+
+func TestTCPConnSpecificMethods(t *testing.T) {
+ la, err := net.ResolveTCPAddr("tcp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.ResolveTCPAddr failed: %v", err)
+ }
+ ln, err := net.ListenTCP("tcp4", la)
+ if err != nil {
+ t.Fatalf("net.ListenTCP failed: %v", err)
+ }
+ ln.Addr()
+ defer ln.Close()
+
+ done := make(chan int)
+ go transponder(t, ln, done)
+
+ ra, err := net.ResolveTCPAddr("tcp4", ln.Addr().String())
+ if err != nil {
+ t.Errorf("net.ResolveTCPAddr failed: %v", err)
+ return
+ }
+ c, err := net.DialTCP("tcp4", nil, ra)
+ if err != nil {
+ t.Errorf("net.DialTCP failed: %v", err)
+ return
+ }
+ c.SetKeepAlive(false)
+ c.SetLinger(0)
+ c.SetNoDelay(false)
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c.Close()
+
+ if _, err := c.Write([]byte("TCPCONN TEST")); err != nil {
+ t.Errorf("net.TCPConn.Write failed: %v", err)
+ return
+ }
+ rb := make([]byte, 128)
+ if _, err := c.Read(rb); err != nil {
+ t.Errorf("net.TCPConn.Read failed: %v", err)
+ return
+ }
+
+ <-done
+}
+
+func TestUDPConnSpecificMethods(t *testing.T) {
+ la, err := net.ResolveUDPAddr("udp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.ResolveUDPAddr failed: %v", err)
+ }
+ c, err := net.ListenUDP("udp4", la)
+ if err != nil {
+ t.Fatalf("net.ListenUDP failed: %v", err)
+ }
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetReadBuffer(2048)
+ c.SetWriteBuffer(2048)
+ defer c.Close()
+
+ wb := []byte("UDPCONN TEST")
+ rb := make([]byte, 128)
+ if _, err := c.WriteToUDP(wb, c.LocalAddr().(*net.UDPAddr)); err != nil {
+ t.Errorf("net.UDPConn.WriteToUDP failed: %v", err)
+ return
+ }
+ if _, _, err := c.ReadFromUDP(rb); err != nil {
+ t.Errorf("net.UDPConn.ReadFromUDP failed: %v", err)
+ return
+ }
+ if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*net.UDPAddr)); err != nil {
+ condErrorf(t, "net.UDPConn.WriteMsgUDP failed: %v", err)
+ return
+ }
+ if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil {
+ condErrorf(t, "net.UDPConn.ReadMsgUDP failed: %v", err)
+ return
+ }
+
+ if f, err := c.File(); err != nil {
+ condErrorf(t, "net.UDPConn.File failed: %v", err)
+ return
+ } else {
+ f.Close()
+ }
+}
+
+func TestIPConnSpecificMethods(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Logf("skipping read test on %q", runtime.GOOS)
+ return
+ }
+ if os.Getuid() != 0 {
+ t.Logf("skipping test; must be root")
+ return
+ }
+
+ la, err := net.ResolveIPAddr("ip4", "127.0.0.1")
+ if err != nil {
+ t.Fatalf("net.ResolveIPAddr failed: %v", err)
+ }
+ c, err := net.ListenIP("ip4:icmp", la)
+ if err != nil {
+ t.Fatalf("net.ListenIP failed: %v", err)
+ }
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ c.SetReadBuffer(2048)
+ c.SetWriteBuffer(2048)
+ defer c.Close()
+
+ id := os.Getpid() & 0xffff
+ wb := newICMPEchoRequest(id, 1, 128, []byte("IPCONN TEST "))
+ rb := make([]byte, 20+128)
+ if _, err := c.WriteToIP(wb, c.LocalAddr().(*net.IPAddr)); err != nil {
+ t.Errorf("net.IPConn.WriteToIP failed: %v", err)
+ return
+ }
+ if _, _, err := c.ReadFromIP(rb); err != nil {
+ t.Errorf("net.IPConn.ReadFromIP failed: %v", err)
+ return
+ }
+ if _, _, err := c.WriteMsgIP(wb, nil, c.LocalAddr().(*net.IPAddr)); err != nil {
+ condErrorf(t, "net.UDPConn.WriteMsgIP failed: %v", err)
+ return
+ }
+ if _, _, _, _, err := c.ReadMsgIP(rb, nil); err != nil {
+ condErrorf(t, "net.UDPConn.ReadMsgIP failed: %v", err)
+ return
+ }
+
+ if f, err := c.File(); err != nil {
+ condErrorf(t, "net.IPConn.File failed: %v", err)
+ return
+ } else {
+ f.Close()
+ }
+}
+
+func TestUnixListenerSpecificMethods(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ t.Logf("skipping read test on %q", runtime.GOOS)
+ return
+ }
+
+ p := "/tmp/gotest.net"
+ os.Remove(p)
+ la, err := net.ResolveUnixAddr("unix", p)
+ if err != nil {
+ t.Fatalf("net.ResolveUnixAddr failed: %v", err)
+ }
+ ln, err := net.ListenUnix("unix", la)
+ if err != nil {
+ t.Fatalf("net.ListenUnix failed: %v", err)
+ }
+ ln.Addr()
+ ln.SetDeadline(time.Now().Add(30 * time.Nanosecond))
+ defer ln.Close()
+ defer os.Remove(p)
+
+ if c, err := ln.Accept(); err != nil {
+ if !err.(net.Error).Timeout() {
+ t.Errorf("net.TCPListener.AcceptTCP failed: %v", err)
+ return
+ }
+ } else {
+ c.Close()
+ }
+ if c, err := ln.AcceptUnix(); err != nil {
+ if !err.(net.Error).Timeout() {
+ t.Errorf("net.TCPListener.AcceptTCP failed: %v", err)
+ return
+ }
+ } else {
+ c.Close()
+ }
+
+ if f, err := ln.File(); err != nil {
+ t.Errorf("net.UnixListener.File failed: %v", err)
+ return
+ } else {
+ f.Close()
+ }
+}
+
+func TestUnixConnSpecificMethods(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ t.Logf("skipping test on %q", runtime.GOOS)
+ return
+ }
+
+ p1, p2 := "/tmp/gotest.net1", "/tmp/gotest.net2"
+ os.Remove(p1)
+ os.Remove(p2)
+
+ a1, err := net.ResolveUnixAddr("unixgram", p1)
+ if err != nil {
+ t.Fatalf("net.ResolveUnixAddr failed: %v", err)
+ }
+ c1, err := net.DialUnix("unixgram", a1, nil)
+ if err != nil {
+ t.Fatalf("net.DialUnix failed: %v", err)
+ }
+ c1.LocalAddr()
+ c1.RemoteAddr()
+ c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetReadBuffer(2048)
+ c1.SetWriteBuffer(2048)
+ defer c1.Close()
+ defer os.Remove(p1)
+
+ a2, err := net.ResolveUnixAddr("unixgram", p2)
+ if err != nil {
+ t.Errorf("net.ResolveUnixAddr failed: %v", err)
+ return
+ }
+ c2, err := net.DialUnix("unixgram", a2, nil)
+ if err != nil {
+ t.Errorf("net.DialUnix failed: %v", err)
+ return
+ }
+ c2.LocalAddr()
+ c2.RemoteAddr()
+ c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetReadBuffer(2048)
+ c2.SetWriteBuffer(2048)
+ defer c2.Close()
+ defer os.Remove(p2)
+
+ wb := []byte("UNIXCONN TEST")
+ rb1 := make([]byte, 128)
+ rb2 := make([]byte, 128)
+ if _, _, err := c1.WriteMsgUnix(wb, nil, a2); err != nil {
+ t.Errorf("net.UnixConn.WriteMsgUnix failed: %v", err)
+ return
+ }
+ if _, _, _, _, err := c2.ReadMsgUnix(rb2, nil); err != nil {
+ t.Errorf("net.UnixConn.ReadMsgUnix failed: %v", err)
+ return
+ }
+ if _, err := c2.WriteToUnix(wb, a1); err != nil {
+ t.Errorf("net.UnixConn.WriteToUnix failed: %v", err)
+ return
+ }
+ if _, _, err := c1.ReadFromUnix(rb1); err != nil {
+ t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
+ return
+ }
+
+ // TODO: http://golang.org/issue/3875
+ net.ListenUnixgram("unixgram", nil)
+
+ if f, err := c1.File(); err != nil {
+ t.Errorf("net.UnixConn.File failed: %v", err)
+ return
+ } else {
+ f.Close()
+ }
+}
+
+func newICMPEchoRequest(id, seqnum, msglen int, filler []byte) []byte {
+ b := newICMPInfoMessage(id, seqnum, msglen, filler)
+ b[0] = 8
+ // calculate ICMP checksum
+ cklen := len(b)
+ s := uint32(0)
+ for i := 0; i < cklen-1; i += 2 {
+ s += uint32(b[i+1])<<8 | uint32(b[i])
+ }
+ if cklen&1 == 1 {
+ s += uint32(b[cklen-1])
+ }
+ s = (s >> 16) + (s & 0xffff)
+ s = s + (s >> 16)
+ // place checksum back in header; using ^= avoids the
+ // assumption the checksum bytes are zero
+ b[2] ^= byte(^s & 0xff)
+ b[3] ^= byte(^s >> 8)
+ return b
+}
+
+func newICMPInfoMessage(id, seqnum, msglen int, filler []byte) []byte {
+ b := make([]byte, msglen)
+ copy(b[8:], bytes.Repeat(filler, (msglen-8)/len(filler)+1))
+ b[0] = 0 // type
+ b[1] = 0 // code
+ b[2] = 0 // checksum
+ b[3] = 0 // checksum
+ b[4] = byte(id >> 8) // identifier
+ b[5] = byte(id & 0xff) // identifier
+ b[6] = byte(seqnum >> 8) // sequence number
+ b[7] = byte(seqnum & 0xff) // sequence number
+ return b
+}
diff --git a/libgo/go/net/rpc/client.go b/libgo/go/net/rpc/client.go
index 7d3d0bb..ee3cc4d 100644
--- a/libgo/go/net/rpc/client.go
+++ b/libgo/go/net/rpc/client.go
@@ -228,7 +228,7 @@ func DialHTTP(network, address string) (*Client, error) {
return DialHTTPPath(network, address, DefaultRPCPath)
}
-// DialHTTPPath connects to an HTTP RPC server
+// DialHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func DialHTTPPath(network, address, path string) (*Client, error) {
var err error
diff --git a/libgo/go/net/rpc/server.go b/libgo/go/net/rpc/server.go
index e528220..8898b98 100644
--- a/libgo/go/net/rpc/server.go
+++ b/libgo/go/net/rpc/server.go
@@ -227,7 +227,7 @@ func (server *Server) Register(rcvr interface{}) error {
return server.register(rcvr, "", false)
}
-// RegisterName is like Register but uses the provided name for the type
+// RegisterName is like Register but uses the provided name for the type
// instead of the receiver's concrete type.
func (server *Server) RegisterName(name string, rcvr interface{}) error {
return server.register(rcvr, name, true)
@@ -261,8 +261,30 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
s.method = make(map[string]*methodType)
// Install the methods
- for m := 0; m < s.typ.NumMethod(); m++ {
- method := s.typ.Method(m)
+ s.method = suitableMethods(s.typ, true)
+
+ if len(s.method) == 0 {
+ str := ""
+ // To help the user, see if a pointer receiver would work.
+ method := suitableMethods(reflect.PtrTo(s.typ), false)
+ if len(method) != 0 {
+ str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
+ } else {
+ str = "rpc.Register: type " + sname + " has no exported methods of suitable type"
+ }
+ log.Print(str)
+ return errors.New(str)
+ }
+ server.serviceMap[s.name] = s
+ return nil
+}
+
+// suitableMethods returns suitable Rpc methods of typ, it will report
+// error using log if reportErr is true.
+func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
+ methods := make(map[string]*methodType)
+ for m := 0; m < typ.NumMethod(); m++ {
+ method := typ.Method(m)
mtype := method.Type
mname := method.Name
// Method must be exported.
@@ -271,46 +293,51 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
}
// Method needs three ins: receiver, *args, *reply.
if mtype.NumIn() != 3 {
- log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
+ if reportErr {
+ log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
+ }
continue
}
// First arg need not be a pointer.
argType := mtype.In(1)
if !isExportedOrBuiltinType(argType) {
- log.Println(mname, "argument type not exported:", argType)
+ if reportErr {
+ log.Println(mname, "argument type not exported:", argType)
+ }
continue
}
// Second arg must be a pointer.
replyType := mtype.In(2)
if replyType.Kind() != reflect.Ptr {
- log.Println("method", mname, "reply type not a pointer:", replyType)
+ if reportErr {
+ log.Println("method", mname, "reply type not a pointer:", replyType)
+ }
continue
}
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) {
- log.Println("method", mname, "reply type not exported:", replyType)
+ if reportErr {
+ log.Println("method", mname, "reply type not exported:", replyType)
+ }
continue
}
// Method needs one out.
if mtype.NumOut() != 1 {
- log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
+ if reportErr {
+ log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
+ }
continue
}
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError {
- log.Println("method", mname, "returns", returnType.String(), "not error")
+ if reportErr {
+ log.Println("method", mname, "returns", returnType.String(), "not error")
+ }
continue
}
- s.method[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
- }
-
- if len(s.method) == 0 {
- s := "rpc Register: type " + sname + " has no exported methods of suitable type"
- log.Print(s)
- return errors.New(s)
+ methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
}
- server.serviceMap[s.name] = s
- return nil
+ return methods
}
// A value sent as a placeholder for the server's response value when the server
@@ -569,7 +596,7 @@ func (server *Server) Accept(lis net.Listener) {
// Register publishes the receiver's methods in the DefaultServer.
func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) }
-// RegisterName is like Register but uses the provided name for the type
+// RegisterName is like Register but uses the provided name for the type
// instead of the receiver's concrete type.
func RegisterName(name string, rcvr interface{}) error {
return DefaultServer.RegisterName(name, rcvr)
@@ -612,7 +639,7 @@ func ServeRequest(codec ServerCodec) error {
}
// Accept accepts connections on the listener and serves requests
-// to DefaultServer for each incoming connection.
+// to DefaultServer for each incoming connection.
// Accept blocks; the caller typically invokes it in a go statement.
func Accept(lis net.Listener) { DefaultServer.Accept(lis) }
diff --git a/libgo/go/net/rpc/server_test.go b/libgo/go/net/rpc/server_test.go
index 62c7b1e..d9ebe71 100644
--- a/libgo/go/net/rpc/server_test.go
+++ b/libgo/go/net/rpc/server_test.go
@@ -349,6 +349,7 @@ func testServeRequest(t *testing.T, server *Server) {
type ReplyNotPointer int
type ArgNotPublic int
type ReplyNotPublic int
+type NeedsPtrType int
type local struct{}
func (t *ReplyNotPointer) ReplyNotPointer(args *Args, reply Reply) error {
@@ -363,19 +364,29 @@ func (t *ReplyNotPublic) ReplyNotPublic(args *Args, reply *local) error {
return nil
}
+func (t *NeedsPtrType) NeedsPtrType(args *Args, reply *Reply) error {
+ return nil
+}
+
// Check that registration handles lots of bad methods and a type with no suitable methods.
func TestRegistrationError(t *testing.T) {
err := Register(new(ReplyNotPointer))
if err == nil {
- t.Errorf("expected error registering ReplyNotPointer")
+ t.Error("expected error registering ReplyNotPointer")
}
err = Register(new(ArgNotPublic))
if err == nil {
- t.Errorf("expected error registering ArgNotPublic")
+ t.Error("expected error registering ArgNotPublic")
}
err = Register(new(ReplyNotPublic))
if err == nil {
- t.Errorf("expected error registering ReplyNotPublic")
+ t.Error("expected error registering ReplyNotPublic")
+ }
+ err = Register(NeedsPtrType(0))
+ if err == nil {
+ t.Error("expected error registering NeedsPtrType")
+ } else if !strings.Contains(err.Error(), "pointer") {
+ t.Error("expected hint when registering NeedsPtrType")
}
}
@@ -499,6 +510,27 @@ func TestClientWriteError(t *testing.T) {
w.done <- true
}
+func TestTCPClose(t *testing.T) {
+ once.Do(startServer)
+
+ client, err := dialHTTP()
+ if err != nil {
+ t.Fatalf("dialing: %v", err)
+ }
+ defer client.Close()
+
+ args := Args{17, 8}
+ var reply Reply
+ err = client.Call("Arith.Mul", args, &reply)
+ if err != nil {
+ t.Fatal("arith error:", err)
+ }
+ t.Logf("Arith: %d*%d=%d\n", args.A, args.B, reply)
+ if reply.C != args.A*args.B {
+ t.Errorf("Add: expected %d got %d", reply.C, args.A*args.B)
+ }
+}
+
func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) {
b.StopTimer()
once.Do(startServer)
diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go
index f5a6d88..2d64f2f 100644
--- a/libgo/go/net/sendfile_windows.go
+++ b/libgo/go/net/sendfile_windows.go
@@ -48,12 +48,12 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, false
}
- c.wio.Lock()
- defer c.wio.Unlock()
if err := c.incref(false); err != nil {
return 0, err, true
}
defer c.decref()
+ c.wio.Lock()
+ defer c.wio.Unlock()
var o sendfileOp
o.Init(c, 'w')
diff --git a/libgo/go/net/sock_posix.go b/libgo/go/net/sock_posix.go
index dc5247a..78417fd 100644
--- a/libgo/go/net/sock_posix.go
+++ b/libgo/go/net/sock_posix.go
@@ -11,12 +11,13 @@ package net
import (
"io"
"syscall"
+ "time"
)
var listenerBacklog = maxListenerBacklog()
// Generic socket creation.
-func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
s, err := syscall.Socket(f, t, p)
@@ -32,13 +33,19 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
return nil, err
}
- var blsa syscall.Sockaddr
if ulsa != nil {
- if blsa, err = listenerSockaddr(s, f, ulsa, toAddr); err != nil {
+ // We provide a socket that listens to a wildcard
+ // address with reusable UDP port when the given ulsa
+ // is an appropriate UDP multicast address prefix.
+ // This makes it possible for a single UDP listener
+ // to join multiple different group addresses, for
+ // multiple UDP listeners that listen on the same UDP
+ // port to join the same group address.
+ if ulsa, err = listenerSockaddr(s, f, ulsa, toAddr); err != nil {
closesocket(s)
return nil, err
}
- if err = syscall.Bind(s, blsa); err != nil {
+ if err = syscall.Bind(s, ulsa); err != nil {
closesocket(s)
return nil, err
}
@@ -50,21 +57,20 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
}
if ursa != nil {
+ if !deadline.IsZero() {
+ fd.wdeadline = deadline.UnixNano()
+ }
if err = fd.connect(ursa); err != nil {
closesocket(s)
fd.Close()
return nil, err
}
fd.isConnected = true
+ fd.wdeadline = 0
}
lsa, _ := syscall.Getsockname(s)
- var laddr Addr
- if ulsa != nil && blsa != ulsa {
- laddr = toAddr(ulsa)
- } else {
- laddr = toAddr(lsa)
- }
+ laddr := toAddr(lsa)
rsa, _ := syscall.Getpeername(s)
raddr := toAddr(rsa)
fd.setAddr(laddr, raddr)
diff --git a/libgo/go/net/tcp_test.go b/libgo/go/net/tcp_test.go
index 53daf5b..f6e4df3 100644
--- a/libgo/go/net/tcp_test.go
+++ b/libgo/go/net/tcp_test.go
@@ -116,3 +116,33 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool) {
sem <- true
}
}
+
+var tcpListenerNameTests = []struct {
+ net string
+ laddr *TCPAddr
+}{
+ {"tcp4", &TCPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"tcp4", &TCPAddr{}},
+ {"tcp4", nil},
+}
+
+func TestTCPListenerName(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Logf("skipping test to avoid external network")
+ return
+ }
+
+ for _, tt := range tcpListenerNameTests {
+ ln, err := ListenTCP(tt.net, tt.laddr)
+ if err != nil {
+ t.Errorf("ListenTCP failed: %v", err)
+ return
+ }
+ defer ln.Close()
+ la := ln.Addr()
+ if a, ok := la.(*TCPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
+ }
+ }
+}
diff --git a/libgo/go/net/tcpsock.go b/libgo/go/net/tcpsock.go
index 47fbf29..6aba1f8 100644
--- a/libgo/go/net/tcpsock.go
+++ b/libgo/go/net/tcpsock.go
@@ -6,6 +6,8 @@
package net
+import "time"
+
// TCPAddr represents the address of a TCP end point.
type TCPAddr struct {
IP IP
@@ -28,7 +30,11 @@ func (a *TCPAddr) String() string {
// "tcp4" or "tcp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
- ip, port, err := hostPortToIP(net, addr)
+ return resolveTCPAddr(net, addr, noDeadline)
+}
+
+func resolveTCPAddr(net, addr string, deadline time.Time) (*TCPAddr, error) {
+ ip, port, err := hostPortToIP(net, addr, deadline)
if err != nil {
return nil, err
}
diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go
index 4121dd8..cec5bd2 100644
--- a/libgo/go/net/tcpsock_plan9.go
+++ b/libgo/go/net/tcpsock_plan9.go
@@ -6,12 +6,26 @@
package net
-import "syscall"
+import (
+ "io"
+ "os"
+ "syscall"
+ "time"
+)
// TCPConn is an implementation of the Conn interface for TCP network
// connections.
type TCPConn struct {
- plan9Conn
+ conn
+}
+
+func newTCPConn(fd *netFD) *TCPConn {
+ return &TCPConn{conn{fd}}
+}
+
+// ReadFrom implements the io.ReaderFrom ReadFrom method.
+func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
+ return 0, syscall.EPLAN9
}
// CloseRead shuts down the reading side of the TCP connection.
@@ -20,7 +34,7 @@ func (c *TCPConn) CloseRead() error {
if !c.ok() {
return syscall.EINVAL
}
- return syscall.EPLAN9
+ return c.fd.CloseRead()
}
// CloseWrite shuts down the writing side of the TCP connection.
@@ -29,6 +43,35 @@ func (c *TCPConn) CloseWrite() error {
if !c.ok() {
return syscall.EINVAL
}
+ return c.fd.CloseWrite()
+}
+
+// SetLinger sets the behavior of Close() on a connection which still
+// has data waiting to be sent or to be acknowledged.
+//
+// If sec < 0 (the default), Close returns immediately and the
+// operating system finishes sending the data in the background.
+//
+// If sec == 0, Close returns immediately and the operating system
+// discards any unsent or unacknowledged data.
+//
+// If sec > 0, Close blocks for at most sec seconds waiting for data
+// to be sent and acknowledged.
+func (c *TCPConn) SetLinger(sec int) error {
+ return syscall.EPLAN9
+}
+
+// SetKeepAlive sets whether the operating system should send
+// keepalive messages on the connection.
+func (c *TCPConn) SetKeepAlive(keepalive bool) error {
+ return syscall.EPLAN9
+}
+
+// SetNoDelay controls whether the operating system should delay
+// packet transmission in hopes of sending fewer packets (Nagle's
+// algorithm). The default is true (no delay), meaning that data is
+// sent as soon as possible after a Write.
+func (c *TCPConn) SetNoDelay(noDelay bool) error {
return syscall.EPLAN9
}
@@ -36,6 +79,13 @@ func (c *TCPConn) CloseWrite() error {
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is
// used as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) {
+ return dialTCP(net, laddr, raddr, noDeadline)
+}
+
+func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
+ if !deadline.IsZero() {
+ panic("net.dialTCP: deadline not implemented on Plan 9")
+ }
switch net {
case "tcp", "tcp4", "tcp6":
default:
@@ -44,46 +94,91 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) {
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- c1, err := dialPlan9(net, laddr, raddr)
+ fd, err := dialPlan9(net, laddr, raddr)
if err != nil {
- return
+ return nil, err
}
- return &TCPConn{*c1}, nil
+ return &TCPConn{conn{fd}}, nil
}
// TCPListener is a TCP network listener. Clients should typically
// use variables of type Listener instead of assuming TCP.
type TCPListener struct {
- plan9Listener
+ fd *netFD
}
+// AcceptTCP accepts the next incoming call and returns the new
+// connection and the remote address.
+func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
+ return nil, syscall.EINVAL
+ }
+ fd, err := l.fd.acceptPlan9()
+ if err != nil {
+ return nil, err
+ }
+ return newTCPConn(fd), nil
+}
+
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
+func (l *TCPListener) Accept() (Conn, error) {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
+ return nil, syscall.EINVAL
+ }
+ c, err := l.AcceptTCP()
+ if err != nil {
+ return nil, err
+ }
+ return c, nil
+}
+
+// Close stops listening on the TCP address.
+// Already Accepted connections are not closed.
func (l *TCPListener) Close() error {
- if l == nil || l.ctl == nil {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
return syscall.EINVAL
}
- if _, err := l.ctl.WriteString("hangup"); err != nil {
- l.ctl.Close()
+ if _, err := l.fd.ctl.WriteString("hangup"); err != nil {
+ l.fd.ctl.Close()
return err
}
- return l.ctl.Close()
+ return l.fd.ctl.Close()
+}
+
+// Addr returns the listener's network address, a *TCPAddr.
+func (l *TCPListener) Addr() Addr { return l.fd.laddr }
+
+// SetDeadline sets the deadline associated with the listener.
+// A zero time value disables the deadline.
+func (l *TCPListener) SetDeadline(t time.Time) error {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
+ return syscall.EINVAL
+ }
+ return setDeadline(l.fd, t)
}
+// File returns a copy of the underlying os.File, set to blocking
+// mode. It is the caller's responsibility to close f when finished.
+// Closing l does not affect f, and closing f does not affect l.
+func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
+
// ListenTCP announces on the TCP address laddr and returns a TCP
// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a
// port of 0, it means to listen on some available port. The caller
// can use l.Addr() to retrieve the chosen address.
-func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err error) {
+func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
return nil, UnknownNetworkError(net)
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ laddr = &TCPAddr{}
}
- l1, err := listenPlan9(net, laddr)
+ fd, err := listenPlan9(net, laddr)
if err != nil {
- return
+ return nil, err
}
- return &TCPListener{*l1}, nil
+ return &TCPListener{fd}, nil
}
diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go
index 2c34d2f..e5b3a09 100644
--- a/libgo/go/net/tcpsock_posix.go
+++ b/libgo/go/net/tcpsock_posix.go
@@ -143,11 +143,19 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
+ return dialTCP(net, laddr, raddr, noDeadline)
+}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, 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
@@ -177,7 +185,7 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
if err == nil {
fd.Close()
}
- fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
}
if err != nil {
@@ -220,29 +228,10 @@ type TCPListener struct {
fd *netFD
}
-// ListenTCP announces on the TCP address laddr and returns a TCP listener.
-// Net must be "tcp", "tcp4", or "tcp6".
-// If laddr has a port of 0, it means to listen on some available port.
-// The caller can use l.Addr() to retrieve the chosen address.
-func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
- if err != nil {
- return nil, err
- }
- err = syscall.Listen(fd.sysfd, listenerBacklog)
- if err != nil {
- closesocket(fd.sysfd)
- return nil, &OpError{"listen", net, laddr, err}
- }
- l := new(TCPListener)
- l.fd = fd
- return l, nil
-}
-
// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
- if l == nil || l.fd == nil || l.fd.sysfd < 0 {
+ if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
fd, err := l.fd.accept(sockaddrToTCP)
@@ -287,3 +276,30 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
// It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
+
+// ListenTCP announces on the TCP address laddr and returns a TCP listener.
+// Net must be "tcp", "tcp4", or "tcp6".
+// If laddr has a port of 0, it means to listen on some available port.
+// The caller can use l.Addr() to retrieve the chosen address.
+func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
+ if laddr == nil {
+ laddr = &TCPAddr{}
+ }
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
+ if err != nil {
+ return nil, err
+ }
+ err = syscall.Listen(fd.sysfd, listenerBacklog)
+ if err != nil {
+ closesocket(fd.sysfd)
+ return nil, &OpError{"listen", net, laddr, err}
+ }
+ l := new(TCPListener)
+ l.fd = fd
+ return l, nil
+}
diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go
index 3777424..855350c 100644
--- a/libgo/go/net/textproto/reader.go
+++ b/libgo/go/net/textproto/reader.go
@@ -486,6 +486,7 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
// letter and any letter following a hyphen to upper case;
// the rest are converted to lowercase. For example, the
// canonical key for "accept-encoding" is "Accept-Encoding".
+// MIME header keys are assumed to be ASCII only.
func CanonicalMIMEHeaderKey(s string) string {
// Quick check for canonical encoding.
upper := true
@@ -502,28 +503,90 @@ func CanonicalMIMEHeaderKey(s string) string {
return s
}
+const toLower = 'a' - 'A'
+
// canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is
// allowed to mutate the provided byte slice before returning the
// string.
func canonicalMIMEHeaderKey(a []byte) string {
- // Canonicalize: first letter upper case
- // and upper case after each dash.
- // (Host, User-Agent, If-Modified-Since).
- // MIME headers are ASCII only, so no Unicode issues.
+ // Look for it in commonHeaders , so that we can avoid an
+ // allocation by sharing the strings among all users
+ // of textproto. If we don't find it, a has been canonicalized
+ // so just return string(a).
upper := true
- for i, v := range a {
- if v == ' ' {
+ lo := 0
+ hi := len(commonHeaders)
+ for i := 0; i < len(a); i++ {
+ // Canonicalize: first letter upper case
+ // and upper case after each dash.
+ // (Host, User-Agent, If-Modified-Since).
+ // MIME headers are ASCII only, so no Unicode issues.
+ if a[i] == ' ' {
a[i] = '-'
upper = true
continue
}
- if upper && 'a' <= v && v <= 'z' {
- a[i] = v + 'A' - 'a'
+ c := a[i]
+ if upper && 'a' <= c && c <= 'z' {
+ c -= toLower
+ } else if !upper && 'A' <= c && c <= 'Z' {
+ c += toLower
}
- if !upper && 'A' <= v && v <= 'Z' {
- a[i] = v + 'a' - 'A'
+ a[i] = c
+ upper = c == '-' // for next time
+
+ if lo < hi {
+ for lo < hi && (len(commonHeaders[lo]) <= i || commonHeaders[lo][i] < c) {
+ lo++
+ }
+ for hi > lo && commonHeaders[hi-1][i] > c {
+ hi--
+ }
}
- upper = v == '-'
+ }
+ if lo < hi && len(commonHeaders[lo]) == len(a) {
+ return commonHeaders[lo]
}
return string(a)
}
+
+var commonHeaders = []string{
+ "Accept",
+ "Accept-Charset",
+ "Accept-Encoding",
+ "Accept-Language",
+ "Accept-Ranges",
+ "Cache-Control",
+ "Cc",
+ "Connection",
+ "Content-Id",
+ "Content-Language",
+ "Content-Length",
+ "Content-Transfer-Encoding",
+ "Content-Type",
+ "Date",
+ "Dkim-Signature",
+ "Etag",
+ "Expires",
+ "From",
+ "Host",
+ "If-Modified-Since",
+ "If-None-Match",
+ "In-Reply-To",
+ "Last-Modified",
+ "Location",
+ "Message-Id",
+ "Mime-Version",
+ "Pragma",
+ "Received",
+ "Return-Path",
+ "Server",
+ "Set-Cookie",
+ "Subject",
+ "To",
+ "User-Agent",
+ "Via",
+ "X-Forwarded-For",
+ "X-Imforwards",
+ "X-Powered-By",
+}
diff --git a/libgo/go/net/textproto/reader_test.go b/libgo/go/net/textproto/reader_test.go
index 9b6c76a..26987f6 100644
--- a/libgo/go/net/textproto/reader_test.go
+++ b/libgo/go/net/textproto/reader_test.go
@@ -24,6 +24,7 @@ var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{
{"uSER-aGENT", "User-Agent"},
{"user-agent", "User-Agent"},
{"USER-AGENT", "User-Agent"},
+ {"üser-agenT", "üser-Agent"}, // non-ASCII unchanged
}
func TestCanonicalMIMEHeaderKey(t *testing.T) {
@@ -241,18 +242,94 @@ func TestRFC959Lines(t *testing.T) {
}
}
+func TestCommonHeaders(t *testing.T) {
+ // need to disable the commonHeaders-based optimization
+ // during this check, or we'd not be testing anything
+ oldch := commonHeaders
+ commonHeaders = []string{}
+ defer func() { commonHeaders = oldch }()
+
+ last := ""
+ for _, h := range oldch {
+ if last > h {
+ t.Errorf("%v is out of order", h)
+ }
+ if last == h {
+ t.Errorf("%v is duplicated", h)
+ }
+ if canon := CanonicalMIMEHeaderKey(h); h != canon {
+ t.Errorf("%v is not canonical", h)
+ }
+ last = h
+ }
+}
+
+var clientHeaders = strings.Replace(`Host: golang.org
+Connection: keep-alive
+Cache-Control: max-age=0
+Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3
+Accept-Encoding: gzip,deflate,sdch
+Accept-Language: en-US,en;q=0.8,fr-CH;q=0.6
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+COOKIE: __utma=000000000.0000000000.0000000000.0000000000.0000000000.00; __utmb=000000000.0.00.0000000000; __utmc=000000000; __utmz=000000000.0000000000.00.0.utmcsr=code.google.com|utmccn=(referral)|utmcmd=referral|utmcct=/p/go/issues/detail
+Non-Interned: test
+
+`, "\n", "\r\n", -1)
+
+var serverHeaders = strings.Replace(`Content-Type: text/html; charset=utf-8
+Content-Encoding: gzip
+Date: Thu, 27 Sep 2012 09:03:33 GMT
+Server: Google Frontend
+Cache-Control: private
+Content-Length: 2298
+VIA: 1.1 proxy.example.com:80 (XXX/n.n.n-nnn)
+Connection: Close
+Non-Interned: test
+
+`, "\n", "\r\n", -1)
+
func BenchmarkReadMIMEHeader(b *testing.B) {
var buf bytes.Buffer
br := bufio.NewReader(&buf)
r := NewReader(br)
for i := 0; i < b.N; i++ {
- buf.WriteString("User-Agent: not mozilla\r\nContent-Length: 23452\r\nContent-Type: text/html; charset-utf8\r\nFoo-Bar: foobar\r\nfoo-bar: some more string\r\n\r\n")
+ var want int
+ var find string
+ if (i & 1) == 1 {
+ buf.WriteString(clientHeaders)
+ want = 10
+ find = "Cookie"
+ } else {
+ buf.WriteString(serverHeaders)
+ want = 9
+ find = "Via"
+ }
+ h, err := r.ReadMIMEHeader()
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(h) != want {
+ b.Fatalf("wrong number of headers: got %d, want %d", len(h), want)
+ }
+ if _, ok := h[find]; !ok {
+ b.Fatalf("did not find key %s", find)
+ }
+ }
+}
+
+func BenchmarkUncommon(b *testing.B) {
+ var buf bytes.Buffer
+ br := bufio.NewReader(&buf)
+ r := NewReader(br)
+ for i := 0; i < b.N; i++ {
+ buf.WriteString("uncommon-header-for-benchmark: foo\r\n\r\n")
h, err := r.ReadMIMEHeader()
if err != nil {
b.Fatal(err)
}
- if len(h) != 4 {
- b.Fatalf("want 4")
+ if _, ok := h["Uncommon-Header-For-Benchmark"]; !ok {
+ b.Fatal("Missing result header.")
}
}
}
diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go
index 672fb72..68d8ced 100644
--- a/libgo/go/net/timeout_test.go
+++ b/libgo/go/net/timeout_test.go
@@ -119,3 +119,114 @@ func TestDeadlineReset(t *testing.T) {
t.Errorf("unexpected return from Accept; err=%v", err)
}
}
+
+func TestTimeoutAccept(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Logf("skipping test on %q", runtime.GOOS)
+ return
+ }
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+ tl := ln.(*TCPListener)
+ tl.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ errc := make(chan error, 1)
+ go func() {
+ _, err := ln.Accept()
+ errc <- err
+ }()
+ select {
+ case <-time.After(1 * time.Second):
+ // Accept shouldn't block indefinitely
+ t.Errorf("Accept didn't return in an expected time")
+ case <-errc:
+ // Pass.
+ }
+}
+
+func TestReadWriteDeadline(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Logf("skipping test on %q", runtime.GOOS)
+ return
+ }
+
+ if !canCancelIO {
+ t.Logf("skipping test on this system")
+ return
+ }
+ const (
+ readTimeout = 50 * time.Millisecond
+ writeTimeout = 250 * time.Millisecond
+ )
+ checkTimeout := func(command string, start time.Time, should time.Duration) {
+ is := time.Now().Sub(start)
+ d := is - should
+ if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
+ t.Errorf("%s timeout test failed: is=%v should=%v\n", command, is, should)
+ }
+ }
+
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("ListenTCP on :0: %v", err)
+ }
+
+ lnquit := make(chan bool)
+
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Accept: %v", err)
+ }
+ defer c.Close()
+ lnquit <- true
+ }()
+
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+
+ start := time.Now()
+ err = c.SetReadDeadline(start.Add(readTimeout))
+ if err != nil {
+ t.Fatalf("SetReadDeadline: %v", err)
+ }
+ err = c.SetWriteDeadline(start.Add(writeTimeout))
+ if err != nil {
+ t.Fatalf("SetWriteDeadline: %v", err)
+ }
+
+ quit := make(chan bool)
+
+ go func() {
+ var buf [10]byte
+ _, err := c.Read(buf[:])
+ if err == nil {
+ t.Errorf("Read should not succeed")
+ }
+ checkTimeout("Read", start, readTimeout)
+ quit <- true
+ }()
+
+ go func() {
+ var buf [10000]byte
+ for {
+ _, err := c.Write(buf[:])
+ if err != nil {
+ break
+ }
+ }
+ checkTimeout("Write", start, writeTimeout)
+ quit <- true
+ }()
+
+ <-quit
+ <-quit
+ <-lnquit
+}
diff --git a/libgo/go/net/udp_test.go b/libgo/go/net/udp_test.go
index f80d3b5..37b904f 100644
--- a/libgo/go/net/udp_test.go
+++ b/libgo/go/net/udp_test.go
@@ -87,3 +87,33 @@ func testWriteToPacketConn(t *testing.T, raddr string) {
t.Fatal("Write should fail")
}
}
+
+var udpConnLocalNameTests = []struct {
+ net string
+ laddr *UDPAddr
+}{
+ {"udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"udp4", &UDPAddr{}},
+ {"udp4", nil},
+}
+
+func TestUDPConnLocalName(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Logf("skipping test to avoid external network")
+ return
+ }
+
+ for _, tt := range udpConnLocalNameTests {
+ c, err := ListenUDP(tt.net, tt.laddr)
+ if err != nil {
+ t.Errorf("ListenUDP failed: %v", err)
+ return
+ }
+ defer c.Close()
+ la := c.LocalAddr()
+ if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
+ }
+ }
+}
diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go
index 62b27d9..bf2107b 100644
--- a/libgo/go/net/udpsock.go
+++ b/libgo/go/net/udpsock.go
@@ -6,7 +6,10 @@
package net
-import "errors"
+import (
+ "errors"
+ "time"
+)
var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
@@ -32,7 +35,11 @@ func (a *UDPAddr) String() string {
// "udp4" or "udp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
- ip, port, err := hostPortToIP(net, addr)
+ return resolveUDPAddr(net, addr, noDeadline)
+}
+
+func resolveUDPAddr(net, addr string, deadline time.Time) (*UDPAddr, error) {
+ ip, port, err := hostPortToIP(net, addr, deadline)
if err != nil {
return nil, err
}
diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go
index aaa7e5b..6a828e1 100644
--- a/libgo/go/net/udpsock_plan9.go
+++ b/libgo/go/net/udpsock_plan9.go
@@ -10,12 +10,13 @@ import (
"errors"
"os"
"syscall"
+ "time"
)
// UDPConn is the implementation of the Conn and PacketConn
// interfaces for UDP network connections.
type UDPConn struct {
- plan9Conn
+ conn
}
// UDP-specific methods.
@@ -31,14 +32,14 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
}
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
+ if c.fd.data == nil {
+ c.fd.data, err = os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0)
if err != nil {
return 0, nil, err
}
}
buf := make([]byte, udpHeaderSize+len(b))
- m, err := c.data.Read(buf)
+ m, err := c.fd.data.Read(buf)
if err != nil {
return
}
@@ -80,23 +81,23 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
if !c.ok() {
return 0, syscall.EINVAL
}
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
+ if c.fd.data == nil {
+ c.fd.data, err = os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0)
if err != nil {
return 0, err
}
}
h := new(udpHeader)
h.raddr = addr.IP.To16()
- h.laddr = c.laddr.(*UDPAddr).IP.To16()
+ h.laddr = c.fd.laddr.(*UDPAddr).IP.To16()
h.ifcaddr = IPv6zero // ignored (receive only)
h.rport = uint16(addr.Port)
- h.lport = uint16(c.laddr.(*UDPAddr).Port)
+ h.lport = uint16(c.fd.laddr.(*UDPAddr).Port)
buf := make([]byte, udpHeaderSize+len(b))
i := copy(buf, h.Bytes())
copy(buf[i:], b)
- return c.data.Write(buf)
+ return c.fd.data.Write(buf)
}
// WriteTo implements the PacketConn WriteTo method.
@@ -106,7 +107,7 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) {
}
a, ok := addr.(*UDPAddr)
if !ok {
- return 0, &OpError{"write", c.dir, addr, syscall.EINVAL}
+ return 0, &OpError{"write", c.fd.dir, addr, syscall.EINVAL}
}
return c.WriteToUDP(b, a)
}
@@ -122,6 +123,13 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is
// used as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
+ return dialUDP(net, laddr, raddr, noDeadline)
+}
+
+func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
+ if !deadline.IsZero() {
+ panic("net.dialUDP: deadline not implemented on Plan 9")
+ }
switch net {
case "udp", "udp4", "udp6":
default:
@@ -130,11 +138,11 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- c1, err := dialPlan9(net, laddr, raddr)
+ fd, err := dialPlan9(net, laddr, raddr)
if err != nil {
- return
+ return nil, err
}
- return &UDPConn{*c1}, nil
+ return &UDPConn{conn{fd}}, nil
}
const udpHeaderSize = 16*3 + 2*2
@@ -169,24 +177,24 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) {
// address laddr. The returned connection c's ReadFrom and WriteTo
// methods can be used to receive and send UDP packets with per-packet
// addressing.
-func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
+func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
return nil, UnknownNetworkError(net)
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ laddr = &UDPAddr{}
}
l, err := listenPlan9(net, laddr)
if err != nil {
- return
+ return nil, err
}
_, err = l.ctl.WriteString("headers")
if err != nil {
- return
+ return nil, err
}
- return &UDPConn{*l.plan9Conn()}, nil
+ return &UDPConn{conn{l.netFD()}}, nil
}
// ListenMulticastUDP listens for incoming multicast UDP packets
diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go
index e075380..d7329bf 100644
--- a/libgo/go/net/udpsock_posix.go
+++ b/libgo/go/net/udpsock_posix.go
@@ -8,7 +8,10 @@
package net
-import "syscall"
+import (
+ "syscall"
+ "time"
+)
func sockaddrToUDP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
@@ -160,6 +163,10 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
+ return dialUDP(net, laddr, raddr, noDeadline)
+}
+
+func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
@@ -168,7 +175,7 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
if err != nil {
return nil, err
}
@@ -186,9 +193,9 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
return nil, UnknownNetworkError(net)
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ laddr = &UDPAddr{}
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
return nil, err
}
@@ -208,7 +215,7 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e
if gaddr == nil || gaddr.IP == nil {
return nil, &OpError{"listenmulticast", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, gaddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
return nil, err
}
diff --git a/libgo/go/net/unicast_posix_test.go b/libgo/go/net/unicast_posix_test.go
index 5b39e25..e1d4b0d 100644
--- a/libgo/go/net/unicast_posix_test.go
+++ b/libgo/go/net/unicast_posix_test.go
@@ -171,9 +171,9 @@ var dualStackListenerTests = []struct {
// Test cases and expected results for the attemping 2nd listen on the same port
// 1st listen 2nd listen darwin freebsd linux openbsd
// ------------------------------------------------------------------------------------
- // "tcp" "" "tcp" "" - - - -
- // "tcp" "" "tcp" "0.0.0.0" - - - -
- // "tcp" "0.0.0.0" "tcp" "" - - - -
+ // "tcp" "" "tcp" "" - - - -
+ // "tcp" "" "tcp" "0.0.0.0" - - - -
+ // "tcp" "0.0.0.0" "tcp" "" - - - -
// ------------------------------------------------------------------------------------
// "tcp" "" "tcp" "[::]" - - - ok
// "tcp" "[::]" "tcp" "" - - - ok
diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go
index 2140375..342e26f 100644
--- a/libgo/go/net/unixsock_plan9.go
+++ b/libgo/go/net/unixsock_plan9.go
@@ -139,6 +139,10 @@ func (c *UnixConn) CloseWrite() error {
// which must be "unix" or "unixgram". If laddr is not nil, it is
// used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
+ return dialUnix(net, laddr, raddr, noDeadline)
+}
+
+func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go
index 2bef5ea..16ebd58 100644
--- a/libgo/go/net/unixsock_posix.go
+++ b/libgo/go/net/unixsock_posix.go
@@ -14,7 +14,7 @@ import (
"time"
)
-func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) {
+func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (fd *netFD, err error) {
var sotype int
switch net {
default:
@@ -59,7 +59,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
f = sockaddrToUnixpacket
}
- fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, f)
+ fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
if err != nil {
goto Error
}
@@ -229,7 +229,11 @@ func (c *UnixConn) CloseWrite() error {
// which must be "unix" or "unixgram". If laddr is not nil, it is used
// as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(net, laddr, raddr, "dial")
+ return dialUnix(net, laddr, raddr, noDeadline)
+}
+
+func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
+ fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
if err != nil {
return nil, err
}
@@ -253,7 +257,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
if laddr != nil {
laddr = &UnixAddr{laddr.Name, net} // make our own copy
}
- fd, err := unixSocket(net, laddr, nil, "listen")
+ fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
return nil, err
}
@@ -309,9 +313,7 @@ func (l *UnixListener) Close() error {
if l.path[0] != '@' {
syscall.Unlink(l.path)
}
- err := l.fd.Close()
- l.fd = nil
- return err
+ return l.fd.Close()
}
// Addr returns the listener's network address.
@@ -344,7 +346,7 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress}
}
- fd, err := unixSocket(net, laddr, nil, "listen")
+ fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
return nil, err
}