diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-10-23 04:31:11 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-10-23 04:31:11 +0000 |
commit | 4ccad563d2a3559f0557bfb177bcf45144219bdf (patch) | |
tree | 46bb86f514fbf6bad82da48e69a18fb09d878834 /libgo/go/net | |
parent | 0b7463235f0e23c624d1911c9b15f531108cc5a6 (diff) | |
download | gcc-4ccad563d2a3559f0557bfb177bcf45144219bdf.tar.gz |
libgo: Update to current sources.
From-SVN: r192704
Diffstat (limited to 'libgo/go/net')
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") + } +} |