aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2014-06-04 23:15:33 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2014-06-04 23:15:33 +0000
commitbae90c989cb020d17a24919ec84c0b8dd2fae2da (patch)
tree89766166feb4ceca2d983169c5360e3f6f521b12 /libgo/go/net
parent82b3da6a714493644a4333bfd8205e3341ed3b8e (diff)
downloadgcc-bae90c989cb020d17a24919ec84c0b8dd2fae2da.zip
gcc-bae90c989cb020d17a24919ec84c0b8dd2fae2da.tar.gz
gcc-bae90c989cb020d17a24919ec84c0b8dd2fae2da.tar.bz2
libgo: Merge from revision 18783:00cce3a34d7e of master library.
This revision was committed January 7, 2014. The next revision deleted runtime/mfinal.c. That will be done in a subsequent merge. This merge changes type descriptors to add a zero field, pointing to a zero value for that type. This is implemented as a common variable. * go-gcc.cc (Gcc_backend::implicit_variable): Add is_common and alignment parameters. Permit init parameter to be NULL. From-SVN: r211249
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/cgo_unix_test.go24
-rw-r--r--libgo/go/net/dial.go7
-rw-r--r--libgo/go/net/dialgoogle_test.go24
-rw-r--r--libgo/go/net/dnsclient_unix.go3
-rw-r--r--libgo/go/net/dnsconfig_unix.go7
-rw-r--r--libgo/go/net/dnsconfig_unix_test.go46
-rw-r--r--libgo/go/net/fd_windows.go48
-rw-r--r--libgo/go/net/hosts_test.go2
-rw-r--r--libgo/go/net/http/client_test.go20
-rw-r--r--libgo/go/net/http/cookie.go2
-rw-r--r--libgo/go/net/http/cookie_test.go28
-rw-r--r--libgo/go/net/http/cookiejar/jar.go6
-rw-r--r--libgo/go/net/http/header.go17
-rw-r--r--libgo/go/net/http/request.go27
-rw-r--r--libgo/go/net/http/request_test.go41
-rw-r--r--libgo/go/net/http/response.go1
-rw-r--r--libgo/go/net/http/response_test.go7
-rw-r--r--libgo/go/net/http/serve_test.go108
-rw-r--r--libgo/go/net/http/server.go57
-rw-r--r--libgo/go/net/http/transfer.go41
-rw-r--r--libgo/go/net/http/transfer_test.go6
-rw-r--r--libgo/go/net/http/transport_test.go2
-rw-r--r--libgo/go/net/ip.go3
-rw-r--r--libgo/go/net/ip_test.go1
-rw-r--r--libgo/go/net/iprawsock_posix.go2
-rw-r--r--libgo/go/net/lookup_plan9.go25
-rw-r--r--libgo/go/net/net_test.go8
-rw-r--r--libgo/go/net/parse.go2
-rw-r--r--libgo/go/net/testdata/resolv.conf5
-rw-r--r--libgo/go/net/z_last_test.go34
30 files changed, 424 insertions, 180 deletions
diff --git a/libgo/go/net/cgo_unix_test.go b/libgo/go/net/cgo_unix_test.go
new file mode 100644
index 0000000..33566ce
--- /dev/null
+++ b/libgo/go/net/cgo_unix_test.go
@@ -0,0 +1,24 @@
+// Copyright 2013 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 cgo,!netgo
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package net
+
+import "testing"
+
+func TestCgoLookupIP(t *testing.T) {
+ host := "localhost"
+ _, err, ok := cgoLookupIP(host)
+ if !ok {
+ t.Errorf("cgoLookupIP must not be a placeholder")
+ }
+ if err != nil {
+ t.Errorf("cgoLookupIP failed: %v", err)
+ }
+ if _, err := goLookupIP(host); err != nil {
+ t.Errorf("goLookupIP failed: %v", err)
+ }
+}
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go
index 6304818..70b66e7 100644
--- a/libgo/go/net/dial.go
+++ b/libgo/go/net/dial.go
@@ -172,7 +172,6 @@ func (d *Dialer) Dial(network, address string) (Conn, error) {
func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Conn, error) {
type racer struct {
Conn
- Addr
error
}
// Sig controls the flow of dial results on lane. It passes a
@@ -184,7 +183,7 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
go func(ra Addr) {
c, err := dialSingle(net, addr, la, ra, deadline)
if _, ok := <-sig; ok {
- lane <- racer{c, ra, err}
+ lane <- racer{c, err}
} else if err == nil {
// We have to return the resources
// that belong to the other
@@ -195,7 +194,6 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
}(ra.toAddr())
}
defer close(sig)
- var failAddr Addr
lastErr := errTimeout
nracers := len(ras)
for nracers > 0 {
@@ -205,12 +203,11 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
if racer.error == nil {
return racer.Conn, nil
}
- failAddr = racer.Addr
lastErr = racer.error
nracers--
}
}
- return nil, &OpError{Op: "dial", Net: net, Addr: failAddr, Err: lastErr}
+ return nil, lastErr
}
// dialSingle attempts to establish and returns a single connection to
diff --git a/libgo/go/net/dialgoogle_test.go b/libgo/go/net/dialgoogle_test.go
index b4ebad0..79d150f 100644
--- a/libgo/go/net/dialgoogle_test.go
+++ b/libgo/go/net/dialgoogle_test.go
@@ -107,30 +107,6 @@ var googleaddrsipv4 = []string{
"[0:0:0:0:0:ffff::%d.%d.%d.%d]:80",
}
-func TestDNSThreadLimit(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Skip("skipping test to avoid external network")
- }
-
- const N = 10000
- c := make(chan int, N)
- for i := 0; i < N; i++ {
- go func(i int) {
- LookupIP(fmt.Sprintf("%d.net-test.golang.org", i))
- c <- 1
- }(i)
- }
- // Don't bother waiting for the stragglers; stop at 0.9 N.
- for i := 0; i < N*9/10; i++ {
- if i%100 == 0 {
- //println("TestDNSThreadLimit:", i)
- }
- <-c
- }
-
- // If we're still here, it worked.
-}
-
func TestDialGoogleIPv4(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go
index 16cf420..a30c9a7 100644
--- a/libgo/go/net/dnsclient_unix.go
+++ b/libgo/go/net/dnsclient_unix.go
@@ -159,7 +159,8 @@ func convertRR_AAAA(records []dnsRR) []IP {
var cfg *dnsConfig
var dnserr error
-func loadConfig() { cfg, dnserr = dnsReadConfig() }
+// Assume dns config file is /etc/resolv.conf here
+func loadConfig() { cfg, dnserr = dnsReadConfig("/etc/resolv.conf") }
var onceLoadConfig sync.Once
diff --git a/libgo/go/net/dnsconfig_unix.go b/libgo/go/net/dnsconfig_unix.go
index 2f0f6c0..7856ebc 100644
--- a/libgo/go/net/dnsconfig_unix.go
+++ b/libgo/go/net/dnsconfig_unix.go
@@ -20,14 +20,13 @@ type dnsConfig struct {
// See resolv.conf(5) on a Linux machine.
// TODO(rsc): Supposed to call uname() and chop the beginning
// of the host name to get the default search domain.
-// We assume it's in resolv.conf anyway.
-func dnsReadConfig() (*dnsConfig, error) {
- file, err := open("/etc/resolv.conf")
+func dnsReadConfig(filename string) (*dnsConfig, error) {
+ file, err := open(filename)
if err != nil {
return nil, &DNSConfigError{err}
}
conf := new(dnsConfig)
- conf.servers = make([]string, 3)[0:0] // small, but the standard limit
+ conf.servers = make([]string, 0, 3) // small, but the standard limit
conf.search = make([]string, 0)
conf.ndots = 1
conf.timeout = 5
diff --git a/libgo/go/net/dnsconfig_unix_test.go b/libgo/go/net/dnsconfig_unix_test.go
new file mode 100644
index 0000000..697c69f
--- /dev/null
+++ b/libgo/go/net/dnsconfig_unix_test.go
@@ -0,0 +1,46 @@
+// Copyright 2013 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 dragonfly freebsd linux netbsd openbsd
+
+package net
+
+import "testing"
+
+func TestDNSReadConfig(t *testing.T) {
+ dnsConfig, err := dnsReadConfig("testdata/resolv.conf")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(dnsConfig.servers) != 1 {
+ t.Errorf("len(dnsConfig.servers) = %d; want %d", len(dnsConfig.servers), 1)
+ }
+ if dnsConfig.servers[0] != "[192.168.1.1]" {
+ t.Errorf("dnsConfig.servers[0] = %s; want %s", dnsConfig.servers[0], "[192.168.1.1]")
+ }
+
+ if len(dnsConfig.search) != 1 {
+ t.Errorf("len(dnsConfig.search) = %d; want %d", len(dnsConfig.search), 1)
+ }
+ if dnsConfig.search[0] != "Home" {
+ t.Errorf("dnsConfig.search[0] = %s; want %s", dnsConfig.search[0], "Home")
+ }
+
+ if dnsConfig.ndots != 5 {
+ t.Errorf("dnsConfig.ndots = %d; want %d", dnsConfig.ndots, 5)
+ }
+
+ if dnsConfig.timeout != 10 {
+ t.Errorf("dnsConfig.timeout = %d; want %d", dnsConfig.timeout, 10)
+ }
+
+ if dnsConfig.attempts != 3 {
+ t.Errorf("dnsConfig.attempts = %d; want %d", dnsConfig.attempts, 3)
+ }
+
+ if dnsConfig.rotate != true {
+ t.Errorf("dnsConfig.rotate = %t; want %t", dnsConfig.rotate, true)
+ }
+}
diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go
index 630fc5e..64d56c7 100644
--- a/libgo/go/net/fd_windows.go
+++ b/libgo/go/net/fd_windows.go
@@ -513,7 +513,12 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
})
}
-func (fd *netFD) acceptOne(toAddr func(syscall.Sockaddr) Addr, rawsa []syscall.RawSockaddrAny, o *operation) (*netFD, error) {
+func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
+ if err := fd.readLock(); err != nil {
+ return nil, err
+ }
+ defer fd.readUnlock()
+
// Get new socket.
s, err := sysSocket(fd.family, fd.sotype, 0)
if err != nil {
@@ -532,7 +537,9 @@ func (fd *netFD) acceptOne(toAddr func(syscall.Sockaddr) Addr, rawsa []syscall.R
}
// Submit accept request.
+ o := &fd.rop
o.handle = s
+ var rawsa [2]syscall.RawSockaddrAny
o.rsan = int32(unsafe.Sizeof(rawsa[0]))
_, err = rsrv.ExecIO(o, "AcceptEx", func(o *operation) error {
return syscall.AcceptEx(o.fd.sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o)
@@ -549,45 +556,6 @@ func (fd *netFD) acceptOne(toAddr func(syscall.Sockaddr) Addr, rawsa []syscall.R
return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err}
}
- return netfd, nil
-}
-
-func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
- if err := fd.readLock(); err != nil {
- return nil, err
- }
- defer fd.readUnlock()
-
- o := &fd.rop
- var netfd *netFD
- var err error
- var rawsa [2]syscall.RawSockaddrAny
- for {
- netfd, err = fd.acceptOne(toAddr, rawsa[:], o)
- if err == nil {
- break
- }
- // Sometimes we see WSAECONNRESET and ERROR_NETNAME_DELETED is
- // returned here. These happen if connection reset is received
- // before AcceptEx could complete. These errors relate to new
- // connection, not to AcceptEx, so ignore broken connection and
- // try AcceptEx again for more connections.
- operr, ok := err.(*OpError)
- if !ok {
- return nil, err
- }
- errno, ok := operr.Err.(syscall.Errno)
- if !ok {
- return nil, err
- }
- switch errno {
- case syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET:
- // ignore these and try again
- default:
- return nil, err
- }
- }
-
// Get local and peer addr out of AcceptEx buffer.
var lrsa, rrsa *syscall.RawSockaddrAny
var llen, rlen int32
diff --git a/libgo/go/net/hosts_test.go b/libgo/go/net/hosts_test.go
index b07ed0b..2fe358e 100644
--- a/libgo/go/net/hosts_test.go
+++ b/libgo/go/net/hosts_test.go
@@ -41,7 +41,7 @@ func TestLookupStaticHost(t *testing.T) {
if len(ips) != len(tt.ips) {
t.Errorf("# of hosts = %v; want %v",
len(ips), len(tt.ips))
- return
+ continue
}
for k, v := range ips {
if tt.ips[k].String() != v {
diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go
index 997d041..e5ad39c 100644
--- a/libgo/go/net/http/client_test.go
+++ b/libgo/go/net/http/client_test.go
@@ -373,24 +373,6 @@ func (j *TestJar) Cookies(u *url.URL) []*Cookie {
return j.perURL[u.Host]
}
-func TestRedirectCookiesOnRequest(t *testing.T) {
- defer afterTest(t)
- var ts *httptest.Server
- ts = httptest.NewServer(echoCookiesRedirectHandler)
- defer ts.Close()
- c := &Client{}
- req, _ := NewRequest("GET", ts.URL, nil)
- req.AddCookie(expectedCookies[0])
- // TODO: Uncomment when an implementation of a RFC6265 cookie jar lands.
- _ = c
- // resp, _ := c.Do(req)
- // matchReturnedCookies(t, expectedCookies, resp.Cookies())
-
- req, _ = NewRequest("GET", ts.URL, nil)
- // resp, _ = c.Do(req)
- // matchReturnedCookies(t, expectedCookies[1:], resp.Cookies())
-}
-
func TestRedirectCookiesJar(t *testing.T) {
defer afterTest(t)
var ts *httptest.Server
@@ -410,8 +392,8 @@ func TestRedirectCookiesJar(t *testing.T) {
}
func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
- t.Logf("Received cookies: %v", given)
if len(given) != len(expected) {
+ t.Logf("Received cookies: %v", given)
t.Errorf("Expected %d cookies, got %d", len(expected), len(given))
}
for _, ec := range expected {
diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go
index 8b01c50..a175921 100644
--- a/libgo/go/net/http/cookie.go
+++ b/libgo/go/net/http/cookie.go
@@ -94,7 +94,6 @@ func readSetCookies(h Header) []*Cookie {
continue
case "domain":
c.Domain = val
- // TODO: Add domain parsing
continue
case "max-age":
secs, err := strconv.Atoi(val)
@@ -121,7 +120,6 @@ func readSetCookies(h Header) []*Cookie {
continue
case "path":
c.Path = val
- // TODO: Add path parsing
continue
}
c.Unparsed = append(c.Unparsed, parts[i])
diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go
index 11b01cc..1aa9d49 100644
--- a/libgo/go/net/http/cookie_test.go
+++ b/libgo/go/net/http/cookie_test.go
@@ -5,9 +5,13 @@
package http
import (
+ "bytes"
"encoding/json"
"fmt"
+ "log"
+ "os"
"reflect"
+ "strings"
"testing"
"time"
)
@@ -51,12 +55,20 @@ var writeSetCookiesTests = []struct {
}
func TestWriteSetCookies(t *testing.T) {
+ defer log.SetOutput(os.Stderr)
+ var logbuf bytes.Buffer
+ log.SetOutput(&logbuf)
+
for i, tt := range writeSetCookiesTests {
if g, e := tt.Cookie.String(), tt.Raw; g != e {
t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g)
continue
}
}
+
+ if got, sub := logbuf.String(), "dropping domain attribute"; !strings.Contains(got, sub) {
+ t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
+ }
}
type headerOnlyResponseWriter Header
@@ -244,6 +256,10 @@ func TestReadCookies(t *testing.T) {
}
func TestCookieSanitizeValue(t *testing.T) {
+ defer log.SetOutput(os.Stderr)
+ var logbuf bytes.Buffer
+ log.SetOutput(&logbuf)
+
tests := []struct {
in, want string
}{
@@ -257,9 +273,17 @@ func TestCookieSanitizeValue(t *testing.T) {
t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want)
}
}
+
+ if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
+ t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
+ }
}
func TestCookieSanitizePath(t *testing.T) {
+ defer log.SetOutput(os.Stderr)
+ var logbuf bytes.Buffer
+ log.SetOutput(&logbuf)
+
tests := []struct {
in, want string
}{
@@ -272,4 +296,8 @@ func TestCookieSanitizePath(t *testing.T) {
t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want)
}
}
+
+ if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
+ t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
+ }
}
diff --git a/libgo/go/net/http/cookiejar/jar.go b/libgo/go/net/http/cookiejar/jar.go
index 389ab58..82f18a1 100644
--- a/libgo/go/net/http/cookiejar/jar.go
+++ b/libgo/go/net/http/cookiejar/jar.go
@@ -34,9 +34,9 @@ import (
type PublicSuffixList interface {
// PublicSuffix returns the public suffix of domain.
//
- // TODO: specify which of the caller and callee is responsible for IP
- // addresses, for leading and trailing dots, for case sensitivity, and
- // for IDN/Punycode.
+ // Domain is a lowercase punycoded domain name (not an IP address)
+ // without leading or trailing dots. The returned value is in the
+ // same form.
PublicSuffix(domain string) string
// String returns a description of the source of this public suffix
diff --git a/libgo/go/net/http/header.go b/libgo/go/net/http/header.go
index ca1ae07..de62bef 100644
--- a/libgo/go/net/http/header.go
+++ b/libgo/go/net/http/header.go
@@ -9,6 +9,7 @@ import (
"net/textproto"
"sort"
"strings"
+ "sync"
"time"
)
@@ -114,18 +115,15 @@ func (s *headerSorter) Len() int { return len(s.kvs) }
func (s *headerSorter) Swap(i, j int) { s.kvs[i], s.kvs[j] = s.kvs[j], s.kvs[i] }
func (s *headerSorter) Less(i, j int) bool { return s.kvs[i].key < s.kvs[j].key }
-// TODO: convert this to a sync.Cache (issue 4720)
-var headerSorterCache = make(chan *headerSorter, 8)
+var headerSorterPool = sync.Pool{
+ New: func() interface{} { return new(headerSorter) },
+}
// sortedKeyValues returns h's keys sorted in the returned kvs
// slice. The headerSorter used to sort is also returned, for possible
// return to headerSorterCache.
func (h Header) sortedKeyValues(exclude map[string]bool) (kvs []keyValues, hs *headerSorter) {
- select {
- case hs = <-headerSorterCache:
- default:
- hs = new(headerSorter)
- }
+ hs = headerSorterPool.Get().(*headerSorter)
if cap(hs.kvs) < len(h) {
hs.kvs = make([]keyValues, 0, len(h))
}
@@ -159,10 +157,7 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
}
}
}
- select {
- case headerSorterCache <- sorter:
- default:
- }
+ headerSorterPool.Put(sorter)
return nil
}
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go
index 57b5d09..7a97770 100644
--- a/libgo/go/net/http/request.go
+++ b/libgo/go/net/http/request.go
@@ -20,6 +20,7 @@ import (
"net/url"
"strconv"
"strings"
+ "sync"
)
const (
@@ -494,25 +495,20 @@ func parseRequestLine(line string) (method, requestURI, proto string, ok bool) {
return line[:s1], line[s1+1 : s2], line[s2+1:], true
}
-// TODO(bradfitz): use a sync.Cache when available
-var textprotoReaderCache = make(chan *textproto.Reader, 4)
+var textprotoReaderPool sync.Pool
func newTextprotoReader(br *bufio.Reader) *textproto.Reader {
- select {
- case r := <-textprotoReaderCache:
- r.R = br
- return r
- default:
- return textproto.NewReader(br)
+ if v := textprotoReaderPool.Get(); v != nil {
+ tr := v.(*textproto.Reader)
+ tr.R = br
+ return tr
}
+ return textproto.NewReader(br)
}
func putTextprotoReader(r *textproto.Reader) {
r.R = nil
- select {
- case textprotoReaderCache <- r:
- default:
- }
+ textprotoReaderPool.Put(r)
}
// ReadRequest reads and parses a request from b.
@@ -677,6 +673,11 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
return
}
ct := r.Header.Get("Content-Type")
+ // RFC 2616, section 7.2.1 - empty type
+ // SHOULD be treated as application/octet-stream
+ if ct == "" {
+ ct = "application/octet-stream"
+ }
ct, _, err = mime.ParseMediaType(ct)
switch {
case ct == "application/x-www-form-urlencoded":
@@ -707,7 +708,7 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
// orders to call too many functions here.
// Clean this up and write more tests.
// request_test.go contains the start of this,
- // in TestRequestMultipartCallOrder.
+ // in TestParseMultipartFormOrder and others.
}
return
}
diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go
index 89303c3..0c1e16b 100644
--- a/libgo/go/net/http/request_test.go
+++ b/libgo/go/net/http/request_test.go
@@ -68,8 +68,9 @@ type parseContentTypeTest struct {
var parseContentTypeTests = []parseContentTypeTest{
{false, stringMap{"Content-Type": {"text/plain"}}},
- // Non-existent keys are not placed. The value nil is illegal.
- {true, stringMap{}},
+ // Empty content type is legal - shoult be treated as
+ // application/octet-stream (RFC 2616, section 7.2.1)
+ {false, stringMap{}},
{true, stringMap{"Content-Type": {"text/plain; boundary="}}},
{false, stringMap{"Content-Type": {"application/unknown"}}},
}
@@ -198,15 +199,39 @@ func TestEmptyMultipartRequest(t *testing.T) {
testMissingFile(t, req)
}
-func TestRequestMultipartCallOrder(t *testing.T) {
+// Test that ParseMultipartForm errors if called
+// after MultipartReader on the same request.
+func TestParseMultipartFormOrder(t *testing.T) {
req := newTestMultipartRequest(t)
- _, err := req.MultipartReader()
- if err != nil {
+ if _, err := req.MultipartReader(); err != nil {
+ t.Fatalf("MultipartReader: %v", err)
+ }
+ if err := req.ParseMultipartForm(1024); err == nil {
+ t.Fatal("expected an error from ParseMultipartForm after call to MultipartReader")
+ }
+}
+
+// Test that MultipartReader errors if called
+// after ParseMultipartForm on the same request.
+func TestMultipartReaderOrder(t *testing.T) {
+ req := newTestMultipartRequest(t)
+ if err := req.ParseMultipartForm(25); err != nil {
+ t.Fatalf("ParseMultipartForm: %v", err)
+ }
+ if _, err := req.MultipartReader(); err == nil {
+ t.Fatal("expected an error from MultipartReader after call to ParseMultipartForm")
+ }
+}
+
+// Test that FormFile errors if called after
+// MultipartReader on the same request.
+func TestFormFileOrder(t *testing.T) {
+ req := newTestMultipartRequest(t)
+ if _, err := req.MultipartReader(); err != nil {
t.Fatalf("MultipartReader: %v", err)
}
- err = req.ParseMultipartForm(1024)
- if err == nil {
- t.Errorf("expected an error from ParseMultipartForm after call to MultipartReader")
+ if _, _, err := req.FormFile(""); err == nil {
+ t.Fatal("expected an error from FormFile after call to MultipartReader")
}
}
diff --git a/libgo/go/net/http/response.go b/libgo/go/net/http/response.go
index 35d0ba3..2ec1d40 100644
--- a/libgo/go/net/http/response.go
+++ b/libgo/go/net/http/response.go
@@ -187,6 +187,7 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
// ContentLength
// Header, values for non-canonical keys will have unpredictable behavior
//
+// Body is closed after it is sent.
func (r *Response) Write(w io.Writer) error {
// Status line
diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go
index 5044306..f731721 100644
--- a/libgo/go/net/http/response_test.go
+++ b/libgo/go/net/http/response_test.go
@@ -14,6 +14,7 @@ import (
"io/ioutil"
"net/url"
"reflect"
+ "regexp"
"strings"
"testing"
)
@@ -406,8 +407,7 @@ func TestWriteResponse(t *testing.T) {
t.Errorf("#%d: %v", i, err)
continue
}
- bout := bytes.NewBuffer(nil)
- err = resp.Write(bout)
+ err = resp.Write(ioutil.Discard)
if err != nil {
t.Errorf("#%d: %v", i, err)
continue
@@ -506,6 +506,9 @@ func TestReadResponseCloseInMiddle(t *testing.T) {
rest, err := ioutil.ReadAll(bufr)
checkErr(err, "ReadAll on remainder")
if e, g := "Next Request Here", string(rest); e != g {
+ g = regexp.MustCompile(`(xx+)`).ReplaceAllStringFunc(g, func(match string) string {
+ return fmt.Sprintf("x(repeated x%d)", len(match))
+ })
fatalf("remainder = %q, expected %q", g, e)
}
}
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index 8961cf4..1dba187 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -1934,6 +1934,31 @@ func TestWriteAfterHijack(t *testing.T) {
}
}
+func TestDoubleHijack(t *testing.T) {
+ req := reqBytes("GET / HTTP/1.1\nHost: golang.org")
+ var buf bytes.Buffer
+ conn := &rwTestConn{
+ Reader: bytes.NewReader(req),
+ Writer: &buf,
+ closec: make(chan bool, 1),
+ }
+ handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
+ conn, _, err := rw.(Hijacker).Hijack()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ _, _, err = rw.(Hijacker).Hijack()
+ if err == nil {
+ t.Errorf("got err = nil; want err != nil")
+ }
+ conn.Close()
+ })
+ ln := &oneConnListener{conn: conn}
+ go Serve(ln, handler)
+ <-conn.closec
+}
+
// http://code.google.com/p/go/issues/detail?id=5955
// Note that this does not test the "request too large"
// exit path from the http server. This is intentional;
@@ -2065,6 +2090,64 @@ func TestNoContentTypeOnNotModified(t *testing.T) {
}
}
+// Issue 6995
+// A server Handler can receive a Request, and then turn around and
+// give a copy of that Request.Body out to the Transport (e.g. any
+// proxy). So then two people own that Request.Body (both the server
+// and the http client), and both think they can close it on failure.
+// Therefore, all incoming server requests Bodies need to be thread-safe.
+func TestTransportAndServerSharedBodyRace(t *testing.T) {
+ defer afterTest(t)
+
+ const bodySize = 1 << 20
+
+ unblockBackend := make(chan bool)
+ backend := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+ io.CopyN(rw, req.Body, bodySize/2)
+ <-unblockBackend
+ }))
+ defer backend.Close()
+
+ backendRespc := make(chan *Response, 1)
+ proxy := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+ if req.RequestURI == "/foo" {
+ rw.Write([]byte("bar"))
+ return
+ }
+ req2, _ := NewRequest("POST", backend.URL, req.Body)
+ req2.ContentLength = bodySize
+
+ bresp, err := DefaultClient.Do(req2)
+ if err != nil {
+ t.Errorf("Proxy outbound request: %v", err)
+ return
+ }
+ _, err = io.CopyN(ioutil.Discard, bresp.Body, bodySize/4)
+ if err != nil {
+ t.Errorf("Proxy copy error: %v", err)
+ return
+ }
+ backendRespc <- bresp // to close later
+
+ // Try to cause a race: Both the DefaultTransport and the proxy handler's Server
+ // will try to read/close req.Body (aka req2.Body)
+ DefaultTransport.(*Transport).CancelRequest(req2)
+ rw.Write([]byte("OK"))
+ }))
+ defer proxy.Close()
+
+ req, _ := NewRequest("POST", proxy.URL, io.LimitReader(neverEnding('a'), bodySize))
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Fatalf("Original request: %v", err)
+ }
+
+ // Cleanup, so we don't leak goroutines.
+ res.Body.Close()
+ close(unblockBackend)
+ (<-backendRespc).Body.Close()
+}
+
func TestResponseWriterWriteStringAllocs(t *testing.T) {
t.Skip("allocs test unreliable with gccgo")
ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -2391,3 +2474,28 @@ Host: golang.org
b.Errorf("b.N=%d but handled %d", b.N, handled)
}
}
+
+func BenchmarkServerHijack(b *testing.B) {
+ b.ReportAllocs()
+ req := reqBytes(`GET / HTTP/1.1
+Host: golang.org
+`)
+ h := HandlerFunc(func(w ResponseWriter, r *Request) {
+ conn, _, err := w.(Hijacker).Hijack()
+ if err != nil {
+ panic(err)
+ }
+ conn.Close()
+ })
+ conn := &rwTestConn{
+ Writer: ioutil.Discard,
+ closec: make(chan bool, 1),
+ }
+ ln := &oneConnListener{conn: conn}
+ for i := 0; i < b.N; i++ {
+ conn.Reader = bytes.NewReader(req)
+ ln.conn = conn
+ Serve(ln, h)
+ <-conn.closec
+ }
+}
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index 0e46863..7ebd857 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -435,56 +435,52 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
return c, nil
}
-// TODO: use a sync.Cache instead
var (
- bufioReaderCache = make(chan *bufio.Reader, 4)
- bufioWriterCache2k = make(chan *bufio.Writer, 4)
- bufioWriterCache4k = make(chan *bufio.Writer, 4)
+ bufioReaderPool sync.Pool
+ bufioWriter2kPool sync.Pool
+ bufioWriter4kPool sync.Pool
)
-func bufioWriterCache(size int) chan *bufio.Writer {
+func bufioWriterPool(size int) *sync.Pool {
switch size {
case 2 << 10:
- return bufioWriterCache2k
+ return &bufioWriter2kPool
case 4 << 10:
- return bufioWriterCache4k
+ return &bufioWriter4kPool
}
return nil
}
func newBufioReader(r io.Reader) *bufio.Reader {
- select {
- case p := <-bufioReaderCache:
- p.Reset(r)
- return p
- default:
- return bufio.NewReader(r)
+ if v := bufioReaderPool.Get(); v != nil {
+ br := v.(*bufio.Reader)
+ br.Reset(r)
+ return br
}
+ return bufio.NewReader(r)
}
func putBufioReader(br *bufio.Reader) {
br.Reset(nil)
- select {
- case bufioReaderCache <- br:
- default:
- }
+ bufioReaderPool.Put(br)
}
func newBufioWriterSize(w io.Writer, size int) *bufio.Writer {
- select {
- case p := <-bufioWriterCache(size):
- p.Reset(w)
- return p
- default:
- return bufio.NewWriterSize(w, size)
+ pool := bufioWriterPool(size)
+ if pool != nil {
+ if v := pool.Get(); v != nil {
+ bw := v.(*bufio.Writer)
+ bw.Reset(w)
+ return bw
+ }
}
+ return bufio.NewWriterSize(w, size)
}
func putBufioWriter(bw *bufio.Writer) {
bw.Reset(nil)
- select {
- case bufioWriterCache(bw.Available()) <- bw:
- default:
+ if pool := bufioWriterPool(bw.Available()); pool != nil {
+ pool.Put(bw)
}
}
@@ -1202,7 +1198,14 @@ func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
if w.wroteHeader {
w.cw.flush()
}
- return w.conn.hijack()
+ // Release the bufioWriter that writes to the chunk writer, it is not
+ // used after a connection has been hijacked.
+ rwc, buf, err = w.conn.hijack()
+ if err == nil {
+ putBufioWriter(w.w)
+ w.w = nil
+ }
+ return rwc, buf, err
}
func (w *response) CloseNotify() <-chan bool {
diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go
index bacd837..4a2bda1 100644
--- a/libgo/go/net/http/transfer.go
+++ b/libgo/go/net/http/transfer.go
@@ -14,6 +14,7 @@ import (
"net/textproto"
"strconv"
"strings"
+ "sync"
)
// transferWriter inspects the fields of a user-supplied Request or Response,
@@ -331,17 +332,17 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
if noBodyExpected(t.RequestMethod) {
t.Body = eofReader
} else {
- t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
+ t.Body = &body{src: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
}
case realLength == 0:
t.Body = eofReader
case realLength > 0:
- t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close}
+ t.Body = &body{src: io.LimitReader(r, realLength), closing: t.Close}
default:
// realLength < 0, i.e. "Content-Length" not mentioned in header
if t.Close {
// Close semantics (i.e. HTTP/1.0)
- t.Body = &body{Reader: r, closing: t.Close}
+ t.Body = &body{src: r, closing: t.Close}
} else {
// Persistent connection (i.e. HTTP/1.1)
t.Body = eofReader
@@ -514,11 +515,13 @@ func fixTrailer(header Header, te []string) (Header, error) {
// Close ensures that the body has been fully read
// and then reads the trailer if necessary.
type body struct {
- io.Reader
+ src io.Reader
hdr interface{} // non-nil (Response or Request) value means read trailer
r *bufio.Reader // underlying wire-format reader for the trailer
closing bool // is the connection to be closed after reading body?
- closed bool
+
+ mu sync.Mutex // guards closed, and calls to Read and Close
+ closed bool
}
// ErrBodyReadAfterClose is returned when reading a Request or Response
@@ -528,10 +531,17 @@ type body struct {
var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
func (b *body) Read(p []byte) (n int, err error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
if b.closed {
return 0, ErrBodyReadAfterClose
}
- n, err = b.Reader.Read(p)
+ return b.readLocked(p)
+}
+
+// Must hold b.mu.
+func (b *body) readLocked(p []byte) (n int, err error) {
+ n, err = b.src.Read(p)
if err == io.EOF {
// Chunked case. Read the trailer.
@@ -543,7 +553,7 @@ func (b *body) Read(p []byte) (n int, err error) {
} else {
// If the server declared the Content-Length, our body is a LimitedReader
// and we need to check whether this EOF arrived early.
- if lr, ok := b.Reader.(*io.LimitedReader); ok && lr.N > 0 {
+ if lr, ok := b.src.(*io.LimitedReader); ok && lr.N > 0 {
err = io.ErrUnexpectedEOF
}
}
@@ -618,6 +628,8 @@ func (b *body) readTrailer() error {
}
func (b *body) Close() error {
+ b.mu.Lock()
+ defer b.mu.Unlock()
if b.closed {
return nil
}
@@ -629,12 +641,25 @@ func (b *body) Close() error {
default:
// Fully consume the body, which will also lead to us reading
// the trailer headers after the body, if present.
- _, err = io.Copy(ioutil.Discard, b)
+ _, err = io.Copy(ioutil.Discard, bodyLocked{b})
}
b.closed = true
return err
}
+// bodyLocked is a io.Reader reading from a *body when its mutex is
+// already held.
+type bodyLocked struct {
+ b *body
+}
+
+func (bl bodyLocked) Read(p []byte) (n int, err error) {
+ if bl.b.closed {
+ return 0, ErrBodyReadAfterClose
+ }
+ return bl.b.readLocked(p)
+}
+
// parseContentLength trims whitespace from s and returns -1 if no value
// is set, or the value if it's >= 0.
func parseContentLength(cl string) (int64, error) {
diff --git a/libgo/go/net/http/transfer_test.go b/libgo/go/net/http/transfer_test.go
index 8627a37..fb5ef37 100644
--- a/libgo/go/net/http/transfer_test.go
+++ b/libgo/go/net/http/transfer_test.go
@@ -12,9 +12,9 @@ import (
func TestBodyReadBadTrailer(t *testing.T) {
b := &body{
- Reader: strings.NewReader("foobar"),
- hdr: true, // force reading the trailer
- r: bufio.NewReader(strings.NewReader("")),
+ src: strings.NewReader("foobar"),
+ hdr: true, // force reading the trailer
+ r: bufio.NewReader(strings.NewReader("")),
}
buf := make([]byte, 7)
n, err := b.Read(buf[:3])
diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go
index e4df30a..2ce2b6b 100644
--- a/libgo/go/net/http/transport_test.go
+++ b/libgo/go/net/http/transport_test.go
@@ -798,8 +798,8 @@ func TestTransportPersistConnLeak(t *testing.T) {
// We expect 0 or 1 extra goroutine, empirically. Allow up to 5.
// Previously we were leaking one per numReq.
- t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
if int(growth) > 5 {
+ t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
t.Error("too many new goroutines")
}
}
diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go
index fd6a7d4..0582009 100644
--- a/libgo/go/net/ip.go
+++ b/libgo/go/net/ip.go
@@ -623,6 +623,9 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
for k := ellipsis + n - 1; k >= ellipsis; k-- {
ip[k] = 0
}
+ } else if ellipsis >= 0 {
+ // Ellipsis must represent at least one 0 group.
+ return nil, zone
}
return ip, zone
}
diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go
index 26b5372..ffeb9d3 100644
--- a/libgo/go/net/ip_test.go
+++ b/libgo/go/net/ip_test.go
@@ -25,6 +25,7 @@ var parseIPTests = []struct {
{"fe80::1%lo0", nil},
{"fe80::1%911", nil},
{"", nil},
+ {"a1:a2:a3:a4::b1:b2:b3:b4", nil}, // Issue 6628
}
func TestParseIP(t *testing.T) {
diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go
index 7228532..a1a008a 100644
--- a/libgo/go/net/iprawsock_posix.go
+++ b/libgo/go/net/iprawsock_posix.go
@@ -19,7 +19,7 @@ import (
// that you do not uses these methods if it is important to receive a
// full packet.
//
-// The Go 1 compatibliity guidelines make it impossible for us to
+// The Go 1 compatibility guidelines make it impossible for us to
// change the behavior of these methods; use Read or ReadMsgIP
// instead.
diff --git a/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go
index f1204a9..a755ff2 100644
--- a/libgo/go/net/lookup_plan9.go
+++ b/libgo/go/net/lookup_plan9.go
@@ -69,10 +69,31 @@ func queryDNS(addr string, typ string) (res []string, err error) {
return query("/net/dns", addr+" "+typ, 1024)
}
+// toLower returns a lower-case version of in. Restricting us to
+// ASCII is sufficient to handle the IP protocol names and allow
+// us to not depend on the strings and unicode packages.
+func toLower(in string) string {
+ for _, c := range in {
+ if 'A' <= c && c <= 'Z' {
+ // Has upper case; need to fix.
+ out := []byte(in)
+ for i := 0; i < len(in); i++ {
+ c := in[i]
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ out[i] = c
+ }
+ return string(out)
+ }
+ }
+ return in
+}
+
// lookupProtocol looks up IP protocol name and returns
// the corresponding protocol number.
func lookupProtocol(name string) (proto int, err error) {
- lines, err := query("/net/cs", "!protocol="+name, 128)
+ lines, err := query("/net/cs", "!protocol="+toLower(name), 128)
if err != nil {
return 0, err
}
@@ -94,7 +115,7 @@ func lookupProtocol(name string) (proto int, err error) {
func lookupHost(host string) (addrs []string, err error) {
// Use /net/cs instead of /net/dns because cs knows about
// host names in local network (e.g. from /lib/ndb/local)
- lines, err := queryCS("tcp", host, "1")
+ lines, err := queryCS("net", host, "1")
if err != nil {
return
}
diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go
index 1320096..c9fb433 100644
--- a/libgo/go/net/net_test.go
+++ b/libgo/go/net/net_test.go
@@ -231,12 +231,12 @@ func TestErrorNil(t *testing.T) {
// Make Listen fail by relistening on the same address.
l, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- t.Fatal("Listen 127.0.0.1:0: %v", err)
+ t.Fatalf("Listen 127.0.0.1:0: %v", err)
}
defer l.Close()
l1, err := Listen("tcp", l.Addr().String())
if err == nil {
- t.Fatal("second Listen %v: %v", l.Addr(), err)
+ t.Fatalf("second Listen %v: %v", l.Addr(), err)
}
if l1 != nil {
t.Fatalf("Listen returned non-nil interface %T(%v) with err != nil", l1, l1)
@@ -245,12 +245,12 @@ func TestErrorNil(t *testing.T) {
// Make ListenPacket fail by relistening on the same address.
lp, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil {
- t.Fatal("Listen 127.0.0.1:0: %v", err)
+ t.Fatalf("Listen 127.0.0.1:0: %v", err)
}
defer lp.Close()
lp1, err := ListenPacket("udp", lp.LocalAddr().String())
if err == nil {
- t.Fatal("second Listen %v: %v", lp.LocalAddr(), err)
+ t.Fatalf("second Listen %v: %v", lp.LocalAddr(), err)
}
if lp1 != nil {
t.Fatalf("ListenPacket returned non-nil interface %T(%v) with err != nil", lp1, lp1)
diff --git a/libgo/go/net/parse.go b/libgo/go/net/parse.go
index 6056de2..ee6e7e9 100644
--- a/libgo/go/net/parse.go
+++ b/libgo/go/net/parse.go
@@ -67,7 +67,7 @@ func open(name string) (*file, error) {
if err != nil {
return nil, err
}
- return &file{fd, make([]byte, os.Getpagesize())[0:0], false}, nil
+ return &file{fd, make([]byte, 0, os.Getpagesize()), false}, nil
}
func byteIndex(s string, c byte) int {
diff --git a/libgo/go/net/testdata/resolv.conf b/libgo/go/net/testdata/resolv.conf
new file mode 100644
index 0000000..b5972e0
--- /dev/null
+++ b/libgo/go/net/testdata/resolv.conf
@@ -0,0 +1,5 @@
+# /etc/resolv.conf
+
+domain Home
+nameserver 192.168.1.1
+options ndots:5 timeout:10 attempts:3 rotate
diff --git a/libgo/go/net/z_last_test.go b/libgo/go/net/z_last_test.go
new file mode 100644
index 0000000..bb00f11
--- /dev/null
+++ b/libgo/go/net/z_last_test.go
@@ -0,0 +1,34 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestDNSThreadLimit(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ const N = 10000
+ c := make(chan int, N)
+ for i := 0; i < N; i++ {
+ go func(i int) {
+ LookupIP(fmt.Sprintf("%d.net-test.golang.org", i))
+ c <- 1
+ }(i)
+ }
+ // Don't bother waiting for the stragglers; stop at 0.9 N.
+ for i := 0; i < N*9/10; i++ {
+ if i%100 == 0 {
+ //println("TestDNSThreadLimit:", i)
+ }
+ <-c
+ }
+
+ // If we're still here, it worked.
+}