summaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-10-23 04:31:11 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-10-23 04:31:11 +0000
commit4ccad563d2a3559f0557bfb177bcf45144219bdf (patch)
tree46bb86f514fbf6bad82da48e69a18fb09d878834 /libgo/go/net
parent0b7463235f0e23c624d1911c9b15f531108cc5a6 (diff)
downloadgcc-4ccad563d2a3559f0557bfb177bcf45144219bdf.tar.gz
libgo: Update to current sources.
From-SVN: r192704
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/cgo_bsd.go4
-rw-r--r--libgo/go/net/cgo_linux.go8
-rw-r--r--libgo/go/net/cgo_netbsd.go14
-rw-r--r--libgo/go/net/cgo_unix.go10
-rw-r--r--libgo/go/net/dial.go2
-rw-r--r--libgo/go/net/dial_test.go2
-rw-r--r--libgo/go/net/dialgoogle_test.go11
-rw-r--r--libgo/go/net/dnsclient_unix.go28
-rw-r--r--libgo/go/net/dnsconfig_unix.go (renamed from libgo/go/net/dnsconfig.go)0
-rw-r--r--libgo/go/net/fd_unix.go (renamed from libgo/go/net/fd.go)97
-rw-r--r--libgo/go/net/fd_windows.go2
-rw-r--r--libgo/go/net/file_unix.go (renamed from libgo/go/net/file.go)14
-rw-r--r--libgo/go/net/http/client.go31
-rw-r--r--libgo/go/net/http/client_test.go6
-rw-r--r--libgo/go/net/http/cookie.go7
-rw-r--r--libgo/go/net/http/fs.go64
-rw-r--r--libgo/go/net/http/fs_test.go192
-rw-r--r--libgo/go/net/http/header.go85
-rw-r--r--libgo/go/net/http/header_test.go130
-rw-r--r--libgo/go/net/http/httptest/recorder.go24
-rw-r--r--libgo/go/net/http/httptest/recorder_test.go90
-rw-r--r--libgo/go/net/http/httputil/dump.go2
-rw-r--r--libgo/go/net/http/httputil/reverseproxy.go58
-rw-r--r--libgo/go/net/http/httputil/reverseproxy_test.go84
-rw-r--r--libgo/go/net/http/lex.go206
-rw-r--r--libgo/go/net/http/lex_test.go65
-rw-r--r--libgo/go/net/http/pprof/pprof.go4
-rw-r--r--libgo/go/net/http/request.go191
-rw-r--r--libgo/go/net/http/request_test.go40
-rw-r--r--libgo/go/net/http/response.go6
-rw-r--r--libgo/go/net/http/serve_test.go241
-rw-r--r--libgo/go/net/http/server.go133
-rw-r--r--libgo/go/net/http/server_test.go95
-rw-r--r--libgo/go/net/http/transfer.go10
-rw-r--r--libgo/go/net/http/transport.go134
-rw-r--r--libgo/go/net/http/transport_test.go68
-rw-r--r--libgo/go/net/interface_test.go10
-rw-r--r--libgo/go/net/ipraw_test.go4
-rw-r--r--libgo/go/net/iprawsock_plan9.go97
-rw-r--r--libgo/go/net/iprawsock_posix.go128
-rw-r--r--libgo/go/net/ipsock.go13
-rw-r--r--libgo/go/net/ipsock_plan9.go51
-rw-r--r--libgo/go/net/ipsock_posix.go5
-rw-r--r--libgo/go/net/lookup.go (renamed from libgo/go/net/doc.go)0
-rw-r--r--libgo/go/net/lookup_test.go12
-rw-r--r--libgo/go/net/mail/message.go12
-rw-r--r--libgo/go/net/mail/message_test.go17
-rw-r--r--libgo/go/net/multicast_posix_test.go (renamed from libgo/go/net/multicast_test.go)71
-rw-r--r--libgo/go/net/net_test.go82
-rw-r--r--libgo/go/net/newpollserver_unix.go (renamed from libgo/go/net/newpollserver.go)2
-rw-r--r--libgo/go/net/port.go73
-rw-r--r--libgo/go/net/port_unix.go69
-rw-r--r--libgo/go/net/rpc/client.go29
-rw-r--r--libgo/go/net/rpc/jsonrpc/all_test.go53
-rw-r--r--libgo/go/net/rpc/jsonrpc/server.go13
-rw-r--r--libgo/go/net/sendfile_freebsd.go105
-rw-r--r--libgo/go/net/sendfile_linux.go2
-rw-r--r--libgo/go/net/sendfile_stub.go2
-rw-r--r--libgo/go/net/sock_posix.go (renamed from libgo/go/net/sock.go)34
-rw-r--r--libgo/go/net/sockopt_posix.go (renamed from libgo/go/net/sockopt.go)0
-rw-r--r--libgo/go/net/sockoptip.go219
-rw-r--r--libgo/go/net/sockoptip_bsd.go36
-rw-r--r--libgo/go/net/sockoptip_darwin.go90
-rw-r--r--libgo/go/net/sockoptip_freebsd.go92
-rw-r--r--libgo/go/net/sockoptip_linux.go101
-rw-r--r--libgo/go/net/sockoptip_netbsd.go39
-rw-r--r--libgo/go/net/sockoptip_openbsd.go90
-rw-r--r--libgo/go/net/sockoptip_posix.go73
-rw-r--r--libgo/go/net/sockoptip_windows.go61
-rw-r--r--libgo/go/net/tcp_test.go118
-rw-r--r--libgo/go/net/tcpsock_plan9.go54
-rw-r--r--libgo/go/net/tcpsock_posix.go120
-rw-r--r--libgo/go/net/textproto/reader.go31
-rw-r--r--libgo/go/net/textproto/reader_test.go17
-rw-r--r--libgo/go/net/textproto/textproto.go26
-rw-r--r--libgo/go/net/udpsock.go4
-rw-r--r--libgo/go/net/udpsock_plan9.go68
-rw-r--r--libgo/go/net/udpsock_posix.go155
-rw-r--r--libgo/go/net/unicast_posix_test.go (renamed from libgo/go/net/unicast_test.go)89
-rw-r--r--libgo/go/net/unixsock_plan9.go158
-rw-r--r--libgo/go/net/unixsock_posix.go114
-rw-r--r--libgo/go/net/url/url.go28
-rw-r--r--libgo/go/net/url/url_test.go29
83 files changed, 2789 insertions, 2065 deletions
diff --git a/libgo/go/net/cgo_bsd.go b/libgo/go/net/cgo_bsd.go
index b8c6ef974e4..27c3e9acb94 100644
--- a/libgo/go/net/cgo_bsd.go
+++ b/libgo/go/net/cgo_bsd.go
@@ -12,6 +12,6 @@ package net
import "syscall"
-func cgoAddrInfoMask() int {
- return syscall.AI_MASK
+func cgoAddrInfoFlags() int {
+ return (syscall.AI_CANONNAME | syscall.AI_V4MAPPED | syscall.AI_ALL) & syscall.AI_MASK
}
diff --git a/libgo/go/net/cgo_linux.go b/libgo/go/net/cgo_linux.go
index 482435221e0..650575cce6c 100644
--- a/libgo/go/net/cgo_linux.go
+++ b/libgo/go/net/cgo_linux.go
@@ -10,6 +10,12 @@ package net
import "syscall"
-func cgoAddrInfoMask() int {
+func cgoAddrInfoFlags() int {
+ // NOTE(rsc): In theory there are approximately balanced
+ // arguments for and against including AI_ADDRCONFIG
+ // in the flags (it includes IPv4 results only on IPv4 systems,
+ // and similarly for IPv6), but in practice setting it causes
+ // getaddrinfo to return the wrong canonical name on Linux.
+ // So definitely leave it out.
return syscall.AI_CANONNAME | syscall.AI_V4MAPPED | syscall.AI_ALL
}
diff --git a/libgo/go/net/cgo_netbsd.go b/libgo/go/net/cgo_netbsd.go
new file mode 100644
index 00000000000..27334af641a
--- /dev/null
+++ b/libgo/go/net/cgo_netbsd.go
@@ -0,0 +1,14 @@
+// Copyright 2011 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
+
+/*
+#include <netdb.h>
+*/
+import "C"
+
+func cgoAddrInfoFlags() int {
+ return C.AI_CANONNAME
+}
diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go
index 6751b8cc0e5..69daedcdf85 100644
--- a/libgo/go/net/cgo_unix.go
+++ b/libgo/go/net/cgo_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux
+// +build darwin freebsd linux netbsd
package net
@@ -102,13 +102,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
var res *syscall.Addrinfo
var hints syscall.Addrinfo
- // NOTE(rsc): In theory there are approximately balanced
- // arguments for and against including AI_ADDRCONFIG
- // in the flags (it includes IPv4 results only on IPv4 systems,
- // and similarly for IPv6), but in practice setting it causes
- // getaddrinfo to return the wrong canonical name on Linux.
- // So definitely leave it out.
- hints.Ai_flags = int32((syscall.AI_ALL | syscall.AI_V4MAPPED | syscall.AI_CANONNAME) & cgoAddrInfoMask())
+ hints.Ai_flags = int32(cgoAddrInfoFlags())
h := syscall.StringBytePtr(name)
syscall.Entersyscall()
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go
index 51912397a46..752f81b618c 100644
--- a/libgo/go/net/dial.go
+++ b/libgo/go/net/dial.go
@@ -81,7 +81,7 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
// Dial("tcp", "google.com:80")
// Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
//
-// For IP networks, addr must be "ip", "ip4" or "ip6" followed
+// For IP networks, net must be "ip", "ip4" or "ip6" followed
// by a colon and a protocol number or name.
//
// Examples:
diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go
index 63b91b37ead..abd6b7fe920 100644
--- a/libgo/go/net/dial_test.go
+++ b/libgo/go/net/dial_test.go
@@ -130,7 +130,7 @@ func TestSelfConnect(t *testing.T) {
n = 1000
}
switch runtime.GOOS {
- case "darwin", "freebsd", "openbsd", "solaris", "windows":
+ case "darwin", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows":
// Non-Linux systems take a long time to figure
// out that there is nothing listening on localhost.
n = 100
diff --git a/libgo/go/net/dialgoogle_test.go b/libgo/go/net/dialgoogle_test.go
index 03c4499720f..426e2ffb001 100644
--- a/libgo/go/net/dialgoogle_test.go
+++ b/libgo/go/net/dialgoogle_test.go
@@ -41,17 +41,6 @@ func doDial(t *testing.T, network, addr string) {
fd.Close()
}
-func TestLookupCNAME(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
- }
- cname, err := LookupCNAME("www.google.com")
- if !strings.HasSuffix(cname, ".l.google.com.") || err != nil {
- t.Errorf(`LookupCNAME("www.google.com.") = %q, %v, want "*.l.google.com.", nil`, cname, err)
- }
-}
-
var googleaddrsipv4 = []string{
"%d.%d.%d.%d:80",
"www.google.com:80",
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go
index 18c39360e40..9e21bb4a0f6 100644
--- a/libgo/go/net/dnsclient_unix.go
+++ b/libgo/go/net/dnsclient_unix.go
@@ -237,24 +237,30 @@ func goLookupIP(name string) (addrs []IP, err error) {
}
var records []dnsRR
var cname string
- cname, records, err = lookup(name, dnsTypeA)
- if err != nil {
- return
- }
+ var err4, err6 error
+ cname, records, err4 = lookup(name, dnsTypeA)
addrs = convertRR_A(records)
if cname != "" {
name = cname
}
- _, records, err = lookup(name, dnsTypeAAAA)
- if err != nil && len(addrs) > 0 {
- // Ignore error because A lookup succeeded.
- err = nil
+ _, records, err6 = lookup(name, dnsTypeAAAA)
+ if err4 != nil && err6 == nil {
+ // Ignore A error because AAAA lookup succeeded.
+ err4 = nil
}
- if err != nil {
- return
+ if err6 != nil && len(addrs) > 0 {
+ // Ignore AAAA error because A lookup succeeded.
+ err6 = nil
}
+ if err4 != nil {
+ return nil, err4
+ }
+ if err6 != nil {
+ return nil, err6
+ }
+
addrs = append(addrs, convertRR_AAAA(records)...)
- return
+ return addrs, nil
}
// goLookupCNAME is the native Go implementation of LookupCNAME.
diff --git a/libgo/go/net/dnsconfig.go b/libgo/go/net/dnsconfig_unix.go
index bb46cc9007c..bb46cc9007c 100644
--- a/libgo/go/net/dnsconfig.go
+++ b/libgo/go/net/dnsconfig_unix.go
diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd_unix.go
index ff4f4f899e5..c55f3362f05 100644
--- a/libgo/go/net/fd.go
+++ b/libgo/go/net/fd_unix.go
@@ -10,6 +10,7 @@ import (
"errors"
"io"
"os"
+ "runtime"
"sync"
"syscall"
"time"
@@ -45,36 +46,16 @@ type netFD struct {
// owned by fd wait server
ncr, ncw int
+
+ // wait server
+ pollServer *pollServer
}
// A pollServer helps FDs determine when to retry a non-blocking
// read or write after they get EAGAIN. When an FD needs to wait,
-// send the fd on s.cr (for a read) or s.cw (for a write) to pass the
-// request to the poll server. Then receive on fd.cr/fd.cw.
+// call s.WaitRead() or s.WaitWrite() to pass the request to the poll server.
// When the pollServer finds that i/o on FD should be possible
-// again, it will send fd on fd.cr/fd.cw to wake any waiting processes.
-// This protocol is implemented as s.WaitRead() and s.WaitWrite().
-//
-// There is one subtlety: when sending on s.cr/s.cw, the
-// poll server is probably in a system call, waiting for an fd
-// to become ready. It's not looking at the request channels.
-// To resolve this, the poll server waits not just on the FDs it has
-// been given but also its own pipe. After sending on the
-// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a
-// byte to the pipe, causing the pollServer's poll system call to
-// return. In response to the pipe being readable, the pollServer
-// re-polls its request channels.
-//
-// Note that the ordering is "send request" and then "wake up server".
-// If the operations were reversed, there would be a race: the poll
-// server might wake up and look at the request channel, see that it
-// was empty, and go back to sleep, all before the requester managed
-// to send the request. Because the send must complete before the wakeup,
-// the request channel must be buffered. A buffer of size 1 is sufficient
-// for any request load. If many processes are trying to submit requests,
-// one will succeed, the pollServer will read the request, and then the
-// channel will be empty for the next process's request. A larger buffer
-// might help batch requests.
+// again, it will send on fd.cr/fd.cw to wake any waiting goroutines.
//
// To avoid races in closing, all fd operations are locked and
// refcounted. when netFD.Close() is called, it calls syscall.Shutdown
@@ -82,7 +63,6 @@ type netFD struct {
// will the fd be closed.
type pollServer struct {
- cr, cw chan *netFD // buffered >= 1
pr, pw *os.File
poll *pollster // low-level OS hooks
sync.Mutex // controls pending and deadline
@@ -279,21 +259,45 @@ func (s *pollServer) WaitWrite(fd *netFD) error {
}
// Network FD methods.
-// All the network FDs use a single pollServer.
+// Spread network FDs over several pollServers.
-var pollserver *pollServer
-var onceStartServer sync.Once
+var pollMaxN int
+var pollservers []*pollServer
+var startServersOnce []func()
+
+func init() {
+ pollMaxN = runtime.NumCPU()
+ if pollMaxN > 8 {
+ pollMaxN = 8 // No improvement then.
+ }
+ pollservers = make([]*pollServer, pollMaxN)
+ startServersOnce = make([]func(), pollMaxN)
+ for i := 0; i < pollMaxN; i++ {
+ k := i
+ once := new(sync.Once)
+ startServersOnce[i] = func() { once.Do(func() { startServer(k) }) }
+ }
+}
-func startServer() {
+func startServer(k int) {
p, err := newPollServer()
if err != nil {
- print("Start pollServer: ", err.Error(), "\n")
+ panic(err)
+ }
+ pollservers[k] = p
+}
+
+func server(fd int) *pollServer {
+ pollN := runtime.GOMAXPROCS(0)
+ if pollN > pollMaxN {
+ pollN = pollMaxN
}
- pollserver = p
+ k := fd % pollN
+ startServersOnce[k]()
+ return pollservers[k]
}
func newFD(fd, family, sotype int, net string) (*netFD, error) {
- onceStartServer.Do(startServer)
if err := syscall.SetNonblock(fd, true); err != nil {
return nil, err
}
@@ -305,6 +309,7 @@ func newFD(fd, family, sotype int, net string) (*netFD, error) {
}
netfd.cr = make(chan error, 1)
netfd.cw = make(chan error, 1)
+ netfd.pollServer = server(fd)
return netfd, nil
}
@@ -324,7 +329,7 @@ func (fd *netFD) setAddr(laddr, raddr Addr) {
func (fd *netFD) connect(ra syscall.Sockaddr) error {
err := syscall.Connect(fd.sysfd, ra)
if err == syscall.EINPROGRESS {
- if err = pollserver.WaitWrite(fd); err != nil {
+ if err = fd.pollServer.WaitWrite(fd); err != nil {
return err
}
var e int
@@ -378,8 +383,8 @@ func (fd *netFD) decref() {
}
func (fd *netFD) Close() error {
- pollserver.Lock() // needed for both fd.incref(true) and pollserver.Evict
- defer pollserver.Unlock()
+ fd.pollServer.Lock() // needed for both fd.incref(true) and pollserver.Evict
+ defer fd.pollServer.Unlock()
if err := fd.incref(true); err != nil {
return err
}
@@ -388,7 +393,7 @@ func (fd *netFD) Close() error {
// the final decref will close fd.sysfd. This should happen
// fairly quickly, since all the I/O is non-blocking, and any
// attempts to block in the pollserver will return errClosing.
- pollserver.Evict(fd)
+ fd.pollServer.Evict(fd)
fd.decref()
return nil
}
@@ -425,7 +430,7 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
if err == syscall.EAGAIN {
err = errTimeout
if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
+ if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
@@ -455,7 +460,7 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
if err == syscall.EAGAIN {
err = errTimeout
if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
+ if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
@@ -483,7 +488,7 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
if err == syscall.EAGAIN {
err = errTimeout
if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
+ if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
@@ -525,7 +530,7 @@ func (fd *netFD) Write(p []byte) (int, error) {
if err == syscall.EAGAIN {
err = errTimeout
if fd.wdeadline >= 0 {
- if err = pollserver.WaitWrite(fd); err == nil {
+ if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
}
@@ -557,7 +562,7 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
if err == syscall.EAGAIN {
err = errTimeout
if fd.wdeadline >= 0 {
- if err = pollserver.WaitWrite(fd); err == nil {
+ if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
}
@@ -584,7 +589,7 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
if err == syscall.EAGAIN {
err = errTimeout
if fd.wdeadline >= 0 {
- if err = pollserver.WaitWrite(fd); err == nil {
+ if err = fd.pollServer.WaitWrite(fd); err == nil {
continue
}
}
@@ -606,7 +611,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
}
defer fd.decref()
- // See ../syscall/exec.go for description of ForkLock.
+ // See ../syscall/exec_unix.go for description of ForkLock.
// It is okay to hold the lock across syscall.Accept
// because we have put fd.sysfd into non-blocking mode.
var s int
@@ -619,7 +624,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
if err == syscall.EAGAIN {
err = errTimeout
if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
+ if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
@@ -636,7 +641,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
syscall.ForkLock.RUnlock()
if netfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil {
- syscall.Close(s)
+ closesocket(s)
return nil, err
}
lsa, _ := syscall.Getsockname(netfd.sysfd)
diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go
index 45f5c2d882f..4ae78397c33 100644
--- a/libgo/go/net/fd_windows.go
+++ b/libgo/go/net/fd_windows.go
@@ -524,7 +524,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
defer fd.decref()
// Get new socket.
- // See ../syscall/exec.go for description of ForkLock.
+ // See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
s, err := syscall.Socket(fd.family, fd.sotype, 0)
if err != nil {
diff --git a/libgo/go/net/file.go b/libgo/go/net/file_unix.go
index 837326e12e6..0a640801779 100644
--- a/libgo/go/net/file.go
+++ b/libgo/go/net/file_unix.go
@@ -29,8 +29,8 @@ func newFileFD(f *os.File) (*netFD, error) {
family := syscall.AF_UNSPEC
toAddr := sockaddrToTCP
- sa, _ := syscall.Getsockname(fd)
- switch sa.(type) {
+ lsa, _ := syscall.Getsockname(fd)
+ switch lsa.(type) {
default:
closesocket(fd)
return nil, syscall.EINVAL
@@ -57,9 +57,9 @@ func newFileFD(f *os.File) (*netFD, error) {
toAddr = sockaddrToUnixpacket
}
}
- laddr := toAddr(sa)
- sa, _ = syscall.Getpeername(fd)
- raddr := toAddr(sa)
+ laddr := toAddr(lsa)
+ rsa, _ := syscall.Getpeername(fd)
+ raddr := toAddr(rsa)
netfd, err := newFD(fd, family, sotype, laddr.Network())
if err != nil {
@@ -84,10 +84,10 @@ func FileConn(f *os.File) (c Conn, err error) {
return newTCPConn(fd), nil
case *UDPAddr:
return newUDPConn(fd), nil
- case *UnixAddr:
- return newUnixConn(fd), nil
case *IPAddr:
return newIPConn(fd), nil
+ case *UnixAddr:
+ return newUnixConn(fd), nil
}
fd.Close()
return nil, syscall.EINVAL
diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go
index 89441424e1d..02891db9adc 100644
--- a/libgo/go/net/http/client.go
+++ b/libgo/go/net/http/client.go
@@ -33,10 +33,11 @@ type Client struct {
// CheckRedirect specifies the policy for handling redirects.
// If CheckRedirect is not nil, the client calls it before
- // following an HTTP redirect. The arguments req and via
- // are the upcoming request and the requests made already,
- // oldest first. If CheckRedirect returns an error, the client
- // returns that error (wrapped in a url.Error) instead of
+ // following an HTTP redirect. The arguments req and via are
+ // the upcoming request and the requests made already, oldest
+ // first. If CheckRedirect returns an error, the Client's Get
+ // method returns both the previous Response and
+ // CheckRedirect's error (wrapped in a url.Error) instead of
// issuing the Request req.
//
// If CheckRedirect is nil, the Client uses its default policy,
@@ -95,7 +96,7 @@ type readClose struct {
//
// When err is nil, resp always contains a non-nil resp.Body.
//
-// Callers should close res.Body when done reading from it. If
+// Callers should close resp.Body when done reading from it. If
// resp.Body is not closed, the Client's underlying RoundTripper
// (typically Transport) may not be able to re-use a persistent TCP
// connection to the server for a subsequent "keep-alive" request.
@@ -221,6 +222,7 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
req := ireq
urlStr := "" // next relative or absolute URL to fetch (after first request)
+ redirectFailed := false
for redirect := 0; ; redirect++ {
if redirect != 0 {
req = new(Request)
@@ -239,6 +241,7 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
err = redirectChecker(req, via)
if err != nil {
+ redirectFailed = true
break
}
}
@@ -268,16 +271,24 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
return
}
- if resp != nil {
- resp.Body.Close()
- }
-
method := ireq.Method
- return nil, &url.Error{
+ urlErr := &url.Error{
Op: method[0:1] + strings.ToLower(method[1:]),
URL: urlStr,
Err: err,
}
+
+ if redirectFailed {
+ // Special case for Go 1 compatibility: return both the response
+ // and an error if the CheckRedirect function failed.
+ // See http://golang.org/issue/3795
+ return resp, urlErr
+ }
+
+ if resp != nil {
+ resp.Body.Close()
+ }
+ return nil, urlErr
}
func defaultCheckRedirect(req *Request, via []*Request) error {
diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go
index 09fcc1c0b40..c61b17d289b 100644
--- a/libgo/go/net/http/client_test.go
+++ b/libgo/go/net/http/client_test.go
@@ -235,6 +235,12 @@ func TestRedirects(t *testing.T) {
if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr {
t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err)
}
+ if res == nil {
+ t.Fatalf("Expected a non-nil Response on CheckRedirect failure (http://golang.org/issue/3795)")
+ }
+ if res.Header.Get("Location") == "" {
+ t.Errorf("no Location header in Response")
+ }
}
var expectedCookies = []*Cookie{
diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go
index 2e30bbff177..43f519d1fba 100644
--- a/libgo/go/net/http/cookie.go
+++ b/libgo/go/net/http/cookie.go
@@ -258,10 +258,5 @@ func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool)
}
func isCookieNameValid(raw string) bool {
- for _, c := range raw {
- if !isToken(byte(c)) {
- return false
- }
- }
- return true
+ return strings.IndexFunc(raw, isNotToken) < 0
}
diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go
index 208d6cabb2c..b6bea0dfaad 100644
--- a/libgo/go/net/http/fs.go
+++ b/libgo/go/net/http/fs.go
@@ -100,6 +100,9 @@ func dirList(w ResponseWriter, f File) {
// The content's Seek method must work: ServeContent uses
// a seek to the end of the content to determine its size.
//
+// If the caller has set w's ETag header, ServeContent uses it to
+// handle requests using If-Range and If-None-Match.
+//
// Note that *os.File implements the io.ReadSeeker interface.
func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
size, err := content.Seek(0, os.SEEK_END)
@@ -122,6 +125,10 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
if checkLastModified(w, r, modtime) {
return
}
+ rangeReq, done := checkETag(w, r)
+ if done {
+ return
+ }
code := StatusOK
@@ -148,7 +155,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
sendSize := size
var sendContent io.Reader = content
if size >= 0 {
- ranges, err := parseRange(r.Header.Get("Range"), size)
+ ranges, err := parseRange(rangeReq, size)
if err != nil {
Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
return
@@ -240,6 +247,9 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
// The Date-Modified header truncates sub-second precision, so
// use mtime < t+1s instead of mtime <= t to check for unmodified.
if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) {
+ h := w.Header()
+ delete(h, "Content-Type")
+ delete(h, "Content-Length")
w.WriteHeader(StatusNotModified)
return true
}
@@ -247,6 +257,58 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
return false
}
+// checkETag implements If-None-Match and If-Range checks.
+// The ETag must have been previously set in the ResponseWriter's headers.
+//
+// The return value is the effective request "Range" header to use and
+// whether this request is now considered done.
+func checkETag(w ResponseWriter, r *Request) (rangeReq string, done bool) {
+ etag := w.Header().get("Etag")
+ rangeReq = r.Header.get("Range")
+
+ // Invalidate the range request if the entity doesn't match the one
+ // the client was expecting.
+ // "If-Range: version" means "ignore the Range: header unless version matches the
+ // current file."
+ // We only support ETag versions.
+ // The caller must have set the ETag on the response already.
+ if ir := r.Header.get("If-Range"); ir != "" && ir != etag {
+ // TODO(bradfitz): handle If-Range requests with Last-Modified
+ // times instead of ETags? I'd rather not, at least for
+ // now. That seems like a bug/compromise in the RFC 2616, and
+ // I've never heard of anybody caring about that (yet).
+ rangeReq = ""
+ }
+
+ if inm := r.Header.get("If-None-Match"); inm != "" {
+ // Must know ETag.
+ if etag == "" {
+ return rangeReq, false
+ }
+
+ // TODO(bradfitz): non-GET/HEAD requests require more work:
+ // sending a different status code on matches, and
+ // also can't use weak cache validators (those with a "W/
+ // prefix). But most users of ServeContent will be using
+ // it on GET or HEAD, so only support those for now.
+ if r.Method != "GET" && r.Method != "HEAD" {
+ return rangeReq, false
+ }
+
+ // TODO(bradfitz): deal with comma-separated or multiple-valued
+ // list of If-None-match values. For now just handle the common
+ // case of a single item.
+ if inm == etag || inm == "*" {
+ h := w.Header()
+ delete(h, "Content-Type")
+ delete(h, "Content-Length")
+ w.WriteHeader(StatusNotModified)
+ return "", true
+ }
+ }
+ return rangeReq, false
+}
+
// name is '/'-separated, not filepath.Separator.
func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) {
const indexPage = "/index.html"
diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go
index 17329fbd59a..09d5cfaf2d4 100644
--- a/libgo/go/net/http/fs_test.go
+++ b/libgo/go/net/http/fs_test.go
@@ -81,6 +81,7 @@ func TestServeFile(t *testing.T) {
}
// Range tests
+Cases:
for _, rt := range ServeFileRangeTests {
if rt.r != "" {
req.Header.Set("Range", rt.r)
@@ -109,7 +110,7 @@ func TestServeFile(t *testing.T) {
t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
}
if strings.HasPrefix(ct, "multipart/byteranges") {
- t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r)
+ t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r, ct)
}
}
if len(rt.ranges) > 1 {
@@ -119,37 +120,41 @@ func TestServeFile(t *testing.T) {
continue
}
if typ != "multipart/byteranges" {
- t.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r)
+ t.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r, typ)
continue
}
if params["boundary"] == "" {
t.Errorf("range=%q content-type = %q; lacks boundary", rt.r, ct)
+ continue
}
if g, w := resp.ContentLength, int64(len(body)); g != w {
t.Errorf("range=%q Content-Length = %d; want %d", rt.r, g, w)
+ continue
}
mr := multipart.NewReader(bytes.NewReader(body), params["boundary"])
for ri, rng := range rt.ranges {
part, err := mr.NextPart()
if err != nil {
- t.Fatalf("range=%q, reading part index %d: %v", rt.r, ri, err)
+ t.Errorf("range=%q, reading part index %d: %v", rt.r, ri, err)
+ continue Cases
+ }
+ wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
+ if g, w := part.Header.Get("Content-Range"), wantContentRange; g != w {
+ t.Errorf("range=%q: part Content-Range = %q; want %q", rt.r, g, w)
}
body, err := ioutil.ReadAll(part)
if err != nil {
- t.Fatalf("range=%q, reading part index %d body: %v", rt.r, ri, err)
+ t.Errorf("range=%q, reading part index %d body: %v", rt.r, ri, err)
+ continue Cases
}
- wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
wantBody := file[rng.start:rng.end]
if !bytes.Equal(body, wantBody) {
t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
}
- if g, w := part.Header.Get("Content-Range"), wantContentRange; g != w {
- t.Errorf("range=%q: part Content-Range = %q; want %q", rt.r, g, w)
- }
}
_, err = mr.NextPart()
if err != io.EOF {
- t.Errorf("range=%q; expected final error io.EOF; got %v", err)
+ t.Errorf("range=%q; expected final error io.EOF; got %v", rt.r, err)
}
}
}
@@ -335,11 +340,6 @@ func TestServeFileMimeType(t *testing.T) {
}
func TestServeFileFromCWD(t *testing.T) {
- if runtime.GOOS == "windows" {
- // TODO(brainman): find out why this test is broken
- t.Logf("Temporarily skipping test on Windows; see http://golang.org/issue/3917")
- return
- }
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
ServeFile(w, r, "fs_test.go")
}))
@@ -348,6 +348,7 @@ func TestServeFileFromCWD(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+ r.Body.Close()
if r.StatusCode != 200 {
t.Fatalf("expected 200 OK, got %s", r.Status)
}
@@ -522,51 +523,140 @@ func TestDirectoryIfNotModified(t *testing.T) {
res.Body.Close()
}
-func TestServeContent(t *testing.T) {
- type req struct {
- name string
- modtime time.Time
- content io.ReadSeeker
+func mustStat(t *testing.T, fileName string) os.FileInfo {
+ fi, err := os.Stat(fileName)
+ if err != nil {
+ t.Fatal(err)
}
- ch := make(chan req, 1)
+ return fi
+}
+
+func TestServeContent(t *testing.T) {
+ type serveParam struct {
+ name string
+ modtime time.Time
+ content io.ReadSeeker
+ contentType string
+ etag string
+ }
+ servec := make(chan serveParam, 1)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- p := <-ch
+ p := <-servec
+ if p.etag != "" {
+ w.Header().Set("ETag", p.etag)
+ }
+ if p.contentType != "" {
+ w.Header().Set("Content-Type", p.contentType)
+ }
ServeContent(w, r, p.name, p.modtime, p.content)
}))
defer ts.Close()
- css, err := os.Open("testdata/style.css")
- if err != nil {
- t.Fatal(err)
- }
- defer css.Close()
-
- ch <- req{"style.css", time.Time{}, css}
- res, err := Get(ts.URL)
- if err != nil {
- t.Fatal(err)
- }
- if g, e := res.Header.Get("Content-Type"), "text/css; charset=utf-8"; g != e {
- t.Errorf("style.css: content type = %q, want %q", g, e)
- }
- if g := res.Header.Get("Last-Modified"); g != "" {
- t.Errorf("want empty Last-Modified; got %q", g)
+ type testCase struct {
+ file string
+ modtime time.Time
+ serveETag string // optional
+ serveContentType string // optional
+ reqHeader map[string]string
+ wantLastMod string
+ wantContentType string
+ wantStatus int
+ }
+ htmlModTime := mustStat(t, "testdata/index.html").ModTime()
+ tests := map[string]testCase{
+ "no_last_modified": {
+ file: "testdata/style.css",
+ wantContentType: "text/css; charset=utf-8",
+ wantStatus: 200,
+ },
+ "with_last_modified": {
+ file: "testdata/index.html",
+ wantContentType: "text/html; charset=utf-8",
+ modtime: htmlModTime,
+ wantLastMod: htmlModTime.UTC().Format(TimeFormat),
+ wantStatus: 200,
+ },
+ "not_modified_modtime": {
+ file: "testdata/style.css",
+ modtime: htmlModTime,
+ reqHeader: map[string]string{
+ "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
+ },
+ wantStatus: 304,
+ },
+ "not_modified_modtime_with_contenttype": {
+ file: "testdata/style.css",
+ serveContentType: "text/css", // explicit content type
+ modtime: htmlModTime,
+ reqHeader: map[string]string{
+ "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
+ },
+ wantStatus: 304,
+ },
+ "not_modified_etag": {
+ file: "testdata/style.css",
+ serveETag: `"foo"`,
+ reqHeader: map[string]string{
+ "If-None-Match": `"foo"`,
+ },
+ wantStatus: 304,
+ },
+ "range_good": {
+ file: "testdata/style.css",
+ serveETag: `"A"`,
+ reqHeader: map[string]string{
+ "Range": "bytes=0-4",
+ },
+ wantStatus: StatusPartialContent,
+ wantContentType: "text/css; charset=utf-8",
+ },
+ // An If-Range resource for entity "A", but entity "B" is now current.
+ // The Range request should be ignored.
+ "range_no_match": {
+ file: "testdata/style.css",
+ serveETag: `"A"`,
+ reqHeader: map[string]string{
+ "Range": "bytes=0-4",
+ "If-Range": `"B"`,
+ },
+ wantStatus: 200,
+ wantContentType: "text/css; charset=utf-8",
+ },
}
+ for testName, tt := range tests {
+ f, err := os.Open(tt.file)
+ if err != nil {
+ t.Fatalf("test %q: %v", testName, err)
+ }
+ defer f.Close()
- fi, err := css.Stat()
- if err != nil {
- t.Fatal(err)
- }
- ch <- req{"style.html", fi.ModTime(), css}
- res, err = Get(ts.URL)
- if err != nil {
- t.Fatal(err)
- }
- if g, e := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != e {
- t.Errorf("style.html: content type = %q, want %q", g, e)
- }
- if g := res.Header.Get("Last-Modified"); g == "" {
- t.Errorf("want non-empty last-modified")
+ servec <- serveParam{
+ name: filepath.Base(tt.file),
+ content: f,
+ modtime: tt.modtime,
+ etag: tt.serveETag,
+ contentType: tt.serveContentType,
+ }
+ req, err := NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for k, v := range tt.reqHeader {
+ req.Header.Set(k, v)
+ }
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.StatusCode != tt.wantStatus {
+ t.Errorf("test %q: status = %d; want %d", testName, res.StatusCode, tt.wantStatus)
+ }
+ if g, e := res.Header.Get("Content-Type"), tt.wantContentType; g != e {
+ t.Errorf("test %q: content-type = %q, want %q", testName, g, e)
+ }
+ if g, e := res.Header.Get("Last-Modified"), tt.wantLastMod; g != e {
+ t.Errorf("test %q: last-modified = %q, want %q", testName, g, e)
+ }
}
}
diff --git a/libgo/go/net/http/header.go b/libgo/go/net/http/header.go
index 6be94f98e74..91417366ae8 100644
--- a/libgo/go/net/http/header.go
+++ b/libgo/go/net/http/header.go
@@ -5,11 +5,11 @@
package http
import (
- "fmt"
"io"
"net/textproto"
"sort"
"strings"
+ "time"
)
// A Header represents the key-value pairs in an HTTP header.
@@ -36,6 +36,14 @@ func (h Header) Get(key string) string {
return textproto.MIMEHeader(h).Get(key)
}
+// get is like Get, but key must already be in CanonicalHeaderKey form.
+func (h Header) get(key string) string {
+ if v := h[key]; len(v) > 0 {
+ return v[0]
+ }
+ return ""
+}
+
// Del deletes the values associated with key.
func (h Header) Del(key string) {
textproto.MIMEHeader(h).Del(key)
@@ -46,24 +54,77 @@ func (h Header) Write(w io.Writer) error {
return h.WriteSubset(w, nil)
}
+var timeFormats = []string{
+ TimeFormat,
+ time.RFC850,
+ time.ANSIC,
+}
+
+// ParseTime parses a time header (such as the Date: header),
+// trying each of the three formats allowed by HTTP/1.1:
+// TimeFormat, time.RFC850, and time.ANSIC.
+func ParseTime(text string) (t time.Time, err error) {
+ for _, layout := range timeFormats {
+ t, err = time.Parse(layout, text)
+ if err == nil {
+ return
+ }
+ }
+ return
+}
+
var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ")
+type writeStringer interface {
+ WriteString(string) (int, error)
+}
+
+// stringWriter implements WriteString on a Writer.
+type stringWriter struct {
+ w io.Writer
+}
+
+func (w stringWriter) WriteString(s string) (n int, err error) {
+ return w.w.Write([]byte(s))
+}
+
+type keyValues struct {
+ key string
+ values []string
+}
+
+type byKey []keyValues
+
+func (s byKey) Len() int { return len(s) }
+func (s byKey) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byKey) Less(i, j int) bool { return s[i].key < s[j].key }
+
+func (h Header) sortedKeyValues(exclude map[string]bool) []keyValues {
+ kvs := make([]keyValues, 0, len(h))
+ for k, vv := range h {
+ if !exclude[k] {
+ kvs = append(kvs, keyValues{k, vv})
+ }
+ }
+ sort.Sort(byKey(kvs))
+ return kvs
+}
+
// WriteSubset writes a header in wire format.
// If exclude is not nil, keys where exclude[key] == true are not written.
func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
- keys := make([]string, 0, len(h))
- for k := range h {
- if exclude == nil || !exclude[k] {
- keys = append(keys, k)
- }
+ ws, ok := w.(writeStringer)
+ if !ok {
+ ws = stringWriter{w}
}
- sort.Strings(keys)
- for _, k := range keys {
- for _, v := range h[k] {
+ for _, kv := range h.sortedKeyValues(exclude) {
+ for _, v := range kv.values {
v = headerNewlineToSpace.Replace(v)
- v = strings.TrimSpace(v)
- if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil {
- return err
+ v = textproto.TrimString(v)
+ for _, s := range []string{kv.key, ": ", v, "\r\n"} {
+ if _, err := ws.WriteString(s); err != nil {
+ return err
+ }
}
}
}
diff --git a/libgo/go/net/http/header_test.go b/libgo/go/net/http/header_test.go
index ccdee8a97bd..fd971a61d05 100644
--- a/libgo/go/net/http/header_test.go
+++ b/libgo/go/net/http/header_test.go
@@ -6,7 +6,9 @@ package http
import (
"bytes"
+ "runtime"
"testing"
+ "time"
)
var headerWriteTests = []struct {
@@ -67,6 +69,24 @@ var headerWriteTests = []struct {
nil,
"Blank: \r\nDouble-Blank: \r\nDouble-Blank: \r\n",
},
+ // Tests header sorting when over the insertion sort threshold side:
+ {
+ Header{
+ "k1": {"1a", "1b"},
+ "k2": {"2a", "2b"},
+ "k3": {"3a", "3b"},
+ "k4": {"4a", "4b"},
+ "k5": {"5a", "5b"},
+ "k6": {"6a", "6b"},
+ "k7": {"7a", "7b"},
+ "k8": {"8a", "8b"},
+ "k9": {"9a", "9b"},
+ },
+ map[string]bool{"k5": true},
+ "k1: 1a\r\nk1: 1b\r\nk2: 2a\r\nk2: 2b\r\nk3: 3a\r\nk3: 3b\r\n" +
+ "k4: 4a\r\nk4: 4b\r\nk6: 6a\r\nk6: 6b\r\n" +
+ "k7: 7a\r\nk7: 7b\r\nk8: 8a\r\nk8: 8b\r\nk9: 9a\r\nk9: 9b\r\n",
+ },
}
func TestHeaderWrite(t *testing.T) {
@@ -79,3 +99,113 @@ func TestHeaderWrite(t *testing.T) {
buf.Reset()
}
}
+
+var parseTimeTests = []struct {
+ h Header
+ err bool
+}{
+ {Header{"Date": {""}}, true},
+ {Header{"Date": {"invalid"}}, true},
+ {Header{"Date": {"1994-11-06T08:49:37Z00:00"}}, true},
+ {Header{"Date": {"Sun, 06 Nov 1994 08:49:37 GMT"}}, false},
+ {Header{"Date": {"Sunday, 06-Nov-94 08:49:37 GMT"}}, false},
+ {Header{"Date": {"Sun Nov 6 08:49:37 1994"}}, false},
+}
+
+func TestParseTime(t *testing.T) {
+ expect := time.Date(1994, 11, 6, 8, 49, 37, 0, time.UTC)
+ for i, test := range parseTimeTests {
+ d, err := ParseTime(test.h.Get("Date"))
+ if err != nil {
+ if !test.err {
+ t.Errorf("#%d:\n got err: %v", i, err)
+ }
+ continue
+ }
+ if test.err {
+ t.Errorf("#%d:\n should err", i)
+ continue
+ }
+ if !expect.Equal(d) {
+ t.Errorf("#%d:\n got: %v\nwant: %v", i, d, expect)
+ }
+ }
+}
+
+type hasTokenTest struct {
+ header string
+ token string
+ want bool
+}
+
+var hasTokenTests = []hasTokenTest{
+ {"", "", false},
+ {"", "foo", false},
+ {"foo", "foo", true},
+ {"foo ", "foo", true},
+ {" foo", "foo", true},
+ {" foo ", "foo", true},
+ {"foo,bar", "foo", true},
+ {"bar,foo", "foo", true},
+ {"bar, foo", "foo", true},
+ {"bar,foo, baz", "foo", true},
+ {"bar, foo,baz", "foo", true},
+ {"bar,foo, baz", "foo", true},
+ {"bar, foo, baz", "foo", true},
+ {"FOO", "foo", true},
+ {"FOO ", "foo", true},
+ {" FOO", "foo", true},
+ {" FOO ", "foo", true},
+ {"FOO,BAR", "foo", true},
+ {"BAR,FOO", "foo", true},
+ {"BAR, FOO", "foo", true},
+ {"BAR,FOO, baz", "foo", true},
+ {"BAR, FOO,BAZ", "foo", true},
+ {"BAR,FOO, BAZ", "foo", true},
+ {"BAR, FOO, BAZ", "foo", true},
+ {"foobar", "foo", false},
+ {"barfoo ", "foo", false},
+}
+
+func TestHasToken(t *testing.T) {
+ for _, tt := range hasTokenTests {
+ if hasToken(tt.header, tt.token) != tt.want {
+ t.Errorf("hasToken(%q, %q) = %v; want %v", tt.header, tt.token, !tt.want, tt.want)
+ }
+ }
+}
+
+func BenchmarkHeaderWriteSubset(b *testing.B) {
+ doHeaderWriteSubset(b.N, b)
+}
+
+func TestHeaderWriteSubsetMallocs(t *testing.T) {
+ doHeaderWriteSubset(100, t)
+}
+
+type errorfer interface {
+ Errorf(string, ...interface{})
+}
+
+func doHeaderWriteSubset(n int, t errorfer) {
+ h := Header(map[string][]string{
+ "Content-Length": {"123"},
+ "Content-Type": {"text/plain"},
+ "Date": {"some date at some time Z"},
+ "Server": {"Go http package"},
+ })
+ var buf bytes.Buffer
+ var m0 runtime.MemStats
+ runtime.ReadMemStats(&m0)
+ for i := 0; i < n; i++ {
+ buf.Reset()
+ h.WriteSubset(&buf, nil)
+ }
+ var m1 runtime.MemStats
+ runtime.ReadMemStats(&m1)
+ if mallocs := m1.Mallocs - m0.Mallocs; n >= 100 && mallocs >= uint64(n) {
+ // TODO(bradfitz,rsc): once we can sort with allocating,
+ // make this an error. See http://golang.org/issue/3761
+ // t.Errorf("did %d mallocs (>= %d iterations); should have avoided mallocs", mallocs, n)
+ }
+}
diff --git a/libgo/go/net/http/httptest/recorder.go b/libgo/go/net/http/httptest/recorder.go
index 9aa0d510bd4..5451f54234c 100644
--- a/libgo/go/net/http/httptest/recorder.go
+++ b/libgo/go/net/http/httptest/recorder.go
@@ -17,6 +17,8 @@ type ResponseRecorder struct {
HeaderMap http.Header // the HTTP response headers
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
Flushed bool
+
+ wroteHeader bool
}
// NewRecorder returns an initialized ResponseRecorder.
@@ -24,6 +26,7 @@ func NewRecorder() *ResponseRecorder {
return &ResponseRecorder{
HeaderMap: make(http.Header),
Body: new(bytes.Buffer),
+ Code: 200,
}
}
@@ -33,26 +36,37 @@ const DefaultRemoteAddr = "1.2.3.4"
// Header returns the response headers.
func (rw *ResponseRecorder) Header() http.Header {
- return rw.HeaderMap
+ m := rw.HeaderMap
+ if m == nil {
+ m = make(http.Header)
+ rw.HeaderMap = m
+ }
+ return m
}
// Write always succeeds and writes to rw.Body, if not nil.
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
+ if !rw.wroteHeader {
+ rw.WriteHeader(200)
+ }
if rw.Body != nil {
rw.Body.Write(buf)
}
- if rw.Code == 0 {
- rw.Code = http.StatusOK
- }
return len(buf), nil
}
// WriteHeader sets rw.Code.
func (rw *ResponseRecorder) WriteHeader(code int) {
- rw.Code = code
+ if !rw.wroteHeader {
+ rw.Code = code
+ }
+ rw.wroteHeader = true
}
// Flush sets rw.Flushed to true.
func (rw *ResponseRecorder) Flush() {
+ if !rw.wroteHeader {
+ rw.WriteHeader(200)
+ }
rw.Flushed = true
}
diff --git a/libgo/go/net/http/httptest/recorder_test.go b/libgo/go/net/http/httptest/recorder_test.go
new file mode 100644
index 00000000000..2b563260c76
--- /dev/null
+++ b/libgo/go/net/http/httptest/recorder_test.go
@@ -0,0 +1,90 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httptest
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+)
+
+func TestRecorder(t *testing.T) {
+ type checkFunc func(*ResponseRecorder) error
+ check := func(fns ...checkFunc) []checkFunc { return fns }
+
+ hasStatus := func(wantCode int) checkFunc {
+ return func(rec *ResponseRecorder) error {
+ if rec.Code != wantCode {
+ return fmt.Errorf("Status = %d; want %d", rec.Code, wantCode)
+ }
+ return nil
+ }
+ }
+ hasContents := func(want string) checkFunc {
+ return func(rec *ResponseRecorder) error {
+ if rec.Body.String() != want {
+ return fmt.Errorf("wrote = %q; want %q", rec.Body.String(), want)
+ }
+ return nil
+ }
+ }
+ hasFlush := func(want bool) checkFunc {
+ return func(rec *ResponseRecorder) error {
+ if rec.Flushed != want {
+ return fmt.Errorf("Flushed = %v; want %v", rec.Flushed, want)
+ }
+ return nil
+ }
+ }
+
+ tests := []struct {
+ name string
+ h func(w http.ResponseWriter, r *http.Request)
+ checks []checkFunc
+ }{
+ {
+ "200 default",
+ func(w http.ResponseWriter, r *http.Request) {},
+ check(hasStatus(200), hasContents("")),
+ },
+ {
+ "first code only",
+ func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(201)
+ w.WriteHeader(202)
+ w.Write([]byte("hi"))
+ },
+ check(hasStatus(201), hasContents("hi")),
+ },
+ {
+ "write sends 200",
+ func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("hi first"))
+ w.WriteHeader(201)
+ w.WriteHeader(202)
+ },
+ check(hasStatus(200), hasContents("hi first"), hasFlush(false)),
+ },
+ {
+ "flush",
+ func(w http.ResponseWriter, r *http.Request) {
+ w.(http.Flusher).Flush() // also sends a 200
+ w.WriteHeader(201)
+ },
+ check(hasStatus(200), hasFlush(true)),
+ },
+ }
+ r, _ := http.NewRequest("GET", "http://foo.com/", nil)
+ for _, tt := range tests {
+ h := http.HandlerFunc(tt.h)
+ rec := NewRecorder()
+ h.ServeHTTP(rec, r)
+ for _, check := range tt.checks {
+ if err := check(rec); err != nil {
+ t.Errorf("%s: %v", tt.name, err)
+ }
+ }
+ }
+}
diff --git a/libgo/go/net/http/httputil/dump.go b/libgo/go/net/http/httputil/dump.go
index 0fb2eeb8c00..0b003566165 100644
--- a/libgo/go/net/http/httputil/dump.go
+++ b/libgo/go/net/http/httputil/dump.go
@@ -75,7 +75,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
// Use the actual Transport code to record what we would send
// on the wire, but not using TCP. Use a Transport with a
- // customer dialer that returns a fake net.Conn that waits
+ // custom dialer that returns a fake net.Conn that waits
// for the full input (and recording it), and then responds
// with a dummy response.
var buf bytes.Buffer // records the output
diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go
index 9c4bd6e09a5..134c452999d 100644
--- a/libgo/go/net/http/httputil/reverseproxy.go
+++ b/libgo/go/net/http/httputil/reverseproxy.go
@@ -17,6 +17,10 @@ import (
"time"
)
+// onExitFlushLoop is a callback set by tests to detect the state of the
+// flushLoop() goroutine.
+var onExitFlushLoop func()
+
// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
@@ -102,8 +106,14 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
outreq.Header.Del("Connection")
}
- if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
- outreq.Header.Set("X-Forwarded-For", clientIp)
+ if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
+ // If we aren't the first proxy retain prior
+ // X-Forwarded-For information as a comma+space
+ // separated list and fold multiple headers into one.
+ if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
+ clientIP = strings.Join(prior, ", ") + ", " + clientIP
+ }
+ outreq.Header.Set("X-Forwarded-For", clientIP)
}
res, err := transport.RoundTrip(outreq)
@@ -112,20 +122,29 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusInternalServerError)
return
}
+ defer res.Body.Close()
copyHeader(rw.Header(), res.Header)
rw.WriteHeader(res.StatusCode)
+ p.copyResponse(rw, res.Body)
+}
- if res.Body != nil {
- var dst io.Writer = rw
- if p.FlushInterval != 0 {
- if wf, ok := rw.(writeFlusher); ok {
- dst = &maxLatencyWriter{dst: wf, latency: p.FlushInterval}
+func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
+ if p.FlushInterval != 0 {
+ if wf, ok := dst.(writeFlusher); ok {
+ mlw := &maxLatencyWriter{
+ dst: wf,
+ latency: p.FlushInterval,
+ done: make(chan bool),
}
+ go mlw.flushLoop()
+ defer mlw.stop()
+ dst = mlw
}
- io.Copy(dst, res.Body)
}
+
+ io.Copy(dst, src)
}
type writeFlusher interface {
@@ -137,22 +156,14 @@ type maxLatencyWriter struct {
dst writeFlusher
latency time.Duration
- lk sync.Mutex // protects init of done, as well Write + Flush
+ lk sync.Mutex // protects Write + Flush
done chan bool
}
-func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
+func (m *maxLatencyWriter) Write(p []byte) (int, error) {
m.lk.Lock()
defer m.lk.Unlock()
- if m.done == nil {
- m.done = make(chan bool)
- go m.flushLoop()
- }
- n, err = m.dst.Write(p)
- if err != nil {
- m.done <- true
- }
- return
+ return m.dst.Write(p)
}
func (m *maxLatencyWriter) flushLoop() {
@@ -160,13 +171,18 @@ func (m *maxLatencyWriter) flushLoop() {
defer t.Stop()
for {
select {
+ case <-m.done:
+ if onExitFlushLoop != nil {
+ onExitFlushLoop()
+ }
+ return
case <-t.C:
m.lk.Lock()
m.dst.Flush()
m.lk.Unlock()
- case <-m.done:
- return
}
}
panic("unreached")
}
+
+func (m *maxLatencyWriter) stop() { m.done <- true }
diff --git a/libgo/go/net/http/httputil/reverseproxy_test.go b/libgo/go/net/http/httputil/reverseproxy_test.go
index 28e9c90ad36..8639271626f 100644
--- a/libgo/go/net/http/httputil/reverseproxy_test.go
+++ b/libgo/go/net/http/httputil/reverseproxy_test.go
@@ -11,7 +11,9 @@ import (
"net/http"
"net/http/httptest"
"net/url"
+ "strings"
"testing"
+ "time"
)
func TestReverseProxy(t *testing.T) {
@@ -70,6 +72,47 @@ func TestReverseProxy(t *testing.T) {
}
}
+func TestXForwardedFor(t *testing.T) {
+ const prevForwardedFor = "client ip"
+ const backendResponse = "I am the backend"
+ const backendStatus = 404
+ backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Header.Get("X-Forwarded-For") == "" {
+ t.Errorf("didn't get X-Forwarded-For header")
+ }
+ if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) {
+ t.Errorf("X-Forwarded-For didn't contain prior data")
+ }
+ w.WriteHeader(backendStatus)
+ w.Write([]byte(backendResponse))
+ }))
+ defer backend.Close()
+ backendURL, err := url.Parse(backend.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ proxyHandler := NewSingleHostReverseProxy(backendURL)
+ frontend := httptest.NewServer(proxyHandler)
+ defer frontend.Close()
+
+ getReq, _ := http.NewRequest("GET", frontend.URL, nil)
+ getReq.Host = "some-name"
+ getReq.Header.Set("Connection", "close")
+ getReq.Header.Set("X-Forwarded-For", prevForwardedFor)
+ getReq.Close = true
+ res, err := http.DefaultClient.Do(getReq)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ if g, e := res.StatusCode, backendStatus; g != e {
+ t.Errorf("got res.StatusCode %d; expected %d", g, e)
+ }
+ bodyBytes, _ := ioutil.ReadAll(res.Body)
+ if g, e := string(bodyBytes), backendResponse; g != e {
+ t.Errorf("got body %q; expected %q", g, e)
+ }
+}
+
var proxyQueryTests = []struct {
baseSuffix string // suffix to add to backend URL
reqSuffix string // suffix to add to frontend's request URL
@@ -107,3 +150,44 @@ func TestReverseProxyQuery(t *testing.T) {
frontend.Close()
}
}
+
+func TestReverseProxyFlushInterval(t *testing.T) {
+ const expected = "hi"
+ backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(expected))
+ }))
+ defer backend.Close()
+
+ backendURL, err := url.Parse(backend.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ proxyHandler := NewSingleHostReverseProxy(backendURL)
+ proxyHandler.FlushInterval = time.Microsecond
+
+ done := make(chan bool)
+ onExitFlushLoop = func() { done <- true }
+ defer func() { onExitFlushLoop = nil }()
+
+ frontend := httptest.NewServer(proxyHandler)
+ defer frontend.Close()
+
+ req, _ := http.NewRequest("GET", frontend.URL, nil)
+ req.Close = true
+ res, err := http.DefaultClient.Do(req)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ defer res.Body.Close()
+ if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected {
+ t.Errorf("got body %q; expected %q", bodyBytes, expected)
+ }
+
+ select {
+ case <-done:
+ // OK
+ case <-time.After(5 * time.Second):
+ t.Error("maxLatencyWriter flushLoop() never exited")
+ }
+}
diff --git a/libgo/go/net/http/lex.go b/libgo/go/net/http/lex.go
index ffb393ccf6a..cb33318f49b 100644
--- a/libgo/go/net/http/lex.go
+++ b/libgo/go/net/http/lex.go
@@ -6,131 +6,91 @@ package http
// This file deals with lexical matters of HTTP
-func isSeparator(c byte) bool {
- switch c {
- case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
- return true
- }
- return false
+var isTokenTable = [127]bool{
+ '!': true,
+ '#': true,
+ '$': true,
+ '%': true,
+ '&': true,
+ '\'': true,
+ '*': true,
+ '+': true,
+ '-': true,
+ '.': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'W': true,
+ 'V': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '|': true,
+ '~': true,
}
-func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 }
-
-func isChar(c byte) bool { return 0 <= c && c <= 127 }
-
-func isAnyText(c byte) bool { return !isCtl(c) }
-
-func isQdText(c byte) bool { return isAnyText(c) && c != '"' }
-
-func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) }
-
-// Valid escaped sequences are not specified in RFC 2616, so for now, we assume
-// that they coincide with the common sense ones used by GO. Malformed
-// characters should probably not be treated as errors by a robust (forgiving)
-// parser, so we replace them with the '?' character.
-func httpUnquotePair(b byte) byte {
- // skip the first byte, which should always be '\'
- switch b {
- case 'a':
- return '\a'
- case 'b':
- return '\b'
- case 'f':
- return '\f'
- case 'n':
- return '\n'
- case 'r':
- return '\r'
- case 't':
- return '\t'
- case 'v':
- return '\v'
- case '\\':
- return '\\'
- case '\'':
- return '\''
- case '"':
- return '"'
- }
- return '?'
-}
-
-// raw must begin with a valid quoted string. Only the first quoted string is
-// parsed and is unquoted in result. eaten is the number of bytes parsed, or -1
-// upon failure.
-func httpUnquote(raw []byte) (eaten int, result string) {
- buf := make([]byte, len(raw))
- if raw[0] != '"' {
- return -1, ""
- }
- eaten = 1
- j := 0 // # of bytes written in buf
- for i := 1; i < len(raw); i++ {
- switch b := raw[i]; b {
- case '"':
- eaten++
- buf = buf[0:j]
- return i + 1, string(buf)
- case '\\':
- if len(raw) < i+2 {
- return -1, ""
- }
- buf[j] = httpUnquotePair(raw[i+1])
- eaten += 2
- j++
- i++
- default:
- if isQdText(b) {
- buf[j] = b
- } else {
- buf[j] = '?'
- }
- eaten++
- j++
- }
- }
- return -1, ""
+func isToken(r rune) bool {
+ i := int(r)
+ return i < len(isTokenTable) && isTokenTable[i]
}
-// This is a best effort parse, so errors are not returned, instead not all of
-// the input string might be parsed. result is always non-nil.
-func httpSplitFieldValue(fv string) (eaten int, result []string) {
- result = make([]string, 0, len(fv))
- raw := []byte(fv)
- i := 0
- chunk := ""
- for i < len(raw) {
- b := raw[i]
- switch {
- case b == '"':
- eaten, unq := httpUnquote(raw[i:len(raw)])
- if eaten < 0 {
- return i, result
- } else {
- i += eaten
- chunk += unq
- }
- case isSeparator(b):
- if chunk != "" {
- result = result[0 : len(result)+1]
- result[len(result)-1] = chunk
- chunk = ""
- }
- i++
- case isToken(b):
- chunk += string(b)
- i++
- case b == '\n' || b == '\r':
- i++
- default:
- chunk += "?"
- i++
- }
- }
- if chunk != "" {
- result = result[0 : len(result)+1]
- result[len(result)-1] = chunk
- chunk = ""
- }
- return i, result
+func isNotToken(r rune) bool {
+ return !isToken(r)
}
diff --git a/libgo/go/net/http/lex_test.go b/libgo/go/net/http/lex_test.go
index 5386f7534db..6d9d294f703 100644
--- a/libgo/go/net/http/lex_test.go
+++ b/libgo/go/net/http/lex_test.go
@@ -8,63 +8,24 @@ import (
"testing"
)
-type lexTest struct {
- Raw string
- Parsed int // # of parsed characters
- Result []string
-}
+func isChar(c rune) bool { return c <= 127 }
-var lexTests = []lexTest{
- {
- Raw: `"abc"def,:ghi`,
- Parsed: 13,
- Result: []string{"abcdef", "ghi"},
- },
- // My understanding of the RFC is that escape sequences outside of
- // quotes are not interpreted?
- {
- Raw: `"\t"\t"\t"`,
- Parsed: 10,
- Result: []string{"\t", "t\t"},
- },
- {
- Raw: `"\yab"\r\n`,
- Parsed: 10,
- Result: []string{"?ab", "r", "n"},
- },
- {
- Raw: "ab\f",
- Parsed: 3,
- Result: []string{"ab?"},
- },
- {
- Raw: "\"ab \" c,de f, gh, ij\n\t\r",
- Parsed: 23,
- Result: []string{"ab ", "c", "de", "f", "gh", "ij"},
- },
-}
+func isCtl(c rune) bool { return c <= 31 || c == 127 }
-func min(x, y int) int {
- if x <= y {
- return x
+func isSeparator(c rune) bool {
+ switch c {
+ case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
+ return true
}
- return y
+ return false
}
-func TestSplitFieldValue(t *testing.T) {
- for k, l := range lexTests {
- parsed, result := httpSplitFieldValue(l.Raw)
- if parsed != l.Parsed {
- t.Errorf("#%d: Parsed %d, expected %d", k, parsed, l.Parsed)
- }
- if len(result) != len(l.Result) {
- t.Errorf("#%d: Result len %d, expected %d", k, len(result), len(l.Result))
- }
- for i := 0; i < min(len(result), len(l.Result)); i++ {
- if result[i] != l.Result[i] {
- t.Errorf("#%d: %d-th entry mismatch. Have {%s}, expect {%s}",
- k, i, result[i], l.Result[i])
- }
+func TestIsToken(t *testing.T) {
+ for i := 0; i <= 130; i++ {
+ r := rune(i)
+ expected := isChar(r) && !isCtl(r) && !isSeparator(r)
+ if isToken(r) != expected {
+ t.Errorf("isToken(0x%x) = %v", r, !expected)
}
}
}
diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go
index 7a9f465c477..d70bf4ed9d3 100644
--- a/libgo/go/net/http/pprof/pprof.go
+++ b/libgo/go/net/http/pprof/pprof.go
@@ -30,6 +30,10 @@
//
// go tool pprof http://localhost:6060/debug/pprof/profile
//
+// Or to look at the goroutine blocking profile:
+//
+// go tool pprof http://localhost:6060/debug/pprof/block
+//
// Or to view all available profiles:
//
// go tool pprof http://localhost:6060/debug/pprof/
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go
index f5bc6eb9100..61557ff8302 100644
--- a/libgo/go/net/http/request.go
+++ b/libgo/go/net/http/request.go
@@ -19,6 +19,7 @@ import (
"mime/multipart"
"net/textproto"
"net/url"
+ "strconv"
"strings"
)
@@ -131,6 +132,12 @@ type Request struct {
// The HTTP client ignores Form and uses Body instead.
Form url.Values
+ // PostForm contains the parsed form data from POST or PUT
+ // body parameters.
+ // This field is only available after ParseForm is called.
+ // The HTTP client ignores PostForm and uses Body instead.
+ PostForm url.Values
+
// MultipartForm is the parsed multipart form, including file uploads.
// This field is only available after ParseMultipartForm is called.
// The HTTP client ignores MultipartForm and uses Body instead.
@@ -369,36 +376,29 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
return bw.Flush()
}
-// Convert decimal at s[i:len(s)] to integer,
-// returning value, string position where the digits stopped,
-// and whether there was a valid number (digits, not too big).
-func atoi(s string, i int) (n, i1 int, ok bool) {
- const Big = 1000000
- if i >= len(s) || s[i] < '0' || s[i] > '9' {
- return 0, 0, false
- }
- n = 0
- for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
- n = n*10 + int(s[i]-'0')
- if n > Big {
- return 0, 0, false
- }
- }
- return n, i, true
-}
-
// ParseHTTPVersion parses a HTTP version string.
// "HTTP/1.0" returns (1, 0, true).
func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
- if len(vers) < 5 || vers[0:5] != "HTTP/" {
+ const Big = 1000000 // arbitrary upper bound
+ switch vers {
+ case "HTTP/1.1":
+ return 1, 1, true
+ case "HTTP/1.0":
+ return 1, 0, true
+ }
+ if !strings.HasPrefix(vers, "HTTP/") {
+ return 0, 0, false
+ }
+ dot := strings.Index(vers, ".")
+ if dot < 0 {
return 0, 0, false
}
- major, i, ok := atoi(vers, 5)
- if !ok || i >= len(vers) || vers[i] != '.' {
+ major, err := strconv.Atoi(vers[5:dot])
+ if err != nil || major < 0 || major > Big {
return 0, 0, false
}
- minor, i, ok = atoi(vers, i+1)
- if !ok || i != len(vers) {
+ minor, err = strconv.Atoi(vers[dot+1:])
+ if err != nil || minor < 0 || minor > Big {
return 0, 0, false
}
return major, minor, true
@@ -513,9 +513,9 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
// the same. In the second case, any Host line is ignored.
req.Host = req.URL.Host
if req.Host == "" {
- req.Host = req.Header.Get("Host")
+ req.Host = req.Header.get("Host")
}
- req.Header.Del("Host")
+ delete(req.Header, "Host")
fixPragmaCacheControl(req.Header)
@@ -594,66 +594,93 @@ func (l *maxBytesReader) Close() error {
return l.r.Close()
}
+func copyValues(dst, src url.Values) {
+ for k, vs := range src {
+ for _, value := range vs {
+ dst.Add(k, value)
+ }
+ }
+}
+
+func parsePostForm(r *Request) (vs url.Values, err error) {
+ if r.Body == nil {
+ err = errors.New("missing form body")
+ return
+ }
+ ct := r.Header.Get("Content-Type")
+ ct, _, err = mime.ParseMediaType(ct)
+ switch {
+ case ct == "application/x-www-form-urlencoded":
+ var reader io.Reader = r.Body
+ maxFormSize := int64(1<<63 - 1)
+ if _, ok := r.Body.(*maxBytesReader); !ok {
+ maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
+ reader = io.LimitReader(r.Body, maxFormSize+1)
+ }
+ b, e := ioutil.ReadAll(reader)
+ if e != nil {
+ if err == nil {
+ err = e
+ }
+ break
+ }
+ if int64(len(b)) > maxFormSize {
+ err = errors.New("http: POST too large")
+ return
+ }
+ vs, e = url.ParseQuery(string(b))
+ if err == nil {
+ err = e
+ }
+ case ct == "multipart/form-data":
+ // handled by ParseMultipartForm (which is calling us, or should be)
+ // TODO(bradfitz): there are too many possible
+ // orders to call too many functions here.
+ // Clean this up and write more tests.
+ // request_test.go contains the start of this,
+ // in TestRequestMultipartCallOrder.
+ }
+ return
+}
+
// ParseForm parses the raw query from the URL.
//
// For POST or PUT requests, it also parses the request body as a form.
+// POST and PUT body parameters take precedence over URL query string values.
// If the request Body's size has not already been limited by MaxBytesReader,
// the size is capped at 10MB.
//
// ParseMultipartForm calls ParseForm automatically.
// It is idempotent.
func (r *Request) ParseForm() (err error) {
- if r.Form != nil {
- return
- }
- if r.URL != nil {
- r.Form, err = url.ParseQuery(r.URL.RawQuery)
+ if r.PostForm == nil {
+ if r.Method == "POST" || r.Method == "PUT" {
+ r.PostForm, err = parsePostForm(r)
+ }
+ if r.PostForm == nil {
+ r.PostForm = make(url.Values)
+ }
}
- if r.Method == "POST" || r.Method == "PUT" {
- if r.Body == nil {
- return errors.New("missing form body")
+ if r.Form == nil {
+ if len(r.PostForm) > 0 {
+ r.Form = make(url.Values)
+ copyValues(r.Form, r.PostForm)
}
- ct := r.Header.Get("Content-Type")
- ct, _, err = mime.ParseMediaType(ct)
- switch {
- case ct == "application/x-www-form-urlencoded":
- var reader io.Reader = r.Body
- maxFormSize := int64(1<<63 - 1)
- if _, ok := r.Body.(*maxBytesReader); !ok {
- maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
- reader = io.LimitReader(r.Body, maxFormSize+1)
- }
- b, e := ioutil.ReadAll(reader)
- if e != nil {
- if err == nil {
- err = e
- }
- break
- }
- if int64(len(b)) > maxFormSize {
- return errors.New("http: POST too large")
- }
- var newValues url.Values
- newValues, e = url.ParseQuery(string(b))
+ var newValues url.Values
+ if r.URL != nil {
+ var e error
+ newValues, e = url.ParseQuery(r.URL.RawQuery)
if err == nil {
err = e
}
- if r.Form == nil {
- r.Form = make(url.Values)
- }
- // Copy values into r.Form. TODO: make this smoother.
- for k, vs := range newValues {
- for _, value := range vs {
- r.Form.Add(k, value)
- }
- }
- case ct == "multipart/form-data":
- // handled by ParseMultipartForm (which is calling us, or should be)
- // TODO(bradfitz): there are too many possible
- // orders to call too many functions here.
- // Clean this up and write more tests.
- // request_test.go contains the start of this,
- // in TestRequestMultipartCallOrder.
+ }
+ if newValues == nil {
+ newValues = make(url.Values)
+ }
+ if r.Form == nil {
+ r.Form = newValues
+ } else {
+ copyValues(r.Form, newValues)
}
}
return err
@@ -699,6 +726,7 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error {
}
// FormValue returns the first value for the named component of the query.
+// POST and PUT body parameters take precedence over URL query string values.
// FormValue calls ParseMultipartForm and ParseForm if necessary.
func (r *Request) FormValue(key string) string {
if r.Form == nil {
@@ -710,6 +738,19 @@ func (r *Request) FormValue(key string) string {
return ""
}
+// PostFormValue returns the first value for the named component of the POST
+// or PUT request body. URL query parameters are ignored.
+// PostFormValue calls ParseMultipartForm and ParseForm if necessary.
+func (r *Request) PostFormValue(key string) string {
+ if r.PostForm == nil {
+ r.ParseMultipartForm(defaultMaxMemory)
+ }
+ if vs := r.PostForm[key]; len(vs) > 0 {
+ return vs[0]
+ }
+ return ""
+}
+
// FormFile returns the first file for the provided form key.
// FormFile calls ParseMultipartForm and ParseForm if necessary.
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
@@ -732,12 +773,16 @@ func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, e
}
func (r *Request) expectsContinue() bool {
- return strings.ToLower(r.Header.Get("Expect")) == "100-continue"
+ return hasToken(r.Header.get("Expect"), "100-continue")
}
func (r *Request) wantsHttp10KeepAlive() bool {
if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
return false
}
- return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive")
+ return hasToken(r.Header.get("Connection"), "keep-alive")
+}
+
+func (r *Request) wantsClose() bool {
+ return hasToken(r.Header.get("Connection"), "close")
}
diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go
index 6e00b9bfd39..db7419b26fe 100644
--- a/libgo/go/net/http/request_test.go
+++ b/libgo/go/net/http/request_test.go
@@ -30,8 +30,8 @@ func TestQuery(t *testing.T) {
}
func TestPostQuery(t *testing.T) {
- req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x",
- strings.NewReader("z=post&both=y"))
+ req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not",
+ strings.NewReader("z=post&both=y&prio=2&empty="))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
if q := req.FormValue("q"); q != "foo" {
@@ -40,8 +40,23 @@ func TestPostQuery(t *testing.T) {
if z := req.FormValue("z"); z != "post" {
t.Errorf(`req.FormValue("z") = %q, want "post"`, z)
}
- if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"x", "y"}) {
- t.Errorf(`req.FormValue("both") = %q, want ["x", "y"]`, both)
+ if bq, found := req.PostForm["q"]; found {
+ t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq)
+ }
+ if bz := req.PostFormValue("z"); bz != "post" {
+ t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz)
+ }
+ if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) {
+ t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs)
+ }
+ if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) {
+ t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both)
+ }
+ if prio := req.FormValue("prio"); prio != "2" {
+ t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio)
+ }
+ if empty := req.FormValue("empty"); empty != "" {
+ t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
}
}
@@ -76,6 +91,23 @@ func TestParseFormUnknownContentType(t *testing.T) {
}
}
+func TestParseFormInitializeOnError(t *testing.T) {
+ nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil)
+ tests := []*Request{
+ nilBody,
+ {Method: "GET", URL: nil},
+ }
+ for i, req := range tests {
+ err := req.ParseForm()
+ if req.Form == nil {
+ t.Errorf("%d. Form not initialized, error %v", i, err)
+ }
+ if req.PostForm == nil {
+ t.Errorf("%d. PostForm not initialized, error %v", i, err)
+ }
+ }
+}
+
func TestMultipartReader(t *testing.T) {
req := &Request{
Method: "POST",
diff --git a/libgo/go/net/http/response.go b/libgo/go/net/http/response.go
index 945ecd8a4b0..92d2f499839 100644
--- a/libgo/go/net/http/response.go
+++ b/libgo/go/net/http/response.go
@@ -107,7 +107,6 @@ func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err error) {
resp = new(Response)
resp.Request = req
- resp.Request.Method = strings.ToUpper(resp.Request.Method)
// Parse the first line of the response.
line, err := tp.ReadLine()
@@ -188,11 +187,6 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
//
func (r *Response) Write(w io.Writer) error {
- // RequestMethod should be upper-case
- if r.Request != nil {
- r.Request.Method = strings.ToUpper(r.Request.Method)
- }
-
// Status line
text := r.Status
if text == "" {
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index c9d73932bb9..71b7b3fb6b7 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -20,8 +20,13 @@ import (
"net/http/httputil"
"net/url"
"os"
+ "os/exec"
"reflect"
+ "runtime"
+ "strconv"
"strings"
+ "sync"
+ "sync/atomic"
"syscall"
"testing"
"time"
@@ -168,6 +173,9 @@ var vtests = []struct {
{"http://someHost.com/someDir/apage", "someHost.com/someDir"},
{"http://otherHost.com/someDir/apage", "someDir"},
{"http://otherHost.com/aDir/apage", "Default"},
+ // redirections for trees
+ {"http://localhost/someDir", "/someDir/"},
+ {"http://someHost.com/someDir", "/someDir/"},
}
func TestHostHandlers(t *testing.T) {
@@ -199,9 +207,19 @@ func TestHostHandlers(t *testing.T) {
t.Errorf("reading response: %v", err)
continue
}
- s := r.Header.Get("Result")
- if s != vt.expected {
- t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected)
+ switch r.StatusCode {
+ case StatusOK:
+ s := r.Header.Get("Result")
+ if s != vt.expected {
+ t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected)
+ }
+ case StatusMovedPermanently:
+ s := r.Header.Get("Location")
+ if s != vt.expected {
+ t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected)
+ }
+ default:
+ t.Errorf("Get(%q) unhandled status code %d", vt.url, r.StatusCode)
}
}
}
@@ -370,7 +388,7 @@ func TestIdentityResponse(t *testing.T) {
})
}
-func testTcpConnectionCloses(t *testing.T, req string, h Handler) {
+func testTCPConnectionCloses(t *testing.T, req string, h Handler) {
s := httptest.NewServer(h)
defer s.Close()
@@ -414,21 +432,28 @@ func testTcpConnectionCloses(t *testing.T, req string, h Handler) {
// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive.
func TestServeHTTP10Close(t *testing.T) {
- testTcpConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
ServeFile(w, r, "testdata/file")
}))
}
+// TestClientCanClose verifies that clients can also force a connection to close.
+func TestClientCanClose(t *testing.T) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ // Nothing.
+ }))
+}
+
// TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close,
// even for HTTP/1.1 requests.
func TestHandlersCanSetConnectionClose11(t *testing.T) {
- testTcpConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Connection", "close")
}))
}
func TestHandlersCanSetConnectionClose10(t *testing.T) {
- testTcpConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Connection", "close")
}))
}
@@ -665,30 +690,51 @@ func TestServerExpect(t *testing.T) {
t.Fatalf("Dial: %v", err)
}
defer conn.Close()
- sendf := func(format string, args ...interface{}) {
- _, err := fmt.Fprintf(conn, format, args...)
- if err != nil {
- t.Fatalf("On test %#v, error writing %q: %v", test, format, err)
- }
- }
+
+ // Only send the body immediately if we're acting like an HTTP client
+ // that doesn't send 100-continue expectations.
+ writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue"
+
go func() {
- sendf("POST /?readbody=%v HTTP/1.1\r\n"+
+ _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+
"Connection: close\r\n"+
"Content-Length: %d\r\n"+
"Expect: %s\r\nHost: foo\r\n\r\n",
test.readBody, test.contentLength, test.expectation)
- if test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" {
+ if err != nil {
+ t.Errorf("On test %#v, error writing request headers: %v", test, err)
+ return
+ }
+ if writeBody {
body := strings.Repeat("A", test.contentLength)
- sendf(body)
+ _, err = fmt.Fprint(conn, body)
+ if err != nil {
+ if !test.readBody {
+ // Server likely already hung up on us.
+ // See larger comment below.
+ t.Logf("On test %#v, acceptable error writing request body: %v", test, err)
+ return
+ }
+ t.Errorf("On test %#v, error writing request body: %v", test, err)
+ }
}
}()
bufr := bufio.NewReader(conn)
line, err := bufr.ReadString('\n')
if err != nil {
- t.Fatalf("ReadString: %v", err)
+ if writeBody && !test.readBody {
+ // This is an acceptable failure due to a possible TCP race:
+ // We were still writing data and the server hung up on us. A TCP
+ // implementation may send a RST if our request body data was known
+ // to be lost, which may trigger our reads to fail.
+ // See RFC 1122 page 88.
+ t.Logf("On test %#v, acceptable error from ReadString: %v", test, err)
+ return
+ }
+ t.Fatalf("On test %#v, ReadString: %v", test, err)
}
if !strings.Contains(line, test.expectedResponse) {
- t.Errorf("for test %#v got first line=%q", test, line)
+ t.Errorf("On test %#v, got first line = %q; want %q", test, line, test.expectedResponse)
}
}
@@ -1112,6 +1158,68 @@ func TestServerBufferedChunking(t *testing.T) {
}
}
+// Tests that the server flushes its response headers out when it's
+// ignoring the response body and waits a bit before forcefully
+// closing the TCP connection, causing the client to get a RST.
+// See http://golang.org/issue/3595
+func TestServerGracefulClose(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ Error(w, "bye", StatusUnauthorized)
+ }))
+ defer ts.Close()
+
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+ const bodySize = 5 << 20
+ req := []byte(fmt.Sprintf("POST / HTTP/1.1\r\nHost: foo.com\r\nContent-Length: %d\r\n\r\n", bodySize))
+ for i := 0; i < bodySize; i++ {
+ req = append(req, 'x')
+ }
+ writeErr := make(chan error)
+ go func() {
+ _, err := conn.Write(req)
+ writeErr <- err
+ }()
+ br := bufio.NewReader(conn)
+ lineNum := 0
+ for {
+ line, err := br.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("ReadLine: %v", err)
+ }
+ lineNum++
+ if lineNum == 1 && !strings.Contains(line, "401 Unauthorized") {
+ t.Errorf("Response line = %q; want a 401", line)
+ }
+ }
+ // Wait for write to finish. This is a broken pipe on both
+ // Darwin and Linux, but checking this isn't the point of
+ // the test.
+ <-writeErr
+}
+
+func TestCaseSensitiveMethod(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.Method != "get" {
+ t.Errorf(`Got method %q; want "get"`, r.Method)
+ }
+ }))
+ defer ts.Close()
+ req, _ := NewRequest("get", ts.URL, nil)
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ res.Body.Close()
+}
+
// TestContentLengthZero tests that for both an HTTP/1.0 and HTTP/1.1
// request (both keep-alive), when a Handler never writes any
// response, the net/http package adds a "Content-Length: 0" response
@@ -1220,3 +1328,100 @@ func BenchmarkClientServer(b *testing.B) {
b.StopTimer()
}
+
+func BenchmarkClientServerParallel4(b *testing.B) {
+ benchmarkClientServerParallel(b, 4)
+}
+
+func BenchmarkClientServerParallel64(b *testing.B) {
+ benchmarkClientServerParallel(b, 64)
+}
+
+func benchmarkClientServerParallel(b *testing.B, conc int) {
+ b.StopTimer()
+ ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
+ fmt.Fprintf(rw, "Hello world.\n")
+ }))
+ defer ts.Close()
+ b.StartTimer()
+
+ numProcs := runtime.GOMAXPROCS(-1) * conc
+ var wg sync.WaitGroup
+ wg.Add(numProcs)
+ n := int32(b.N)
+ for p := 0; p < numProcs; p++ {
+ go func() {
+ for atomic.AddInt32(&n, -1) >= 0 {
+ res, err := Get(ts.URL)
+ if err != nil {
+ b.Logf("Get: %v", err)
+ continue
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ b.Logf("ReadAll: %v", err)
+ continue
+ }
+ body := string(all)
+ if body != "Hello world.\n" {
+ panic("Got body: " + body)
+ }
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+// A benchmark for profiling the server without the HTTP client code.
+// The client code runs in a subprocess.
+//
+// For use like:
+// $ go test -c
+// $ ./http.test -test.run=XX -test.bench=BenchmarkServer -test.benchtime=15s -test.cpuprofile=http.prof
+// $ go tool pprof http.test http.prof
+// (pprof) web
+func BenchmarkServer(b *testing.B) {
+ // Child process mode;
+ if url := os.Getenv("TEST_BENCH_SERVER_URL"); url != "" {
+ n, err := strconv.Atoi(os.Getenv("TEST_BENCH_CLIENT_N"))
+ if err != nil {
+ panic(err)
+ }
+ for i := 0; i < n; i++ {
+ res, err := Get(url)
+ if err != nil {
+ log.Panicf("Get: %v", err)
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ log.Panicf("ReadAll: %v", err)
+ }
+ body := string(all)
+ if body != "Hello world.\n" {
+ log.Panicf("Got body: %q", body)
+ }
+ }
+ os.Exit(0)
+ return
+ }
+
+ var res = []byte("Hello world.\n")
+ b.StopTimer()
+ ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
+ rw.Header().Set("Content-Type", "text/html; charset=utf-8")
+ rw.Write(res)
+ }))
+ defer ts.Close()
+ b.StartTimer()
+
+ cmd := exec.Command(os.Args[0], "-test.run=XXXX", "-test.bench=BenchmarkServer")
+ cmd.Env = append([]string{
+ fmt.Sprintf("TEST_BENCH_CLIENT_N=%d", b.N),
+ fmt.Sprintf("TEST_BENCH_SERVER_URL=%s", ts.URL),
+ }, os.Environ()...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ b.Errorf("Test failure: %v, with output: %s", err, out)
+ }
+}
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index b74b7629809..ee57e01276d 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -129,7 +129,7 @@ type response struct {
// maxBytesReader hits its max size. It is checked in
// WriteHeader, to make sure we don't consume the the
// remaining request body to try to advance to the next HTTP
- // request. Instead, when this is set, we stop doing
+ // request. Instead, when this is set, we stop reading
// subsequent requests on this connection and stop reading
// input from it.
requestBodyLimitHit bool
@@ -287,7 +287,7 @@ func (w *response) WriteHeader(code int) {
// Check for a explicit (and valid) Content-Length header.
var hasCL bool
var contentLength int64
- if clenStr := w.header.Get("Content-Length"); clenStr != "" {
+ if clenStr := w.header.get("Content-Length"); clenStr != "" {
var err error
contentLength, err = strconv.ParseInt(clenStr, 10, 64)
if err == nil {
@@ -303,12 +303,11 @@ func (w *response) WriteHeader(code int) {
if !connectionHeaderSet {
w.header.Set("Connection", "keep-alive")
}
- } else if !w.req.ProtoAtLeast(1, 1) {
- // Client did not ask to keep connection alive.
+ } else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() {
w.closeAfterReply = true
}
- if w.header.Get("Connection") == "close" {
+ if w.header.get("Connection") == "close" {
w.closeAfterReply = true
}
@@ -332,7 +331,7 @@ func (w *response) WriteHeader(code int) {
if code == StatusNotModified {
// Must not have body.
for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
- if w.header.Get(header) != "" {
+ if w.header.get(header) != "" {
// TODO: return an error if WriteHeader gets a return parameter
// or set a flag on w to make future Writes() write an error page?
// for now just log and drop the header.
@@ -342,7 +341,7 @@ func (w *response) WriteHeader(code int) {
}
} else {
// If no content type, apply sniffing algorithm to body.
- if w.header.Get("Content-Type") == "" && w.req.Method != "HEAD" {
+ if w.header.get("Content-Type") == "" && w.req.Method != "HEAD" {
w.needSniff = true
}
}
@@ -351,7 +350,7 @@ func (w *response) WriteHeader(code int) {
w.Header().Set("Date", time.Now().UTC().Format(TimeFormat))
}
- te := w.header.Get("Transfer-Encoding")
+ te := w.header.get("Transfer-Encoding")
hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
@@ -391,7 +390,7 @@ func (w *response) WriteHeader(code int) {
return
}
- if w.closeAfterReply && !hasToken(w.header.Get("Connection"), "close") {
+ if w.closeAfterReply && !hasToken(w.header.get("Connection"), "close") {
w.header.Set("Connection", "close")
}
@@ -518,14 +517,14 @@ func (w *response) finishRequest() {
// HTTP/1.0 clients keep their "keep-alive" connections alive, and for
// HTTP/1.1 clients is just as good as the alternative: sending a
// chunked response and immediately sending the zero-length EOF chunk.
- if w.written == 0 && w.header.Get("Content-Length") == "" {
+ if w.written == 0 && w.header.get("Content-Length") == "" {
w.header.Set("Content-Length", "0")
}
// If this was an HTTP/1.0 request with keep-alive and we sent a
// Content-Length back, we can make this a keep-alive response ...
if w.req.wantsHttp10KeepAlive() {
- sentLength := w.header.Get("Content-Length") != ""
- if sentLength && w.header.Get("Connection") == "keep-alive" {
+ sentLength := w.header.get("Content-Length") != ""
+ if sentLength && w.header.get("Connection") == "keep-alive" {
w.closeAfterReply = false
}
}
@@ -564,18 +563,31 @@ func (w *response) Flush() {
w.conn.buf.Flush()
}
-// Close the connection.
-func (c *conn) close() {
+func (c *conn) finalFlush() {
if c.buf != nil {
c.buf.Flush()
c.buf = nil
}
+}
+
+// Close the connection.
+func (c *conn) close() {
+ c.finalFlush()
if c.rwc != nil {
c.rwc.Close()
c.rwc = nil
}
}
+// 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() {
+ c.finalFlush()
+ if tcp, ok := c.rwc.(*net.TCPConn); ok {
+ tcp.CloseWrite()
+ }
+}
+
// Serve a new connection.
func (c *conn) serve() {
defer func() {
@@ -637,7 +649,7 @@ func (c *conn) serve() {
break
}
req.Header.Del("Expect")
- } else if req.Header.Get("Expect") != "" {
+ } else if req.Header.get("Expect") != "" {
// TODO(bradfitz): let ServeHTTP handlers handle
// requests with non-standard expectation[s]? Seems
// theoretical at best, and doesn't fit into the
@@ -672,6 +684,20 @@ 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)
+ }
break
}
}
@@ -849,13 +875,15 @@ func RedirectHandler(url string, code int) Handler {
// redirecting any request containing . or .. elements to an
// equivalent .- and ..-free URL.
type ServeMux struct {
- mu sync.RWMutex
- m map[string]muxEntry
+ mu sync.RWMutex
+ m map[string]muxEntry
+ hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
explicit bool
h Handler
+ pattern string
}
// NewServeMux allocates and returns a new ServeMux.
@@ -896,8 +924,7 @@ func cleanPath(p string) string {
// Find a handler on a handler map given a path string
// Most-specific (longest) pattern wins
-func (mux *ServeMux) match(path string) Handler {
- var h Handler
+func (mux *ServeMux) match(path string) (h Handler, pattern string) {
var n = 0
for k, v := range mux.m {
if !pathMatch(k, path) {
@@ -906,39 +933,59 @@ func (mux *ServeMux) match(path string) Handler {
if h == nil || len(k) > n {
n = len(k)
h = v.h
+ pattern = v.pattern
+ }
+ }
+ return
+}
+
+// Handler returns the handler to use for the given request,
+// consulting r.Method, r.Host, and r.URL.Path. It always returns
+// a non-nil handler. If the path is not in its canonical form, the
+// handler will be an internally-generated handler that redirects
+// to the canonical path.
+//
+// Handler also returns the registered pattern that matches the
+// request or, in the case of internally-generated redirects,
+// the pattern that will match after following the redirect.
+//
+// If there is no registered handler that applies to the request,
+// Handler returns a ``page not found'' handler and an empty pattern.
+func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
+ if r.Method != "CONNECT" {
+ if p := cleanPath(r.URL.Path); p != r.URL.Path {
+ _, pattern = mux.handler(r.Host, p)
+ return RedirectHandler(p, StatusMovedPermanently), pattern
}
}
- return h
+
+ return mux.handler(r.Host, r.URL.Path)
}
-// handler returns the handler to use for the request r.
-func (mux *ServeMux) handler(r *Request) Handler {
+// handler is the main implementation of Handler.
+// The path is known to be in canonical form, except for CONNECT methods.
+func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
- h := mux.match(r.Host + r.URL.Path)
+ if mux.hosts {
+ h, pattern = mux.match(host + path)
+ }
if h == nil {
- h = mux.match(r.URL.Path)
+ h, pattern = mux.match(path)
}
if h == nil {
- h = NotFoundHandler()
+ h, pattern = NotFoundHandler(), ""
}
- return h
+ return
}
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
- if r.Method != "CONNECT" {
- // Clean path to canonical form and redirect.
- if p := cleanPath(r.URL.Path); p != r.URL.Path {
- w.Header().Set("Location", p)
- w.WriteHeader(StatusMovedPermanently)
- return
- }
- }
- mux.handler(r).ServeHTTP(w, r)
+ h, _ := mux.Handler(r)
+ h.ServeHTTP(w, r)
}
// Handle registers the handler for the given pattern.
@@ -957,14 +1004,26 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
panic("http: multiple registrations for " + pattern)
}
- mux.m[pattern] = muxEntry{explicit: true, h: handler}
+ mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
+
+ if pattern[0] != '/' {
+ mux.hosts = true
+ }
// Helpful behavior:
// If pattern is /tree/, insert an implicit permanent redirect for /tree.
// It can be overridden by an explicit registration.
n := len(pattern)
if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
- mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(pattern, StatusMovedPermanently)}
+ // If pattern contains a host name, strip it and use remaining
+ // path for redirect.
+ path := pattern
+ if pattern[0] != '/' {
+ // In pattern, at least the last character is a '/', so
+ // strings.Index can't be -1.
+ path = pattern[strings.Index(pattern, "/"):]
+ }
+ mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}
}
}
diff --git a/libgo/go/net/http/server_test.go b/libgo/go/net/http/server_test.go
new file mode 100644
index 00000000000..8b4e8c6d6f6
--- /dev/null
+++ b/libgo/go/net/http/server_test.go
@@ -0,0 +1,95 @@
+// 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 (
+ "net/url"
+ "testing"
+)
+
+var serveMuxRegister = []struct {
+ pattern string
+ h Handler
+}{
+ {"/dir/", serve(200)},
+ {"/search", serve(201)},
+ {"codesearch.google.com/search", serve(202)},
+ {"codesearch.google.com/", serve(203)},
+}
+
+// serve returns a handler that sends a response with the given code.
+func serve(code int) HandlerFunc {
+ return func(w ResponseWriter, r *Request) {
+ w.WriteHeader(code)
+ }
+}
+
+var serveMuxTests = []struct {
+ method string
+ host string
+ path string
+ code int
+ pattern string
+}{
+ {"GET", "google.com", "/", 404, ""},
+ {"GET", "google.com", "/dir", 301, "/dir/"},
+ {"GET", "google.com", "/dir/", 200, "/dir/"},
+ {"GET", "google.com", "/dir/file", 200, "/dir/"},
+ {"GET", "google.com", "/search", 201, "/search"},
+ {"GET", "google.com", "/search/", 404, ""},
+ {"GET", "google.com", "/search/foo", 404, ""},
+ {"GET", "codesearch.google.com", "/search", 202, "codesearch.google.com/search"},
+ {"GET", "codesearch.google.com", "/search/", 203, "codesearch.google.com/"},
+ {"GET", "codesearch.google.com", "/search/foo", 203, "codesearch.google.com/"},
+ {"GET", "codesearch.google.com", "/", 203, "codesearch.google.com/"},
+ {"GET", "images.google.com", "/search", 201, "/search"},
+ {"GET", "images.google.com", "/search/", 404, ""},
+ {"GET", "images.google.com", "/search/foo", 404, ""},
+ {"GET", "google.com", "/../search", 301, "/search"},
+ {"GET", "google.com", "/dir/..", 301, ""},
+ {"GET", "google.com", "/dir/..", 301, ""},
+ {"GET", "google.com", "/dir/./file", 301, "/dir/"},
+
+ // The /foo -> /foo/ redirect applies to CONNECT requests
+ // but the path canonicalization does not.
+ {"CONNECT", "google.com", "/dir", 301, "/dir/"},
+ {"CONNECT", "google.com", "/../search", 404, ""},
+ {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
+ {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
+ {"CONNECT", "google.com", "/dir/./file", 200, "/dir/"},
+}
+
+func TestServeMuxHandler(t *testing.T) {
+ mux := NewServeMux()
+ for _, e := range serveMuxRegister {
+ mux.Handle(e.pattern, e.h)
+ }
+
+ for _, tt := range serveMuxTests {
+ r := &Request{
+ Method: tt.method,
+ Host: tt.host,
+ URL: &url.URL{
+ Path: tt.path,
+ },
+ }
+ h, pattern := mux.Handler(r)
+ cs := &codeSaver{h: Header{}}
+ h.ServeHTTP(cs, r)
+ if pattern != tt.pattern || cs.code != tt.code {
+ t.Errorf("%s %s %s = %d, %q, want %d, %q", tt.method, tt.host, tt.path, cs.code, pattern, tt.code, tt.pattern)
+ }
+ }
+}
+
+// A codeSaver is a ResponseWriter that saves the code passed to WriteHeader.
+type codeSaver struct {
+ h Header
+ code int
+}
+
+func (cs *codeSaver) Header() Header { return cs.h }
+func (cs *codeSaver) Write(p []byte) (int, error) { return len(p), nil }
+func (cs *codeSaver) WriteHeader(code int) { cs.code = code }
diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go
index 9e9d84172d0..1fc1e63a960 100644
--- a/libgo/go/net/http/transfer.go
+++ b/libgo/go/net/http/transfer.go
@@ -432,7 +432,7 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
}
// Logic based on Content-Length
- cl := strings.TrimSpace(header.Get("Content-Length"))
+ cl := strings.TrimSpace(header.get("Content-Length"))
if cl != "" {
n, err := strconv.ParseInt(cl, 10, 64)
if err != nil || n < 0 {
@@ -454,7 +454,7 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
// Logic based on media type. The purpose of the following code is just
// to detect whether the unsupported "multipart/byteranges" is being
// used. A proper Content-Type parser is needed in the future.
- if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") {
+ if strings.Contains(strings.ToLower(header.get("Content-Type")), "multipart/byteranges") {
return -1, ErrNotSupported
}
@@ -469,14 +469,14 @@ func shouldClose(major, minor int, header Header) bool {
if major < 1 {
return true
} else if major == 1 && minor == 0 {
- if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") {
+ if !strings.Contains(strings.ToLower(header.get("Connection")), "keep-alive") {
return true
}
return false
} else {
// TODO: Should split on commas, toss surrounding white space,
// and check each field.
- if strings.ToLower(header.Get("Connection")) == "close" {
+ if strings.ToLower(header.get("Connection")) == "close" {
header.Del("Connection")
return true
}
@@ -486,7 +486,7 @@ func shouldClose(major, minor int, header Header) bool {
// Parse the trailer header
func fixTrailer(header Header, te []string) (Header, error) {
- raw := header.Get("Trailer")
+ raw := header.get("Trailer")
if raw == "" {
return nil, nil
}
diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go
index 6131d0d1ee1..651f3ce0081 100644
--- a/libgo/go/net/http/transport.go
+++ b/libgo/go/net/http/transport.go
@@ -24,6 +24,7 @@ import (
"os"
"strings"
"sync"
+ "time"
)
// DefaultTransport is the default implementation of Transport and is
@@ -265,6 +266,11 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
pconn.close()
return false
}
+ for _, exist := range t.idleConn[key] {
+ if exist == pconn {
+ log.Fatalf("dup idle pconn %p in freelist", pconn)
+ }
+ }
t.idleConn[key] = append(t.idleConn[key], pconn)
t.idleLk.Unlock()
return true
@@ -295,7 +301,7 @@ func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) {
return
}
}
- return
+ panic("unreachable")
}
func (t *Transport) dial(network, addr string) (c net.Conn, err error) {
@@ -329,6 +335,8 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
cacheKey: cm.String(),
conn: conn,
reqch: make(chan requestAndChan, 50),
+ writech: make(chan writeRequest, 50),
+ closech: make(chan struct{}),
}
switch {
@@ -373,7 +381,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
// Initiate TLS and check remote host name against certificate.
cfg := t.TLSClientConfig
if cfg == nil || cfg.ServerName == "" {
- host, _, _ := net.SplitHostPort(cm.addr())
+ host := cm.tlsHost()
if cfg == nil {
cfg = &tls.Config{ServerName: host}
} else {
@@ -397,6 +405,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
pconn.br = bufio.NewReader(pconn.conn)
pconn.bw = bufio.NewWriter(pconn.conn)
go pconn.readLoop()
+ go pconn.writeLoop()
return pconn, nil
}
@@ -504,7 +513,9 @@ type persistConn struct {
closed bool // whether conn has been closed
br *bufio.Reader // from conn
bw *bufio.Writer // to conn
- reqch chan requestAndChan // written by roundTrip(); read by readLoop()
+ reqch chan requestAndChan // written by roundTrip; read by readLoop
+ writech chan writeRequest // written by roundTrip; read by writeLoop
+ closech chan struct{} // broadcast close when readLoop (TCP connection) closes
isProxy bool
// mutateHeaderFunc is an optional func to modify extra
@@ -537,6 +548,7 @@ func remoteSideClosed(err error) bool {
}
func (pc *persistConn) readLoop() {
+ defer close(pc.closech)
alive := true
var lastbody io.ReadCloser // last response body, if any, read on this connection
@@ -563,7 +575,11 @@ func (pc *persistConn) readLoop() {
lastbody.Close() // assumed idempotent
lastbody = nil
}
- resp, err := ReadResponse(pc.br, rc.req)
+
+ var resp *Response
+ if err == nil {
+ resp, err = ReadResponse(pc.br, rc.req)
+ }
if err != nil {
pc.close()
@@ -592,12 +608,12 @@ func (pc *persistConn) readLoop() {
var waitForBodyRead chan bool
if hasBody {
lastbody = resp.Body
- waitForBodyRead = make(chan bool)
+ waitForBodyRead = make(chan bool, 1)
resp.Body.(*bodyEOFSignal).fn = func() {
if alive && !pc.t.putIdleConn(pc) {
alive = false
}
- if !alive {
+ if !alive || pc.isBroken() {
pc.close()
}
waitForBodyRead <- true
@@ -633,6 +649,28 @@ func (pc *persistConn) readLoop() {
}
}
+func (pc *persistConn) writeLoop() {
+ for {
+ select {
+ case wr := <-pc.writech:
+ if pc.isBroken() {
+ wr.ch <- errors.New("http: can't write HTTP request on broken connection")
+ continue
+ }
+ err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra)
+ if err == nil {
+ err = pc.bw.Flush()
+ }
+ if err != nil {
+ pc.markBroken()
+ }
+ wr.ch <- err
+ case <-pc.closech:
+ return
+ }
+ }
+}
+
type responseAndError struct {
res *Response
err error
@@ -648,6 +686,15 @@ type requestAndChan struct {
addedGzip bool
}
+// A writeRequest is sent by the readLoop's goroutine to the
+// writeLoop's goroutine to write a request while the read loop
+// concurrently waits on both the write response and the server's
+// reply.
+type writeRequest struct {
+ req *transportRequest
+ ch chan<- error
+}
+
func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
if pc.mutateHeaderFunc != nil {
pc.mutateHeaderFunc(req.extraHeaders())
@@ -670,16 +717,49 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
pc.numExpectedResponses++
pc.lk.Unlock()
- err = req.Request.write(pc.bw, pc.isProxy, req.extra)
- if err != nil {
- pc.close()
- return
+ // Write the request concurrently with waiting for a response,
+ // in case the server decides to reply before reading our full
+ // request body.
+ writeErrCh := make(chan error, 1)
+ pc.writech <- writeRequest{req, writeErrCh}
+
+ resc := make(chan responseAndError, 1)
+ pc.reqch <- requestAndChan{req.Request, resc, requestedGzip}
+
+ var re responseAndError
+ var pconnDeadCh = pc.closech
+ var failTicker <-chan time.Time
+WaitResponse:
+ for {
+ select {
+ case err := <-writeErrCh:
+ if err != nil {
+ re = responseAndError{nil, err}
+ break WaitResponse
+ }
+ case <-pconnDeadCh:
+ // The persist connection is dead. This shouldn't
+ // usually happen (only with Connection: close responses
+ // with no response bodies), but if it does happen it
+ // means either a) the remote server hung up on us
+ // prematurely, or b) the readLoop sent us a response &
+ // closed its closech at roughly the same time, and we
+ // selected this case first, in which case a response
+ // might still be coming soon.
+ //
+ // We can't avoid the select race in b) by using a unbuffered
+ // resc channel instead, because then goroutines can
+ // leak if we exit due to other errors.
+ pconnDeadCh = nil // avoid spinning
+ failTicker = time.After(100 * time.Millisecond) // arbitrary time to wait for resc
+ case <-failTicker:
+ re = responseAndError{nil, errors.New("net/http: transport closed before response was received")}
+ break WaitResponse
+ case re = <-resc:
+ break WaitResponse
+ }
}
- pc.bw.Flush()
- ch := make(chan responseAndError, 1)
- pc.reqch <- requestAndChan{req.Request, ch, requestedGzip}
- re := <-ch
pc.lk.Lock()
pc.numExpectedResponses--
pc.lk.Unlock()
@@ -687,6 +767,15 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
return re.res, re.err
}
+// markBroken marks a connection as broken (so it's not reused).
+// It differs from close in that it doesn't close the underlying
+// connection for use when it's still being read.
+func (pc *persistConn) markBroken() {
+ pc.lk.Lock()
+ defer pc.lk.Unlock()
+ pc.broken = true
+}
+
func (pc *persistConn) close() {
pc.lk.Lock()
defer pc.lk.Unlock()
@@ -728,6 +817,7 @@ type bodyEOFSignal struct {
body io.ReadCloser
fn func()
isClosed bool
+ once sync.Once
}
func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
@@ -735,9 +825,8 @@ func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
if es.isClosed && n > 0 {
panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725")
}
- if err == io.EOF && es.fn != nil {
- es.fn()
- es.fn = nil
+ if err == io.EOF {
+ es.condfn()
}
return
}
@@ -748,13 +837,18 @@ func (es *bodyEOFSignal) Close() (err error) {
}
es.isClosed = true
err = es.body.Close()
- if err == nil && es.fn != nil {
- es.fn()
- es.fn = nil
+ if err == nil {
+ es.condfn()
}
return
}
+func (es *bodyEOFSignal) condfn() {
+ if es.fn != nil {
+ es.once.Do(es.fn)
+ }
+}
+
type readFirstCloseBoth struct {
io.ReadCloser
io.Closer
diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go
index e676bf6db39..e4072e88fed 100644
--- a/libgo/go/net/http/transport_test.go
+++ b/libgo/go/net/http/transport_test.go
@@ -833,6 +833,74 @@ func TestIssue3644(t *testing.T) {
}
}
+// Test that a client receives a server's reply, even if the server doesn't read
+// the entire request body.
+func TestIssue3595(t *testing.T) {
+ const deniedMsg = "sorry, denied."
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ Error(w, deniedMsg, StatusUnauthorized)
+ }))
+ defer ts.Close()
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+ res, err := c.Post(ts.URL, "application/octet-stream", neverEnding('a'))
+ if err != nil {
+ t.Errorf("Post: %v", err)
+ return
+ }
+ got, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("Body ReadAll: %v", err)
+ }
+ if !strings.Contains(string(got), deniedMsg) {
+ t.Errorf("Known bug: response %q does not contain %q", got, deniedMsg)
+ }
+}
+
+func TestTransportConcurrency(t *testing.T) {
+ const maxProcs = 16
+ const numReqs = 500
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ fmt.Fprintf(w, "%v", r.FormValue("echo"))
+ }))
+ defer ts.Close()
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+ reqs := make(chan string)
+ defer close(reqs)
+
+ var wg sync.WaitGroup
+ wg.Add(numReqs)
+ for i := 0; i < maxProcs*2; i++ {
+ go func() {
+ for req := range reqs {
+ res, err := c.Get(ts.URL + "/?echo=" + req)
+ if err != nil {
+ t.Errorf("error on req %s: %v", req, err)
+ wg.Done()
+ continue
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Errorf("read error on req %s: %v", req, err)
+ wg.Done()
+ continue
+ }
+ if string(all) != req {
+ t.Errorf("body of req %s = %q; want %q", req, all, req)
+ }
+ wg.Done()
+ res.Body.Close()
+ }
+ }()
+ }
+ for i := 0; i < numReqs; i++ {
+ reqs <- fmt.Sprintf("request-%d", i)
+ }
+ wg.Wait()
+}
+
type fooProto struct{}
func (fooProto) RoundTrip(req *Request) (*Response, error) {
diff --git a/libgo/go/net/interface_test.go b/libgo/go/net/interface_test.go
index 0a33bfdb517..2fe0f60caea 100644
--- a/libgo/go/net/interface_test.go
+++ b/libgo/go/net/interface_test.go
@@ -24,7 +24,7 @@ func TestInterfaces(t *testing.T) {
if err != nil {
t.Fatalf("Interfaces failed: %v", err)
}
- t.Logf("table: len/cap = %v/%v\n", len(ift), cap(ift))
+ t.Logf("table: len/cap = %v/%v", len(ift), cap(ift))
for _, ifi := range ift {
ifxi, err := InterfaceByIndex(ifi.Index)
@@ -41,7 +41,7 @@ func TestInterfaces(t *testing.T) {
if !sameInterface(ifxn, &ifi) {
t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, *ifxn, ifi)
}
- t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU)
+ t.Logf("%q: flags %q, ifindex %v, mtu %v", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU)
t.Logf("\thardware address %q", ifi.HardwareAddr.String())
testInterfaceAddrs(t, &ifi)
testInterfaceMulticastAddrs(t, &ifi)
@@ -53,7 +53,7 @@ func TestInterfaceAddrs(t *testing.T) {
if err != nil {
t.Fatalf("InterfaceAddrs failed: %v", err)
}
- t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat))
+ t.Logf("table: len/cap = %v/%v", len(ifat), cap(ifat))
testAddrs(t, ifat)
}
@@ -77,7 +77,7 @@ func testAddrs(t *testing.T, ifat []Addr) {
for _, ifa := range ifat {
switch ifa.(type) {
case *IPAddr, *IPNet:
- t.Logf("\tinterface address %q\n", ifa.String())
+ t.Logf("\tinterface address %q", ifa.String())
default:
t.Errorf("\tunexpected type: %T", ifa)
}
@@ -88,7 +88,7 @@ func testMulticastAddrs(t *testing.T, ifmat []Addr) {
for _, ifma := range ifmat {
switch ifma.(type) {
case *IPAddr:
- t.Logf("\tjoined group address %q\n", ifma.String())
+ t.Logf("\tjoined group address %q", ifma.String())
default:
t.Errorf("\tunexpected type: %T", ifma)
}
diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go
index 6136202727c..d37272c1062 100644
--- a/libgo/go/net/ipraw_test.go
+++ b/libgo/go/net/ipraw_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !plan9
+
package net
import (
@@ -24,7 +26,7 @@ var icmpTests = []struct {
func TestICMP(t *testing.T) {
if os.Getuid() != 0 {
- t.Logf("test disabled; must be root")
+ t.Logf("skipping test; must be root")
return
}
diff --git a/libgo/go/net/iprawsock_plan9.go b/libgo/go/net/iprawsock_plan9.go
index ea3321b7e2f..6de2ee33d84 100644
--- a/libgo/go/net/iprawsock_plan9.go
+++ b/libgo/go/net/iprawsock_plan9.go
@@ -7,14 +7,37 @@
package net
import (
+ "os"
"syscall"
"time"
)
-// IPConn is the implementation of the Conn and PacketConn
-// interfaces for IP network connections.
+// IPConn is the implementation of the Conn and PacketConn interfaces
+// for IP network connections.
type IPConn bool
+// Implementation of the Conn interface - see Conn for documentation.
+
+// Read implements the Conn Read method.
+func (c *IPConn) Read(b []byte) (int, error) {
+ return 0, syscall.EPLAN9
+}
+
+// Write implements the Conn Write method.
+func (c *IPConn) Write(b []byte) (int, error) {
+ return 0, syscall.EPLAN9
+}
+
+// LocalAddr returns the local network address.
+func (c *IPConn) LocalAddr() Addr {
+ return nil
+}
+
+// RemoteAddr returns the remote network address.
+func (c *IPConn) RemoteAddr() Addr {
+ return nil
+}
+
// SetDeadline implements the Conn SetDeadline method.
func (c *IPConn) SetDeadline(t time.Time) error {
return syscall.EPLAN9
@@ -30,16 +53,23 @@ func (c *IPConn) SetWriteDeadline(t time.Time) error {
return syscall.EPLAN9
}
-// Implementation of the Conn interface - see Conn for documentation.
+// SetReadBuffer sets the size of the operating system's receive
+// buffer associated with the connection.
+func (c *IPConn) SetReadBuffer(bytes int) error {
+ return syscall.EPLAN9
+}
-// Read implements the Conn Read method.
-func (c *IPConn) Read(b []byte) (int, error) {
- return 0, syscall.EPLAN9
+// SetWriteBuffer sets the size of the operating system's transmit
+// buffer associated with the connection.
+func (c *IPConn) SetWriteBuffer(bytes int) error {
+ return syscall.EPLAN9
}
-// Write implements the Conn Write method.
-func (c *IPConn) Write(b []byte) (int, error) {
- return 0, 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 *IPConn) File() (f *os.File, err error) {
+ return nil, syscall.EPLAN9
}
// Close closes the IP connection.
@@ -47,16 +77,6 @@ func (c *IPConn) Close() error {
return syscall.EPLAN9
}
-// LocalAddr returns the local network address.
-func (c *IPConn) LocalAddr() Addr {
- return nil
-}
-
-// RemoteAddr returns the remote network address, a *IPAddr.
-func (c *IPConn) RemoteAddr() Addr {
- return nil
-}
-
// IP-specific methods.
// ReadFromIP reads an IP packet from c, copying the payload into b.
@@ -75,12 +95,21 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
return 0, nil, syscall.EPLAN9
}
-// WriteToIP writes an IP packet to addr via c, copying the payload from b.
+// ReadMsgIP reads a packet from c, copying the payload into b and the
+// associdated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet and the source address of the packet.
+func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
+ return 0, 0, 0, nil, syscall.EPLAN9
+}
+
+// WriteToIP writes an IP packet to addr via c, copying the payload
+// from b.
//
-// WriteToIP can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToIP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
return 0, syscall.EPLAN9
}
@@ -90,16 +119,24 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
return 0, syscall.EPLAN9
}
-// 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.
+// WriteMsgIP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
+ return 0, 0, syscall.EPLAN9
+}
+
+// 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 nil, syscall.EPLAN9
}
-// ListenIP listens for incoming IP packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send IP
-// packets with per-packet addressing.
+// ListenIP listens for incoming IP packets addressed to the local
+// address laddr. The returned connection c's ReadFrom and WriteTo
+// methods can be used to receive and send IP packets with per-packet
+// addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go
index dda81ddf881..d0f0b567ac8 100644
--- a/libgo/go/net/iprawsock_posix.go
+++ b/libgo/go/net/iprawsock_posix.go
@@ -9,9 +9,7 @@
package net
import (
- "os"
"syscall"
- "time"
)
func sockaddrToIP(sa syscall.Sockaddr) Addr {
@@ -55,94 +53,10 @@ func (a *IPAddr) toAddr() sockaddr {
// IPConn is the implementation of the Conn and PacketConn
// interfaces for IP network connections.
type IPConn struct {
- fd *netFD
+ conn
}
-func newIPConn(fd *netFD) *IPConn { return &IPConn{fd} }
-
-func (c *IPConn) 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 *IPConn) Read(b []byte) (int, error) {
- n, _, err := c.ReadFrom(b)
- return n, err
-}
-
-// Write implements the Conn Write method.
-func (c *IPConn) Write(b []byte) (int, error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the IP connection.
-func (c *IPConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
-// LocalAddr returns the local network address.
-func (c *IPConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *IPAddr.
-func (c *IPConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *IPConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *IPConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *IPConn) 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 *IPConn) 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 *IPConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
+func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} }
// IP-specific methods.
@@ -184,6 +98,25 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
return n, uaddr.toAddr(), err
}
+// ReadMsgIP reads a packet from c, copying the payload into b and the
+// associdated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet and the source address of the packet.
+func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
+ if !c.ok() {
+ return 0, 0, 0, nil, syscall.EINVAL
+ }
+ var sa syscall.Sockaddr
+ n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+ switch sa := sa.(type) {
+ case *syscall.SockaddrInet4:
+ addr = &IPAddr{sa.Addr[0:]}
+ case *syscall.SockaddrInet6:
+ addr = &IPAddr{sa.Addr[0:]}
+ }
+ return
+}
+
// WriteToIP writes an IP packet to addr via c, copying the payload from b.
//
// WriteToIP can be made to time out and return
@@ -213,6 +146,20 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
return c.WriteToIP(b, a)
}
+// WriteMsgIP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
+ if !c.ok() {
+ return 0, 0, syscall.EINVAL
+ }
+ sa, err := addr.sockaddr(c.fd.family)
+ if err != nil {
+ return 0, 0, &OpError{"write", c.fd.net, addr, err}
+ }
+ return c.fd.WriteMsg(b, oob, sa)
+}
+
// 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) {
@@ -255,8 +202,3 @@ func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
}
return newIPConn(fd), nil
}
-
-// 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 *IPConn) File() (f *os.File, err error) { return c.fd.dup() }
diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go
index bfbce18a414..84547c7a6a8 100644
--- a/libgo/go/net/ipsock.go
+++ b/libgo/go/net/ipsock.go
@@ -129,17 +129,10 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err error) {
}
}
- p, i, ok := dtoi(port, 0)
- if !ok || i != len(port) {
- p, err = LookupPort(net, port)
- if err != nil {
- return nil, 0, err
- }
- }
- if p < 0 || p > 0xFFFF {
- return nil, 0, &AddrError{"invalid port", port}
+ p, err := parsePort(net, port)
+ if err != nil {
+ return nil, 0, err
}
return addr, p, nil
-
}
diff --git a/libgo/go/net/ipsock_plan9.go b/libgo/go/net/ipsock_plan9.go
index eab0bf3e899..7cc2d714d8a 100644
--- a/libgo/go/net/ipsock_plan9.go
+++ b/libgo/go/net/ipsock_plan9.go
@@ -14,9 +14,12 @@ import (
"time"
)
-// probeIPv6Stack returns two boolean values. If the first boolean value is
-// true, kernel supports basic IPv6 functionality. If the second
-// boolean value is true, kernel supports IPv6 IPv4-mapping.
+// /sys/include/ape/sys/socket.h:/SOMAXCONN
+var listenerBacklog = 5
+
+// probeIPv6Stack returns two boolean values. If the first boolean
+// value is true, kernel supports basic IPv6 functionality. If the
+// second boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
return false, false
}
@@ -48,6 +51,7 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) {
if err != nil {
return
}
+ defer f.Close()
n, err := f.Read(buf[:])
if err != nil {
return
@@ -162,6 +166,25 @@ 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
@@ -192,6 +215,7 @@ func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string,
var buf [16]byte
n, err := f.Read(buf[:])
if err != nil {
+ f.Close()
return
}
return f, dest, proto, string(buf[:n]), nil
@@ -204,14 +228,17 @@ func dialPlan9(net string, laddr, raddr Addr) (c *plan9Conn, err error) {
}
_, err = f.WriteString("connect " + dest)
if err != nil {
+ f.Close()
return
}
laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
if err != nil {
+ f.Close()
return
}
raddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/remote")
if err != nil {
+ f.Close()
return
}
return newPlan9Conn(proto, name, f, laddr, raddr), nil
@@ -230,10 +257,12 @@ func listenPlan9(net string, laddr Addr) (l *plan9Listener, err error) {
}
_, err = f.WriteString("announce " + dest)
if err != nil {
+ f.Close()
return
}
laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
if err != nil {
+ f.Close()
return
}
l = new(plan9Listener)
@@ -257,15 +286,18 @@ func (l *plan9Listener) acceptPlan9() (c *plan9Conn, err error) {
var buf [16]byte
n, err := f.Read(buf[:])
if err != nil {
+ f.Close()
return
}
name := string(buf[:n])
laddr, err := readPlan9Addr(l.proto, l.dir+"/local")
if err != nil {
+ f.Close()
return
}
raddr, err := readPlan9Addr(l.proto, l.dir+"/remote")
if err != nil {
+ f.Close()
return
}
return newPlan9Conn(l.proto, name, f, laddr, raddr), nil
@@ -287,3 +319,16 @@ func (l *plan9Listener) Close() error {
}
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
+}
diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go
index ed313195c97..171889207d7 100644
--- a/libgo/go/net/ipsock_posix.go
+++ b/libgo/go/net/ipsock_posix.go
@@ -97,10 +97,13 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
return syscall.AF_INET6, true
}
- if mode == "listen" && laddr.isWildcard() {
+ if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
if supportsIPv4map {
return syscall.AF_INET6, false
}
+ if laddr == nil {
+ return syscall.AF_INET, false
+ }
return laddr.family(), false
}
diff --git a/libgo/go/net/doc.go b/libgo/go/net/lookup.go
index 3a44e528eb2..3a44e528eb2 100644
--- a/libgo/go/net/doc.go
+++ b/libgo/go/net/lookup.go
diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go
index 3a61dfb29c2..84f089e8697 100644
--- a/libgo/go/net/lookup_test.go
+++ b/libgo/go/net/lookup_test.go
@@ -9,6 +9,7 @@ package net
import (
"flag"
+ "strings"
"testing"
)
@@ -79,6 +80,17 @@ func TestGoogleDNSAddr(t *testing.T) {
}
}
+func TestLookupIANACNAME(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Logf("skipping test to avoid external network")
+ return
+ }
+ cname, err := LookupCNAME("www.iana.org")
+ if !strings.HasSuffix(cname, ".icann.org.") || err != nil {
+ t.Errorf(`LookupCNAME("www.iana.org.") = %q, %v, want "*.icann.org.", nil`, cname, err)
+ }
+}
+
var revAddrTests = []struct {
Addr string
Reverse string
diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go
index 93cc4d1edd4..96c796e7804 100644
--- a/libgo/go/net/mail/message.go
+++ b/libgo/go/net/mail/message.go
@@ -127,7 +127,7 @@ func (h Header) AddressList(key string) ([]*Address, error) {
if hdr == "" {
return nil, ErrHeaderNotPresent
}
- return newAddrParser(hdr).parseAddressList()
+ return ParseAddressList(hdr)
}
// Address represents a single mail address.
@@ -138,6 +138,16 @@ type Address struct {
Address string // user@domain
}
+// Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>"
+func ParseAddress(address string) (*Address, error) {
+ return newAddrParser(address).parseAddress()
+}
+
+// ParseAddressList parses the given string as a list of addresses.
+func ParseAddressList(list string) ([]*Address, error) {
+ return newAddrParser(list).parseAddressList()
+}
+
// String formats the address as a valid RFC 5322 address.
// If the address's name contains non-ASCII characters
// the name will be rendered according to RFC 2047.
diff --git a/libgo/go/net/mail/message_test.go b/libgo/go/net/mail/message_test.go
index fd17eb414a7..2e746f4a722 100644
--- a/libgo/go/net/mail/message_test.go
+++ b/libgo/go/net/mail/message_test.go
@@ -227,13 +227,24 @@ func TestAddressParsing(t *testing.T) {
},
}
for _, test := range tests {
- addrs, err := newAddrParser(test.addrsStr).parseAddressList()
+ if len(test.exp) == 1 {
+ addr, err := ParseAddress(test.addrsStr)
+ if err != nil {
+ t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
+ continue
+ }
+ if !reflect.DeepEqual([]*Address{addr}, test.exp) {
+ t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
+ }
+ }
+
+ addrs, err := ParseAddressList(test.addrsStr)
if err != nil {
- t.Errorf("Failed parsing %q: %v", test.addrsStr, err)
+ t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
continue
}
if !reflect.DeepEqual(addrs, test.exp) {
- t.Errorf("Parse of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
+ t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
}
}
}
diff --git a/libgo/go/net/multicast_test.go b/libgo/go/net/multicast_posix_test.go
index 50c0f0a82c9..3767a6bc178 100644
--- a/libgo/go/net/multicast_test.go
+++ b/libgo/go/net/multicast_posix_test.go
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !plan9
+
package net
import (
"errors"
"os"
"runtime"
- "syscall"
"testing"
)
@@ -46,7 +47,7 @@ var multicastListenerTests = []struct {
// listener with same address family, same group address and same port.
func TestMulticastListener(t *testing.T) {
switch runtime.GOOS {
- case "netbsd", "openbsd", "plan9", "solaris", "windows":
+ case "netbsd", "openbsd", "plan9", "windows":
t.Logf("skipping test on %q", runtime.GOOS)
return
case "linux":
@@ -57,7 +58,7 @@ func TestMulticastListener(t *testing.T) {
}
for _, tt := range multicastListenerTests {
- if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) {
+ if tt.ipv6 && (!*testIPv6 || !supportsIPv6 || os.Getuid() != 0) {
continue
}
ifi, err := availMulticastInterface(t, tt.flags)
@@ -75,12 +76,6 @@ func TestMulticastListener(t *testing.T) {
}
checkMulticastListener(t, err, c2, tt.gaddr)
c2.Close()
- switch c1.fd.family {
- case syscall.AF_INET:
- testIPv4MulticastSocketOptions(t, c1.fd, ifi)
- case syscall.AF_INET6:
- testIPv6MulticastSocketOptions(t, c1.fd, ifi)
- }
c1.Close()
}
}
@@ -174,61 +169,3 @@ func multicastRIBContains(t *testing.T, ip IP) bool {
}
return false
}
-
-func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
- _, err := ipv4MulticastInterface(fd)
- if err != nil {
- t.Fatalf("ipv4MulticastInterface failed: %v", err)
- }
- if ifi != nil {
- err = setIPv4MulticastInterface(fd, ifi)
- if err != nil {
- t.Fatalf("setIPv4MulticastInterface failed: %v", err)
- }
- }
- _, err = ipv4MulticastTTL(fd)
- if err != nil {
- t.Fatalf("ipv4MulticastTTL failed: %v", err)
- }
- err = setIPv4MulticastTTL(fd, 1)
- if err != nil {
- t.Fatalf("setIPv4MulticastTTL failed: %v", err)
- }
- _, err = ipv4MulticastLoopback(fd)
- if err != nil {
- t.Fatalf("ipv4MulticastLoopback failed: %v", err)
- }
- err = setIPv4MulticastLoopback(fd, false)
- if err != nil {
- t.Fatalf("setIPv4MulticastLoopback failed: %v", err)
- }
-}
-
-func testIPv6MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
- _, err := ipv6MulticastInterface(fd)
- if err != nil {
- t.Fatalf("ipv6MulticastInterface failed: %v", err)
- }
- if ifi != nil {
- err = setIPv6MulticastInterface(fd, ifi)
- if err != nil {
- t.Fatalf("setIPv6MulticastInterface failed: %v", err)
- }
- }
- _, err = ipv6MulticastHopLimit(fd)
- if err != nil {
- t.Fatalf("ipv6MulticastHopLimit failed: %v", err)
- }
- err = setIPv6MulticastHopLimit(fd, 1)
- if err != nil {
- t.Fatalf("setIPv6MulticastHopLimit failed: %v", err)
- }
- _, err = ipv6MulticastLoopback(fd)
- if err != nil {
- t.Fatalf("ipv6MulticastLoopback failed: %v", err)
- }
- err = setIPv6MulticastLoopback(fd, false)
- if err != nil {
- t.Fatalf("setIPv6MulticastLoopback failed: %v", err)
- }
-}
diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go
index fd145e1d70f..623a788f9ab 100644
--- a/libgo/go/net/net_test.go
+++ b/libgo/go/net/net_test.go
@@ -6,6 +6,8 @@ package net
import (
"io"
+ "io/ioutil"
+ "os"
"runtime"
"testing"
"time"
@@ -16,15 +18,15 @@ func TestShutdown(t *testing.T) {
t.Logf("skipping test on %q", runtime.GOOS)
return
}
- l, err := Listen("tcp", "127.0.0.1:0")
+ ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- if l, err = Listen("tcp6", "[::1]:0"); err != nil {
+ if ln, err = Listen("tcp6", "[::1]:0"); err != nil {
t.Fatalf("ListenTCP on :0: %v", err)
}
}
go func() {
- c, err := l.Accept()
+ c, err := ln.Accept()
if err != nil {
t.Fatalf("Accept: %v", err)
}
@@ -37,7 +39,7 @@ func TestShutdown(t *testing.T) {
c.Close()
}()
- c, err := Dial("tcp", l.Addr().String())
+ c, err := Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -58,8 +60,62 @@ func TestShutdown(t *testing.T) {
}
}
+func TestShutdownUnix(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows", "plan9":
+ t.Logf("skipping test on %q", runtime.GOOS)
+ return
+ }
+ f, err := ioutil.TempFile("", "go_net_unixtest")
+ if err != nil {
+ t.Fatalf("TempFile: %s", err)
+ }
+ f.Close()
+ tmpname := f.Name()
+ os.Remove(tmpname)
+ ln, err := Listen("unix", tmpname)
+ if err != nil {
+ t.Fatalf("ListenUnix on %s: %s", tmpname, err)
+ }
+ defer os.Remove(tmpname)
+
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Accept: %v", err)
+ }
+ var buf [10]byte
+ n, err := c.Read(buf[:])
+ if n != 0 || err != io.EOF {
+ t.Fatalf("server Read = %d, %v; want 0, io.EOF", n, err)
+ }
+ c.Write([]byte("response"))
+ c.Close()
+ }()
+
+ c, err := Dial("unix", tmpname)
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+
+ err = c.(*UnixConn).CloseWrite()
+ if err != nil {
+ t.Fatalf("CloseWrite: %v", err)
+ }
+ var buf [10]byte
+ n, err := c.Read(buf[:])
+ if err != nil {
+ t.Fatalf("client Read: %d, %v", n, err)
+ }
+ got := string(buf[:n])
+ if got != "response" {
+ t.Errorf("read = %q, want \"response\"", got)
+ }
+}
+
func TestTCPListenClose(t *testing.T) {
- l, err := Listen("tcp", "127.0.0.1:0")
+ ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
@@ -67,11 +123,12 @@ func TestTCPListenClose(t *testing.T) {
done := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
- l.Close()
+ ln.Close()
}()
go func() {
- _, err = l.Accept()
+ c, err := ln.Accept()
if err == nil {
+ c.Close()
t.Error("Accept succeeded")
} else {
t.Logf("Accept timeout error: %s (any error is fine)", err)
@@ -86,7 +143,12 @@ func TestTCPListenClose(t *testing.T) {
}
func TestUDPListenClose(t *testing.T) {
- l, err := ListenPacket("udp", "127.0.0.1:0")
+ switch runtime.GOOS {
+ case "plan9":
+ t.Logf("skipping test on %q", runtime.GOOS)
+ return
+ }
+ ln, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
@@ -95,10 +157,10 @@ func TestUDPListenClose(t *testing.T) {
done := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
- l.Close()
+ ln.Close()
}()
go func() {
- _, _, err = l.ReadFrom(buf)
+ _, _, err = ln.ReadFrom(buf)
if err == nil {
t.Error("ReadFrom succeeded")
} else {
diff --git a/libgo/go/net/newpollserver.go b/libgo/go/net/newpollserver_unix.go
index d34bb511f7f..618b5b10ba4 100644
--- a/libgo/go/net/newpollserver.go
+++ b/libgo/go/net/newpollserver_unix.go
@@ -13,8 +13,6 @@ import (
func newPollServer() (s *pollServer, err error) {
s = new(pollServer)
- s.cr = make(chan *netFD, 1)
- s.cw = make(chan *netFD, 1)
if s.pr, s.pw, err = os.Pipe(); err != nil {
return nil, err
}
diff --git a/libgo/go/net/port.go b/libgo/go/net/port.go
index 16780da1160..c24f4ed5b17 100644
--- a/libgo/go/net/port.go
+++ b/libgo/go/net/port.go
@@ -1,69 +1,24 @@
-// Copyright 2009 The Go Authors. All rights reserved.
+// 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
-
-// Read system port mappings from /etc/services
+// Network service port manipulations
package net
-import "sync"
-
-var services map[string]map[string]int
-var servicesError error
-var onceReadServices sync.Once
-
-func readServices() {
- services = make(map[string]map[string]int)
- var file *file
- if file, servicesError = open("/etc/services"); servicesError != nil {
- return
- }
- for line, ok := file.readLine(); ok; line, ok = file.readLine() {
- // "http 80/tcp www www-http # World Wide Web HTTP"
- if i := byteIndex(line, '#'); i >= 0 {
- line = line[0:i]
- }
- f := getFields(line)
- if len(f) < 2 {
- continue
- }
- portnet := f[1] // "tcp/80"
- port, j, ok := dtoi(portnet, 0)
- if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' {
- continue
- }
- netw := portnet[j+1:] // "tcp"
- m, ok1 := services[netw]
- if !ok1 {
- m = make(map[string]int)
- services[netw] = m
- }
- for i := 0; i < len(f); i++ {
- if i != 1 { // f[1] was port/net
- m[f[i]] = port
- }
+// parsePort parses port as a network service port number for both
+// TCP and UDP.
+func parsePort(net, port string) (int, error) {
+ p, i, ok := dtoi(port, 0)
+ if !ok || i != len(port) {
+ var err error
+ p, err = LookupPort(net, port)
+ if err != nil {
+ return 0, err
}
}
- file.close()
-}
-
-// goLookupPort is the native Go implementation of LookupPort.
-func goLookupPort(network, service string) (port int, err error) {
- onceReadServices.Do(readServices)
-
- switch network {
- case "tcp4", "tcp6":
- network = "tcp"
- case "udp4", "udp6":
- network = "udp"
- }
-
- if m, ok := services[network]; ok {
- if port, ok = m[service]; ok {
- return
- }
+ if p < 0 || p > 0xFFFF {
+ return 0, &AddrError{"invalid port", port}
}
- return 0, &AddrError{"unknown port", network + "/" + service}
+ return p, nil
}
diff --git a/libgo/go/net/port_unix.go b/libgo/go/net/port_unix.go
new file mode 100644
index 00000000000..16780da1160
--- /dev/null
+++ b/libgo/go/net/port_unix.go
@@ -0,0 +1,69 @@
+// 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.
+
+// +build darwin freebsd linux netbsd openbsd
+
+// Read system port mappings from /etc/services
+
+package net
+
+import "sync"
+
+var services map[string]map[string]int
+var servicesError error
+var onceReadServices sync.Once
+
+func readServices() {
+ services = make(map[string]map[string]int)
+ var file *file
+ if file, servicesError = open("/etc/services"); servicesError != nil {
+ return
+ }
+ for line, ok := file.readLine(); ok; line, ok = file.readLine() {
+ // "http 80/tcp www www-http # World Wide Web HTTP"
+ if i := byteIndex(line, '#'); i >= 0 {
+ line = line[0:i]
+ }
+ f := getFields(line)
+ if len(f) < 2 {
+ continue
+ }
+ portnet := f[1] // "tcp/80"
+ port, j, ok := dtoi(portnet, 0)
+ if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' {
+ continue
+ }
+ netw := portnet[j+1:] // "tcp"
+ m, ok1 := services[netw]
+ if !ok1 {
+ m = make(map[string]int)
+ services[netw] = m
+ }
+ for i := 0; i < len(f); i++ {
+ if i != 1 { // f[1] was port/net
+ m[f[i]] = port
+ }
+ }
+ }
+ file.close()
+}
+
+// goLookupPort is the native Go implementation of LookupPort.
+func goLookupPort(network, service string) (port int, err error) {
+ onceReadServices.Do(readServices)
+
+ switch network {
+ case "tcp4", "tcp6":
+ network = "tcp"
+ case "udp4", "udp6":
+ network = "udp"
+ }
+
+ if m, ok := services[network]; ok {
+ if port, ok = m[service]; ok {
+ return
+ }
+ }
+ return 0, &AddrError{"unknown port", network + "/" + service}
+}
diff --git a/libgo/go/net/rpc/client.go b/libgo/go/net/rpc/client.go
index db2da8e4418..7d3d0bb9b87 100644
--- a/libgo/go/net/rpc/client.go
+++ b/libgo/go/net/rpc/client.go
@@ -88,10 +88,13 @@ func (client *Client) send(call *Call) {
err := client.codec.WriteRequest(&client.request, call.Args)
if err != nil {
client.mutex.Lock()
+ call = client.pending[seq]
delete(client.pending, seq)
client.mutex.Unlock()
- call.Error = err
- call.done()
+ if call != nil {
+ call.Error = err
+ call.done()
+ }
}
}
@@ -113,12 +116,18 @@ func (client *Client) input() {
delete(client.pending, seq)
client.mutex.Unlock()
- if response.Error == "" {
- err = client.codec.ReadResponseBody(call.Reply)
+ switch {
+ case call == nil:
+ // We've got no pending call. That usually means that
+ // WriteRequest partially failed, and call was already
+ // removed; response is a server telling us about an
+ // error reading request body. We should still attempt
+ // to read error body, but there's no one to give it to.
+ err = client.codec.ReadResponseBody(nil)
if err != nil {
- call.Error = errors.New("reading body " + err.Error())
+ err = errors.New("reading error body: " + err.Error())
}
- } else {
+ case response.Error != "":
// We've got an error response. Give this to the request;
// any subsequent requests will get the ReadResponseBody
// error if there is one.
@@ -127,8 +136,14 @@ func (client *Client) input() {
if err != nil {
err = errors.New("reading error body: " + err.Error())
}
+ call.done()
+ default:
+ err = client.codec.ReadResponseBody(call.Reply)
+ if err != nil {
+ call.Error = errors.New("reading body " + err.Error())
+ }
+ call.done()
}
- call.done()
}
// Terminate pending calls.
client.sending.Lock()
diff --git a/libgo/go/net/rpc/jsonrpc/all_test.go b/libgo/go/net/rpc/jsonrpc/all_test.go
index adc29d5a1b3..3c7c4d48fa6 100644
--- a/libgo/go/net/rpc/jsonrpc/all_test.go
+++ b/libgo/go/net/rpc/jsonrpc/all_test.go
@@ -24,6 +24,12 @@ type Reply struct {
type Arith int
+type ArithAddResp struct {
+ Id interface{} `json:"id"`
+ Result Reply `json:"result"`
+ Error interface{} `json:"error"`
+}
+
func (t *Arith) Add(args *Args, reply *Reply) error {
reply.C = args.A + args.B
return nil
@@ -50,13 +56,39 @@ func init() {
rpc.Register(new(Arith))
}
-func TestServer(t *testing.T) {
- type addResp struct {
- Id interface{} `json:"id"`
- Result Reply `json:"result"`
- Error interface{} `json:"error"`
+func TestServerNoParams(t *testing.T) {
+ cli, srv := net.Pipe()
+ defer cli.Close()
+ go ServeConn(srv)
+ dec := json.NewDecoder(cli)
+
+ fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "123"}`)
+ var resp ArithAddResp
+ if err := dec.Decode(&resp); err != nil {
+ t.Fatalf("Decode after no params: %s", err)
+ }
+ if resp.Error == nil {
+ t.Fatalf("Expected error, got nil")
+ }
+}
+
+func TestServerEmptyMessage(t *testing.T) {
+ cli, srv := net.Pipe()
+ defer cli.Close()
+ go ServeConn(srv)
+ dec := json.NewDecoder(cli)
+
+ fmt.Fprintf(cli, "{}")
+ var resp ArithAddResp
+ if err := dec.Decode(&resp); err != nil {
+ t.Fatalf("Decode after empty: %s", err)
}
+ if resp.Error == nil {
+ t.Fatalf("Expected error, got nil")
+ }
+}
+func TestServer(t *testing.T) {
cli, srv := net.Pipe()
defer cli.Close()
go ServeConn(srv)
@@ -65,7 +97,7 @@ func TestServer(t *testing.T) {
// Send hand-coded requests to server, parse responses.
for i := 0; i < 10; i++ {
fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "\u%04d", "params": [{"A": %d, "B": %d}]}`, i, i, i+1)
- var resp addResp
+ var resp ArithAddResp
err := dec.Decode(&resp)
if err != nil {
t.Fatalf("Decode: %s", err)
@@ -80,15 +112,6 @@ func TestServer(t *testing.T) {
t.Fatalf("resp: bad result: %d+%d=%d", i, i+1, resp.Result.C)
}
}
-
- fmt.Fprintf(cli, "{}\n")
- var resp addResp
- if err := dec.Decode(&resp); err != nil {
- t.Fatalf("Decode after empty: %s", err)
- }
- if resp.Error == nil {
- t.Fatalf("Expected error, got nil")
- }
}
func TestClient(t *testing.T) {
diff --git a/libgo/go/net/rpc/jsonrpc/server.go b/libgo/go/net/rpc/jsonrpc/server.go
index 4c54553a723..5bc05fd0a71 100644
--- a/libgo/go/net/rpc/jsonrpc/server.go
+++ b/libgo/go/net/rpc/jsonrpc/server.go
@@ -12,6 +12,8 @@ import (
"sync"
)
+var errMissingParams = errors.New("jsonrpc: request body missing params")
+
type serverCodec struct {
dec *json.Decoder // for reading JSON values
enc *json.Encoder // for writing JSON values
@@ -50,12 +52,8 @@ type serverRequest struct {
func (r *serverRequest) reset() {
r.Method = ""
- if r.Params != nil {
- *r.Params = (*r.Params)[0:0]
- }
- if r.Id != nil {
- *r.Id = (*r.Id)[0:0]
- }
+ r.Params = nil
+ r.Id = nil
}
type serverResponse struct {
@@ -88,6 +86,9 @@ func (c *serverCodec) ReadRequestBody(x interface{}) error {
if x == nil {
return nil
}
+ if c.req.Params == nil {
+ return errMissingParams
+ }
// JSON params is array value.
// RPC params is struct.
// Unmarshal into array containing struct for now.
diff --git a/libgo/go/net/sendfile_freebsd.go b/libgo/go/net/sendfile_freebsd.go
new file mode 100644
index 00000000000..85000061040
--- /dev/null
+++ b/libgo/go/net/sendfile_freebsd.go
@@ -0,0 +1,105 @@
+// Copyright 2011 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"
+)
+
+// maxSendfileSize is the largest chunk size we ask the kernel to copy
+// at a time.
+const maxSendfileSize int = 4 << 20
+
+// sendFile copies the contents of r to c using the sendfile
+// system call to minimize copies.
+//
+// if handled == true, sendFile returns the number of bytes copied and any
+// non-EOF error.
+//
+// if handled == false, sendFile performed no work.
+func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
+ // FreeBSD uses 0 as the "until EOF" value. If you pass in more bytes than the
+ // file contains, it will loop back to the beginning ad nauseum until it's sent
+ // exactly the number of bytes told to. As such, we need to know exactly how many
+ // bytes to send.
+ var remain int64 = 0
+
+ lr, ok := r.(*io.LimitedReader)
+ if ok {
+ remain, r = lr.N, lr.R
+ if remain <= 0 {
+ return 0, nil, true
+ }
+ }
+ f, ok := r.(*os.File)
+ if !ok {
+ return 0, nil, false
+ }
+
+ if remain == 0 {
+ fi, err := f.Stat()
+ if err != nil {
+ return 0, err, false
+ }
+
+ remain = fi.Size()
+ }
+
+ // The other quirk with FreeBSD's sendfile implementation is that it doesn't
+ // use the current position of the file -- if you pass it offset 0, it starts
+ // from offset 0. There's no way to tell it "start from current position", so
+ // we have to manage that explicitly.
+ pos, err := f.Seek(0, os.SEEK_CUR)
+ if err != nil {
+ return 0, err, false
+ }
+
+ c.wio.Lock()
+ defer c.wio.Unlock()
+ if err := c.incref(false); err != nil {
+ return 0, err, true
+ }
+ defer c.decref()
+
+ dst := c.sysfd
+ src := int(f.Fd())
+ for remain > 0 {
+ n := maxSendfileSize
+ if int64(n) > remain {
+ n = int(remain)
+ }
+ pos1 := pos
+ n, err1 := syscall.Sendfile(dst, src, &pos1, n)
+ if n > 0 {
+ pos += int64(n)
+ written += int64(n)
+ remain -= int64(n)
+ }
+ if n == 0 && err1 == nil {
+ break
+ }
+ if err1 == syscall.EAGAIN && c.wdeadline >= 0 {
+ if err1 = c.pollServer.WaitWrite(c); err1 == nil {
+ continue
+ }
+ }
+ if err1 == syscall.EINTR {
+ continue
+ }
+ if err1 != nil {
+ // This includes syscall.ENOSYS (no kernel
+ // support) and syscall.EINVAL (fd types which
+ // don't implement sendfile together)
+ err = &OpError{"sendfile", c.net, c.raddr, err1}
+ break
+ }
+ }
+ if lr != nil {
+ lr.N = remain
+ }
+ return written, err, written > 0
+}
diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go
index a0d53036263..5ee18f9cccd 100644
--- a/libgo/go/net/sendfile_linux.go
+++ b/libgo/go/net/sendfile_linux.go
@@ -59,7 +59,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
break
}
if err1 == syscall.EAGAIN && c.wdeadline >= 0 {
- if err1 = pollserver.WaitWrite(c); err1 == nil {
+ if err1 = c.pollServer.WaitWrite(c); err1 == nil {
continue
}
}
diff --git a/libgo/go/net/sendfile_stub.go b/libgo/go/net/sendfile_stub.go
index ff76ab9cf0e..3660849c182 100644
--- a/libgo/go/net/sendfile_stub.go
+++ b/libgo/go/net/sendfile_stub.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd netbsd openbsd
+// +build darwin netbsd openbsd
package net
diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock_posix.go
index 3ae16054e47..dc5247a7f38 100644
--- a/libgo/go/net/sock.go
+++ b/libgo/go/net/sock_posix.go
@@ -16,8 +16,8 @@ import (
var listenerBacklog = maxListenerBacklog()
// Generic socket creation.
-func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
- // See ../syscall/exec.go for description of ForkLock.
+func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, 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)
if err != nil {
@@ -27,21 +27,18 @@ func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toA
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
- err = setDefaultSockopts(s, f, t, ipv6only)
- if err != nil {
+ if err = setDefaultSockopts(s, f, t, ipv6only); err != nil {
closesocket(s)
return nil, err
}
- var bla syscall.Sockaddr
- if la != nil {
- bla, err = listenerSockaddr(s, f, la, toAddr)
- if err != nil {
+ var blsa syscall.Sockaddr
+ if ulsa != nil {
+ if blsa, err = listenerSockaddr(s, f, ulsa, toAddr); err != nil {
closesocket(s)
return nil, err
}
- err = syscall.Bind(s, bla)
- if err != nil {
+ if err = syscall.Bind(s, blsa); err != nil {
closesocket(s)
return nil, err
}
@@ -52,8 +49,8 @@ func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toA
return nil, err
}
- if ra != nil {
- if err = fd.connect(ra); err != nil {
+ if ursa != nil {
+ if err = fd.connect(ursa); err != nil {
closesocket(s)
fd.Close()
return nil, err
@@ -61,16 +58,15 @@ func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toA
fd.isConnected = true
}
- sa, _ := syscall.Getsockname(s)
+ lsa, _ := syscall.Getsockname(s)
var laddr Addr
- if la != nil && bla != la {
- laddr = toAddr(la)
+ if ulsa != nil && blsa != ulsa {
+ laddr = toAddr(ulsa)
} else {
- laddr = toAddr(sa)
+ laddr = toAddr(lsa)
}
- sa, _ = syscall.Getpeername(s)
- raddr := toAddr(sa)
-
+ rsa, _ := syscall.Getpeername(s)
+ raddr := toAddr(rsa)
fd.setAddr(laddr, raddr)
return fd, nil
}
diff --git a/libgo/go/net/sockopt.go b/libgo/go/net/sockopt_posix.go
index b139c427654..b139c427654 100644
--- a/libgo/go/net/sockopt.go
+++ b/libgo/go/net/sockopt_posix.go
diff --git a/libgo/go/net/sockoptip.go b/libgo/go/net/sockoptip.go
deleted file mode 100644
index 1fcad4018cc..00000000000
--- a/libgo/go/net/sockoptip.go
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2011 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
-
-// IP-level socket options
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4TOS(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv4TOS(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4TTL(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv4TTL(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
- if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
- return err
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
-}
-
-func leaveIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
- if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
- return err
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
-}
-
-func ipv6HopLimit(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6HopLimit(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- if v == 0 {
- return nil, nil
- }
- ifi, err := InterfaceByIndex(v)
- if err != nil {
- return nil, err
- }
- return ifi, nil
-}
-
-func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
- var v int
- if ifi != nil {
- v = ifi.Index
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6MulticastHopLimit(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6MulticastHopLimit(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv6MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPv6Mreq{}
- copy(mreq.Multiaddr[:], ip)
- if ifi != nil {
- mreq.Interface = uint32(ifi.Index)
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq))
-}
-
-func leaveIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPv6Mreq{}
- copy(mreq.Multiaddr[:], ip)
- if ifi != nil {
- mreq.Interface = uint32(ifi.Index)
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq))
-}
diff --git a/libgo/go/net/sockoptip_bsd.go b/libgo/go/net/sockoptip_bsd.go
index 19e2b142e92..263f8552176 100644
--- a/libgo/go/net/sockoptip_bsd.go
+++ b/libgo/go/net/sockoptip_bsd.go
@@ -4,8 +4,6 @@
// +build darwin freebsd netbsd openbsd
-// IP-level socket options for BSD variants
-
package net
import (
@@ -13,48 +11,30 @@ import (
"syscall"
)
-func ipv4MulticastTTL(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+ ip, err := interfaceToIPv4Addr(ifi)
if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
+ return os.NewSyscallError("setsockopt", err)
}
- return int(v), nil
-}
-
-func setIPv4MulticastTTL(fd *netFD, v int) error {
+ var a [4]byte
+ copy(a[:], ip.To4())
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, byte(v))
+ err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
-func ipv6TrafficClass(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6TrafficClass(fd *netFD, v int) error {
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v)
+ err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
diff --git a/libgo/go/net/sockoptip_darwin.go b/libgo/go/net/sockoptip_darwin.go
deleted file mode 100644
index 52b237c4b8d..00000000000
--- a/libgo/go/net/sockoptip_darwin.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2011 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.
-
-// IP-level socket options for Darwin
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3]))
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- ip, err := interfaceToIPv4Addr(ifi)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- var x [4]byte
- copy(x[:], ip.To4())
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/libgo/go/net/sockoptip_freebsd.go b/libgo/go/net/sockoptip_freebsd.go
deleted file mode 100644
index 4a3bc2e82c8..00000000000
--- a/libgo/go/net/sockoptip_freebsd.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2011 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.
-
-// IP-level socket options for FreeBSD
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- if int(mreq.Ifindex) == 0 {
- return nil, nil
- }
- return InterfaceByIndex(int(mreq.Ifindex))
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- var v int32
- if ifi != nil {
- v = int32(ifi.Index)
- }
- mreq := &syscall.IPMreqn{Ifindex: v}
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/libgo/go/net/sockoptip_linux.go b/libgo/go/net/sockoptip_linux.go
index 169718f14aa..225fb0c4c6c 100644
--- a/libgo/go/net/sockoptip_linux.go
+++ b/libgo/go/net/sockoptip_linux.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// IP-level socket options for Linux
-
package net
import (
@@ -11,21 +9,6 @@ import (
"syscall"
)
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- if int(mreq.Ifindex) == 0 {
- return nil, nil
- }
- return InterfaceByIndex(int(mreq.Ifindex))
-}
-
func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
var v int32
if ifi != nil {
@@ -43,42 +26,6 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
return nil
}
-func ipv4MulticastTTL(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
- if err != nil {
- return -1, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv4MulticastTTL(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(false); err != nil {
return err
@@ -90,51 +37,3 @@ func setIPv4MulticastLoopback(fd *netFD, v bool) error {
}
return nil
}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6TrafficClass(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6TrafficClass(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/libgo/go/net/sockoptip_netbsd.go b/libgo/go/net/sockoptip_netbsd.go
deleted file mode 100644
index 446d92aa343..00000000000
--- a/libgo/go/net/sockoptip_netbsd.go
+++ /dev/null
@@ -1,39 +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.
-
-// IP-level socket options for NetBSD
-
-package net
-
-import "syscall"
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- // TODO: Implement this
- return nil, syscall.EAFNOSUPPORT
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- // TODO: Implement this
- return syscall.EAFNOSUPPORT
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EAFNOSUPPORT
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- // TODO: Implement this
- return syscall.EAFNOSUPPORT
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EAFNOSUPPORT
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- // TODO: Implement this
- return syscall.EAFNOSUPPORT
-}
diff --git a/libgo/go/net/sockoptip_openbsd.go b/libgo/go/net/sockoptip_openbsd.go
deleted file mode 100644
index f3e42f1a9bc..00000000000
--- a/libgo/go/net/sockoptip_openbsd.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2011 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.
-
-// IP-level socket options for OpenBSD
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3]))
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- ip, err := interfaceToIPv4Addr(ifi)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- var x [4]byte
- copy(x[:], ip.To4())
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/libgo/go/net/sockoptip_posix.go b/libgo/go/net/sockoptip_posix.go
new file mode 100644
index 00000000000..e4c56a0e4b2
--- /dev/null
+++ b/libgo/go/net/sockoptip_posix.go
@@ -0,0 +1,73 @@
+// Copyright 2011 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
+
+package net
+
+import (
+ "os"
+ "syscall"
+)
+
+func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
+ mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+ if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
+ return err
+ }
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
+
+func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
+ var v int
+ if ifi != nil {
+ v = ifi.Index
+ }
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
+
+func setIPv6MulticastLoopback(fd *netFD, v bool) error {
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
+
+func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
+ mreq := &syscall.IPv6Mreq{}
+ copy(mreq.Multiaddr[:], ip)
+ if ifi != nil {
+ mreq.Interface = uint32(ifi.Index)
+ }
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
diff --git a/libgo/go/net/sockoptip_windows.go b/libgo/go/net/sockoptip_windows.go
index b9db3334d5f..3e248441ab3 100644
--- a/libgo/go/net/sockoptip_windows.go
+++ b/libgo/go/net/sockoptip_windows.go
@@ -2,90 +2,41 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// IP-level socket options for Windows
-
package net
import (
"os"
"syscall"
+ "unsafe"
)
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- // TODO: Implement this
- return nil, syscall.EWINDOWS
-}
-
func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
ip, err := interfaceToIPv4Addr(ifi)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
- var x [4]byte
- copy(x[:], ip.To4())
+ var a [4]byte
+ copy(a[:], ip.To4())
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
+ err = syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_IF), (*byte)(unsafe.Pointer(&a[0])), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
-func ipv4MulticastTTL(fd *netFD) (int, error) {
- // TODO: Implement this
- return -1, syscall.EWINDOWS
-}
-
-func setIPv4MulticastTTL(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EWINDOWS
-}
-
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
+ vv := int32(boolint(v))
+ err := syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&vv)), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
-
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EWINDOWS
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- // TODO: Implement this
- return syscall.EWINDOWS
-}
-
-func ipv6TrafficClass(fd *netFD) (int, error) {
- // TODO: Implement this
- return 0, syscall.EWINDOWS
-}
-
-func setIPv6TrafficClass(fd *netFD, v int) error {
- // TODO: Implement this
- return syscall.EWINDOWS
}
diff --git a/libgo/go/net/tcp_test.go b/libgo/go/net/tcp_test.go
new file mode 100644
index 00000000000..53daf5b0999
--- /dev/null
+++ b/libgo/go/net/tcp_test.go
@@ -0,0 +1,118 @@
+// 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
+
+import (
+ "runtime"
+ "testing"
+ "time"
+)
+
+func BenchmarkTCPOneShot(b *testing.B) {
+ benchmarkTCP(b, false, false)
+}
+
+func BenchmarkTCPOneShotTimeout(b *testing.B) {
+ benchmarkTCP(b, false, true)
+}
+
+func BenchmarkTCPPersistent(b *testing.B) {
+ benchmarkTCP(b, true, false)
+}
+
+func BenchmarkTCPPersistentTimeout(b *testing.B) {
+ benchmarkTCP(b, true, true)
+}
+
+func benchmarkTCP(b *testing.B, persistent, timeout bool) {
+ const msgLen = 512
+ conns := b.N
+ numConcurrent := runtime.GOMAXPROCS(-1) * 16
+ msgs := 1
+ if persistent {
+ conns = numConcurrent
+ msgs = b.N / conns
+ if msgs == 0 {
+ msgs = 1
+ }
+ if conns > b.N {
+ conns = b.N
+ }
+ }
+ sendMsg := func(c Conn, buf []byte) bool {
+ n, err := c.Write(buf)
+ if n != len(buf) || err != nil {
+ b.Logf("Write failed: %v", err)
+ return false
+ }
+ return true
+ }
+ recvMsg := func(c Conn, buf []byte) bool {
+ for read := 0; read != len(buf); {
+ n, err := c.Read(buf)
+ read += n
+ if err != nil {
+ b.Logf("Read failed: %v", err)
+ return false
+ }
+ }
+ return true
+ }
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ b.Fatalf("Listen failed: %v", err)
+ }
+ defer ln.Close()
+ // Acceptor.
+ go func() {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ break
+ }
+ // Server connection.
+ go func(c Conn) {
+ defer c.Close()
+ if timeout {
+ c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire.
+ }
+ var buf [msgLen]byte
+ for m := 0; m < msgs; m++ {
+ if !recvMsg(c, buf[:]) || !sendMsg(c, buf[:]) {
+ break
+ }
+ }
+ }(c)
+ }
+ }()
+ sem := make(chan bool, numConcurrent)
+ for i := 0; i < conns; i++ {
+ sem <- true
+ // Client connection.
+ go func() {
+ defer func() {
+ <-sem
+ }()
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ b.Logf("Dial failed: %v", err)
+ return
+ }
+ defer c.Close()
+ if timeout {
+ c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire.
+ }
+ var buf [msgLen]byte
+ for m := 0; m < msgs; m++ {
+ if !sendMsg(c, buf[:]) || !recvMsg(c, buf[:]) {
+ break
+ }
+ }
+ }()
+ }
+ for i := 0; i < cap(sem); i++ {
+ sem <- true
+ }
+}
diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go
index 35f56966eae..4121dd89e9a 100644
--- a/libgo/go/net/tcpsock_plan9.go
+++ b/libgo/go/net/tcpsock_plan9.go
@@ -2,36 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TCP for Plan 9
+// TCP sockets for Plan 9
package net
-import (
- "syscall"
- "time"
-)
+import "syscall"
-// TCPConn is an implementation of the Conn interface
-// for TCP network connections.
+// TCPConn is an implementation of the Conn interface for TCP network
+// connections.
type TCPConn struct {
plan9Conn
}
-// SetDeadline implements the Conn SetDeadline method.
-func (c *TCPConn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *TCPConn) SetReadDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *TCPConn) SetWriteDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
@@ -51,8 +33,8 @@ func (c *TCPConn) CloseWrite() error {
}
// DialTCP connects to the remote address raddr on the network net,
-// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
-// as the local address for the connection.
+// 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) {
switch net {
case "tcp", "tcp4", "tcp6":
@@ -69,17 +51,27 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) {
return &TCPConn{*c1}, nil
}
-// TCPListener is a TCP network listener.
-// Clients should typically use variables of type Listener
-// instead of assuming TCP.
+// TCPListener is a TCP network listener. Clients should typically
+// use variables of type Listener instead of assuming TCP.
type TCPListener struct {
plan9Listener
}
-// 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 (l *TCPListener) Close() error {
+ if l == nil || l.ctl == nil {
+ return syscall.EINVAL
+ }
+ if _, err := l.ctl.WriteString("hangup"); err != nil {
+ l.ctl.Close()
+ return err
+ }
+ return l.ctl.Close()
+}
+
+// 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) {
switch net {
case "tcp", "tcp4", "tcp6":
diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go
index e6b1937fb2c..2c34d2fda7d 100644
--- a/libgo/go/net/tcpsock_posix.go
+++ b/libgo/go/net/tcpsock_posix.go
@@ -66,27 +66,15 @@ func (a *TCPAddr) toAddr() sockaddr {
// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
- fd *netFD
+ conn
}
func newTCPConn(fd *netFD) *TCPConn {
- c := &TCPConn{fd}
+ c := &TCPConn{conn{fd}}
c.SetNoDelay(true)
return c
}
-func (c *TCPConn) 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 *TCPConn) Read(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Read(b)
-}
-
// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
if n, err, handled := sendFile(c.fd, r); handled {
@@ -95,22 +83,6 @@ func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
return genericReadFrom(c, r)
}
-// Write implements the Conn Write method.
-func (c *TCPConn) Write(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the TCP connection.
-func (c *TCPConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
@@ -129,64 +101,6 @@ func (c *TCPConn) CloseWrite() error {
return c.fd.CloseWrite()
}
-// LocalAddr returns the local network address, a *TCPAddr.
-func (c *TCPConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *TCPAddr.
-func (c *TCPConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *TCPConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *TCPConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *TCPConn) 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 *TCPConn) 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 *TCPConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
-
// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
@@ -225,11 +139,6 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
return setNoDelay(c.fd, noDelay)
}
-// 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 *TCPConn) File() (f *os.File, err error) { return c.fd.dup() }
-
// DialTCP connects to the remote address raddr on the network net,
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
// as the local address for the connection.
@@ -257,8 +166,17 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
// use the result. See also:
// http://golang.org/issue/2690
// http://stackoverflow.com/questions/4949858/
- for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ {
- fd.Close()
+ //
+ // The opposite can also happen: if we ask the kernel to pick an appropriate
+ // originating local address, sometimes it picks one that is already in use.
+ // So if the error is EADDRNOTAVAIL, we have to try again too, just for
+ // a different reason.
+ //
+ // The kernel socket code is no doubt enjoying watching us squirm.
+ for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
+ if err == nil {
+ fd.Close()
+ }
fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
}
@@ -268,7 +186,12 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
return newTCPConn(fd), nil
}
-func selfConnect(fd *netFD) bool {
+func selfConnect(fd *netFD, err error) bool {
+ // If the connect failed, we clearly didn't connect to ourselves.
+ if err != nil {
+ return false
+ }
+
// The socket constructor can return an fd with raddr nil under certain
// unknown conditions. The errors in the calls there to Getpeername
// are discarded, but we can't catch the problem there because those
@@ -285,6 +208,11 @@ func selfConnect(fd *netFD) bool {
return l.Port == r.Port && l.IP.Equal(r.IP)
}
+func spuriousENOTAVAIL(err error) bool {
+ e, ok := err.(*OpError)
+ return ok && e.Err == syscall.EADDRNOTAVAIL
+}
+
// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go
index 125feb3e885..3777424534d 100644
--- a/libgo/go/net/textproto/reader.go
+++ b/libgo/go/net/textproto/reader.go
@@ -452,16 +452,18 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
return m, err
}
- // Key ends at first colon; must not have spaces.
+ // Key ends at first colon; should not have spaces but
+ // they appear in the wild, violating specs, so we
+ // remove them if present.
i := bytes.IndexByte(kv, ':')
if i < 0 {
return m, ProtocolError("malformed MIME header line: " + string(kv))
}
- key := string(kv[0:i])
- if strings.Index(key, " ") >= 0 {
- key = strings.TrimRight(key, " ")
+ endKey := i
+ for endKey > 0 && kv[endKey-1] == ' ' {
+ endKey--
}
- key = CanonicalMIMEHeaderKey(key)
+ key := canonicalMIMEHeaderKey(kv[:endKey])
// Skip initial spaces in value.
i++ // skip colon
@@ -486,25 +488,28 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
// canonical key for "accept-encoding" is "Accept-Encoding".
func CanonicalMIMEHeaderKey(s string) string {
// Quick check for canonical encoding.
- needUpper := true
+ upper := true
for i := 0; i < len(s); i++ {
c := s[i]
- if needUpper && 'a' <= c && c <= 'z' {
- goto MustRewrite
+ if upper && 'a' <= c && c <= 'z' {
+ return canonicalMIMEHeaderKey([]byte(s))
}
- if !needUpper && 'A' <= c && c <= 'Z' {
- goto MustRewrite
+ if !upper && 'A' <= c && c <= 'Z' {
+ return canonicalMIMEHeaderKey([]byte(s))
}
- needUpper = c == '-'
+ upper = c == '-'
}
return s
+}
-MustRewrite:
+// 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.
- a := []byte(s)
upper := true
for i, v := range a {
if v == ' ' {
diff --git a/libgo/go/net/textproto/reader_test.go b/libgo/go/net/textproto/reader_test.go
index 7c5d16227ff..9b6c76a0d08 100644
--- a/libgo/go/net/textproto/reader_test.go
+++ b/libgo/go/net/textproto/reader_test.go
@@ -6,6 +6,7 @@ package textproto
import (
"bufio"
+ "bytes"
"io"
"reflect"
"strings"
@@ -239,3 +240,19 @@ func TestRFC959Lines(t *testing.T) {
}
}
}
+
+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")
+ h, err := r.ReadMIMEHeader()
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(h) != 4 {
+ b.Fatalf("want 4")
+ }
+ }
+}
diff --git a/libgo/go/net/textproto/textproto.go b/libgo/go/net/textproto/textproto.go
index ad5840cf7da..e7ad8773dc6 100644
--- a/libgo/go/net/textproto/textproto.go
+++ b/libgo/go/net/textproto/textproto.go
@@ -121,3 +121,29 @@ func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err error) {
}
return id, nil
}
+
+// TrimString returns s without leading and trailing ASCII space.
+func TrimString(s string) string {
+ for len(s) > 0 && isASCIISpace(s[0]) {
+ s = s[1:]
+ }
+ for len(s) > 0 && isASCIISpace(s[len(s)-1]) {
+ s = s[:len(s)-1]
+ }
+ return s
+}
+
+// TrimBytes returns b without leading and trailing ASCII space.
+func TrimBytes(b []byte) []byte {
+ for len(b) > 0 && isASCIISpace(b[0]) {
+ b = b[1:]
+ }
+ for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
+ b = b[:len(b)-1]
+ }
+ return b
+}
+
+func isASCIISpace(b byte) bool {
+ return b == ' ' || b == '\t' || b == '\n' || b == '\r'
+}
diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go
index b3520cf09f3..62b27d95e9c 100644
--- a/libgo/go/net/udpsock.go
+++ b/libgo/go/net/udpsock.go
@@ -6,6 +6,10 @@
package net
+import "errors"
+
+var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
+
// UDPAddr represents the address of a UDP end point.
type UDPAddr struct {
IP IP
diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go
index 4f298a42f87..aaa7e5b28cb 100644
--- a/libgo/go/net/udpsock_plan9.go
+++ b/libgo/go/net/udpsock_plan9.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// UDP for Plan 9
+// UDP sockets for Plan 9
package net
@@ -10,7 +10,6 @@ import (
"errors"
"os"
"syscall"
- "time"
)
// UDPConn is the implementation of the Conn and PacketConn
@@ -19,29 +18,15 @@ type UDPConn struct {
plan9Conn
}
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UDPConn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UDPConn) SetReadDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UDPConn) SetWriteDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
// UDP-specific methods.
// ReadFromUDP reads a UDP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
-// ReadFromUDP can be made to time out and return an error with Timeout() == true
-// after a fixed time limit; see SetDeadline and SetReadDeadline.
+// ReadFromUDP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
@@ -75,12 +60,22 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
return c.ReadFromUDP(b)
}
-// WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
+// ReadMsgUDP reads a packet from c, copying the payload into b and
+// the associdated out-of-band data into oob. It returns the number
+// of bytes copied into b, the number of bytes copied into oob, the
+// flags that were set on the packet and the source address of the
+// packet.
+func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
+ return 0, 0, 0, nil, syscall.EPLAN9
+}
+
+// WriteToUDP writes a UDP packet to addr via c, copying the payload
+// from b.
//
-// WriteToUDP can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToUDP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
if !c.ok() {
return 0, syscall.EINVAL
@@ -116,9 +111,16 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) {
return c.WriteToUDP(b, a)
}
+// WriteMsgUDP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
+ return 0, 0, syscall.EPLAN9
+}
+
// DialUDP connects to the remote address raddr on the network net,
-// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
-// as the local address for the connection.
+// 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) {
switch net {
case "udp", "udp4", "udp6":
@@ -163,10 +165,10 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) {
return h, b
}
-// ListenUDP listens for incoming UDP packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP
-// packets with per-packet addressing.
+// ListenUDP listens for incoming UDP packets addressed to the local
+// 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) {
switch net {
case "udp", "udp4", "udp6":
@@ -188,9 +190,9 @@ func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
}
// ListenMulticastUDP listens for incoming multicast UDP packets
-// addressed to the group address gaddr on ifi, which specifies
-// the interface to join. ListenMulticastUDP uses default
-// multicast interface if ifi is nil.
+// addressed to the group address gaddr on ifi, which specifies the
+// interface to join. ListenMulticastUDP uses default multicast
+// interface if ifi is nil.
func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go
index 9c6b6d39336..e075380c8e3 100644
--- a/libgo/go/net/udpsock_posix.go
+++ b/libgo/go/net/udpsock_posix.go
@@ -8,14 +8,7 @@
package net
-import (
- "errors"
- "os"
- "syscall"
- "time"
-)
-
-var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
+import "syscall"
func sockaddrToUDP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
@@ -58,96 +51,10 @@ func (a *UDPAddr) toAddr() sockaddr {
// UDPConn is the implementation of the Conn and PacketConn
// interfaces for UDP network connections.
type UDPConn struct {
- fd *netFD
-}
-
-func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} }
-
-func (c *UDPConn) 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 *UDPConn) 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 *UDPConn) Write(b []byte) (int, error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the UDP connection.
-func (c *UDPConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
-// LocalAddr returns the local network address.
-func (c *UDPConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *UDPAddr.
-func (c *UDPConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UDPConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UDPConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UDPConn) 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 *UDPConn) SetReadBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadBuffer(c.fd, bytes)
+ conn
}
-// SetWriteBuffer sets the size of the operating system's
-// transmit buffer associated with the connection.
-func (c *UDPConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
+func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
// UDP-specific methods.
@@ -180,6 +87,26 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
return n, uaddr.toAddr(), err
}
+// ReadMsgUDP reads a packet from c, copying the payload into b and
+// the associdated out-of-band data into oob. It returns the number
+// of bytes copied into b, the number of bytes copied into oob, the
+// flags that were set on the packet and the source address of the
+// packet.
+func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
+ if !c.ok() {
+ return 0, 0, 0, nil, syscall.EINVAL
+ }
+ var sa syscall.Sockaddr
+ n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+ switch sa := sa.(type) {
+ case *syscall.SockaddrInet4:
+ addr = &UDPAddr{sa.Addr[0:], sa.Port}
+ case *syscall.SockaddrInet6:
+ addr = &UDPAddr{sa.Addr[0:], sa.Port}
+ }
+ return
+}
+
// WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
//
// WriteToUDP can be made to time out and return
@@ -212,10 +139,22 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
return c.WriteToUDP(b, a)
}
-// 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 *UDPConn) File() (f *os.File, err error) { return c.fd.dup() }
+// WriteMsgUDP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
+ if !c.ok() {
+ return 0, 0, syscall.EINVAL
+ }
+ if c.fd.isConnected {
+ return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
+ }
+ sa, err := addr.sockaddr(c.fd.family)
+ if err != nil {
+ return 0, 0, &OpError{"write", c.fd.net, addr, err}
+ }
+ return c.fd.WriteMsg(b, oob, sa)
+}
// DialUDP connects to the remote address raddr on the network net,
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
@@ -335,14 +274,6 @@ func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
return nil
}
-func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
- err := leaveIPv4Group(c.fd, ifi, ip)
- if err != nil {
- return &OpError{"leaveipv4group", c.fd.net, &IPAddr{ip}, err}
- }
- return nil
-}
-
func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
err := joinIPv6Group(c.fd, ifi, ip)
if err != nil {
@@ -350,11 +281,3 @@ func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
}
return nil
}
-
-func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
- err := leaveIPv6Group(c.fd, ifi, ip)
- if err != nil {
- return &OpError{"leaveipv6group", c.fd.net, &IPAddr{ip}, err}
- }
- return nil
-}
diff --git a/libgo/go/net/unicast_test.go b/libgo/go/net/unicast_posix_test.go
index e5dd013db67..5b39e25acbf 100644
--- a/libgo/go/net/unicast_test.go
+++ b/libgo/go/net/unicast_posix_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !plan9
+
package net
import (
@@ -59,13 +61,6 @@ func TestTCPListener(t *testing.T) {
checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
l2, err := Listen(tt.net, tt.laddr+":"+port)
checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
- fd := l1.(*TCPListener).fd
- switch fd.family {
- case syscall.AF_INET:
- testIPv4UnicastSocketOptions(t, fd)
- case syscall.AF_INET6:
- testIPv6UnicastSocketOptions(t, fd)
- }
l1.Close()
}
}
@@ -104,13 +99,6 @@ func TestUDPListener(t *testing.T) {
checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
l2, err := ListenPacket(tt.net, tt.laddr+":"+port)
checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
- fd := l1.(*UDPConn).fd
- switch fd.family {
- case syscall.AF_INET:
- testIPv4UnicastSocketOptions(t, fd)
- case syscall.AF_INET6:
- testIPv6UnicastSocketOptions(t, fd)
- }
l1.Close()
}
}
@@ -468,44 +456,6 @@ func checkDualStackAddrFamily(t *testing.T, net, laddr string, fd *netFD) {
}
}
-func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) {
- _, err := ipv4TOS(fd)
- if err != nil {
- t.Fatalf("ipv4TOS failed: %v", err)
- }
- err = setIPv4TOS(fd, 1)
- if err != nil {
- t.Fatalf("setIPv4TOS failed: %v", err)
- }
- _, err = ipv4TTL(fd)
- if err != nil {
- t.Fatalf("ipv4TTL failed: %v", err)
- }
- err = setIPv4TTL(fd, 1)
- if err != nil {
- t.Fatalf("setIPv4TTL failed: %v", err)
- }
-}
-
-func testIPv6UnicastSocketOptions(t *testing.T, fd *netFD) {
- _, err := ipv6TrafficClass(fd)
- if err != nil {
- t.Fatalf("ipv6TrafficClass failed: %v", err)
- }
- err = setIPv6TrafficClass(fd, 1)
- if err != nil {
- t.Fatalf("setIPv6TrafficClass failed: %v", err)
- }
- _, err = ipv6HopLimit(fd)
- if err != nil {
- t.Fatalf("ipv6HopLimit failed: %v", err)
- }
- err = setIPv6HopLimit(fd, 1)
- if err != nil {
- t.Fatalf("setIPv6HopLimit failed: %v", err)
- }
-}
-
var prohibitionaryDialArgTests = []struct {
net string
addr string
@@ -536,3 +486,38 @@ func TestProhibitionaryDialArgs(t *testing.T) {
}
}
}
+
+func TestWildWildcardListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Logf("skipping test on %q", runtime.GOOS)
+ return
+ }
+
+ if testing.Short() || !*testExternal {
+ t.Logf("skipping test to avoid external network")
+ return
+ }
+
+ defer func() {
+ if recover() != nil {
+ t.Fatalf("panicked")
+ }
+ }()
+
+ if ln, err := Listen("tcp", ""); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenPacket("udp", ""); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenTCP("tcp", nil); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenUDP("udp", nil); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenIP("ip:icmp", nil); err == nil {
+ ln.Close()
+ }
+}
diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go
index 7b4ae6bd116..21403754a7a 100644
--- a/libgo/go/net/unixsock_plan9.go
+++ b/libgo/go/net/unixsock_plan9.go
@@ -7,40 +7,33 @@
package net
import (
+ "os"
"syscall"
"time"
)
-// UnixConn is an implementation of the Conn interface
-// for connections to Unix domain sockets.
+// UnixConn is an implementation of the Conn interface for connections
+// to Unix domain sockets.
type UnixConn bool
// Implementation of the Conn interface - see Conn for documentation.
// Read implements the Conn Read method.
-func (c *UnixConn) Read(b []byte) (n int, err error) {
+func (c *UnixConn) Read(b []byte) (int, error) {
return 0, syscall.EPLAN9
}
// Write implements the Conn Write method.
-func (c *UnixConn) Write(b []byte) (n int, err error) {
+func (c *UnixConn) Write(b []byte) (int, error) {
return 0, syscall.EPLAN9
}
-// Close closes the Unix domain connection.
-func (c *UnixConn) Close() error {
- return syscall.EPLAN9
-}
-
-// LocalAddr returns the local network address, a *UnixAddr.
-// Unlike in other protocols, LocalAddr is usually nil for dialed connections.
+// LocalAddr returns the local network address.
func (c *UnixConn) LocalAddr() Addr {
return nil
}
-// RemoteAddr returns the remote network address, a *UnixAddr.
-// Unlike in other protocols, RemoteAddr is usually nil for connections
-// accepted by a listener.
+// RemoteAddr returns the remote network address.
func (c *UnixConn) RemoteAddr() Addr {
return nil
}
@@ -60,47 +53,144 @@ func (c *UnixConn) 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 *UnixConn) SetReadBuffer(bytes int) error {
+ return syscall.EPLAN9
+}
+
+// SetWriteBuffer sets the size of the operating system's transmit
+// buffer associated with the connection.
+func (c *UnixConn) 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 *UnixConn) File() (f *os.File, err error) {
+ return nil, syscall.EPLAN9
+}
+
+// Close closes the Unix domain connection.
+func (c *UnixConn) Close() error {
+ return syscall.EPLAN9
+}
+
+// ReadFromUnix reads a packet from c, copying the payload into b. It
+// returns the number of bytes copied into b and the source address of
+// the packet.
+//
+// ReadFromUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
+func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) {
+ return 0, nil, syscall.EPLAN9
+}
+
// ReadFrom implements the PacketConn ReadFrom method.
-func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
- err = syscall.EPLAN9
- return
+func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) {
+ return 0, nil, syscall.EPLAN9
+}
+
+// ReadMsgUnix reads a packet from c, copying the payload into b and
+// the associated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet, and the source address of the packet.
+func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
+ return 0, 0, 0, nil, syscall.EPLAN9
+}
+
+// WriteToUnix writes a packet to addr via c, copying the payload from b.
+//
+// WriteToUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
+func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) {
+ return 0, syscall.EPLAN9
}
// WriteTo implements the PacketConn WriteTo method.
-func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
- err = syscall.EPLAN9
- return
+func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) {
+ return 0, syscall.EPLAN9
+}
+
+// WriteMsgUnix writes a packet to addr via c, copying the payload
+// from b and the associated out-of-band data from oob. It returns
+// the number of payload and out-of-band bytes written.
+func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
+ return 0, 0, syscall.EPLAN9
+}
+
+// CloseRead shuts down the reading side of the Unix domain
+// connection. Most callers should just use Close.
+func (c *UnixConn) CloseRead() error {
+ return syscall.EPLAN9
+}
+
+// CloseWrite shuts down the writing side of the Unix domain
+// connection. Most callers should just use Close.
+func (c *UnixConn) CloseWrite() error {
+ return syscall.EPLAN9
}
// DialUnix connects to the remote address raddr on the network net,
-// 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) (c *UnixConn, err 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 nil, syscall.EPLAN9
}
-// UnixListener is a Unix domain socket listener.
-// Clients should typically use variables of type Listener
-// instead of assuming Unix domain sockets.
+// UnixListener is a Unix domain socket listener. Clients should
+// typically use variables of type Listener instead of assuming Unix
+// domain sockets.
type UnixListener bool
-// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
-// Net must be "unix" (stream sockets).
-func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) {
+// ListenUnix announces on the Unix domain socket laddr and returns a
+// Unix listener. Net must be "unix" (stream sockets).
+func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
return nil, syscall.EPLAN9
}
-// Accept implements the Accept method in the Listener interface;
-// it waits for the next call and returns a generic Conn.
-func (l *UnixListener) Accept() (c Conn, err error) {
+// AcceptUnix accepts the next incoming call and returns the new
+// connection and the remote address.
+func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
return nil, syscall.EPLAN9
}
-// Close stops listening on the Unix address.
-// Already accepted connections are not closed.
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
+func (l *UnixListener) Accept() (Conn, error) {
+ return nil, syscall.EPLAN9
+}
+
+// Close stops listening on the Unix address. Already accepted
+// connections are not closed.
func (l *UnixListener) Close() error {
return syscall.EPLAN9
}
// Addr returns the listener's network address.
func (l *UnixListener) Addr() Addr { return nil }
+
+// SetDeadline sets the deadline associated with the listener.
+// A zero time value disables the deadline.
+func (l *UnixListener) 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 *UnixListener) File() (*os.File, error) {
+ return nil, syscall.EPLAN9
+}
+
+// ListenUnixgram listens for incoming Unix datagram packets addressed
+// to the local address laddr. The returned connection c's ReadFrom
+// and WriteTo methods can be used to receive and send UDP packets
+// with per-packet addressing. The network net must be "unixgram".
+func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
+ return nil, syscall.EPLAN9
+}
diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go
index 57d784c71cf..2bef5eaaf12 100644
--- a/libgo/go/net/unixsock_posix.go
+++ b/libgo/go/net/unixsock_posix.go
@@ -111,99 +111,10 @@ func sotypeToNet(sotype int) string {
// UnixConn is an implementation of the Conn interface
// for connections to Unix domain sockets.
type UnixConn struct {
- fd *netFD
+ conn
}
-func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{fd} }
-
-func (c *UnixConn) 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 *UnixConn) Read(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Read(b)
-}
-
-// Write implements the Conn Write method.
-func (c *UnixConn) Write(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the Unix domain connection.
-func (c *UnixConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
-// LocalAddr returns the local network address, a *UnixAddr.
-// Unlike in other protocols, LocalAddr is usually nil for dialed connections.
-func (c *UnixConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *UnixAddr.
-// Unlike in other protocols, RemoteAddr is usually nil for connections
-// accepted by a listener.
-func (c *UnixConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UnixConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UnixConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UnixConn) 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 *UnixConn) 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 *UnixConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
+func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} }
// ReadFromUnix reads a packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the source address
@@ -296,10 +207,23 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
return c.fd.WriteMsg(b, oob, nil)
}
-// 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 *UnixConn) File() (f *os.File, err error) { return c.fd.dup() }
+// CloseRead shuts down the reading side of the Unix domain connection.
+// Most callers should just use Close.
+func (c *UnixConn) CloseRead() error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.fd.CloseRead()
+}
+
+// CloseWrite shuts down the writing side of the Unix domain connection.
+// Most callers should just use Close.
+func (c *UnixConn) CloseWrite() error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.fd.CloseWrite()
+}
// DialUnix connects to the remote address raddr on the network net,
// which must be "unix" or "unixgram". If laddr is not nil, it is used
diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go
index 17bf0d3a342..d1fff89da79 100644
--- a/libgo/go/net/url/url.go
+++ b/libgo/go/net/url/url.go
@@ -7,7 +7,9 @@
package url
import (
+ "bytes"
"errors"
+ "sort"
"strconv"
"strings"
)
@@ -519,12 +521,16 @@ func parseQuery(m Values, query string) (err error) {
}
key, err1 := QueryUnescape(key)
if err1 != nil {
- err = err1
+ if err == nil {
+ err = err1
+ }
continue
}
value, err1 = QueryUnescape(value)
if err1 != nil {
- err = err1
+ if err == nil {
+ err = err1
+ }
continue
}
m[key] = append(m[key], value)
@@ -538,14 +544,24 @@ func (v Values) Encode() string {
if v == nil {
return ""
}
- parts := make([]string, 0, len(v)) // will be large enough for most uses
- for k, vs := range v {
+ var buf bytes.Buffer
+ keys := make([]string, 0, len(v))
+ for k := range v {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ vs := v[k]
prefix := QueryEscape(k) + "="
for _, v := range vs {
- parts = append(parts, prefix+QueryEscape(v))
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(prefix)
+ buf.WriteString(QueryEscape(v))
}
}
- return strings.Join(parts, "&")
+ return buf.String()
}
// resolvePath applies special path segments from refs and applies
diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go
index 75e8abe4eb3..64f11700278 100644
--- a/libgo/go/net/url/url_test.go
+++ b/libgo/go/net/url/url_test.go
@@ -7,6 +7,7 @@ package url
import (
"fmt"
"reflect"
+ "strings"
"testing"
)
@@ -453,20 +454,24 @@ func TestEscape(t *testing.T) {
//}
type EncodeQueryTest struct {
- m Values
- expected string
- expected1 string
+ m Values
+ expected string
}
var encodeQueryTests = []EncodeQueryTest{
- {nil, "", ""},
- {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"},
- {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"},
+ {nil, ""},
+ {Values{"q": {"puppies"}, "oe": {"utf8"}}, "oe=utf8&q=puppies"},
+ {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7"},
+ {Values{
+ "a": {"a1", "a2", "a3"},
+ "b": {"b1", "b2", "b3"},
+ "c": {"c1", "c2", "c3"},
+ }, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"},
}
func TestEncodeQuery(t *testing.T) {
for _, tt := range encodeQueryTests {
- if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 {
+ if q := tt.m.Encode(); q != tt.expected {
t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected)
}
}
@@ -775,3 +780,13 @@ func TestRequestURI(t *testing.T) {
}
}
}
+
+func TestParseFailure(t *testing.T) {
+ // Test that the first parse error is returned.
+ const url = "%gh&%ij"
+ _, err := ParseQuery(url)
+ errStr := fmt.Sprint(err)
+ if !strings.Contains(errStr, "%gh") {
+ t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
+ }
+}