summaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/cgo_android.go14
-rw-r--r--libgo/go/net/cgo_linux.go2
-rw-r--r--libgo/go/net/conn_test.go2
-rw-r--r--libgo/go/net/dial.go17
-rw-r--r--libgo/go/net/dial_test.go16
-rw-r--r--libgo/go/net/dnsclient.go4
-rw-r--r--libgo/go/net/dnsclient_unix.go293
-rw-r--r--libgo/go/net/dnsclient_unix_test.go109
-rw-r--r--libgo/go/net/dnsconfig_unix.go39
-rw-r--r--libgo/go/net/dnsconfig_unix_test.go91
-rw-r--r--libgo/go/net/fd_unix.go4
-rw-r--r--libgo/go/net/fd_windows.go20
-rw-r--r--libgo/go/net/file_stub.go38
-rw-r--r--libgo/go/net/file_test.go2
-rw-r--r--libgo/go/net/file_unix.go2
-rw-r--r--libgo/go/net/hosts.go2
-rw-r--r--libgo/go/net/http/client.go28
-rw-r--r--libgo/go/net/http/client_test.go37
-rw-r--r--libgo/go/net/http/cookie.go10
-rw-r--r--libgo/go/net/http/cookie_test.go32
-rw-r--r--libgo/go/net/http/cookiejar/jar.go2
-rw-r--r--libgo/go/net/http/export_test.go36
-rw-r--r--libgo/go/net/http/fs.go43
-rw-r--r--libgo/go/net/http/fs_test.go59
-rw-r--r--libgo/go/net/http/httptest/server.go2
-rw-r--r--libgo/go/net/http/httptest/server_test.go24
-rw-r--r--libgo/go/net/http/httputil/chunked.go203
-rw-r--r--libgo/go/net/http/httputil/chunked_test.go159
-rw-r--r--libgo/go/net/http/httputil/dump.go12
-rw-r--r--libgo/go/net/http/httputil/dump_test.go30
-rw-r--r--libgo/go/net/http/httputil/httputil.go13
-rw-r--r--libgo/go/net/http/httputil/reverseproxy.go16
-rw-r--r--libgo/go/net/http/internal/chunked.go (renamed from libgo/go/net/http/chunked.go)19
-rw-r--r--libgo/go/net/http/internal/chunked_test.go (renamed from libgo/go/net/http/chunked_test.go)23
-rw-r--r--libgo/go/net/http/main_test.go (renamed from libgo/go/net/http/z_last_test.go)30
-rw-r--r--libgo/go/net/http/pprof/pprof.go4
-rw-r--r--libgo/go/net/http/readrequest_test.go41
-rw-r--r--libgo/go/net/http/request.go60
-rw-r--r--libgo/go/net/http/request_test.go70
-rw-r--r--libgo/go/net/http/requestwrite_test.go62
-rw-r--r--libgo/go/net/http/response_test.go31
-rw-r--r--libgo/go/net/http/serve_test.go270
-rw-r--r--libgo/go/net/http/server.go78
-rw-r--r--libgo/go/net/http/transfer.go19
-rw-r--r--libgo/go/net/http/transport.go139
-rw-r--r--libgo/go/net/http/transport_test.go187
-rw-r--r--libgo/go/net/ip.go38
-rw-r--r--libgo/go/net/ip_test.go26
-rw-r--r--libgo/go/net/ipraw_test.go5
-rw-r--r--libgo/go/net/iprawsock_posix.go4
-rw-r--r--libgo/go/net/ipsock_posix.go4
-rw-r--r--libgo/go/net/lookup.go51
-rw-r--r--libgo/go/net/lookup_stub.go49
-rw-r--r--libgo/go/net/lookup_test.go164
-rw-r--r--libgo/go/net/lookup_unix.go2
-rw-r--r--libgo/go/net/lookup_windows.go79
-rw-r--r--libgo/go/net/mail/message.go13
-rw-r--r--libgo/go/net/mail/message_test.go10
-rw-r--r--libgo/go/net/multicast_test.go2
-rw-r--r--libgo/go/net/net.go1
-rw-r--r--libgo/go/net/parse.go22
-rw-r--r--libgo/go/net/parse_test.go4
-rw-r--r--libgo/go/net/port_test.go6
-rw-r--r--libgo/go/net/port_unix.go2
-rw-r--r--libgo/go/net/rpc/client.go12
-rw-r--r--libgo/go/net/rpc/client_test.go55
-rw-r--r--libgo/go/net/rpc/debug.go2
-rw-r--r--libgo/go/net/rpc/server.go25
-rw-r--r--libgo/go/net/singleflight.go66
-rw-r--r--libgo/go/net/smtp/smtp_test.go2
-rw-r--r--libgo/go/net/sock_bsd.go2
-rw-r--r--libgo/go/net/sock_posix.go46
-rw-r--r--libgo/go/net/sock_stub.go (renamed from libgo/go/net/sock_solaris.go)2
-rw-r--r--libgo/go/net/sockopt_bsd.go4
-rw-r--r--libgo/go/net/sockopt_posix.go2
-rw-r--r--libgo/go/net/sockopt_stub.go37
-rw-r--r--libgo/go/net/sockoptip_bsd.go2
-rw-r--r--libgo/go/net/sockoptip_posix.go2
-rw-r--r--libgo/go/net/sockoptip_stub.go14
-rw-r--r--libgo/go/net/tcpsock_posix.go8
-rw-r--r--libgo/go/net/tcpsockopt_darwin.go12
-rw-r--r--libgo/go/net/tcpsockopt_dragonfly.go13
-rw-r--r--libgo/go/net/tcpsockopt_openbsd.go17
-rw-r--r--libgo/go/net/tcpsockopt_posix.go2
-rw-r--r--libgo/go/net/tcpsockopt_solaris.go27
-rw-r--r--libgo/go/net/tcpsockopt_stub.go20
-rw-r--r--libgo/go/net/tcpsockopt_unix.go10
-rw-r--r--libgo/go/net/tcpsockopt_windows.go12
-rw-r--r--libgo/go/net/testdata/domain-resolv.conf5
-rw-r--r--libgo/go/net/testdata/empty-resolv.conf1
-rw-r--r--libgo/go/net/testdata/resolv.conf6
-rw-r--r--libgo/go/net/testdata/search-resolv.conf5
-rw-r--r--libgo/go/net/udp_test.go41
-rw-r--r--libgo/go/net/udpsock_posix.go6
-rw-r--r--libgo/go/net/unicast_posix_test.go3
-rw-r--r--libgo/go/net/unix_test.go15
-rw-r--r--libgo/go/net/unixsock_posix.go15
-rw-r--r--libgo/go/net/url/url.go29
-rw-r--r--libgo/go/net/url/url_test.go56
-rw-r--r--libgo/go/net/z_last_test.go62
100 files changed, 2499 insertions, 1015 deletions
diff --git a/libgo/go/net/cgo_android.go b/libgo/go/net/cgo_android.go
new file mode 100644
index 00000000000..3819ce56a4f
--- /dev/null
+++ b/libgo/go/net/cgo_android.go
@@ -0,0 +1,14 @@
+// Copyright 2014 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
+
+package net
+
+//#include <netdb.h>
+import "C"
+
+func cgoAddrInfoFlags() C.int {
+ return C.AI_CANONNAME
+}
diff --git a/libgo/go/net/cgo_linux.go b/libgo/go/net/cgo_linux.go
index 77522f9141b..0e332261acc 100644
--- a/libgo/go/net/cgo_linux.go
+++ b/libgo/go/net/cgo_linux.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 cgo,!netgo
+// +build !android,cgo,!netgo
package net
diff --git a/libgo/go/net/conn_test.go b/libgo/go/net/conn_test.go
index 37bb4e2c071..9c9d1a8057d 100644
--- a/libgo/go/net/conn_test.go
+++ b/libgo/go/net/conn_test.go
@@ -38,7 +38,7 @@ func TestConnAndListener(t *testing.T) {
}
case "unixpacket":
switch runtime.GOOS {
- case "darwin", "nacl", "openbsd", "plan9", "windows":
+ case "android", "darwin", "nacl", "openbsd", "plan9", "windows":
continue
case "freebsd": // FreeBSD 8 doesn't support unixpacket
continue
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go
index 93569c253cd..e6f0436cdd3 100644
--- a/libgo/go/net/dial.go
+++ b/libgo/go/net/dial.go
@@ -118,9 +118,8 @@ func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
// "unixpacket".
//
// For TCP and UDP networks, addresses have the form host:port.
-// If host is a literal IPv6 address or host name, it must be enclosed
-// in square brackets as in "[::1]:80", "[ipv6-host]:http" or
-// "[ipv6-host%zone]:80".
+// If host is a literal IPv6 address it must be enclosed
+// in square brackets as in "[::1]:80" or "[ipv6-host%zone]:80".
// The functions JoinHostPort and SplitHostPort manipulate addresses
// in this form.
//
@@ -214,14 +213,12 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
nracers := len(ras)
for nracers > 0 {
sig <- true
- select {
- case racer := <-lane:
- if racer.error == nil {
- return racer.Conn, nil
- }
- lastErr = racer.error
- nracers--
+ racer := <-lane
+ if racer.error == nil {
+ return racer.Conn, nil
}
+ lastErr = racer.error
+ nracers--
}
return nil, lastErr
}
diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go
index f9260fd281b..42898d669f7 100644
--- a/libgo/go/net/dial_test.go
+++ b/libgo/go/net/dial_test.go
@@ -119,6 +119,7 @@ func TestSelfConnect(t *testing.T) {
// TODO(brainman): do not know why it hangs.
t.Skip("skipping known-broken test on windows")
}
+
// Test that Dial does not honor self-connects.
// See the comment in DialTCP.
@@ -149,8 +150,12 @@ func TestSelfConnect(t *testing.T) {
for i := 0; i < n; i++ {
c, err := DialTimeout("tcp", addr, time.Millisecond)
if err == nil {
+ if c.LocalAddr().String() == addr {
+ t.Errorf("#%d: Dial %q self-connect", i, addr)
+ } else {
+ t.Logf("#%d: Dial %q succeeded - possibly racing with other listener", i, addr)
+ }
c.Close()
- t.Errorf("#%d: Dial %q succeeded", i, addr)
}
}
}
@@ -334,6 +339,8 @@ func numTCP() (ntcp, nopen, nclose int, err error) {
}
func TestDialMultiFDLeak(t *testing.T) {
+ t.Skip("flaky test - golang.org/issue/8764")
+
if !supportsIPv4 || !supportsIPv6 {
t.Skip("neither ipv4 nor ipv6 is supported")
}
@@ -460,6 +467,11 @@ func TestDialer(t *testing.T) {
}
func TestDialDualStackLocalhost(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
if ips, err := LookupIP("localhost"); err != nil {
t.Fatalf("LookupIP failed: %v", err)
} else if len(ips) < 2 || !supportsIPv4 || !supportsIPv6 {
@@ -488,7 +500,7 @@ func TestDialDualStackLocalhost(t *testing.T) {
}
d := &Dialer{DualStack: true}
- for _ = range dss.lns {
+ for range dss.lns {
if c, err := d.Dial("tcp", "localhost:"+dss.port); err != nil {
t.Errorf("Dial failed: %v", err)
} else {
diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go
index 9bffa11f916..e8014e4ffc9 100644
--- a/libgo/go/net/dnsclient.go
+++ b/libgo/go/net/dnsclient.go
@@ -196,9 +196,7 @@ func (addrs byPriorityWeight) shuffleByWeight() {
s += int(addrs[i].Weight)
if s > n {
if i > 0 {
- t := addrs[i]
- copy(addrs[1:i+1], addrs[0:i])
- addrs[0] = t
+ addrs[0], addrs[i] = addrs[i], addrs[0]
}
break
}
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go
index 3713efd0e3c..7511083f795 100644
--- a/libgo/go/net/dnsclient_unix.go
+++ b/libgo/go/net/dnsclient_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 dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
// DNS client: see RFC 1035.
// Has to be linked into package net for Dial.
@@ -16,6 +16,7 @@
package net
import (
+ "errors"
"io"
"math/rand"
"os"
@@ -23,118 +24,174 @@ import (
"time"
)
-// Send a request on the connection and hope for a reply.
-// Up to cfg.attempts attempts.
-func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) {
- _, useTCP := c.(*TCPConn)
- if len(name) >= 256 {
- return nil, &DNSError{Err: "name too long", Name: name}
+// A dnsConn represents a DNS transport endpoint.
+type dnsConn interface {
+ Conn
+
+ // readDNSResponse reads a DNS response message from the DNS
+ // transport endpoint and returns the received DNS response
+ // message.
+ readDNSResponse() (*dnsMsg, error)
+
+ // writeDNSQuery writes a DNS query message to the DNS
+ // connection endpoint.
+ writeDNSQuery(*dnsMsg) error
+}
+
+func (c *UDPConn) readDNSResponse() (*dnsMsg, error) {
+ b := make([]byte, 512) // see RFC 1035
+ n, err := c.Read(b)
+ if err != nil {
+ return nil, err
}
- out := new(dnsMsg)
- out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
- out.question = []dnsQuestion{
- {name, qtype, dnsClassINET},
+ msg := &dnsMsg{}
+ if !msg.Unpack(b[:n]) {
+ return nil, errors.New("cannot unmarshal DNS message")
}
- out.recursion_desired = true
- msg, ok := out.Pack()
+ return msg, nil
+}
+
+func (c *UDPConn) writeDNSQuery(msg *dnsMsg) error {
+ b, ok := msg.Pack()
if !ok {
- return nil, &DNSError{Err: "internal error - cannot pack message", Name: name}
+ return errors.New("cannot marshal DNS message")
}
- if useTCP {
- mlen := uint16(len(msg))
- msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...)
+ if _, err := c.Write(b); err != nil {
+ return err
}
- for attempt := 0; attempt < cfg.attempts; attempt++ {
- n, err := c.Write(msg)
+ return nil
+}
+
+func (c *TCPConn) readDNSResponse() (*dnsMsg, error) {
+ b := make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
+ if _, err := io.ReadFull(c, b[:2]); err != nil {
+ return nil, err
+ }
+ l := int(b[0])<<8 | int(b[1])
+ if l > len(b) {
+ b = make([]byte, l)
+ }
+ n, err := io.ReadFull(c, b[:l])
+ if err != nil {
+ return nil, err
+ }
+ msg := &dnsMsg{}
+ if !msg.Unpack(b[:n]) {
+ return nil, errors.New("cannot unmarshal DNS message")
+ }
+ return msg, nil
+}
+
+func (c *TCPConn) writeDNSQuery(msg *dnsMsg) error {
+ b, ok := msg.Pack()
+ if !ok {
+ return errors.New("cannot marshal DNS message")
+ }
+ l := uint16(len(b))
+ b = append([]byte{byte(l >> 8), byte(l)}, b...)
+ if _, err := c.Write(b); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *Dialer) dialDNS(network, server string) (dnsConn, error) {
+ switch network {
+ case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
+ default:
+ return nil, UnknownNetworkError(network)
+ }
+ // Calling Dial here is scary -- we have to be sure not to
+ // dial a name that will require a DNS lookup, or Dial will
+ // call back here to translate it. The DNS config parser has
+ // already checked that all the cfg.servers[i] are IP
+ // addresses, which Dial will use without a DNS lookup.
+ c, err := d.Dial(network, server)
+ if err != nil {
+ return nil, err
+ }
+ switch network {
+ case "tcp", "tcp4", "tcp6":
+ return c.(*TCPConn), nil
+ case "udp", "udp4", "udp6":
+ return c.(*UDPConn), nil
+ }
+ panic("unreachable")
+}
+
+// exchange sends a query on the connection and hopes for a response.
+func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
+ d := Dialer{Timeout: timeout}
+ out := dnsMsg{
+ dnsMsgHdr: dnsMsgHdr{
+ recursion_desired: true,
+ },
+ question: []dnsQuestion{
+ {name, qtype, dnsClassINET},
+ },
+ }
+ for _, network := range []string{"udp", "tcp"} {
+ c, err := d.dialDNS(network, server)
if err != nil {
return nil, err
}
-
- if cfg.timeout == 0 {
- c.SetReadDeadline(noDeadline)
- } else {
- c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
+ defer c.Close()
+ if timeout > 0 {
+ c.SetDeadline(time.Now().Add(timeout))
}
- buf := make([]byte, 2000)
- if useTCP {
- n, err = io.ReadFull(c, buf[:2])
- if err != nil {
- if e, ok := err.(Error); ok && e.Timeout() {
- continue
- }
- }
- mlen := int(buf[0])<<8 | int(buf[1])
- if mlen > len(buf) {
- buf = make([]byte, mlen)
- }
- n, err = io.ReadFull(c, buf[:mlen])
- } else {
- n, err = c.Read(buf)
+ out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
+ if err := c.writeDNSQuery(&out); err != nil {
+ return nil, err
}
+ in, err := c.readDNSResponse()
if err != nil {
- if e, ok := err.(Error); ok && e.Timeout() {
- continue
- }
return nil, err
}
- buf = buf[:n]
- in := new(dnsMsg)
- if !in.Unpack(buf) || in.id != out.id {
+ if in.id != out.id {
+ return nil, errors.New("DNS message ID mismatch")
+ }
+ if in.truncated { // see RFC 5966
continue
}
return in, nil
}
- var server string
- if a := c.RemoteAddr(); a != nil {
- server = a.String()
- }
- return nil, &DNSError{Err: "no answer from server", Name: name, Server: server, IsTimeout: true}
+ return nil, errors.New("no answer from DNS server")
}
// Do a lookup for a single name, which must be rooted
// (otherwise answer will not find the answers).
-func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
+func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) {
if len(cfg.servers) == 0 {
return "", nil, &DNSError{Err: "no DNS servers", Name: name}
}
- for i := 0; i < len(cfg.servers); i++ {
- // Calling Dial here is scary -- we have to be sure
- // not to dial a name that will require a DNS lookup,
- // or Dial will call back here to translate it.
- // The DNS config parser has already checked that
- // all the cfg.servers[i] are IP addresses, which
- // Dial will use without a DNS lookup.
- server := cfg.servers[i] + ":53"
- c, cerr := Dial("udp", server)
- if cerr != nil {
- err = cerr
- continue
- }
- msg, merr := exchange(cfg, c, name, qtype)
- c.Close()
- if merr != nil {
- err = merr
- continue
- }
- if msg.truncated { // see RFC 5966
- c, cerr = Dial("tcp", server)
- if cerr != nil {
- err = cerr
+ if len(name) >= 256 {
+ return "", nil, &DNSError{Err: "DNS name too long", Name: name}
+ }
+ timeout := time.Duration(cfg.timeout) * time.Second
+ var lastErr error
+ for i := 0; i < cfg.attempts; i++ {
+ for _, server := range cfg.servers {
+ server = JoinHostPort(server, "53")
+ msg, err := exchange(server, name, qtype, timeout)
+ if err != nil {
+ lastErr = &DNSError{
+ Err: err.Error(),
+ Name: name,
+ Server: server,
+ }
+ if nerr, ok := err.(Error); ok && nerr.Timeout() {
+ lastErr.(*DNSError).IsTimeout = true
+ }
continue
}
- msg, merr = exchange(cfg, c, name, qtype)
- c.Close()
- if merr != nil {
- err = merr
- continue
+ cname, addrs, err := answer(name, server, msg, qtype)
+ if err == nil || err.(*DNSError).Err == noSuchHost {
+ return cname, addrs, err
}
- }
- cname, addrs, err = answer(name, server, msg, qtype)
- if err == nil || err.(*DNSError).Err == noSuchHost {
- break
+ lastErr = err
}
}
- return
+ return "", nil, lastErr
}
func convertRR_A(records []dnsRR) []IP {
@@ -240,13 +297,10 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
}
// Can try as ordinary name.
cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
- if err == nil {
+ if rooted || err == nil {
return
}
}
- if rooted {
- return
- }
// Otherwise, try suffixes.
for i := 0; i < len(cfg.dnsConfig.search); i++ {
@@ -260,15 +314,15 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
}
}
- // Last ditch effort: try unsuffixed.
- rname := name
- if !rooted {
- rname += "."
- }
- cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
- if err == nil {
- return
+ // Last ditch effort: try unsuffixed only if we haven't already,
+ // that is, name is not rooted and has less than ndots dots.
+ if count(name, '.') < cfg.dnsConfig.ndots {
+ cname, addrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
+ if err == nil {
+ return
+ }
}
+
if e, ok := err.(*DNSError); ok {
// Show original name passed to lookup, not suffixed one.
// In general we might have tried many suffixes; showing
@@ -320,31 +374,36 @@ func goLookupIP(name string) (addrs []IP, err error) {
return
}
}
- var records []dnsRR
- var cname string
- var err4, err6 error
- cname, records, err4 = lookup(name, dnsTypeA)
- addrs = convertRR_A(records)
- if cname != "" {
- name = cname
- }
- _, records, err6 = lookup(name, dnsTypeAAAA)
- if err4 != nil && err6 == nil {
- // Ignore A error because AAAA lookup succeeded.
- err4 = nil
+ type racer struct {
+ qtype uint16
+ rrs []dnsRR
+ error
}
- if err6 != nil && len(addrs) > 0 {
- // Ignore AAAA error because A lookup succeeded.
- err6 = nil
+ lane := make(chan racer, 1)
+ qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA}
+ for _, qtype := range qtypes {
+ go func(qtype uint16) {
+ _, rrs, err := lookup(name, qtype)
+ lane <- racer{qtype, rrs, err}
+ }(qtype)
}
- if err4 != nil {
- return nil, err4
+ var lastErr error
+ for range qtypes {
+ racer := <-lane
+ if racer.error != nil {
+ lastErr = racer.error
+ continue
+ }
+ switch racer.qtype {
+ case dnsTypeA:
+ addrs = append(addrs, convertRR_A(racer.rrs)...)
+ case dnsTypeAAAA:
+ addrs = append(addrs, convertRR_AAAA(racer.rrs)...)
+ }
}
- if err6 != nil {
- return nil, err6
+ if len(addrs) == 0 && lastErr != nil {
+ return nil, lastErr
}
-
- addrs = append(addrs, convertRR_AAAA(records)...)
return addrs, nil
}
diff --git a/libgo/go/net/dnsclient_unix_test.go b/libgo/go/net/dnsclient_unix_test.go
index 2350142d610..1167c26b39d 100644
--- a/libgo/go/net/dnsclient_unix_test.go
+++ b/libgo/go/net/dnsclient_unix_test.go
@@ -16,19 +16,79 @@ import (
"time"
)
-func TestTCPLookup(t *testing.T) {
+var dnsTransportFallbackTests = []struct {
+ server string
+ name string
+ qtype uint16
+ timeout int
+ rcode int
+}{
+ // Querying "com." with qtype=255 usually makes an answer
+ // which requires more than 512 bytes.
+ {"8.8.8.8:53", "com.", dnsTypeALL, 2, dnsRcodeSuccess},
+ {"8.8.4.4:53", "com.", dnsTypeALL, 4, dnsRcodeSuccess},
+}
+
+func TestDNSTransportFallback(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- c, err := Dial("tcp", "8.8.8.8:53")
- if err != nil {
- t.Fatalf("Dial failed: %v", err)
+
+ for _, tt := range dnsTransportFallbackTests {
+ timeout := time.Duration(tt.timeout) * time.Second
+ msg, err := exchange(tt.server, tt.name, tt.qtype, timeout)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ switch msg.rcode {
+ case tt.rcode, dnsRcodeServerFailure:
+ default:
+ t.Errorf("got %v from %v; want %v", msg.rcode, tt.server, tt.rcode)
+ continue
+ }
}
- defer c.Close()
- cfg := &dnsConfig{timeout: 10, attempts: 3}
- _, err = exchange(cfg, c, "com.", dnsTypeALL)
- if err != nil {
- t.Fatalf("exchange failed: %v", err)
+}
+
+// See RFC 6761 for further information about the reserved, pseudo
+// domain names.
+var specialDomainNameTests = []struct {
+ name string
+ qtype uint16
+ rcode int
+}{
+ // Name resoltion APIs and libraries should not recongnize the
+ // followings as special.
+ {"1.0.168.192.in-addr.arpa.", dnsTypePTR, dnsRcodeNameError},
+ {"test.", dnsTypeALL, dnsRcodeNameError},
+ {"example.com.", dnsTypeALL, dnsRcodeSuccess},
+
+ // Name resoltion APIs and libraries should recongnize the
+ // followings as special and should not send any queries.
+ // Though, we test those names here for verifying nagative
+ // answers at DNS query-response interaction level.
+ {"localhost.", dnsTypeALL, dnsRcodeNameError},
+ {"invalid.", dnsTypeALL, dnsRcodeNameError},
+}
+
+func TestSpecialDomainName(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ server := "8.8.8.8:53"
+ for _, tt := range specialDomainNameTests {
+ msg, err := exchange(server, tt.name, tt.qtype, 0)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ switch msg.rcode {
+ case tt.rcode, dnsRcodeServerFailure:
+ default:
+ t.Errorf("got %v from %v; want %v", msg.rcode, server, tt.rcode)
+ continue
+ }
}
}
@@ -144,7 +204,7 @@ func TestReloadResolvConfChange(t *testing.T) {
if _, err := goLookupIP("golang.org"); err != nil {
t.Fatalf("goLookupIP(good) failed: %v", err)
}
- r.WantServers([]string{"[8.8.8.8]"})
+ r.WantServers([]string{"8.8.8.8"})
// Using a bad resolv.conf when we had a good one
// before should not update the config
@@ -155,5 +215,32 @@ func TestReloadResolvConfChange(t *testing.T) {
// A new good config should get picked up
r.SetConf("nameserver 8.8.4.4")
- r.WantServers([]string{"[8.8.4.4]"})
+ r.WantServers([]string{"8.8.4.4"})
+}
+
+func BenchmarkGoLookupIP(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ goLookupIP("www.example.com")
+ }
+}
+
+func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ goLookupIP("some.nonexistent")
+ }
+}
+
+func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
+ onceLoadConfig.Do(loadDefaultConfig)
+ if cfg.dnserr != nil || cfg.dnsConfig == nil {
+ b.Fatalf("loadConfig failed: %v", cfg.dnserr)
+ }
+ // This looks ugly but it's safe as long as benchmarks are run
+ // sequentially in package testing.
+ orig := cfg.dnsConfig
+ cfg.dnsConfig.servers = append([]string{"203.0.113.254"}, cfg.dnsConfig.servers...) // use TEST-NET-3 block, see RFC 5737
+ for i := 0; i < b.N; i++ {
+ goLookupIP("www.example.com")
+ }
+ cfg.dnsConfig = orig
}
diff --git a/libgo/go/net/dnsconfig_unix.go b/libgo/go/net/dnsconfig_unix.go
index db45716f124..66ab7c4dd30 100644
--- a/libgo/go/net/dnsconfig_unix.go
+++ b/libgo/go/net/dnsconfig_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 dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
// Read system DNS config from /etc/resolv.conf
@@ -25,13 +25,12 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
if err != nil {
return nil, &DNSConfigError{err}
}
- conf := new(dnsConfig)
- conf.servers = make([]string, 0, 3) // small, but the standard limit
- conf.search = make([]string, 0)
- conf.ndots = 1
- conf.timeout = 5
- conf.attempts = 2
- conf.rotate = false
+ defer file.close()
+ conf := &dnsConfig{
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ }
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
f := getFields(line)
if len(f) < 1 {
@@ -39,30 +38,20 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
}
switch f[0] {
case "nameserver": // add one name server
- a := conf.servers
- n := len(a)
- if len(f) > 1 && n < cap(a) {
+ if len(f) > 1 && len(conf.servers) < 3 { // small, but the standard limit
// One more check: make sure server name is
// just an IP address. Otherwise we need DNS
// to look it up.
- name := f[1]
- switch len(ParseIP(name)) {
- case 16:
- name = "[" + name + "]"
- fallthrough
- case 4:
- a = a[0 : n+1]
- a[n] = name
- conf.servers = a
+ if parseIPv4(f[1]) != nil {
+ conf.servers = append(conf.servers, f[1])
+ } else if ip, _ := parseIPv6(f[1], true); ip != nil {
+ conf.servers = append(conf.servers, f[1])
}
}
case "domain": // set search path to just this domain
if len(f) > 1 {
- conf.search = make([]string, 1)
- conf.search[0] = f[1]
- } else {
- conf.search = make([]string, 0)
+ conf.search = []string{f[1]}
}
case "search": // set search path to given servers
@@ -99,8 +88,6 @@ func dnsReadConfig(filename string) (*dnsConfig, error) {
}
}
}
- file.close()
-
return conf, nil
}
diff --git a/libgo/go/net/dnsconfig_unix_test.go b/libgo/go/net/dnsconfig_unix_test.go
index 37ed4931dbe..94fb0c32e24 100644
--- a/libgo/go/net/dnsconfig_unix_test.go
+++ b/libgo/go/net/dnsconfig_unix_test.go
@@ -6,41 +6,64 @@
package net
-import "testing"
+import (
+ "reflect"
+ "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)
- }
+var dnsReadConfigTests = []struct {
+ name string
+ conf dnsConfig
+}{
+ {
+ name: "testdata/resolv.conf",
+ conf: dnsConfig{
+ servers: []string{"8.8.8.8", "2001:4860:4860::8888", "fe80::1%lo0"},
+ search: []string{"localdomain"},
+ ndots: 5,
+ timeout: 10,
+ attempts: 3,
+ rotate: true,
+ },
+ },
+ {
+ name: "testdata/domain-resolv.conf",
+ conf: dnsConfig{
+ servers: []string{"8.8.8.8"},
+ search: []string{"localdomain"},
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ },
+ },
+ {
+ name: "testdata/search-resolv.conf",
+ conf: dnsConfig{
+ servers: []string{"8.8.8.8"},
+ search: []string{"test", "invalid"},
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ },
+ },
+ {
+ name: "testdata/empty-resolv.conf",
+ conf: dnsConfig{
+ ndots: 1,
+ timeout: 5,
+ attempts: 2,
+ },
+ },
+}
- if dnsConfig.rotate != true {
- t.Errorf("dnsConfig.rotate = %t; want %t", dnsConfig.rotate, true)
+func TestDNSReadConfig(t *testing.T) {
+ for _, tt := range dnsReadConfigTests {
+ conf, err := dnsReadConfig(tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(conf, &tt.conf) {
+ t.Errorf("got %v; want %v", conf, &tt.conf)
+ }
}
}
diff --git a/libgo/go/net/fd_unix.go b/libgo/go/net/fd_unix.go
index 7c73ddca777..7a97faeba34 100644
--- a/libgo/go/net/fd_unix.go
+++ b/libgo/go/net/fd_unix.go
@@ -401,7 +401,7 @@ func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
return
}
-func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err error) {
+func (fd *netFD) accept() (netfd *netFD, err error) {
if err := fd.readLock(); err != nil {
return nil, err
}
@@ -438,7 +438,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
return nil, err
}
lsa, _ := syscall.Getsockname(netfd.sysfd)
- netfd.setAddr(toAddr(lsa), toAddr(rsa))
+ netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
return netfd, nil
}
diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go
index d1129dccc47..f3a534a1de0 100644
--- a/libgo/go/net/fd_windows.go
+++ b/libgo/go/net/fd_windows.go
@@ -294,6 +294,18 @@ func (fd *netFD) init() error {
fd.skipSyncNotif = true
}
}
+ // Disable SIO_UDP_CONNRESET behavior.
+ // http://support.microsoft.com/kb/263823
+ switch fd.net {
+ case "udp", "udp4", "udp6":
+ ret := uint32(0)
+ flag := uint32(0)
+ size := uint32(unsafe.Sizeof(flag))
+ err := syscall.WSAIoctl(fd.sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
+ if err != nil {
+ return os.NewSyscallError("WSAIoctl", err)
+ }
+ }
fd.rop.mode = 'r'
fd.wop.mode = 'w'
fd.rop.fd = fd
@@ -520,7 +532,7 @@ 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) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD, error) {
// Get new socket.
s, err := sysSocket(fd.family, fd.sotype, 0)
if err != nil {
@@ -559,7 +571,7 @@ func (fd *netFD) acceptOne(toAddr func(syscall.Sockaddr) Addr, rawsa []syscall.R
return netfd, nil
}
-func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
+func (fd *netFD) accept() (*netFD, error) {
if err := fd.readLock(); err != nil {
return nil, err
}
@@ -570,7 +582,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
var err error
var rawsa [2]syscall.RawSockaddrAny
for {
- netfd, err = fd.acceptOne(toAddr, rawsa[:], o)
+ netfd, err = fd.acceptOne(rawsa[:], o)
if err == nil {
break
}
@@ -603,7 +615,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
lsa, _ := lrsa.Sockaddr()
rsa, _ := rrsa.Sockaddr()
- netfd.setAddr(toAddr(lsa), toAddr(rsa))
+ netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
return netfd, nil
}
diff --git a/libgo/go/net/file_stub.go b/libgo/go/net/file_stub.go
new file mode 100644
index 00000000000..4281072ef93
--- /dev/null
+++ b/libgo/go/net/file_stub.go
@@ -0,0 +1,38 @@
+// 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 nacl
+
+package net
+
+import (
+ "os"
+ "syscall"
+)
+
+// FileConn returns a copy of the network connection corresponding to
+// the open file f. 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 FileConn(f *os.File) (c Conn, err error) {
+ return nil, syscall.ENOPROTOOPT
+
+}
+
+// FileListener returns a copy of the network listener corresponding
+// to the open file f. It is the caller's responsibility to close l
+// when finished. Closing l does not affect f, and closing f does not
+// affect l.
+func FileListener(f *os.File) (l Listener, err error) {
+ return nil, syscall.ENOPROTOOPT
+
+}
+
+// FilePacketConn returns a copy of the packet network connection
+// corresponding to the open file f. 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 FilePacketConn(f *os.File) (c PacketConn, err error) {
+ return nil, syscall.ENOPROTOOPT
+}
diff --git a/libgo/go/net/file_test.go b/libgo/go/net/file_test.go
index d81bca78249..6fab06a9c6e 100644
--- a/libgo/go/net/file_test.go
+++ b/libgo/go/net/file_test.go
@@ -89,7 +89,7 @@ var fileListenerTests = []struct {
func TestFileListener(t *testing.T) {
switch runtime.GOOS {
- case "windows":
+ case "nacl", "windows":
t.Skipf("skipping test on %q", runtime.GOOS)
}
diff --git a/libgo/go/net/file_unix.go b/libgo/go/net/file_unix.go
index 07b3ecf6263..214a4196c8e 100644
--- a/libgo/go/net/file_unix.go
+++ b/libgo/go/net/file_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 dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package net
diff --git a/libgo/go/net/hosts.go b/libgo/go/net/hosts.go
index e6674ba3415..9400503e41e 100644
--- a/libgo/go/net/hosts.go
+++ b/libgo/go/net/hosts.go
@@ -51,7 +51,7 @@ func readHosts() {
}
}
// Update the data cache.
- hosts.expire = time.Now().Add(cacheMaxAge)
+ hosts.expire = now.Add(cacheMaxAge)
hosts.path = hp
hosts.byName = hs
hosts.byAddr = is
diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go
index a5a3abe6138..ce884d1f07b 100644
--- a/libgo/go/net/http/client.go
+++ b/libgo/go/net/http/client.go
@@ -101,6 +101,30 @@ type RoundTripper interface {
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
+// refererForURL returns a referer without any authentication info or
+// an empty string if lastReq scheme is https and newReq scheme is http.
+func refererForURL(lastReq, newReq *url.URL) string {
+ // https://tools.ietf.org/html/rfc7231#section-5.5.2
+ // "Clients SHOULD NOT include a Referer header field in a
+ // (non-secure) HTTP request if the referring page was
+ // transferred with a secure protocol."
+ if lastReq.Scheme == "https" && newReq.Scheme == "http" {
+ return ""
+ }
+ referer := lastReq.String()
+ if lastReq.User != nil {
+ // This is not very efficient, but is the best we can
+ // do without:
+ // - introducing a new method on URL
+ // - creating a race condition
+ // - copying the URL struct manually, which would cause
+ // maintenance problems down the line
+ auth := lastReq.User.String() + "@"
+ referer = strings.Replace(referer, auth, "", 1)
+ }
+ return referer
+}
+
// Used in Send to implement io.ReadCloser by bundling together the
// bufio.Reader through which we read the response, and the underlying
// network connection.
@@ -324,8 +348,8 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
if len(via) > 0 {
// Add the Referer header.
lastReq := via[len(via)-1]
- if lastReq.URL.Scheme != "https" {
- nreq.Header.Set("Referer", lastReq.URL.String())
+ if ref := refererForURL(lastReq.URL, nreq.URL); ref != "" {
+ nreq.Header.Set("Referer", ref)
}
err = redirectChecker(nreq, via)
diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go
index 6392c1baf39..56b6563c486 100644
--- a/libgo/go/net/http/client_test.go
+++ b/libgo/go/net/http/client_test.go
@@ -1036,3 +1036,40 @@ func TestClientTrailers(t *testing.T) {
t.Errorf("Response trailers = %#v; want %#v", res.Trailer, want)
}
}
+
+func TestReferer(t *testing.T) {
+ tests := []struct {
+ lastReq, newReq string // from -> to URLs
+ want string
+ }{
+ // don't send user:
+ {"http://gopher@test.com", "http://link.com", "http://test.com"},
+ {"https://gopher@test.com", "https://link.com", "https://test.com"},
+
+ // don't send a user and password:
+ {"http://gopher:go@test.com", "http://link.com", "http://test.com"},
+ {"https://gopher:go@test.com", "https://link.com", "https://test.com"},
+
+ // nothing to do:
+ {"http://test.com", "http://link.com", "http://test.com"},
+ {"https://test.com", "https://link.com", "https://test.com"},
+
+ // https to http doesn't send a referer:
+ {"https://test.com", "http://link.com", ""},
+ {"https://gopher:go@test.com", "http://link.com", ""},
+ }
+ for _, tt := range tests {
+ l, err := url.Parse(tt.lastReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+ n, err := url.Parse(tt.newReq)
+ if err != nil {
+ t.Fatal(err)
+ }
+ r := ExportRefererForURL(l, n)
+ if r != tt.want {
+ t.Errorf("refererForURL(%q, %q) = %q; want %q", tt.lastReq, tt.newReq, r, tt.want)
+ }
+ }
+}
diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go
index dc60ba87f5f..a0d0fdbbd07 100644
--- a/libgo/go/net/http/cookie.go
+++ b/libgo/go/net/http/cookie.go
@@ -56,7 +56,7 @@ func readSetCookies(h Header) []*Cookie {
if !isCookieNameValid(name) {
continue
}
- value, success := parseCookieValue(value)
+ value, success := parseCookieValue(value, true)
if !success {
continue
}
@@ -76,7 +76,7 @@ func readSetCookies(h Header) []*Cookie {
attr, val = attr[:j], attr[j+1:]
}
lowerAttr := strings.ToLower(attr)
- val, success = parseCookieValue(val)
+ val, success = parseCookieValue(val, false)
if !success {
c.Unparsed = append(c.Unparsed, parts[i])
continue
@@ -205,7 +205,7 @@ func readCookies(h Header, filter string) []*Cookie {
if filter != "" && filter != name {
continue
}
- val, success := parseCookieValue(val)
+ val, success := parseCookieValue(val, true)
if !success {
continue
}
@@ -345,9 +345,9 @@ func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string {
return string(buf)
}
-func parseCookieValue(raw string) (string, bool) {
+func parseCookieValue(raw string, allowDoubleQuote bool) (string, bool) {
// Strip the quotes, if present.
- if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
+ if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
raw = raw[1 : len(raw)-1]
}
for i := 0; i < len(raw); i++ {
diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go
index f78f37299f4..98dc2fade0d 100644
--- a/libgo/go/net/http/cookie_test.go
+++ b/libgo/go/net/http/cookie_test.go
@@ -313,6 +313,14 @@ var readCookiesTests = []struct {
{Name: "c2", Value: "v2"},
},
},
+ {
+ Header{"Cookie": {`Cookie-1="v$1"; c2="v2"`}},
+ "",
+ []*Cookie{
+ {Name: "Cookie-1", Value: "v$1"},
+ {Name: "c2", Value: "v2"},
+ },
+ },
}
func TestReadCookies(t *testing.T) {
@@ -327,6 +335,30 @@ func TestReadCookies(t *testing.T) {
}
}
+func TestSetCookieDoubleQuotes(t *testing.T) {
+ res := &Response{Header: Header{}}
+ res.Header.Add("Set-Cookie", `quoted0=none; max-age=30`)
+ res.Header.Add("Set-Cookie", `quoted1="cookieValue"; max-age=31`)
+ res.Header.Add("Set-Cookie", `quoted2=cookieAV; max-age="32"`)
+ res.Header.Add("Set-Cookie", `quoted3="both"; max-age="33"`)
+ got := res.Cookies()
+ want := []*Cookie{
+ {Name: "quoted0", Value: "none", MaxAge: 30},
+ {Name: "quoted1", Value: "cookieValue", MaxAge: 31},
+ {Name: "quoted2", Value: "cookieAV"},
+ {Name: "quoted3", Value: "both"},
+ }
+ if len(got) != len(want) {
+ t.Fatal("got %d cookies, want %d", len(got), len(want))
+ }
+ for i, w := range want {
+ g := got[i]
+ if g.Name != w.Name || g.Value != w.Value || g.MaxAge != w.MaxAge {
+ t.Errorf("cookie #%d:\ngot %v\nwant %v", i, g, w)
+ }
+ }
+}
+
func TestCookieSanitizeValue(t *testing.T) {
defer log.SetOutput(os.Stderr)
var logbuf bytes.Buffer
diff --git a/libgo/go/net/http/cookiejar/jar.go b/libgo/go/net/http/cookiejar/jar.go
index 389ab58e418..0e0fac9286e 100644
--- a/libgo/go/net/http/cookiejar/jar.go
+++ b/libgo/go/net/http/cookiejar/jar.go
@@ -30,7 +30,7 @@ import (
// set a cookie for bar.com.
//
// A public suffix list implementation is in the package
-// code.google.com/p/go.net/publicsuffix.
+// golang.org/x/net/publicsuffix.
type PublicSuffixList interface {
// PublicSuffix returns the public suffix of domain.
//
diff --git a/libgo/go/net/http/export_test.go b/libgo/go/net/http/export_test.go
index 960563b2409..87b6c0773aa 100644
--- a/libgo/go/net/http/export_test.go
+++ b/libgo/go/net/http/export_test.go
@@ -9,6 +9,7 @@ package http
import (
"net"
+ "net/url"
"time"
)
@@ -57,6 +58,26 @@ func (t *Transport) IdleConnChMapSizeForTesting() int {
return len(t.idleConnCh)
}
+func (t *Transport) IsIdleForTesting() bool {
+ t.idleMu.Lock()
+ defer t.idleMu.Unlock()
+ return t.wantIdle
+}
+
+func (t *Transport) RequestIdleConnChForTesting() {
+ t.getIdleConnCh(connectMethod{nil, "http", "example.com"})
+}
+
+func (t *Transport) PutIdleTestConn() bool {
+ c, _ := net.Pipe()
+ return t.putIdleConn(&persistConn{
+ t: t,
+ conn: c, // dummy
+ closech: make(chan struct{}), // so it can be closed
+ cacheKey: connectMethodKey{"", "http", "example.com"},
+ })
+}
+
func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
f := func() <-chan time.Time {
return ch
@@ -66,7 +87,22 @@ func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
func ResetCachedEnvironment() {
httpProxyEnv.reset()
+ httpsProxyEnv.reset()
noProxyEnv.reset()
}
var DefaultUserAgent = defaultUserAgent
+
+func ExportRefererForURL(lastReq, newReq *url.URL) string {
+ return refererForURL(lastReq, newReq)
+}
+
+// SetPendingDialHooks sets the hooks that run before and after handling
+// pending dials.
+func SetPendingDialHooks(before, after func()) {
+ prePendingDial, postPendingDial = before, after
+}
+
+var ExportServerNewConn = (*Server).newConn
+
+var ExportCloseWriteAndWait = (*conn).closeWriteAndWait
diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go
index 8576cf844a3..e322f710a5d 100644
--- a/libgo/go/net/http/fs.go
+++ b/libgo/go/net/http/fs.go
@@ -22,8 +22,12 @@ import (
"time"
)
-// A Dir implements http.FileSystem using the native file
-// system restricted to a specific directory tree.
+// A Dir implements FileSystem using the native file system restricted to a
+// specific directory tree.
+//
+// While the FileSystem.Open method takes '/'-separated paths, a Dir's string
+// value is a filename on the native file system, not a URL, so it is separated
+// by filepath.Separator, which isn't necessarily '/'.
//
// An empty Dir is treated as ".".
type Dir string
@@ -139,7 +143,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
if checkLastModified(w, r, modtime) {
return
}
- rangeReq, done := checkETag(w, r)
+ rangeReq, done := checkETag(w, r, modtime)
if done {
return
}
@@ -212,12 +216,6 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
code = StatusPartialContent
w.Header().Set("Content-Range", ra.contentRange(size))
case len(ranges) > 1:
- for _, ra := range ranges {
- if ra.start > size {
- Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
- return
- }
- }
sendSize = rangesMIMESize(ranges, ctype, size)
code = StatusPartialContent
@@ -281,11 +279,14 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
}
// checkETag implements If-None-Match and If-Range checks.
-// The ETag must have been previously set in the ResponseWriter's headers.
+//
+// The ETag or modtime must have been previously set in the
+// ResponseWriter's headers. The modtime is only compared at second
+// granularity and may be the zero value to mean unknown.
//
// 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) {
+func checkETag(w ResponseWriter, r *Request, modtime time.Time) (rangeReq string, done bool) {
etag := w.Header().get("Etag")
rangeReq = r.Header.get("Range")
@@ -296,11 +297,17 @@ func checkETag(w ResponseWriter, r *Request) (rangeReq string, done bool) {
// 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 = ""
+ // The If-Range value is typically the ETag value, but it may also be
+ // the modtime date. See golang.org/issue/8367.
+ timeMatches := false
+ if !modtime.IsZero() {
+ if t, err := ParseTime(ir); err == nil && t.Unix() == modtime.Unix() {
+ timeMatches = true
+ }
+ }
+ if !timeMatches {
+ rangeReq = ""
+ }
}
if inm := r.Header.get("If-None-Match"); inm != "" {
@@ -378,7 +385,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
// use contents of index.html for directory, if present
if d.IsDir() {
- index := name + indexPage
+ index := strings.TrimSuffix(name, "/") + indexPage
ff, err := fs.Open(index)
if err == nil {
defer ff.Close()
@@ -400,7 +407,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
return
}
- // serverContent will check modification time
+ // serveContent will check modification time
sizeFunc := func() (int64, error) { return d.Size(), nil }
serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f)
}
diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go
index c9a77c9f6aa..2ddd4ca5fe9 100644
--- a/libgo/go/net/http/fs_test.go
+++ b/libgo/go/net/http/fs_test.go
@@ -721,6 +721,28 @@ func TestServeContent(t *testing.T) {
wantStatus: 200,
wantContentType: "text/css; charset=utf-8",
},
+ "range_with_modtime": {
+ file: "testdata/style.css",
+ modtime: time.Date(2014, 6, 25, 17, 12, 18, 0 /* nanos */, time.UTC),
+ reqHeader: map[string]string{
+ "Range": "bytes=0-4",
+ "If-Range": "Wed, 25 Jun 2014 17:12:18 GMT",
+ },
+ wantStatus: StatusPartialContent,
+ wantContentType: "text/css; charset=utf-8",
+ wantLastMod: "Wed, 25 Jun 2014 17:12:18 GMT",
+ },
+ "range_with_modtime_nanos": {
+ file: "testdata/style.css",
+ modtime: time.Date(2014, 6, 25, 17, 12, 18, 123 /* nanos */, time.UTC),
+ reqHeader: map[string]string{
+ "Range": "bytes=0-4",
+ "If-Range": "Wed, 25 Jun 2014 17:12:18 GMT",
+ },
+ wantStatus: StatusPartialContent,
+ wantContentType: "text/css; charset=utf-8",
+ wantLastMod: "Wed, 25 Jun 2014 17:12:18 GMT",
+ },
}
for testName, tt := range tests {
var content io.ReadSeeker
@@ -860,4 +882,41 @@ func TestLinuxSendfileChild(*testing.T) {
}
}
+func TestFileServerCleanPath(t *testing.T) {
+ tests := []struct {
+ path string
+ wantCode int
+ wantOpen []string
+ }{
+ {"/", 200, []string{"/", "/index.html"}},
+ {"/dir", 301, []string{"/dir"}},
+ {"/dir/", 200, []string{"/dir", "/dir/index.html"}},
+ }
+ for _, tt := range tests {
+ var log []string
+ rr := httptest.NewRecorder()
+ req, _ := NewRequest("GET", "http://foo.localhost"+tt.path, nil)
+ FileServer(fileServerCleanPathDir{&log}).ServeHTTP(rr, req)
+ if !reflect.DeepEqual(log, tt.wantOpen) {
+ t.Logf("For %s: Opens = %q; want %q", tt.path, log, tt.wantOpen)
+ }
+ if rr.Code != tt.wantCode {
+ t.Logf("For %s: Response code = %d; want %d", tt.path, rr.Code, tt.wantCode)
+ }
+ }
+}
+
+type fileServerCleanPathDir struct {
+ log *[]string
+}
+
+func (d fileServerCleanPathDir) Open(path string) (File, error) {
+ *(d.log) = append(*(d.log), path)
+ if path == "/" || path == "/dir" || path == "/dir/" {
+ // Just return back something that's a directory.
+ return Dir(".").Open(".")
+ }
+ return nil, os.ErrNotExist
+}
+
type panicOnSeek struct{ io.ReadSeeker }
diff --git a/libgo/go/net/http/httptest/server.go b/libgo/go/net/http/httptest/server.go
index 7f265552f52..789e7bf41e6 100644
--- a/libgo/go/net/http/httptest/server.go
+++ b/libgo/go/net/http/httptest/server.go
@@ -203,7 +203,7 @@ func (h *waitGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// localhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time).
-// generated from src/pkg/crypto/tls:
+// generated from src/crypto/tls:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
diff --git a/libgo/go/net/http/httptest/server_test.go b/libgo/go/net/http/httptest/server_test.go
index 4fc4c702082..500a9f0b800 100644
--- a/libgo/go/net/http/httptest/server_test.go
+++ b/libgo/go/net/http/httptest/server_test.go
@@ -8,7 +8,6 @@ import (
"io/ioutil"
"net/http"
"testing"
- "time"
)
func TestServer(t *testing.T) {
@@ -28,26 +27,3 @@ func TestServer(t *testing.T) {
t.Errorf("got %q, want hello", string(got))
}
}
-
-func TestIssue7264(t *testing.T) {
- t.Skip("broken test - removed at tip")
- for i := 0; i < 1000; i++ {
- func() {
- inHandler := make(chan bool, 1)
- ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- inHandler <- true
- }))
- defer ts.Close()
- tr := &http.Transport{
- ResponseHeaderTimeout: time.Nanosecond,
- }
- defer tr.CloseIdleConnections()
- c := &http.Client{Transport: tr}
- res, err := c.Get(ts.URL)
- <-inHandler
- if err == nil {
- res.Body.Close()
- }
- }()
- }
-}
diff --git a/libgo/go/net/http/httputil/chunked.go b/libgo/go/net/http/httputil/chunked.go
deleted file mode 100644
index 9632bfd19d5..00000000000
--- a/libgo/go/net/http/httputil/chunked.go
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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.
-
-// The wire protocol for HTTP's "chunked" Transfer-Encoding.
-
-// This code is duplicated in net/http and net/http/httputil.
-// Please make any changes in both files.
-
-package httputil
-
-import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "io"
-)
-
-const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
-
-var ErrLineTooLong = errors.New("header line too long")
-
-// newChunkedReader returns a new chunkedReader that translates the data read from r
-// out of HTTP "chunked" format before returning it.
-// The chunkedReader returns io.EOF when the final 0-length chunk is read.
-//
-// newChunkedReader is not needed by normal applications. The http package
-// automatically decodes chunking when reading response bodies.
-func newChunkedReader(r io.Reader) io.Reader {
- br, ok := r.(*bufio.Reader)
- if !ok {
- br = bufio.NewReader(r)
- }
- return &chunkedReader{r: br}
-}
-
-type chunkedReader struct {
- r *bufio.Reader
- n uint64 // unread bytes in chunk
- err error
- buf [2]byte
-}
-
-func (cr *chunkedReader) beginChunk() {
- // chunk-size CRLF
- var line []byte
- line, cr.err = readLine(cr.r)
- if cr.err != nil {
- return
- }
- cr.n, cr.err = parseHexUint(line)
- if cr.err != nil {
- return
- }
- if cr.n == 0 {
- cr.err = io.EOF
- }
-}
-
-func (cr *chunkedReader) chunkHeaderAvailable() bool {
- n := cr.r.Buffered()
- if n > 0 {
- peek, _ := cr.r.Peek(n)
- return bytes.IndexByte(peek, '\n') >= 0
- }
- return false
-}
-
-func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
- for cr.err == nil {
- if cr.n == 0 {
- if n > 0 && !cr.chunkHeaderAvailable() {
- // We've read enough. Don't potentially block
- // reading a new chunk header.
- break
- }
- cr.beginChunk()
- continue
- }
- if len(b) == 0 {
- break
- }
- rbuf := b
- if uint64(len(rbuf)) > cr.n {
- rbuf = rbuf[:cr.n]
- }
- var n0 int
- n0, cr.err = cr.r.Read(rbuf)
- n += n0
- b = b[n0:]
- cr.n -= uint64(n0)
- // If we're at the end of a chunk, read the next two
- // bytes to verify they are "\r\n".
- if cr.n == 0 && cr.err == nil {
- if _, cr.err = io.ReadFull(cr.r, cr.buf[:2]); cr.err == nil {
- if cr.buf[0] != '\r' || cr.buf[1] != '\n' {
- cr.err = errors.New("malformed chunked encoding")
- }
- }
- }
- }
- return n, cr.err
-}
-
-// Read a line of bytes (up to \n) from b.
-// Give up if the line exceeds maxLineLength.
-// The returned bytes are a pointer into storage in
-// the bufio, so they are only valid until the next bufio read.
-func readLine(b *bufio.Reader) (p []byte, err error) {
- if p, err = b.ReadSlice('\n'); err != nil {
- // We always know when EOF is coming.
- // If the caller asked for a line, there should be a line.
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- } else if err == bufio.ErrBufferFull {
- err = ErrLineTooLong
- }
- return nil, err
- }
- if len(p) >= maxLineLength {
- return nil, ErrLineTooLong
- }
- return trimTrailingWhitespace(p), nil
-}
-
-func trimTrailingWhitespace(b []byte) []byte {
- 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'
-}
-
-// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
-// "chunked" format before writing them to w. Closing the returned chunkedWriter
-// sends the final 0-length chunk that marks the end of the stream.
-//
-// newChunkedWriter is not needed by normal applications. The http
-// package adds chunking automatically if handlers don't set a
-// Content-Length header. Using newChunkedWriter inside a handler
-// would result in double chunking or chunking with a Content-Length
-// length, both of which are wrong.
-func newChunkedWriter(w io.Writer) io.WriteCloser {
- return &chunkedWriter{w}
-}
-
-// Writing to chunkedWriter translates to writing in HTTP chunked Transfer
-// Encoding wire format to the underlying Wire chunkedWriter.
-type chunkedWriter struct {
- Wire io.Writer
-}
-
-// Write the contents of data as one chunk to Wire.
-// NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has
-// a bug since it does not check for success of io.WriteString
-func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
-
- // Don't send 0-length data. It looks like EOF for chunked encoding.
- if len(data) == 0 {
- return 0, nil
- }
-
- if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil {
- return 0, err
- }
- if n, err = cw.Wire.Write(data); err != nil {
- return
- }
- if n != len(data) {
- err = io.ErrShortWrite
- return
- }
- _, err = io.WriteString(cw.Wire, "\r\n")
-
- return
-}
-
-func (cw *chunkedWriter) Close() error {
- _, err := io.WriteString(cw.Wire, "0\r\n")
- return err
-}
-
-func parseHexUint(v []byte) (n uint64, err error) {
- for _, b := range v {
- n <<= 4
- switch {
- case '0' <= b && b <= '9':
- b = b - '0'
- case 'a' <= b && b <= 'f':
- b = b - 'a' + 10
- case 'A' <= b && b <= 'F':
- b = b - 'A' + 10
- default:
- return 0, errors.New("invalid byte in chunk length")
- }
- n |= uint64(b)
- }
- return
-}
diff --git a/libgo/go/net/http/httputil/chunked_test.go b/libgo/go/net/http/httputil/chunked_test.go
deleted file mode 100644
index a7a57746885..00000000000
--- a/libgo/go/net/http/httputil/chunked_test.go
+++ /dev/null
@@ -1,159 +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.
-
-// This code is duplicated in net/http and net/http/httputil.
-// Please make any changes in both files.
-
-package httputil
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "strings"
- "testing"
-)
-
-func TestChunk(t *testing.T) {
- var b bytes.Buffer
-
- w := newChunkedWriter(&b)
- const chunk1 = "hello, "
- const chunk2 = "world! 0123456789abcdef"
- w.Write([]byte(chunk1))
- w.Write([]byte(chunk2))
- w.Close()
-
- if g, e := b.String(), "7\r\nhello, \r\n17\r\nworld! 0123456789abcdef\r\n0\r\n"; g != e {
- t.Fatalf("chunk writer wrote %q; want %q", g, e)
- }
-
- r := newChunkedReader(&b)
- data, err := ioutil.ReadAll(r)
- if err != nil {
- t.Logf(`data: "%s"`, data)
- t.Fatalf("ReadAll from reader: %v", err)
- }
- if g, e := string(data), chunk1+chunk2; g != e {
- t.Errorf("chunk reader read %q; want %q", g, e)
- }
-}
-
-func TestChunkReadMultiple(t *testing.T) {
- // Bunch of small chunks, all read together.
- {
- var b bytes.Buffer
- w := newChunkedWriter(&b)
- w.Write([]byte("foo"))
- w.Write([]byte("bar"))
- w.Close()
-
- r := newChunkedReader(&b)
- buf := make([]byte, 10)
- n, err := r.Read(buf)
- if n != 6 || err != io.EOF {
- t.Errorf("Read = %d, %v; want 6, EOF", n, err)
- }
- buf = buf[:n]
- if string(buf) != "foobar" {
- t.Errorf("Read = %q; want %q", buf, "foobar")
- }
- }
-
- // One big chunk followed by a little chunk, but the small bufio.Reader size
- // should prevent the second chunk header from being read.
- {
- var b bytes.Buffer
- w := newChunkedWriter(&b)
- // fillBufChunk is 11 bytes + 3 bytes header + 2 bytes footer = 16 bytes,
- // the same as the bufio ReaderSize below (the minimum), so even
- // though we're going to try to Read with a buffer larger enough to also
- // receive "foo", the second chunk header won't be read yet.
- const fillBufChunk = "0123456789a"
- const shortChunk = "foo"
- w.Write([]byte(fillBufChunk))
- w.Write([]byte(shortChunk))
- w.Close()
-
- r := newChunkedReader(bufio.NewReaderSize(&b, 16))
- buf := make([]byte, len(fillBufChunk)+len(shortChunk))
- n, err := r.Read(buf)
- if n != len(fillBufChunk) || err != nil {
- t.Errorf("Read = %d, %v; want %d, nil", n, err, len(fillBufChunk))
- }
- buf = buf[:n]
- if string(buf) != fillBufChunk {
- t.Errorf("Read = %q; want %q", buf, fillBufChunk)
- }
-
- n, err = r.Read(buf)
- if n != len(shortChunk) || err != io.EOF {
- t.Errorf("Read = %d, %v; want %d, EOF", n, err, len(shortChunk))
- }
- }
-
- // And test that we see an EOF chunk, even though our buffer is already full:
- {
- r := newChunkedReader(bufio.NewReader(strings.NewReader("3\r\nfoo\r\n0\r\n")))
- buf := make([]byte, 3)
- n, err := r.Read(buf)
- if n != 3 || err != io.EOF {
- t.Errorf("Read = %d, %v; want 3, EOF", n, err)
- }
- if string(buf) != "foo" {
- t.Errorf("buf = %q; want foo", buf)
- }
- }
-}
-
-func TestChunkReaderAllocs(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping in short mode")
- }
- var buf bytes.Buffer
- w := newChunkedWriter(&buf)
- a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc")
- w.Write(a)
- w.Write(b)
- w.Write(c)
- w.Close()
-
- readBuf := make([]byte, len(a)+len(b)+len(c)+1)
- byter := bytes.NewReader(buf.Bytes())
- bufr := bufio.NewReader(byter)
- mallocs := testing.AllocsPerRun(100, func() {
- byter.Seek(0, 0)
- bufr.Reset(byter)
- r := newChunkedReader(bufr)
- n, err := io.ReadFull(r, readBuf)
- if n != len(readBuf)-1 {
- t.Fatalf("read %d bytes; want %d", n, len(readBuf)-1)
- }
- if err != io.ErrUnexpectedEOF {
- t.Fatalf("read error = %v; want ErrUnexpectedEOF", err)
- }
- })
- if mallocs > 1.5 {
- t.Errorf("mallocs = %v; want 1", mallocs)
- }
-}
-
-func TestParseHexUint(t *testing.T) {
- for i := uint64(0); i <= 1234; i++ {
- line := []byte(fmt.Sprintf("%x", i))
- got, err := parseHexUint(line)
- if err != nil {
- t.Fatalf("on %d: %v", i, err)
- }
- if got != i {
- t.Errorf("for input %q = %d; want %d", line, got, i)
- }
- }
- _, err := parseHexUint([]byte("bogus"))
- if err == nil {
- t.Error("expected error on bogus input")
- }
-}
diff --git a/libgo/go/net/http/httputil/dump.go b/libgo/go/net/http/httputil/dump.go
index 2a7a413d01a..ac8f103f9b9 100644
--- a/libgo/go/net/http/httputil/dump.go
+++ b/libgo/go/net/http/httputil/dump.go
@@ -95,19 +95,27 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
// with a dummy response.
var buf bytes.Buffer // records the output
pr, pw := io.Pipe()
+ defer pr.Close()
+ defer pw.Close()
dr := &delegateReader{c: make(chan io.Reader)}
// Wait for the request before replying with a dummy response:
go func() {
- http.ReadRequest(bufio.NewReader(pr))
+ req, err := http.ReadRequest(bufio.NewReader(pr))
+ if err == nil {
+ // Ensure all the body is read; otherwise
+ // we'll get a partial dump.
+ io.Copy(ioutil.Discard, req.Body)
+ req.Body.Close()
+ }
dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\n\r\n")
}()
t := &http.Transport{
+ DisableKeepAlives: true,
Dial: func(net, addr string) (net.Conn, error) {
return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil
},
}
- defer t.CloseIdleConnections()
_, err := t.RoundTrip(reqSend)
diff --git a/libgo/go/net/http/httputil/dump_test.go b/libgo/go/net/http/httputil/dump_test.go
index e1ffb3935ac..024ee5a86f4 100644
--- a/libgo/go/net/http/httputil/dump_test.go
+++ b/libgo/go/net/http/httputil/dump_test.go
@@ -111,6 +111,30 @@ var dumpTests = []dumpTest{
NoBody: true,
},
+
+ // Request with Body > 8196 (default buffer size)
+ {
+ Req: http.Request{
+ Method: "POST",
+ URL: &url.URL{
+ Scheme: "http",
+ Host: "post.tld",
+ Path: "/",
+ },
+ ContentLength: 8193,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ },
+
+ Body: bytes.Repeat([]byte("a"), 8193),
+
+ WantDumpOut: "POST / HTTP/1.1\r\n" +
+ "Host: post.tld\r\n" +
+ "User-Agent: Go 1.1 package http\r\n" +
+ "Content-Length: 8193\r\n" +
+ "Accept-Encoding: gzip\r\n\r\n" +
+ strings.Repeat("a", 8193),
+ },
}
func TestDumpRequest(t *testing.T) {
@@ -125,6 +149,8 @@ func TestDumpRequest(t *testing.T) {
tt.Req.Body = ioutil.NopCloser(bytes.NewReader(b))
case func() io.ReadCloser:
tt.Req.Body = b()
+ default:
+ t.Fatalf("Test %d: unsupported Body of %T", i, tt.Body)
}
}
setBody()
@@ -159,7 +185,9 @@ func TestDumpRequest(t *testing.T) {
}
}
if dg := runtime.NumGoroutine() - numg0; dg > 4 {
- t.Errorf("Unexpectedly large number of new goroutines: %d new", dg)
+ buf := make([]byte, 4096)
+ buf = buf[:runtime.Stack(buf, true)]
+ t.Errorf("Unexpectedly large number of new goroutines: %d new: %s", dg, buf)
}
}
diff --git a/libgo/go/net/http/httputil/httputil.go b/libgo/go/net/http/httputil/httputil.go
index 74fb6c6556f..2e523e9e269 100644
--- a/libgo/go/net/http/httputil/httputil.go
+++ b/libgo/go/net/http/httputil/httputil.go
@@ -6,7 +6,10 @@
// more common ones in the net/http package.
package httputil
-import "io"
+import (
+ "io"
+ "net/http/internal"
+)
// NewChunkedReader returns a new chunkedReader that translates the data read from r
// out of HTTP "chunked" format before returning it.
@@ -15,7 +18,7 @@ import "io"
// NewChunkedReader is not needed by normal applications. The http package
// automatically decodes chunking when reading response bodies.
func NewChunkedReader(r io.Reader) io.Reader {
- return newChunkedReader(r)
+ return internal.NewChunkedReader(r)
}
// NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP
@@ -28,5 +31,9 @@ func NewChunkedReader(r io.Reader) io.Reader {
// would result in double chunking or chunking with a Content-Length
// length, both of which are wrong.
func NewChunkedWriter(w io.Writer) io.WriteCloser {
- return newChunkedWriter(w)
+ return internal.NewChunkedWriter(w)
}
+
+// ErrLineTooLong is returned when reading malformed chunked data
+// with lines that are too long.
+var ErrLineTooLong = internal.ErrLineTooLong
diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go
index 48ada5f5fdb..ab463701803 100644
--- a/libgo/go/net/http/httputil/reverseproxy.go
+++ b/libgo/go/net/http/httputil/reverseproxy.go
@@ -40,6 +40,12 @@ type ReverseProxy struct {
// response body.
// If zero, no periodic flushing is done.
FlushInterval time.Duration
+
+ // ErrorLog specifies an optional logger for errors
+ // that occur when attempting to proxy the request.
+ // If nil, logging goes to os.Stderr via the log package's
+ // standard logger.
+ ErrorLog *log.Logger
}
func singleJoiningSlash(a, b string) string {
@@ -138,7 +144,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
res, err := transport.RoundTrip(outreq)
if err != nil {
- log.Printf("http: proxy error: %v", err)
+ p.logf("http: proxy error: %v", err)
rw.WriteHeader(http.StatusInternalServerError)
return
}
@@ -171,6 +177,14 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
io.Copy(dst, src)
}
+func (p *ReverseProxy) logf(format string, args ...interface{}) {
+ if p.ErrorLog != nil {
+ p.ErrorLog.Printf(format, args...)
+ } else {
+ log.Printf(format, args...)
+ }
+}
+
type writeFlusher interface {
io.Writer
http.Flusher
diff --git a/libgo/go/net/http/chunked.go b/libgo/go/net/http/internal/chunked.go
index 749f29d3269..9294deb3e5e 100644
--- a/libgo/go/net/http/chunked.go
+++ b/libgo/go/net/http/internal/chunked.go
@@ -4,10 +4,9 @@
// The wire protocol for HTTP's "chunked" Transfer-Encoding.
-// This code is duplicated in net/http and net/http/httputil.
-// Please make any changes in both files.
-
-package http
+// Package internal contains HTTP internals shared by net/http and
+// net/http/httputil.
+package internal
import (
"bufio"
@@ -21,13 +20,13 @@ const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
var ErrLineTooLong = errors.New("header line too long")
-// newChunkedReader returns a new chunkedReader that translates the data read from r
+// NewChunkedReader returns a new chunkedReader that translates the data read from r
// out of HTTP "chunked" format before returning it.
// The chunkedReader returns io.EOF when the final 0-length chunk is read.
//
-// newChunkedReader is not needed by normal applications. The http package
+// NewChunkedReader is not needed by normal applications. The http package
// automatically decodes chunking when reading response bodies.
-func newChunkedReader(r io.Reader) io.Reader {
+func NewChunkedReader(r io.Reader) io.Reader {
br, ok := r.(*bufio.Reader)
if !ok {
br = bufio.NewReader(r)
@@ -135,16 +134,16 @@ func isASCIISpace(b byte) bool {
return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}
-// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
+// NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP
// "chunked" format before writing them to w. Closing the returned chunkedWriter
// sends the final 0-length chunk that marks the end of the stream.
//
-// newChunkedWriter is not needed by normal applications. The http
+// NewChunkedWriter is not needed by normal applications. The http
// package adds chunking automatically if handlers don't set a
// Content-Length header. Using newChunkedWriter inside a handler
// would result in double chunking or chunking with a Content-Length
// length, both of which are wrong.
-func newChunkedWriter(w io.Writer) io.WriteCloser {
+func NewChunkedWriter(w io.Writer) io.WriteCloser {
return &chunkedWriter{w}
}
diff --git a/libgo/go/net/http/chunked_test.go b/libgo/go/net/http/internal/chunked_test.go
index 34544790aff..ebc626ea9d0 100644
--- a/libgo/go/net/http/chunked_test.go
+++ b/libgo/go/net/http/internal/chunked_test.go
@@ -2,10 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This code is duplicated in net/http and net/http/httputil.
-// Please make any changes in both files.
-
-package http
+package internal
import (
"bufio"
@@ -20,7 +17,7 @@ import (
func TestChunk(t *testing.T) {
var b bytes.Buffer
- w := newChunkedWriter(&b)
+ w := NewChunkedWriter(&b)
const chunk1 = "hello, "
const chunk2 = "world! 0123456789abcdef"
w.Write([]byte(chunk1))
@@ -31,7 +28,7 @@ func TestChunk(t *testing.T) {
t.Fatalf("chunk writer wrote %q; want %q", g, e)
}
- r := newChunkedReader(&b)
+ r := NewChunkedReader(&b)
data, err := ioutil.ReadAll(r)
if err != nil {
t.Logf(`data: "%s"`, data)
@@ -46,12 +43,12 @@ func TestChunkReadMultiple(t *testing.T) {
// Bunch of small chunks, all read together.
{
var b bytes.Buffer
- w := newChunkedWriter(&b)
+ w := NewChunkedWriter(&b)
w.Write([]byte("foo"))
w.Write([]byte("bar"))
w.Close()
- r := newChunkedReader(&b)
+ r := NewChunkedReader(&b)
buf := make([]byte, 10)
n, err := r.Read(buf)
if n != 6 || err != io.EOF {
@@ -67,7 +64,7 @@ func TestChunkReadMultiple(t *testing.T) {
// should prevent the second chunk header from being read.
{
var b bytes.Buffer
- w := newChunkedWriter(&b)
+ w := NewChunkedWriter(&b)
// fillBufChunk is 11 bytes + 3 bytes header + 2 bytes footer = 16 bytes,
// the same as the bufio ReaderSize below (the minimum), so even
// though we're going to try to Read with a buffer larger enough to also
@@ -78,7 +75,7 @@ func TestChunkReadMultiple(t *testing.T) {
w.Write([]byte(shortChunk))
w.Close()
- r := newChunkedReader(bufio.NewReaderSize(&b, 16))
+ r := NewChunkedReader(bufio.NewReaderSize(&b, 16))
buf := make([]byte, len(fillBufChunk)+len(shortChunk))
n, err := r.Read(buf)
if n != len(fillBufChunk) || err != nil {
@@ -97,7 +94,7 @@ func TestChunkReadMultiple(t *testing.T) {
// And test that we see an EOF chunk, even though our buffer is already full:
{
- r := newChunkedReader(bufio.NewReader(strings.NewReader("3\r\nfoo\r\n0\r\n")))
+ r := NewChunkedReader(bufio.NewReader(strings.NewReader("3\r\nfoo\r\n0\r\n")))
buf := make([]byte, 3)
n, err := r.Read(buf)
if n != 3 || err != io.EOF {
@@ -114,7 +111,7 @@ func TestChunkReaderAllocs(t *testing.T) {
t.Skip("skipping in short mode")
}
var buf bytes.Buffer
- w := newChunkedWriter(&buf)
+ w := NewChunkedWriter(&buf)
a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc")
w.Write(a)
w.Write(b)
@@ -127,7 +124,7 @@ func TestChunkReaderAllocs(t *testing.T) {
mallocs := testing.AllocsPerRun(100, func() {
byter.Seek(0, 0)
bufr.Reset(byter)
- r := newChunkedReader(bufr)
+ r := NewChunkedReader(bufr)
n, err := io.ReadFull(r, readBuf)
if n != len(readBuf)-1 {
t.Fatalf("read %d bytes; want %d", n, len(readBuf)-1)
diff --git a/libgo/go/net/http/z_last_test.go b/libgo/go/net/http/main_test.go
index 5a0cc119849..b8c71fd19fd 100644
--- a/libgo/go/net/http/z_last_test.go
+++ b/libgo/go/net/http/main_test.go
@@ -5,7 +5,9 @@
package http_test
import (
+ "fmt"
"net/http"
+ "os"
"runtime"
"sort"
"strings"
@@ -13,6 +15,14 @@ import (
"time"
)
+func TestMain(m *testing.M) {
+ v := m.Run()
+ if v == 0 && goroutineLeaked() {
+ os.Exit(1)
+ }
+ os.Exit(v)
+}
+
func interestingGoroutines() (gs []string) {
buf := make([]byte, 2<<20)
buf = buf[:runtime.Stack(buf, true)]
@@ -30,6 +40,7 @@ func interestingGoroutines() (gs []string) {
// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
strings.Contains(stack, "runtime.goexit") ||
strings.Contains(stack, "created by runtime.gc") ||
+ strings.Contains(stack, "net/http_test.interestingGoroutines") ||
strings.Contains(stack, "runtime.MHeap_Scavenger") {
continue
}
@@ -40,10 +51,10 @@ func interestingGoroutines() (gs []string) {
}
// Verify the other tests didn't leave any goroutines running.
-// This is in a file named z_last_test.go so it sorts at the end.
-func TestGoroutinesRunning(t *testing.T) {
+func goroutineLeaked() bool {
if testing.Short() {
- t.Skip("not counting goroutines for leakage in -short mode")
+ // not counting goroutines for leakage in -short mode
+ return false
}
gs := interestingGoroutines()
@@ -54,13 +65,14 @@ func TestGoroutinesRunning(t *testing.T) {
n++
}
- t.Logf("num goroutines = %d", n)
- if n > 0 {
- t.Error("Too many goroutines.")
- for stack, count := range stackCount {
- t.Logf("%d instances of:\n%s", count, stack)
- }
+ if n == 0 {
+ return false
+ }
+ fmt.Fprintf(os.Stderr, "Too many goroutines running after net/http test(s).\n")
+ for stack, count := range stackCount {
+ fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
}
+ return true
}
func afterTest(t *testing.T) {
diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go
index 0c7548e3ef3..a23f1bc4bc6 100644
--- a/libgo/go/net/http/pprof/pprof.go
+++ b/libgo/go/net/http/pprof/pprof.go
@@ -162,6 +162,10 @@ func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Unknown profile: %s\n", name)
return
}
+ gc, _ := strconv.Atoi(r.FormValue("gc"))
+ if name == "heap" && gc > 0 {
+ runtime.GC()
+ }
p.WriteTo(w, debug)
return
}
diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go
index ffdd6a892da..e930d99af62 100644
--- a/libgo/go/net/http/readrequest_test.go
+++ b/libgo/go/net/http/readrequest_test.go
@@ -11,6 +11,7 @@ import (
"io"
"net/url"
"reflect"
+ "strings"
"testing"
)
@@ -295,14 +296,39 @@ var reqTests = []reqTest{
noTrailer,
noError,
},
+
+ // Connection: close. golang.org/issue/8261
+ {
+ "GET / HTTP/1.1\r\nHost: issue8261.com\r\nConnection: close\r\n\r\n",
+ &Request{
+ Method: "GET",
+ URL: &url.URL{
+ Path: "/",
+ },
+ Header: Header{
+ // This wasn't removed from Go 1.0 to
+ // Go 1.3, so locking it in that we
+ // keep this:
+ "Connection": []string{"close"},
+ },
+ Host: "issue8261.com",
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Close: true,
+ RequestURI: "/",
+ },
+
+ noBody,
+ noTrailer,
+ noError,
+ },
}
func TestReadRequest(t *testing.T) {
for i := range reqTests {
tt := &reqTests[i]
- var braw bytes.Buffer
- braw.WriteString(tt.Raw)
- req, err := ReadRequest(bufio.NewReader(&braw))
+ req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.Raw)))
if err != nil {
if err.Error() != tt.Error {
t.Errorf("#%d: error %q, want error %q", i, err.Error(), tt.Error)
@@ -311,21 +337,22 @@ func TestReadRequest(t *testing.T) {
}
rbody := req.Body
req.Body = nil
- diff(t, fmt.Sprintf("#%d Request", i), req, tt.Req)
+ testName := fmt.Sprintf("Test %d (%q)", i, tt.Raw)
+ diff(t, testName, req, tt.Req)
var bout bytes.Buffer
if rbody != nil {
_, err := io.Copy(&bout, rbody)
if err != nil {
- t.Fatalf("#%d. copying body: %v", i, err)
+ t.Fatalf("%s: copying body: %v", testName, err)
}
rbody.Close()
}
body := bout.String()
if body != tt.Body {
- t.Errorf("#%d: Body = %q want %q", i, body, tt.Body)
+ t.Errorf("%s: Body = %q want %q", testName, body, tt.Body)
}
if !reflect.DeepEqual(tt.Trailer, req.Trailer) {
- t.Errorf("#%d. Trailers differ.\n got: %v\nwant: %v", i, req.Trailer, tt.Trailer)
+ t.Errorf("%s: Trailers differ.\n got: %v\nwant: %v", testName, req.Trailer, tt.Trailer)
}
}
}
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go
index a67092066ad..487eebcb841 100644
--- a/libgo/go/net/http/request.go
+++ b/libgo/go/net/http/request.go
@@ -10,6 +10,7 @@ import (
"bufio"
"bytes"
"crypto/tls"
+ "encoding/base64"
"errors"
"fmt"
"io"
@@ -390,10 +391,16 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
w = bw
}
- fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
+ _, err := fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
+ if err != nil {
+ return err
+ }
// Header lines
- fmt.Fprintf(w, "Host: %s\r\n", host)
+ _, err = fmt.Fprintf(w, "Host: %s\r\n", host)
+ if err != nil {
+ return err
+ }
// Use the defaultUserAgent unless the Header contains one, which
// may be blank to not send the header.
@@ -404,7 +411,10 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
}
}
if userAgent != "" {
- fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
+ _, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
+ if err != nil {
+ return err
+ }
}
// Process Body,ContentLength,Close,Trailer
@@ -429,7 +439,10 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
}
}
- io.WriteString(w, "\r\n")
+ _, err = io.WriteString(w, "\r\n")
+ if err != nil {
+ return err
+ }
// Write body and trailer
err = tw.WriteBody(w)
@@ -509,6 +522,35 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
return req, nil
}
+// BasicAuth returns the username and password provided in the request's
+// Authorization header, if the request uses HTTP Basic Authentication.
+// See RFC 2617, Section 2.
+func (r *Request) BasicAuth() (username, password string, ok bool) {
+ auth := r.Header.Get("Authorization")
+ if auth == "" {
+ return
+ }
+ return parseBasicAuth(auth)
+}
+
+// parseBasicAuth parses an HTTP Basic Authentication string.
+// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
+func parseBasicAuth(auth string) (username, password string, ok bool) {
+ if !strings.HasPrefix(auth, "Basic ") {
+ return
+ }
+ c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic "))
+ if err != nil {
+ return
+ }
+ cs := string(c)
+ s := strings.IndexByte(cs, ':')
+ if s < 0 {
+ return
+ }
+ return cs[:s], cs[s+1:], true
+}
+
// SetBasicAuth sets the request's Authorization header to use HTTP
// Basic Authentication with the provided username and password.
//
@@ -623,6 +665,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
return nil, err
}
+ req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false)
return req, nil
}
@@ -807,8 +850,10 @@ 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.
-// To access multiple values of the same key use ParseForm.
+// FormValue calls ParseMultipartForm and ParseForm if necessary and ignores
+// any errors returned by these functions.
+// To access multiple values of the same key, call ParseForm and
+// then inspect Request.Form directly.
func (r *Request) FormValue(key string) string {
if r.Form == nil {
r.ParseMultipartForm(defaultMaxMemory)
@@ -821,7 +866,8 @@ func (r *Request) FormValue(key string) string {
// 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.
+// PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores
+// any errors returned by these functions.
func (r *Request) PostFormValue(key string) string {
if r.PostForm == nil {
r.ParseMultipartForm(defaultMaxMemory)
diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go
index b9fa3c2bfc4..759ea4e8b5d 100644
--- a/libgo/go/net/http/request_test.go
+++ b/libgo/go/net/http/request_test.go
@@ -7,6 +7,7 @@ package http_test
import (
"bufio"
"bytes"
+ "encoding/base64"
"fmt"
"io"
"io/ioutil"
@@ -396,6 +397,75 @@ func TestParseHTTPVersion(t *testing.T) {
}
}
+type getBasicAuthTest struct {
+ username, password string
+ ok bool
+}
+
+type parseBasicAuthTest getBasicAuthTest
+
+type basicAuthCredentialsTest struct {
+ username, password string
+}
+
+var getBasicAuthTests = []struct {
+ username, password string
+ ok bool
+}{
+ {"Aladdin", "open sesame", true},
+ {"Aladdin", "open:sesame", true},
+ {"", "", true},
+}
+
+func TestGetBasicAuth(t *testing.T) {
+ for _, tt := range getBasicAuthTests {
+ r, _ := NewRequest("GET", "http://example.com/", nil)
+ r.SetBasicAuth(tt.username, tt.password)
+ username, password, ok := r.BasicAuth()
+ if ok != tt.ok || username != tt.username || password != tt.password {
+ t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok},
+ getBasicAuthTest{tt.username, tt.password, tt.ok})
+ }
+ }
+ // Unauthenticated request.
+ r, _ := NewRequest("GET", "http://example.com/", nil)
+ username, password, ok := r.BasicAuth()
+ if ok {
+ t.Errorf("expected false from BasicAuth when the request is unauthenticated")
+ }
+ want := basicAuthCredentialsTest{"", ""}
+ if username != want.username || password != want.password {
+ t.Errorf("expected credentials: %#v when the request is unauthenticated, got %#v",
+ want, basicAuthCredentialsTest{username, password})
+ }
+}
+
+var parseBasicAuthTests = []struct {
+ header, username, password string
+ ok bool
+}{
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true},
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true},
+ {"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
+ {base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
+ {"Basic ", "", "", false},
+ {"Basic Aladdin:open sesame", "", "", false},
+ {`Digest username="Aladdin"`, "", "", false},
+}
+
+func TestParseBasicAuth(t *testing.T) {
+ for _, tt := range parseBasicAuthTests {
+ r, _ := NewRequest("GET", "http://example.com/", nil)
+ r.Header.Set("Authorization", tt.header)
+ username, password, ok := r.BasicAuth()
+ if ok != tt.ok || username != tt.username || password != tt.password {
+ t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok},
+ getBasicAuthTest{tt.username, tt.password, tt.ok})
+ }
+ }
+}
+
type logWrites struct {
t *testing.T
dst *[]string
diff --git a/libgo/go/net/http/requestwrite_test.go b/libgo/go/net/http/requestwrite_test.go
index dc0e204cac9..7a6bd587863 100644
--- a/libgo/go/net/http/requestwrite_test.go
+++ b/libgo/go/net/http/requestwrite_test.go
@@ -280,7 +280,7 @@ var reqWriteTests = []reqWriteTest{
ContentLength: 10, // but we're going to send only 5 bytes
},
Body: []byte("12345"),
- WantError: errors.New("http: Request.ContentLength=10 with Body length 5"),
+ WantError: errors.New("http: ContentLength=10 with Body length 5"),
},
// Request with a ContentLength of 4 but an 8 byte body.
@@ -294,7 +294,7 @@ var reqWriteTests = []reqWriteTest{
ContentLength: 4, // but we're going to try to send 8 bytes
},
Body: []byte("12345678"),
- WantError: errors.New("http: Request.ContentLength=4 with Body length 8"),
+ WantError: errors.New("http: ContentLength=4 with Body length 8"),
},
// Request with a 5 ContentLength and nil body.
@@ -563,3 +563,61 @@ func mustParseURL(s string) *url.URL {
}
return u
}
+
+type writerFunc func([]byte) (int, error)
+
+func (f writerFunc) Write(p []byte) (int, error) { return f(p) }
+
+// TestRequestWriteError tests the Write err != nil checks in (*Request).write.
+func TestRequestWriteError(t *testing.T) {
+ failAfter, writeCount := 0, 0
+ errFail := errors.New("fake write failure")
+
+ // w is the buffered io.Writer to write the request to. It
+ // fails exactly once on its Nth Write call, as controlled by
+ // failAfter. It also tracks the number of calls in
+ // writeCount.
+ w := struct {
+ io.ByteWriter // to avoid being wrapped by a bufio.Writer
+ io.Writer
+ }{
+ nil,
+ writerFunc(func(p []byte) (n int, err error) {
+ writeCount++
+ if failAfter == 0 {
+ err = errFail
+ }
+ failAfter--
+ return len(p), err
+ }),
+ }
+
+ req, _ := NewRequest("GET", "http://example.com/", nil)
+ const writeCalls = 4 // number of Write calls in current implementation
+ sawGood := false
+ for n := 0; n <= writeCalls+2; n++ {
+ failAfter = n
+ writeCount = 0
+ err := req.Write(w)
+ var wantErr error
+ if n < writeCalls {
+ wantErr = errFail
+ }
+ if err != wantErr {
+ t.Errorf("for fail-after %d Writes, err = %v; want %v", n, err, wantErr)
+ continue
+ }
+ if err == nil {
+ sawGood = true
+ if writeCount != writeCalls {
+ t.Fatalf("writeCalls constant is outdated in test")
+ }
+ }
+ if writeCount > writeCalls || writeCount > n+1 {
+ t.Errorf("for fail-after %d, saw unexpectedly high (%d) write calls", n, writeCount)
+ }
+ }
+ if !sawGood {
+ t.Fatalf("writeCalls constant is outdated in test")
+ }
+}
diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go
index 4b8946f7ae4..06e940d9aba 100644
--- a/libgo/go/net/http/response_test.go
+++ b/libgo/go/net/http/response_test.go
@@ -12,6 +12,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net/http/internal"
"net/url"
"reflect"
"regexp"
@@ -376,6 +377,34 @@ some body`,
"Body here\n",
},
+
+ // 206 Partial Content. golang.org/issue/8923
+ {
+ "HTTP/1.1 206 Partial Content\r\n" +
+ "Content-Type: text/plain; charset=utf-8\r\n" +
+ "Accept-Ranges: bytes\r\n" +
+ "Content-Range: bytes 0-5/1862\r\n" +
+ "Content-Length: 6\r\n\r\n" +
+ "foobar",
+
+ Response{
+ Status: "206 Partial Content",
+ StatusCode: 206,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("GET"),
+ Header: Header{
+ "Accept-Ranges": []string{"bytes"},
+ "Content-Length": []string{"6"},
+ "Content-Type": []string{"text/plain; charset=utf-8"},
+ "Content-Range": []string{"bytes 0-5/1862"},
+ },
+ ContentLength: 6,
+ },
+
+ "foobar",
+ },
}
func TestReadResponse(t *testing.T) {
@@ -451,7 +480,7 @@ func TestReadResponseCloseInMiddle(t *testing.T) {
}
var wr io.Writer = &buf
if test.chunked {
- wr = newChunkedWriter(wr)
+ wr = internal.NewChunkedWriter(wr)
}
if test.compressed {
buf.WriteString("Content-Encoding: gzip\r\n")
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index 8371dd82f58..6bd168d3de3 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -15,6 +15,7 @@ import (
"io"
"io/ioutil"
"log"
+ "math/rand"
"net"
. "net/http"
"net/http/httptest"
@@ -777,6 +778,35 @@ func TestChunkedResponseHeaders(t *testing.T) {
}
}
+func TestIdentityResponseHeaders(t *testing.T) {
+ defer afterTest(t)
+ log.SetOutput(ioutil.Discard) // is noisy otherwise
+ defer log.SetOutput(os.Stderr)
+
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Transfer-Encoding", "identity")
+ w.(Flusher).Flush()
+ fmt.Fprintf(w, "I am an identity response.")
+ }))
+ defer ts.Close()
+
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get error: %v", err)
+ }
+ defer res.Body.Close()
+
+ if g, e := res.TransferEncoding, []string(nil); !reflect.DeepEqual(g, e) {
+ t.Errorf("expected TransferEncoding of %v; got %v", e, g)
+ }
+ if _, haveCL := res.Header["Content-Length"]; haveCL {
+ t.Errorf("Unexpected Content-Length")
+ }
+ if !res.Close {
+ t.Errorf("expected Connection: close; got %v", res.Close)
+ }
+}
+
// Test304Responses verifies that 304s don't declare that they're
// chunking in their response headers and aren't allowed to produce
// output.
@@ -1188,6 +1218,82 @@ func TestTimeoutHandler(t *testing.T) {
}
}
+// See issues 8209 and 8414.
+func TestTimeoutHandlerRace(t *testing.T) {
+ defer afterTest(t)
+
+ delayHi := HandlerFunc(func(w ResponseWriter, r *Request) {
+ ms, _ := strconv.Atoi(r.URL.Path[1:])
+ if ms == 0 {
+ ms = 1
+ }
+ for i := 0; i < ms; i++ {
+ w.Write([]byte("hi"))
+ time.Sleep(time.Millisecond)
+ }
+ })
+
+ ts := httptest.NewServer(TimeoutHandler(delayHi, 20*time.Millisecond, ""))
+ defer ts.Close()
+
+ var wg sync.WaitGroup
+ gate := make(chan bool, 10)
+ n := 50
+ if testing.Short() {
+ n = 10
+ gate = make(chan bool, 3)
+ }
+ for i := 0; i < n; i++ {
+ gate <- true
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer func() { <-gate }()
+ res, err := Get(fmt.Sprintf("%s/%d", ts.URL, rand.Intn(50)))
+ if err == nil {
+ io.Copy(ioutil.Discard, res.Body)
+ res.Body.Close()
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+// See issues 8209 and 8414.
+func TestTimeoutHandlerRaceHeader(t *testing.T) {
+ defer afterTest(t)
+
+ delay204 := HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(204)
+ })
+
+ ts := httptest.NewServer(TimeoutHandler(delay204, time.Nanosecond, ""))
+ defer ts.Close()
+
+ var wg sync.WaitGroup
+ gate := make(chan bool, 50)
+ n := 500
+ if testing.Short() {
+ n = 10
+ }
+ for i := 0; i < n; i++ {
+ gate <- true
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer func() { <-gate }()
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer res.Body.Close()
+ io.Copy(ioutil.Discard, res.Body)
+ }()
+ }
+ wg.Wait()
+}
+
// Verifies we don't path.Clean() on the wrong parts in redirects.
func TestRedirectMunging(t *testing.T) {
req, _ := NewRequest("GET", "http://example.com/", nil)
@@ -2405,13 +2511,13 @@ func TestServerConnState(t *testing.T) {
}
want := map[int][]ConnState{
- 1: []ConnState{StateNew, StateActive, StateIdle, StateActive, StateClosed},
- 2: []ConnState{StateNew, StateActive, StateIdle, StateActive, StateClosed},
- 3: []ConnState{StateNew, StateActive, StateHijacked},
- 4: []ConnState{StateNew, StateActive, StateHijacked},
- 5: []ConnState{StateNew, StateClosed},
- 6: []ConnState{StateNew, StateActive, StateClosed},
- 7: []ConnState{StateNew, StateActive, StateIdle, StateClosed},
+ 1: {StateNew, StateActive, StateIdle, StateActive, StateClosed},
+ 2: {StateNew, StateActive, StateIdle, StateActive, StateClosed},
+ 3: {StateNew, StateActive, StateHijacked},
+ 4: {StateNew, StateActive, StateHijacked},
+ 5: {StateNew, StateClosed},
+ 6: {StateNew, StateActive, StateClosed},
+ 7: {StateNew, StateActive, StateIdle, StateClosed},
}
logString := func(m map[int][]ConnState) string {
var b bytes.Buffer
@@ -2531,6 +2637,126 @@ func TestServerConnStateNew(t *testing.T) {
}
}
+type closeWriteTestConn struct {
+ rwTestConn
+ didCloseWrite bool
+}
+
+func (c *closeWriteTestConn) CloseWrite() error {
+ c.didCloseWrite = true
+ return nil
+}
+
+func TestCloseWrite(t *testing.T) {
+ var srv Server
+ var testConn closeWriteTestConn
+ c, err := ExportServerNewConn(&srv, &testConn)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ExportCloseWriteAndWait(c)
+ if !testConn.didCloseWrite {
+ t.Error("didn't see CloseWrite call")
+ }
+}
+
+// This verifies that a handler can Flush and then Hijack.
+//
+// An similar test crashed once during development, but it was only
+// testing this tangentially and temporarily until another TODO was
+// fixed.
+//
+// So add an explicit test for this.
+func TestServerFlushAndHijack(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ io.WriteString(w, "Hello, ")
+ w.(Flusher).Flush()
+ conn, buf, _ := w.(Hijacker).Hijack()
+ buf.WriteString("6\r\nworld!\r\n0\r\n\r\n")
+ if err := buf.Flush(); err != nil {
+ t.Error(err)
+ }
+ if err := conn.Close(); err != nil {
+ t.Error(err)
+ }
+ }))
+ defer ts.Close()
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := "Hello, world!"; string(all) != want {
+ t.Errorf("Got %q; want %q", all, want)
+ }
+}
+
+// golang.org/issue/8534 -- the Server shouldn't reuse a connection
+// for keep-alive after it's seen any Write error (e.g. a timeout) on
+// that net.Conn.
+//
+// To test, verify we don't timeout or see fewer unique client
+// addresses (== unique connections) than requests.
+func TestServerKeepAliveAfterWriteError(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in -short mode")
+ }
+ defer afterTest(t)
+ const numReq = 3
+ addrc := make(chan string, numReq)
+ ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ addrc <- r.RemoteAddr
+ time.Sleep(500 * time.Millisecond)
+ w.(Flusher).Flush()
+ }))
+ ts.Config.WriteTimeout = 250 * time.Millisecond
+ ts.Start()
+ defer ts.Close()
+
+ errc := make(chan error, numReq)
+ go func() {
+ defer close(errc)
+ for i := 0; i < numReq; i++ {
+ res, err := Get(ts.URL)
+ if res != nil {
+ res.Body.Close()
+ }
+ errc <- err
+ }
+ }()
+
+ timeout := time.NewTimer(numReq * 2 * time.Second) // 4x overkill
+ defer timeout.Stop()
+ addrSeen := map[string]bool{}
+ numOkay := 0
+ for {
+ select {
+ case v := <-addrc:
+ addrSeen[v] = true
+ case err, ok := <-errc:
+ if !ok {
+ if len(addrSeen) != numReq {
+ t.Errorf("saw %d unique client addresses; want %d", len(addrSeen), numReq)
+ }
+ if numOkay != 0 {
+ t.Errorf("got %d successful client requests; want 0", numOkay)
+ }
+ return
+ }
+ if err == nil {
+ numOkay++
+ }
+ case <-timeout.C:
+ t.Fatal("timeout waiting for requests to complete")
+ }
+ }
+}
+
func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs()
b.StopTimer()
@@ -2560,24 +2786,44 @@ func BenchmarkClientServer(b *testing.B) {
}
func BenchmarkClientServerParallel4(b *testing.B) {
- benchmarkClientServerParallel(b, 4)
+ benchmarkClientServerParallel(b, 4, false)
}
func BenchmarkClientServerParallel64(b *testing.B) {
- benchmarkClientServerParallel(b, 64)
+ benchmarkClientServerParallel(b, 64, false)
}
-func benchmarkClientServerParallel(b *testing.B, parallelism int) {
+func BenchmarkClientServerParallelTLS4(b *testing.B) {
+ benchmarkClientServerParallel(b, 4, true)
+}
+
+func BenchmarkClientServerParallelTLS64(b *testing.B) {
+ benchmarkClientServerParallel(b, 64, true)
+}
+
+func benchmarkClientServerParallel(b *testing.B, parallelism int, useTLS bool) {
b.ReportAllocs()
- ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
+ ts := httptest.NewUnstartedServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
fmt.Fprintf(rw, "Hello world.\n")
}))
+ if useTLS {
+ ts.StartTLS()
+ } else {
+ ts.Start()
+ }
defer ts.Close()
b.ResetTimer()
b.SetParallelism(parallelism)
b.RunParallel(func(pb *testing.PB) {
+ noVerifyTransport := &Transport{
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ },
+ }
+ defer noVerifyTransport.CloseIdleConnections()
+ client := &Client{Transport: noVerifyTransport}
for pb.Next() {
- res, err := Get(ts.URL)
+ res, err := client.Get(ts.URL)
if err != nil {
b.Logf("Get: %v", err)
continue
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index eae097eb8e9..008d5aa7a74 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -42,6 +42,12 @@ var (
// and then return. Returning signals that the request is finished
// and that the HTTP server can move on to the next request on
// the connection.
+//
+// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
+// that the effect of the panic was isolated to the active request.
+// It recovers the panic, logs a stack trace to the server error log,
+// and hangs up the connection.
+//
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
@@ -108,6 +114,8 @@ type conn struct {
remoteAddr string // network address of remote side
server *Server // the Server on which the connection arrived
rwc net.Conn // i/o connection
+ w io.Writer // checkConnErrorWriter's copy of wrc, not zeroed on Hijack
+ werr error // any errors writing to w
sr liveSwitchReader // where the LimitReader reads from; usually the rwc
lr *io.LimitedReader // io.LimitReader(sr)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
@@ -426,13 +434,14 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
c.remoteAddr = rwc.RemoteAddr().String()
c.server = srv
c.rwc = rwc
+ c.w = rwc
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
c.sr = liveSwitchReader{r: c.rwc}
c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
br := newBufioReader(c.lr)
- bw := newBufioWriterSize(c.rwc, 4<<10)
+ bw := newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
c.buf = bufio.NewReadWriter(br, bw)
return c, nil
}
@@ -833,13 +842,20 @@ func (cw *chunkWriter) writeHeader(p []byte) {
} else if hasCL {
delHeader("Transfer-Encoding")
} else if w.req.ProtoAtLeast(1, 1) {
- // HTTP/1.1 or greater: use chunked transfer encoding
- // to avoid closing the connection at EOF.
- // TODO: this blows away any custom or stacked Transfer-Encoding they
- // might have set. Deal with that as need arises once we have a valid
- // use case.
- cw.chunking = true
- setHeader.transferEncoding = "chunked"
+ // HTTP/1.1 or greater: Transfer-Encoding has been set to identity, and no
+ // content-length has been provided. The connection must be closed after the
+ // reply is written, and no chunking is to be done. This is the setup
+ // recommended in the Server-Sent Events candidate recommendation 11,
+ // section 8.
+ if hasTE && te == "identity" {
+ cw.chunking = false
+ w.closeAfterReply = true
+ } else {
+ // HTTP/1.1 or greater: use chunked transfer encoding
+ // to avoid closing the connection at EOF.
+ cw.chunking = true
+ setHeader.transferEncoding = "chunked"
+ }
} else {
// HTTP version < 1.1: cannot do chunked transfer
// encoding and we don't know the Content-Length so
@@ -943,8 +959,10 @@ func (w *response) bodyAllowed() bool {
// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes
// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type)
// and which writes the chunk headers, if needed.
-// 4. conn.buf, a bufio.Writer of default (4kB) bytes
-// 5. the rwc, the net.Conn.
+// 4. conn.buf, a bufio.Writer of default (4kB) bytes, writing to ->
+// 5. checkConnErrorWriter{c}, which notes any non-nil error on Write
+// and populates c.werr with it if so. but otherwise writes to:
+// 6. the rwc, the net.Conn.
//
// TODO(bradfitz): short-circuit some of the buffering when the
// initial header contains both a Content-Type and Content-Length.
@@ -1014,6 +1032,12 @@ func (w *response) finishRequest() {
// Did not write enough. Avoid getting out of sync.
w.closeAfterReply = true
}
+
+ // There was some error writing to the underlying connection
+ // during the request, so don't re-use this conn.
+ if w.conn.werr != nil {
+ w.closeAfterReply = true
+ }
}
func (w *response) Flush() {
@@ -1058,15 +1082,21 @@ func (c *conn) close() {
// This timeout is somewhat arbitrary (~latency around the planet).
const rstAvoidanceDelay = 500 * time.Millisecond
+type closeWriter interface {
+ CloseWrite() error
+}
+
+var _ closeWriter = (*net.TCPConn)(nil)
+
// closeWrite flushes any outstanding data and sends a FIN packet (if
// client is connected via TCP), signalling that we're done. We then
-// pause for a bit, hoping the client processes it before `any
+// pause for a bit, hoping the client processes it before any
// subsequent RST.
//
// See http://golang.org/issue/3595
func (c *conn) closeWriteAndWait() {
c.finalFlush()
- if tcp, ok := c.rwc.(*net.TCPConn); ok {
+ if tcp, ok := c.rwc.(closeWriter); ok {
tcp.CloseWrite()
}
time.Sleep(rstAvoidanceDelay)
@@ -1916,9 +1946,9 @@ func (tw *timeoutWriter) Header() Header {
func (tw *timeoutWriter) Write(p []byte) (int, error) {
tw.mu.Lock()
- timedOut := tw.timedOut
- tw.mu.Unlock()
- if timedOut {
+ defer tw.mu.Unlock()
+ tw.wroteHeader = true // implicitly at least
+ if tw.timedOut {
return 0, ErrHandlerTimeout
}
return tw.w.Write(p)
@@ -1926,12 +1956,11 @@ func (tw *timeoutWriter) Write(p []byte) (int, error) {
func (tw *timeoutWriter) WriteHeader(code int) {
tw.mu.Lock()
+ defer tw.mu.Unlock()
if tw.timedOut || tw.wroteHeader {
- tw.mu.Unlock()
return
}
tw.wroteHeader = true
- tw.mu.Unlock()
tw.w.WriteHeader(code)
}
@@ -2050,3 +2079,18 @@ func (c *loggingConn) Close() (err error) {
log.Printf("%s.Close() = %v", c.name, err)
return
}
+
+// checkConnErrorWriter writes to c.rwc and records any write errors to c.werr.
+// It only contains one field (and a pointer field at that), so it
+// fits in an interface value without an extra allocation.
+type checkConnErrorWriter struct {
+ c *conn
+}
+
+func (w checkConnErrorWriter) Write(p []byte) (n int, err error) {
+ n, err = w.c.w.Write(p) // c.w == c.rwc, except after a hijack, when rwc is nil.
+ if err != nil && w.c.werr == nil {
+ w.c.werr = err
+ }
+ return
+}
diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go
index 7f63686528a..520500330bc 100644
--- a/libgo/go/net/http/transfer.go
+++ b/libgo/go/net/http/transfer.go
@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net/http/internal"
"net/textproto"
"sort"
"strconv"
@@ -18,6 +19,10 @@ import (
"sync"
)
+// ErrLineTooLong is returned when reading request or response bodies
+// with malformed chunked encoding.
+var ErrLineTooLong = internal.ErrLineTooLong
+
type errorReader struct {
err error
}
@@ -198,7 +203,7 @@ func (t *transferWriter) WriteBody(w io.Writer) error {
// Write body
if t.Body != nil {
if chunked(t.TransferEncoding) {
- cw := newChunkedWriter(w)
+ cw := internal.NewChunkedWriter(w)
_, err = io.Copy(cw, t.Body)
if err == nil {
err = cw.Close()
@@ -223,7 +228,7 @@ func (t *transferWriter) WriteBody(w io.Writer) error {
}
if !t.ResponseToHEAD && t.ContentLength != -1 && t.ContentLength != ncopy {
- return fmt.Errorf("http: Request.ContentLength=%d with Body length %d",
+ return fmt.Errorf("http: ContentLength=%d with Body length %d",
t.ContentLength, ncopy)
}
@@ -298,7 +303,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t.StatusCode = rr.StatusCode
t.ProtoMajor = rr.ProtoMajor
t.ProtoMinor = rr.ProtoMinor
- t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
+ t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header, true)
isResponse = true
if rr.Request != nil {
t.RequestMethod = rr.Request.Method
@@ -365,7 +370,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
if noBodyExpected(t.RequestMethod) {
t.Body = eofReader
} else {
- t.Body = &body{src: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
+ t.Body = &body{src: internal.NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
}
case realLength == 0:
t.Body = eofReader
@@ -497,7 +502,7 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
// Determine whether to hang up after sending a request and body, or
// receiving a response and body
// 'header' is the request headers
-func shouldClose(major, minor int, header Header) bool {
+func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool {
if major < 1 {
return true
} else if major == 1 && minor == 0 {
@@ -509,7 +514,9 @@ func shouldClose(major, minor int, header Header) bool {
// TODO: Should split on commas, toss surrounding white space,
// and check each field.
if strings.ToLower(header.get("Connection")) == "close" {
- header.Del("Connection")
+ if removeCloseHeader {
+ header.Del("Connection")
+ }
return true
}
}
diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go
index b1cc632a782..782f7cd395b 100644
--- a/libgo/go/net/http/transport.go
+++ b/libgo/go/net/http/transport.go
@@ -43,17 +43,20 @@ var DefaultTransport RoundTripper = &Transport{
// MaxIdleConnsPerHost.
const DefaultMaxIdleConnsPerHost = 2
-// Transport is an implementation of RoundTripper that supports http,
-// https, and http proxies (for either http or https with CONNECT).
+// Transport is an implementation of RoundTripper that supports HTTP,
+// HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT).
// Transport can also cache connections for future re-use.
type Transport struct {
- idleMu sync.Mutex
- idleConn map[connectMethodKey][]*persistConn
- idleConnCh map[connectMethodKey]chan *persistConn
+ idleMu sync.Mutex
+ wantIdle bool // user has requested to close all idle conns
+ idleConn map[connectMethodKey][]*persistConn
+ idleConnCh map[connectMethodKey]chan *persistConn
+
reqMu sync.Mutex
reqCanceler map[*Request]func()
- altMu sync.RWMutex
- altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
+
+ altMu sync.RWMutex
+ altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
// Proxy specifies a function to return a proxy for a given
// Request. If the function returns a non-nil error, the
@@ -61,11 +64,22 @@ type Transport struct {
// If Proxy is nil or returns a nil *URL, no proxy is used.
Proxy func(*Request) (*url.URL, error)
- // Dial specifies the dial function for creating TCP
- // connections.
+ // Dial specifies the dial function for creating unencrypted
+ // TCP connections.
// If Dial is nil, net.Dial is used.
Dial func(network, addr string) (net.Conn, error)
+ // DialTLS specifies an optional dial function for creating
+ // TLS connections for non-proxied HTTPS requests.
+ //
+ // If DialTLS is nil, Dial and TLSClientConfig are used.
+ //
+ // If DialTLS is set, the Dial hook is not used for HTTPS
+ // requests and the TLSClientConfig and TLSHandshakeTimeout
+ // are ignored. The returned net.Conn is assumed to already be
+ // past the TLS handshake.
+ DialTLS func(network, addr string) (net.Conn, error)
+
// TLSClientConfig specifies the TLS configuration to use with
// tls.Client. If nil, the default configuration is used.
TLSClientConfig *tls.Config
@@ -105,15 +119,28 @@ type Transport struct {
// ProxyFromEnvironment returns the URL of the proxy to use for a
// given request, as indicated by the environment variables
-// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy).
-// An error is returned if the proxy environment is invalid.
+// HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions
+// thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https
+// requests.
+//
+// The environment values may be either a complete URL or a
+// "host[:port]", in which case the "http" scheme is assumed.
+// An error is returned if the value is a different form.
+//
// A nil URL and nil error are returned if no proxy is defined in the
-// environment, or a proxy should not be used for the given request.
+// environment, or a proxy should not be used for the given request,
+// as defined by NO_PROXY.
//
// As a special case, if req.URL.Host is "localhost" (with or without
// a port number), then a nil URL and nil error will be returned.
func ProxyFromEnvironment(req *Request) (*url.URL, error) {
- proxy := httpProxyEnv.Get()
+ var proxy string
+ if req.URL.Scheme == "https" {
+ proxy = httpsProxyEnv.Get()
+ }
+ if proxy == "" {
+ proxy = httpProxyEnv.Get()
+ }
if proxy == "" {
return nil, nil
}
@@ -238,6 +265,7 @@ func (t *Transport) CloseIdleConnections() {
m := t.idleConn
t.idleConn = nil
t.idleConnCh = nil
+ t.wantIdle = true
t.idleMu.Unlock()
for _, conns := range m {
for _, pconn := range conns {
@@ -265,6 +293,9 @@ var (
httpProxyEnv = &envOnce{
names: []string{"HTTP_PROXY", "http_proxy"},
}
+ httpsProxyEnv = &envOnce{
+ names: []string{"HTTPS_PROXY", "https_proxy"},
+ }
noProxyEnv = &envOnce{
names: []string{"NO_PROXY", "no_proxy"},
}
@@ -305,7 +336,7 @@ func (t *Transport) connectMethodForRequest(treq *transportRequest) (cm connectM
if t.Proxy != nil {
cm.proxyURL, err = t.Proxy(treq.Request)
}
- return cm, nil
+ return cm, err
}
// proxyAuth returns the Proxy-Authorization header to set
@@ -358,6 +389,11 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
delete(t.idleConnCh, key)
}
}
+ if t.wantIdle {
+ t.idleMu.Unlock()
+ pconn.close()
+ return false
+ }
if t.idleConn == nil {
t.idleConn = make(map[connectMethodKey][]*persistConn)
}
@@ -386,6 +422,7 @@ func (t *Transport) getIdleConnCh(cm connectMethod) chan *persistConn {
key := cm.key()
t.idleMu.Lock()
defer t.idleMu.Unlock()
+ t.wantIdle = false
if t.idleConnCh == nil {
t.idleConnCh = make(map[connectMethodKey]chan *persistConn)
}
@@ -444,6 +481,9 @@ func (t *Transport) dial(network, addr string) (c net.Conn, err error) {
return net.Dial(network, addr)
}
+// Testing hooks:
+var prePendingDial, postPendingDial func()
+
// getConn dials and creates a new persistConn to the target as
// specified in the connectMethod. This includes doing a proxy CONNECT
// and/or setting up TLS. If this doesn't return an error, the persistConn
@@ -460,9 +500,17 @@ func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error
dialc := make(chan dialRes)
handlePendingDial := func() {
- if v := <-dialc; v.err == nil {
- t.putIdleConn(v.pc)
+ if prePendingDial != nil {
+ prePendingDial()
}
+ go func() {
+ if v := <-dialc; v.err == nil {
+ t.putIdleConn(v.pc)
+ }
+ if postPendingDial != nil {
+ postPendingDial()
+ }
+ }()
}
cancelc := make(chan struct{})
@@ -484,53 +532,65 @@ func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error
// else's dial that they didn't use.
// But our dial is still going, so give it away
// when it finishes:
- go handlePendingDial()
+ handlePendingDial()
return pc, nil
case <-cancelc:
- go handlePendingDial()
+ handlePendingDial()
return nil, errors.New("net/http: request canceled while waiting for connection")
}
}
func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
- conn, err := t.dial("tcp", cm.addr())
- if err != nil {
- if cm.proxyURL != nil {
- err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err)
- }
- return nil, err
- }
-
- pa := cm.proxyAuth()
-
pconn := &persistConn{
t: t,
cacheKey: cm.key(),
- conn: conn,
reqch: make(chan requestAndChan, 1),
writech: make(chan writeRequest, 1),
closech: make(chan struct{}),
writeErrCh: make(chan error, 1),
}
+ tlsDial := t.DialTLS != nil && cm.targetScheme == "https" && cm.proxyURL == nil
+ if tlsDial {
+ var err error
+ pconn.conn, err = t.DialTLS("tcp", cm.addr())
+ if err != nil {
+ return nil, err
+ }
+ if tc, ok := pconn.conn.(*tls.Conn); ok {
+ cs := tc.ConnectionState()
+ pconn.tlsState = &cs
+ }
+ } else {
+ conn, err := t.dial("tcp", cm.addr())
+ if err != nil {
+ if cm.proxyURL != nil {
+ err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err)
+ }
+ return nil, err
+ }
+ pconn.conn = conn
+ }
+ // Proxy setup.
switch {
case cm.proxyURL == nil:
- // Do nothing.
+ // Do nothing. Not using a proxy.
case cm.targetScheme == "http":
pconn.isProxy = true
- if pa != "" {
+ if pa := cm.proxyAuth(); pa != "" {
pconn.mutateHeaderFunc = func(h Header) {
h.Set("Proxy-Authorization", pa)
}
}
case cm.targetScheme == "https":
+ conn := pconn.conn
connectReq := &Request{
Method: "CONNECT",
URL: &url.URL{Opaque: cm.targetAddr},
Host: cm.targetAddr,
Header: make(Header),
}
- if pa != "" {
+ if pa := cm.proxyAuth(); pa != "" {
connectReq.Header.Set("Proxy-Authorization", pa)
}
connectReq.Write(conn)
@@ -551,7 +611,7 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
}
}
- if cm.targetScheme == "https" {
+ if cm.targetScheme == "https" && !tlsDial {
// Initiate TLS and check remote host name against certificate.
cfg := t.TLSClientConfig
if cfg == nil || cfg.ServerName == "" {
@@ -564,7 +624,7 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
cfg = &clone
}
}
- plainConn := conn
+ plainConn := pconn.conn
tlsConn := tls.Client(plainConn, cfg)
errc := make(chan error, 2)
var timer *time.Timer // for canceling TLS handshake
@@ -980,11 +1040,14 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
}
// Ask for a compressed version if the caller didn't set their
- // own value for Accept-Encoding. We only attempted to
+ // own value for Accept-Encoding. We only attempt to
// uncompress the gzip stream if we were the layer that
// requested it.
requestedGzip := false
- if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" && req.Method != "HEAD" {
+ if !pc.t.DisableCompression &&
+ req.Header.Get("Accept-Encoding") == "" &&
+ req.Header.Get("Range") == "" &&
+ req.Method != "HEAD" {
// Request gzip only, not deflate. Deflate is ambiguous and
// not as universally supported anyway.
// See: http://www.gzip.org/zlib/zlib_faq.html#faq38
@@ -993,6 +1056,10 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
// due to a bug in nginx:
// http://trac.nginx.org/nginx/ticket/358
// http://golang.org/issue/5522
+ //
+ // We don't request gzip if the request is for a range, since
+ // auto-decoding a portion of a gzipped document will just fail
+ // anyway. See http://golang.org/issue/8923
requestedGzip = true
req.extraHeaders().Set("Accept-Encoding", "gzip")
}
diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go
index 964ca0fca54..defa6337082 100644
--- a/libgo/go/net/http/transport_test.go
+++ b/libgo/go/net/http/transport_test.go
@@ -1063,20 +1063,18 @@ func TestTransportConcurrency(t *testing.T) {
var wg sync.WaitGroup
wg.Add(numReqs)
- tr := &Transport{
- Dial: func(netw, addr string) (c net.Conn, err error) {
- // Due to the Transport's "socket late
- // binding" (see idleConnCh in transport.go),
- // the numReqs HTTP requests below can finish
- // with a dial still outstanding. So count
- // our dials as work too so the leak checker
- // doesn't complain at us.
- wg.Add(1)
- defer wg.Done()
- return net.Dial(netw, addr)
- },
- }
+ // Due to the Transport's "socket late binding" (see
+ // idleConnCh in transport.go), the numReqs HTTP requests
+ // below can finish with a dial still outstanding. To keep
+ // the leak checker happy, keep track of pending dials and
+ // wait for them to finish (and be closed or returned to the
+ // idle pool) before we close idle connections.
+ SetPendingDialHooks(func() { wg.Add(1) }, wg.Done)
+ defer SetPendingDialHooks(nil, nil)
+
+ tr := &Transport{}
defer tr.CloseIdleConnections()
+
c := &Client{Transport: tr}
reqs := make(chan string)
defer close(reqs)
@@ -1703,26 +1701,40 @@ Content-Length: %d
}
type proxyFromEnvTest struct {
- req string // URL to fetch; blank means "http://example.com"
- env string
- noenv string
+ req string // URL to fetch; blank means "http://example.com"
+
+ env string // HTTP_PROXY
+ httpsenv string // HTTPS_PROXY
+ noenv string // NO_RPXY
+
want string
wanterr error
}
func (t proxyFromEnvTest) String() string {
var buf bytes.Buffer
+ space := func() {
+ if buf.Len() > 0 {
+ buf.WriteByte(' ')
+ }
+ }
if t.env != "" {
fmt.Fprintf(&buf, "http_proxy=%q", t.env)
}
+ if t.httpsenv != "" {
+ space()
+ fmt.Fprintf(&buf, "https_proxy=%q", t.httpsenv)
+ }
if t.noenv != "" {
- fmt.Fprintf(&buf, " no_proxy=%q", t.noenv)
+ space()
+ fmt.Fprintf(&buf, "no_proxy=%q", t.noenv)
}
req := "http://example.com"
if t.req != "" {
req = t.req
}
- fmt.Fprintf(&buf, " req=%q", req)
+ space()
+ fmt.Fprintf(&buf, "req=%q", req)
return strings.TrimSpace(buf.String())
}
@@ -1733,7 +1745,15 @@ var proxyFromEnvTests = []proxyFromEnvTest{
{env: "https://cache.corp.example.com", want: "https://cache.corp.example.com"},
{env: "http://127.0.0.1:8080", want: "http://127.0.0.1:8080"},
{env: "https://127.0.0.1:8080", want: "https://127.0.0.1:8080"},
+
+ // Don't use secure for http
+ {req: "http://insecure.tld/", env: "http.proxy.tld", httpsenv: "secure.proxy.tld", want: "http://http.proxy.tld"},
+ // Use secure for https.
+ {req: "https://secure.tld/", env: "http.proxy.tld", httpsenv: "secure.proxy.tld", want: "http://secure.proxy.tld"},
+ {req: "https://secure.tld/", env: "http.proxy.tld", httpsenv: "https://secure.proxy.tld", want: "https://secure.proxy.tld"},
+
{want: "<nil>"},
+
{noenv: "example.com", req: "http://example.com/", env: "proxy", want: "<nil>"},
{noenv: ".example.com", req: "http://example.com/", env: "proxy", want: "<nil>"},
{noenv: "ample.com", req: "http://example.com/", env: "proxy", want: "http://proxy"},
@@ -1745,6 +1765,7 @@ func TestProxyFromEnvironment(t *testing.T) {
ResetProxyEnv()
for _, tt := range proxyFromEnvTests {
os.Setenv("HTTP_PROXY", tt.env)
+ os.Setenv("HTTPS_PROXY", tt.httpsenv)
os.Setenv("NO_PROXY", tt.noenv)
ResetCachedEnvironment()
reqURL := tt.req
@@ -2098,6 +2119,136 @@ func TestTransportClosesBodyOnError(t *testing.T) {
}
}
+func TestTransportDialTLS(t *testing.T) {
+ var mu sync.Mutex // guards following
+ var gotReq, didDial bool
+
+ ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ mu.Lock()
+ gotReq = true
+ mu.Unlock()
+ }))
+ defer ts.Close()
+ tr := &Transport{
+ DialTLS: func(netw, addr string) (net.Conn, error) {
+ mu.Lock()
+ didDial = true
+ mu.Unlock()
+ c, err := tls.Dial(netw, addr, &tls.Config{
+ InsecureSkipVerify: true,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return c, c.Handshake()
+ },
+ }
+ defer tr.CloseIdleConnections()
+ client := &Client{Transport: tr}
+ res, err := client.Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ mu.Lock()
+ if !gotReq {
+ t.Error("didn't get request")
+ }
+ if !didDial {
+ t.Error("didn't use dial hook")
+ }
+}
+
+// Test for issue 8755
+// Ensure that if a proxy returns an error, it is exposed by RoundTrip
+func TestRoundTripReturnsProxyError(t *testing.T) {
+ badProxy := func(*http.Request) (*url.URL, error) {
+ return nil, errors.New("errorMessage")
+ }
+
+ tr := &Transport{Proxy: badProxy}
+
+ req, _ := http.NewRequest("GET", "http://example.com", nil)
+
+ _, err := tr.RoundTrip(req)
+
+ if err == nil {
+ t.Error("Expected proxy error to be returned by RoundTrip")
+ }
+}
+
+// tests that putting an idle conn after a call to CloseIdleConns does return it
+func TestTransportCloseIdleConnsThenReturn(t *testing.T) {
+ tr := &Transport{}
+ wantIdle := func(when string, n int) bool {
+ got := tr.IdleConnCountForTesting("|http|example.com") // key used by PutIdleTestConn
+ if got == n {
+ return true
+ }
+ t.Errorf("%s: idle conns = %d; want %d", when, got, n)
+ return false
+ }
+ wantIdle("start", 0)
+ if !tr.PutIdleTestConn() {
+ t.Fatal("put failed")
+ }
+ if !tr.PutIdleTestConn() {
+ t.Fatal("second put failed")
+ }
+ wantIdle("after put", 2)
+ tr.CloseIdleConnections()
+ if !tr.IsIdleForTesting() {
+ t.Error("should be idle after CloseIdleConnections")
+ }
+ wantIdle("after close idle", 0)
+ if tr.PutIdleTestConn() {
+ t.Fatal("put didn't fail")
+ }
+ wantIdle("after second put", 0)
+
+ tr.RequestIdleConnChForTesting() // should toggle the transport out of idle mode
+ if tr.IsIdleForTesting() {
+ t.Error("shouldn't be idle after RequestIdleConnChForTesting")
+ }
+ if !tr.PutIdleTestConn() {
+ t.Fatal("after re-activation")
+ }
+ wantIdle("after final put", 1)
+}
+
+// This tests that an client requesting a content range won't also
+// implicitly ask for gzip support. If they want that, they need to do it
+// on their own.
+// golang.org/issue/8923
+func TestTransportRangeAndGzip(t *testing.T) {
+ defer afterTest(t)
+ reqc := make(chan *Request, 1)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ reqc <- r
+ }))
+ defer ts.Close()
+
+ req, _ := NewRequest("GET", ts.URL, nil)
+ req.Header.Set("Range", "bytes=7-11")
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ select {
+ case r := <-reqc:
+ if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
+ t.Error("Transport advertised gzip support in the Accept header")
+ }
+ if r.Header.Get("Range") == "" {
+ t.Error("no Range in request")
+ }
+ case <-time.After(10 * time.Second):
+ t.Fatal("timeout")
+ }
+ res.Body.Close()
+}
+
func wantBody(res *http.Response, err error, want string) error {
if err != nil {
return err
diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go
index 0582009b8bd..4a93e97b39d 100644
--- a/libgo/go/net/ip.go
+++ b/libgo/go/net/ip.go
@@ -287,6 +287,7 @@ func (ip IP) String() string {
if j > i && j-i > e1-e0 {
e0 = i
e1 = j
+ i = j
}
}
// The symbol "::" MUST NOT be used to shorten just one 16 bit 0 field.
@@ -295,21 +296,23 @@ func (ip IP) String() string {
e1 = -1
}
+ const maxLen = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
+ b := make([]byte, 0, maxLen)
+
// Print with possible :: in place of run of zeros
- var s string
for i := 0; i < IPv6len; i += 2 {
if i == e0 {
- s += "::"
+ b = append(b, ':', ':')
i = e1
if i >= IPv6len {
break
}
} else if i > 0 {
- s += ":"
+ b = append(b, ':')
}
- s += itox((uint(p[i])<<8)|uint(p[i+1]), 1)
+ b = appendHex(b, (uint32(p[i])<<8)|uint32(p[i+1]))
}
- return s
+ return string(b)
}
// ipEmptyString is like ip.String except that it returns
@@ -419,14 +422,14 @@ func (m IPMask) Size() (ones, bits int) {
// String returns the hexadecimal form of m, with no punctuation.
func (m IPMask) String() string {
- s := ""
- for _, b := range m {
- s += itox(uint(b), 2)
- }
- if len(s) == 0 {
+ if len(m) == 0 {
return "<nil>"
}
- return s
+ buf := make([]byte, len(m)*2)
+ for i, b := range m {
+ buf[i*2], buf[i*2+1] = hexDigit[b>>4], hexDigit[b&0xf]
+ }
+ return string(buf)
}
func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) {
@@ -646,11 +649,16 @@ func (e *ParseError) Error() string {
// If s is not a valid textual representation of an IP address,
// ParseIP returns nil.
func ParseIP(s string) IP {
- if ip := parseIPv4(s); ip != nil {
- return ip
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '.':
+ return parseIPv4(s)
+ case ':':
+ ip, _ := parseIPv6(s, false)
+ return ip
+ }
}
- ip, _ := parseIPv6(s, false)
- return ip
+ return nil
}
// ParseCIDR parses s as a CIDR notation IP address and mask,
diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go
index ffeb9d315e7..485ff51153b 100644
--- a/libgo/go/net/ip_test.go
+++ b/libgo/go/net/ip_test.go
@@ -44,6 +44,14 @@ func TestParseIP(t *testing.T) {
}
}
+func BenchmarkParseIP(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, tt := range parseIPTests {
+ ParseIP(tt.in)
+ }
+ }
+}
+
// Issue 6339
func TestMarshalEmptyIP(t *testing.T) {
for _, in := range [][]byte{nil, []byte("")} {
@@ -91,6 +99,16 @@ func TestIPString(t *testing.T) {
}
}
+func BenchmarkIPString(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, tt := range ipStringTests {
+ if tt.in != nil {
+ tt.in.String()
+ }
+ }
+ }
+}
+
var ipMaskTests = []struct {
in IP
mask IPMask
@@ -131,6 +149,14 @@ func TestIPMaskString(t *testing.T) {
}
}
+func BenchmarkIPMaskString(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, tt := range ipMaskStringTests {
+ tt.in.String()
+ }
+ }
+}
+
var parseCIDRTests = []struct {
in string
ip IP
diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go
index 0632dafc65e..92dc8dc5694 100644
--- a/libgo/go/net/ipraw_test.go
+++ b/libgo/go/net/ipraw_test.go
@@ -68,6 +68,11 @@ func skipRawSocketTest(t *testing.T) (skip bool, skipmsg string) {
}
func TestResolveIPAddr(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
for _, tt := range resolveIPAddrTests {
addr, err := ResolveIPAddr(tt.net, tt.litAddrOrName)
if err != tt.err {
diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go
index bbb3f3ed66c..99b081ba8c8 100644
--- a/libgo/go/net/iprawsock_posix.go
+++ b/libgo/go/net/iprawsock_posix.go
@@ -198,7 +198,7 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
if raddr == nil {
return nil, &OpError{Op: "dial", Net: netProto, Addr: nil, Err: errMissingAddress}
}
- fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
+ fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_RAW, proto, "dial")
if err != nil {
return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: err}
}
@@ -219,7 +219,7 @@ func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
default:
return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: UnknownNetworkError(netProto)}
}
- fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
+ fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_RAW, proto, "listen")
if err != nil {
return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: err}
}
diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go
index 2ba4c8efd53..f9ebe40a21e 100644
--- a/libgo/go/net/ipsock_posix.go
+++ b/libgo/go/net/ipsock_posix.go
@@ -132,9 +132,9 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
// Internet sockets (TCP, UDP, IP)
-func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string) (fd *netFD, err error) {
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
- return socket(net, family, sotype, proto, ipv6only, laddr, raddr, deadline, toAddr)
+ return socket(net, family, sotype, proto, ipv6only, laddr, raddr, deadline)
}
func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go
index 20f20578cde..aeffe6c9b72 100644
--- a/libgo/go/net/lookup.go
+++ b/libgo/go/net/lookup.go
@@ -40,10 +40,16 @@ func lookupIPMerge(host string) (addrs []IP, err error) {
addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
return lookupIP(host)
})
+ return lookupIPReturn(addrsi, err, shared)
+}
+
+// lookupIPReturn turns the return values from singleflight.Do into
+// the return values from LookupIP.
+func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IP, error) {
if err != nil {
return nil, err
}
- addrs = addrsi.([]IP)
+ addrs := addrsi.([]IP)
if shared {
clone := make([]IP, len(addrs))
copy(clone, addrs)
@@ -52,41 +58,40 @@ func lookupIPMerge(host string) (addrs []IP, err error) {
return addrs, nil
}
+// lookupIPDeadline looks up a hostname with a deadline.
func lookupIPDeadline(host string, deadline time.Time) (addrs []IP, err error) {
if deadline.IsZero() {
return lookupIPMerge(host)
}
- // TODO(bradfitz): consider pushing the deadline down into the
- // name resolution functions. But that involves fixing it for
- // the native Go resolver, cgo, Windows, etc.
- //
- // In the meantime, just use a goroutine. Most users affected
- // by http://golang.org/issue/2631 are due to TCP connections
- // to unresponsive hosts, not DNS.
+ // We could push the deadline down into the name resolution
+ // functions. However, the most commonly used implementation
+ // calls getaddrinfo, which has no timeout.
+
timeout := deadline.Sub(time.Now())
if timeout <= 0 {
- err = errTimeout
- return
+ return nil, errTimeout
}
t := time.NewTimer(timeout)
defer t.Stop()
- type res struct {
- addrs []IP
- err error
- }
- resc := make(chan res, 1)
- go func() {
- a, err := lookupIPMerge(host)
- resc <- res{a, err}
- }()
+
+ ch := lookupGroup.DoChan(host, func() (interface{}, error) {
+ return lookupIP(host)
+ })
+
select {
case <-t.C:
- err = errTimeout
- case r := <-resc:
- addrs, err = r.addrs, r.err
+ // The DNS lookup timed out for some reason. Force
+ // future requests to start the DNS lookup again
+ // rather than waiting for the current lookup to
+ // complete. See issue 8602.
+ lookupGroup.Forget(host)
+
+ return nil, errTimeout
+
+ case r := <-ch:
+ return lookupIPReturn(r.v, r.err, r.shared)
}
- return
}
// LookupPort looks up the port for the given network and service.
diff --git a/libgo/go/net/lookup_stub.go b/libgo/go/net/lookup_stub.go
new file mode 100644
index 00000000000..502aafb2702
--- /dev/null
+++ b/libgo/go/net/lookup_stub.go
@@ -0,0 +1,49 @@
+// 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 nacl
+
+package net
+
+import "syscall"
+
+func lookupProtocol(name string) (proto int, err error) {
+ return 0, syscall.ENOPROTOOPT
+}
+
+func lookupHost(host string) (addrs []string, err error) {
+ return nil, syscall.ENOPROTOOPT
+}
+
+func lookupIP(host string) (ips []IP, err error) {
+ return nil, syscall.ENOPROTOOPT
+}
+
+func lookupPort(network, service string) (port int, err error) {
+ return 0, syscall.ENOPROTOOPT
+}
+
+func lookupCNAME(name string) (cname string, err error) {
+ return "", syscall.ENOPROTOOPT
+}
+
+func lookupSRV(service, proto, name string) (cname string, srvs []*SRV, err error) {
+ return "", nil, syscall.ENOPROTOOPT
+}
+
+func lookupMX(name string) (mxs []*MX, err error) {
+ return nil, syscall.ENOPROTOOPT
+}
+
+func lookupNS(name string) (nss []*NS, err error) {
+ return nil, syscall.ENOPROTOOPT
+}
+
+func lookupTXT(name string) (txts []string, err error) {
+ return nil, syscall.ENOPROTOOPT
+}
+
+func lookupAddr(addr string) (ptrs []string, err error) {
+ return nil, syscall.ENOPROTOOPT
+}
diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go
index 3355e469489..057e1322b99 100644
--- a/libgo/go/net/lookup_test.go
+++ b/libgo/go/net/lookup_test.go
@@ -15,87 +15,181 @@ import (
var testExternal = flag.Bool("external", true, "allow use of external networks during long test")
-func TestGoogleSRV(t *testing.T) {
+var lookupGoogleSRVTests = []struct {
+ service, proto, name string
+ cname, target string
+}{
+ {
+ "xmpp-server", "tcp", "google.com",
+ ".google.com", ".google.com",
+ },
+ {
+ "", "", "_xmpp-server._tcp.google.com", // non-standard back door
+ ".google.com", ".google.com",
+ },
+}
+
+func TestLookupGoogleSRV(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- _, addrs, err := LookupSRV("xmpp-server", "tcp", "google.com")
- if err != nil {
- t.Errorf("failed: %s", err)
+
+ for _, tt := range lookupGoogleSRVTests {
+ cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(srvs) == 0 {
+ t.Error("got no record")
+ }
+ if !strings.Contains(cname, tt.cname) {
+ t.Errorf("got %q; want %q", cname, tt.cname)
+ }
+ for _, srv := range srvs {
+ if !strings.Contains(srv.Target, tt.target) {
+ t.Errorf("got %v; want a record containing %q", srv, tt.target)
+ }
+ }
}
- if len(addrs) == 0 {
- t.Errorf("no results")
+}
+
+func TestLookupGmailMX(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
}
- // Non-standard back door.
- _, addrs, err = LookupSRV("", "", "_xmpp-server._tcp.google.com")
+ mxs, err := LookupMX("gmail.com")
if err != nil {
- t.Errorf("back door failed: %s", err)
+ t.Fatal(err)
}
- if len(addrs) == 0 {
- t.Errorf("back door no results")
+ if len(mxs) == 0 {
+ t.Error("got no record")
+ }
+ for _, mx := range mxs {
+ if !strings.Contains(mx.Host, ".google.com") {
+ t.Errorf("got %v; want a record containing .google.com.", mx)
+ }
}
}
-func TestGmailMX(t *testing.T) {
+func TestLookupGmailNS(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- mx, err := LookupMX("gmail.com")
+
+ nss, err := LookupNS("gmail.com")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
+ }
+ if len(nss) == 0 {
+ t.Error("got no record")
}
- if len(mx) == 0 {
- t.Errorf("no results")
+ for _, ns := range nss {
+ if !strings.Contains(ns.Host, ".google.com") {
+ t.Errorf("got %v; want a record containing .google.com.", ns)
+ }
}
}
-func TestGmailNS(t *testing.T) {
+func TestLookupGmailTXT(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- ns, err := LookupNS("gmail.com")
+
+ txts, err := LookupTXT("gmail.com")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
+ }
+ if len(txts) == 0 {
+ t.Error("got no record")
+ }
+ for _, txt := range txts {
+ if !strings.Contains(txt, "spf") {
+ t.Errorf("got %q; want a spf record", txt)
+ }
+ }
+}
+
+var lookupGooglePublicDNSAddrs = []struct {
+ addr string
+ name string
+}{
+ {"8.8.8.8", ".google.com."},
+ {"8.8.4.4", ".google.com."},
+ {"2001:4860:4860::8888", ".google.com."},
+ {"2001:4860:4860::8844", ".google.com."},
+}
+
+func TestLookupGooglePublicDNSAddr(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
}
- if len(ns) == 0 {
- t.Errorf("no results")
+
+ for _, tt := range lookupGooglePublicDNSAddrs {
+ names, err := LookupAddr(tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(names) == 0 {
+ t.Error("got no record")
+ }
+ for _, name := range names {
+ if !strings.HasSuffix(name, tt.name) {
+ t.Errorf("got %q; want a record containing %q", name, tt.name)
+ }
+ }
}
}
-func TestGmailTXT(t *testing.T) {
+func TestLookupIANACNAME(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- txt, err := LookupTXT("gmail.com")
+
+ cname, err := LookupCNAME("www.iana.org")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
}
- if len(txt) == 0 || len(txt[0]) == 0 {
- t.Errorf("no results")
+ if !strings.HasSuffix(cname, ".icann.org.") {
+ t.Errorf("got %q; want a record containing .icann.org.", cname)
}
}
-func TestGoogleDNSAddr(t *testing.T) {
+func TestLookupGoogleHost(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- names, err := LookupAddr("8.8.8.8")
+
+ addrs, err := LookupHost("google.com")
if err != nil {
- t.Errorf("failed: %s", err)
+ t.Fatal(err)
+ }
+ if len(addrs) == 0 {
+ t.Error("got no record")
}
- if len(names) == 0 {
- t.Errorf("no results")
+ for _, addr := range addrs {
+ if ParseIP(addr) == nil {
+ t.Errorf("got %q; want a literal ip address", addr)
+ }
}
}
-func TestLookupIANACNAME(t *testing.T) {
+func TestLookupGoogleIP(t *testing.T) {
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
- 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)
+
+ ips, err := LookupIP("google.com")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(ips) == 0 {
+ t.Error("got no record")
+ }
+ for _, ip := range ips {
+ if ip.To4() == nil && ip.To16() == nil {
+ t.Errorf("got %v; want an ip address", ip)
+ }
}
}
diff --git a/libgo/go/net/lookup_unix.go b/libgo/go/net/lookup_unix.go
index b1d2f8f31a9..a54578456d7 100644
--- a/libgo/go/net/lookup_unix.go
+++ b/libgo/go/net/lookup_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 dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package net
diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go
index 130364231d4..6a925b0a7ad 100644
--- a/libgo/go/net/lookup_windows.go
+++ b/libgo/go/net/lookup_windows.go
@@ -210,14 +210,21 @@ func lookupCNAME(name string) (cname string, err error) {
defer releaseThread()
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
+ // windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s
+ if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS {
+ // if there are no aliases, the canonical name is the input name
+ if name == "" || name[len(name)-1] != '.' {
+ return name + ".", nil
+ }
+ return name, nil
+ }
if e != nil {
return "", os.NewSyscallError("LookupCNAME", e)
}
defer syscall.DnsRecordListFree(r, 1)
- if r != nil && r.Type == syscall.DNS_TYPE_CNAME {
- v := (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0]))
- cname = syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."
- }
+
+ resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r)
+ cname = syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(resolved))[:]) + "."
return
}
@@ -236,8 +243,9 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
return "", nil, os.NewSyscallError("LookupSRV", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
addrs = make([]*SRV, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_SRV; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_SRV, target) {
v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
addrs = append(addrs, &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight})
}
@@ -254,8 +262,9 @@ func lookupMX(name string) (mx []*MX, err error) {
return nil, os.NewSyscallError("LookupMX", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
mx = make([]*MX, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_MX; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) {
v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
mx = append(mx, &MX{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]) + ".", v.Preference})
}
@@ -272,8 +281,9 @@ func lookupNS(name string) (ns []*NS, err error) {
return nil, os.NewSyscallError("LookupNS", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
ns = make([]*NS, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_NS; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_NS, name) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
}
@@ -289,9 +299,10 @@ func lookupTXT(name string) (txt []string, err error) {
return nil, os.NewSyscallError("LookupTXT", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
txt = make([]string, 0, 10)
- if r != nil && r.Type == syscall.DNS_TYPE_TEXT {
- d := (*syscall.DNSTXTData)(unsafe.Pointer(&r.Data[0]))
+ for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) {
+ d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount] {
s := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
txt = append(txt, s)
@@ -313,10 +324,58 @@ func lookupAddr(addr string) (name []string, err error) {
return nil, os.NewSyscallError("LookupAddr", e)
}
defer syscall.DnsRecordListFree(r, 1)
+
name = make([]string, 0, 10)
- for p := r; p != nil && p.Type == syscall.DNS_TYPE_PTR; p = p.Next {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
name = append(name, syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))
}
return name, nil
}
+
+const dnsSectionMask = 0x0003
+
+// returns only results applicable to name and resolves CNAME entries
+func validRecs(r *syscall.DNSRecord, dnstype uint16, name string) []*syscall.DNSRecord {
+ cname := syscall.StringToUTF16Ptr(name)
+ if dnstype != syscall.DNS_TYPE_CNAME {
+ cname = resolveCNAME(cname, r)
+ }
+ rec := make([]*syscall.DNSRecord, 0, 10)
+ for p := r; p != nil; p = p.Next {
+ if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
+ continue
+ }
+ if p.Type != dnstype {
+ continue
+ }
+ if !syscall.DnsNameCompare(cname, p.Name) {
+ continue
+ }
+ rec = append(rec, p)
+ }
+ return rec
+}
+
+// returns the last CNAME in chain
+func resolveCNAME(name *uint16, r *syscall.DNSRecord) *uint16 {
+ // limit cname resolving to 10 in case of a infinite CNAME loop
+Cname:
+ for cnameloop := 0; cnameloop < 10; cnameloop++ {
+ for p := r; p != nil; p = p.Next {
+ if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
+ continue
+ }
+ if p.Type != syscall.DNS_TYPE_CNAME {
+ continue
+ }
+ if !syscall.DnsNameCompare(name, p.Name) {
+ continue
+ }
+ name = (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0])).Host
+ continue Cname
+ }
+ break
+ }
+ return name
+}
diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go
index ba0778caa73..19aa888d872 100644
--- a/libgo/go/net/mail/message.go
+++ b/libgo/go/net/mail/message.go
@@ -28,6 +28,7 @@ import (
"strconv"
"strings"
"time"
+ "unicode"
)
var debug = debugT(false)
@@ -445,7 +446,7 @@ func decodeRFC2047Word(s string) (string, error) {
return "", errors.New("address not RFC 2047 encoded")
}
charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
- if charset != "iso-8859-1" && charset != "utf-8" {
+ if charset != "us-ascii" && charset != "iso-8859-1" && charset != "utf-8" {
return "", fmt.Errorf("charset not supported: %q", charset)
}
@@ -466,6 +467,16 @@ func decodeRFC2047Word(s string) (string, error) {
}
switch charset {
+ case "us-ascii":
+ b := new(bytes.Buffer)
+ for _, c := range dec {
+ if c >= 0x80 {
+ b.WriteRune(unicode.ReplacementChar)
+ } else {
+ b.WriteRune(rune(c))
+ }
+ }
+ return b.String(), nil
case "iso-8859-1":
b := new(bytes.Buffer)
for _, c := range dec {
diff --git a/libgo/go/net/mail/message_test.go b/libgo/go/net/mail/message_test.go
index eb9c8cbdc9b..6ba48be04fa 100644
--- a/libgo/go/net/mail/message_test.go
+++ b/libgo/go/net/mail/message_test.go
@@ -194,6 +194,16 @@ func TestAddressParsing(t *testing.T) {
},
},
},
+ // RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal.
+ {
+ `=?us-ascii?q?J=6Frg_Doe?= <joerg@example.com>`,
+ []*Address{
+ {
+ Name: `Jorg Doe`,
+ Address: "joerg@example.com",
+ },
+ },
+ },
// RFC 2047 "Q"-encoded UTF-8 address.
{
`=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`,
diff --git a/libgo/go/net/multicast_test.go b/libgo/go/net/multicast_test.go
index 63dbce88e9a..5f253f44a45 100644
--- a/libgo/go/net/multicast_test.go
+++ b/libgo/go/net/multicast_test.go
@@ -25,7 +25,7 @@ var ipv4MulticastListenerTests = []struct {
// port.
func TestIPv4MulticastListener(t *testing.T) {
switch runtime.GOOS {
- case "nacl", "plan9":
+ case "android", "nacl", "plan9":
t.Skipf("skipping test on %q", runtime.GOOS)
case "solaris":
t.Skipf("skipping test on solaris, see issue 7399")
diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go
index ca56af54fc6..cb31af5e347 100644
--- a/libgo/go/net/net.go
+++ b/libgo/go/net/net.go
@@ -32,7 +32,6 @@ The Listen function creates servers:
conn, err := ln.Accept()
if err != nil {
// handle error
- continue
}
go handleConnection(conn)
}
diff --git a/libgo/go/net/parse.go b/libgo/go/net/parse.go
index ee6e7e99522..e1d0130c9ac 100644
--- a/libgo/go/net/parse.go
+++ b/libgo/go/net/parse.go
@@ -210,18 +210,18 @@ func itod(i uint) string {
return string(b[bp:])
}
-// Convert i to hexadecimal string.
-func itox(i uint, min int) string {
- // Assemble hexadecimal in reverse order.
- var b [32]byte
- bp := len(b)
- for ; i > 0 || min > 0; i /= 16 {
- bp--
- b[bp] = "0123456789abcdef"[byte(i%16)]
- min--
+// Convert i to a hexadecimal string. Leading zeros are not printed.
+func appendHex(dst []byte, i uint32) []byte {
+ if i == 0 {
+ return append(dst, '0')
}
-
- return string(b[bp:])
+ for j := 7; j >= 0; j-- {
+ v := i >> uint(j*4)
+ if v > 0 {
+ dst = append(dst, hexDigit[v&0xf])
+ }
+ }
+ return dst
}
// Number of occurrences of b in s.
diff --git a/libgo/go/net/parse_test.go b/libgo/go/net/parse_test.go
index b86bc32884b..7b213b75bde 100644
--- a/libgo/go/net/parse_test.go
+++ b/libgo/go/net/parse_test.go
@@ -12,9 +12,9 @@ import (
)
func TestReadLine(t *testing.T) {
- // /etc/services file does not exist on windows and Plan 9.
+ // /etc/services file does not exist on android, plan9, windows.
switch runtime.GOOS {
- case "plan9", "windows":
+ case "android", "plan9", "windows":
t.Skipf("skipping test on %q", runtime.GOOS)
}
filename := "/etc/services" // a nice big file
diff --git a/libgo/go/net/port_test.go b/libgo/go/net/port_test.go
index 9e8968f359c..4811ade69e0 100644
--- a/libgo/go/net/port_test.go
+++ b/libgo/go/net/port_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "runtime"
"testing"
)
@@ -43,6 +44,11 @@ var porttests = []portTest{
}
func TestLookupPort(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
for i := 0; i < len(porttests); i++ {
tt := porttests[i]
if port, err := LookupPort(tt.netw, tt.name); port != tt.port || (err == nil) != tt.ok {
diff --git a/libgo/go/net/port_unix.go b/libgo/go/net/port_unix.go
index 89558c1f029..348c771c351 100644
--- a/libgo/go/net/port_unix.go
+++ b/libgo/go/net/port_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 dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
// Read system port mappings from /etc/services
diff --git a/libgo/go/net/rpc/client.go b/libgo/go/net/rpc/client.go
index 21f79b06844..d0c4a69214e 100644
--- a/libgo/go/net/rpc/client.go
+++ b/libgo/go/net/rpc/client.go
@@ -41,10 +41,10 @@ type Call struct {
type Client struct {
codec ClientCodec
- sending sync.Mutex
+ reqMutex sync.Mutex // protects following
+ request Request
mutex sync.Mutex // protects following
- request Request
seq uint64
pending map[uint64]*Call
closing bool // user has called Close
@@ -69,8 +69,8 @@ type ClientCodec interface {
}
func (client *Client) send(call *Call) {
- client.sending.Lock()
- defer client.sending.Unlock()
+ client.reqMutex.Lock()
+ defer client.reqMutex.Unlock()
// Register this call.
client.mutex.Lock()
@@ -146,7 +146,7 @@ func (client *Client) input() {
}
}
// Terminate pending calls.
- client.sending.Lock()
+ client.reqMutex.Lock()
client.mutex.Lock()
client.shutdown = true
closing := client.closing
@@ -162,7 +162,7 @@ func (client *Client) input() {
call.done()
}
client.mutex.Unlock()
- client.sending.Unlock()
+ client.reqMutex.Unlock()
if debugLog && err != io.EOF && !closing {
log.Println("rpc: client protocol error:", err)
}
diff --git a/libgo/go/net/rpc/client_test.go b/libgo/go/net/rpc/client_test.go
index bbfc1ec3a3e..5dd111b299f 100644
--- a/libgo/go/net/rpc/client_test.go
+++ b/libgo/go/net/rpc/client_test.go
@@ -6,6 +6,10 @@ package rpc
import (
"errors"
+ "fmt"
+ "net"
+ "runtime"
+ "strings"
"testing"
)
@@ -34,3 +38,54 @@ func TestCloseCodec(t *testing.T) {
t.Error("client.Close did not close codec")
}
}
+
+// Test that errors in gob shut down the connection. Issue 7689.
+
+type R struct {
+ msg []byte // Not exported, so R does not work with gob.
+}
+
+type S struct{}
+
+func (s *S) Recv(nul *struct{}, reply *R) error {
+ *reply = R{[]byte("foo")}
+ return nil
+}
+
+func TestGobError(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping test; see http://golang.org/issue/8908")
+ }
+ defer func() {
+ err := recover()
+ if err == nil {
+ t.Fatal("no error")
+ }
+ if !strings.Contains("reading body EOF", err.(error).Error()) {
+ t.Fatal("expected `reading body EOF', got", err)
+ }
+ }()
+ Register(new(S))
+
+ listen, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ panic(err)
+ }
+ go Accept(listen)
+
+ client, err := Dial("tcp", listen.Addr().String())
+ if err != nil {
+ panic(err)
+ }
+
+ var reply Reply
+ err = client.Call("S.Recv", &struct{}{}, &reply)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("%#v\n", reply)
+ client.Close()
+
+ listen.Close()
+}
diff --git a/libgo/go/net/rpc/debug.go b/libgo/go/net/rpc/debug.go
index 926466d6255..98b2c1c6c4a 100644
--- a/libgo/go/net/rpc/debug.go
+++ b/libgo/go/net/rpc/debug.go
@@ -11,9 +11,9 @@ package rpc
import (
"fmt"
+ "html/template"
"net/http"
"sort"
- "text/template"
)
const debugText = `<html>
diff --git a/libgo/go/net/rpc/server.go b/libgo/go/net/rpc/server.go
index 6b264b46b8e..83728d55a18 100644
--- a/libgo/go/net/rpc/server.go
+++ b/libgo/go/net/rpc/server.go
@@ -395,6 +395,7 @@ type gobServerCodec struct {
dec *gob.Decoder
enc *gob.Encoder
encBuf *bufio.Writer
+ closed bool
}
func (c *gobServerCodec) ReadRequestHeader(r *Request) error {
@@ -407,15 +408,32 @@ func (c *gobServerCodec) ReadRequestBody(body interface{}) error {
func (c *gobServerCodec) WriteResponse(r *Response, body interface{}) (err error) {
if err = c.enc.Encode(r); err != nil {
+ if c.encBuf.Flush() == nil {
+ // Gob couldn't encode the header. Should not happen, so if it does,
+ // shut down the connection to signal that the connection is broken.
+ log.Println("rpc: gob error encoding response:", err)
+ c.Close()
+ }
return
}
if err = c.enc.Encode(body); err != nil {
+ if c.encBuf.Flush() == nil {
+ // Was a gob problem encoding the body but the header has been written.
+ // Shut down the connection to signal that the connection is broken.
+ log.Println("rpc: gob error encoding body:", err)
+ c.Close()
+ }
return
}
return c.encBuf.Flush()
}
func (c *gobServerCodec) Close() error {
+ if c.closed {
+ // Only call c.rwc.Close once; otherwise the semantics are undefined.
+ return nil
+ }
+ c.closed = true
return c.rwc.Close()
}
@@ -426,7 +444,12 @@ func (c *gobServerCodec) Close() error {
// connection. To use an alternate codec, use ServeCodec.
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
buf := bufio.NewWriter(conn)
- srv := &gobServerCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(buf), buf}
+ srv := &gobServerCodec{
+ rwc: conn,
+ dec: gob.NewDecoder(conn),
+ enc: gob.NewEncoder(buf),
+ encBuf: buf,
+ }
server.ServeCodec(srv)
}
diff --git a/libgo/go/net/singleflight.go b/libgo/go/net/singleflight.go
index dc58affdaac..bf599f0cc94 100644
--- a/libgo/go/net/singleflight.go
+++ b/libgo/go/net/singleflight.go
@@ -8,10 +8,18 @@ import "sync"
// call is an in-flight or completed singleflight.Do call
type call struct {
- wg sync.WaitGroup
- val interface{}
- err error
- dups int
+ wg sync.WaitGroup
+
+ // These fields are written once before the WaitGroup is done
+ // and are only read after the WaitGroup is done.
+ val interface{}
+ err error
+
+ // These fields are read and written with the singleflight
+ // mutex held before the WaitGroup is done, and are read but
+ // not written after the WaitGroup is done.
+ dups int
+ chans []chan<- singleflightResult
}
// singleflight represents a class of work and forms a namespace in
@@ -21,6 +29,14 @@ type singleflight struct {
m map[string]*call // lazily initialized
}
+// singleflightResult holds the results of Do, so they can be passed
+// on a channel.
+type singleflightResult struct {
+ v interface{}
+ err error
+ shared bool
+}
+
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
@@ -42,12 +58,52 @@ func (g *singleflight) Do(key string, fn func() (interface{}, error)) (v interfa
g.m[key] = c
g.mu.Unlock()
+ g.doCall(c, key, fn)
+ return c.val, c.err, c.dups > 0
+}
+
+// DoChan is like Do but returns a channel that will receive the
+// results when they are ready.
+func (g *singleflight) DoChan(key string, fn func() (interface{}, error)) <-chan singleflightResult {
+ ch := make(chan singleflightResult, 1)
+ g.mu.Lock()
+ if g.m == nil {
+ g.m = make(map[string]*call)
+ }
+ if c, ok := g.m[key]; ok {
+ c.dups++
+ c.chans = append(c.chans, ch)
+ g.mu.Unlock()
+ return ch
+ }
+ c := &call{chans: []chan<- singleflightResult{ch}}
+ c.wg.Add(1)
+ g.m[key] = c
+ g.mu.Unlock()
+
+ go g.doCall(c, key, fn)
+
+ return ch
+}
+
+// doCall handles the single call for a key.
+func (g *singleflight) doCall(c *call, key string, fn func() (interface{}, error)) {
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
+ for _, ch := range c.chans {
+ ch <- singleflightResult{c.val, c.err, c.dups > 0}
+ }
g.mu.Unlock()
+}
- return c.val, c.err, c.dups > 0
+// Forget tells the singleflight to forget about a key. Future calls
+// to Do for this key will call the function rather than waiting for
+// an earlier call to complete.
+func (g *singleflight) Forget(key string) {
+ g.mu.Lock()
+ delete(g.m, key)
+ g.mu.Unlock()
}
diff --git a/libgo/go/net/smtp/smtp_test.go b/libgo/go/net/smtp/smtp_test.go
index 3fba1ea5ae3..5c659e8a095 100644
--- a/libgo/go/net/smtp/smtp_test.go
+++ b/libgo/go/net/smtp/smtp_test.go
@@ -669,7 +669,7 @@ func sendMail(hostPort string) error {
// localhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time).
-// generated from src/pkg/crypto/tls:
+// generated from src/crypto/tls:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
diff --git a/libgo/go/net/sock_bsd.go b/libgo/go/net/sock_bsd.go
index 48fb7852757..6c37109f5e4 100644
--- a/libgo/go/net/sock_bsd.go
+++ b/libgo/go/net/sock_bsd.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 dragonfly freebsd nacl netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
diff --git a/libgo/go/net/sock_posix.go b/libgo/go/net/sock_posix.go
index c80c7d6a2f1..3f956df65a6 100644
--- a/libgo/go/net/sock_posix.go
+++ b/libgo/go/net/sock_posix.go
@@ -36,7 +36,7 @@ type sockaddr interface {
// socket returns a network file descriptor that is ready for
// asynchronous I/O using the network poller.
-func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, deadline time.Time) (fd *netFD, err error) {
s, err := sysSocket(family, sotype, proto)
if err != nil {
return nil, err
@@ -75,27 +75,51 @@ func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr s
if laddr != nil && raddr == nil {
switch sotype {
case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
- if err := fd.listenStream(laddr, listenerBacklog, toAddr); err != nil {
+ if err := fd.listenStream(laddr, listenerBacklog); err != nil {
fd.Close()
return nil, err
}
return fd, nil
case syscall.SOCK_DGRAM:
- if err := fd.listenDatagram(laddr, toAddr); err != nil {
+ if err := fd.listenDatagram(laddr); err != nil {
fd.Close()
return nil, err
}
return fd, nil
}
}
- if err := fd.dial(laddr, raddr, deadline, toAddr); err != nil {
+ if err := fd.dial(laddr, raddr, deadline); err != nil {
fd.Close()
return nil, err
}
return fd, nil
}
-func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) error {
+func (fd *netFD) addrFunc() func(syscall.Sockaddr) Addr {
+ switch fd.family {
+ case syscall.AF_INET, syscall.AF_INET6:
+ switch fd.sotype {
+ case syscall.SOCK_STREAM:
+ return sockaddrToTCP
+ case syscall.SOCK_DGRAM:
+ return sockaddrToUDP
+ case syscall.SOCK_RAW:
+ return sockaddrToIP
+ }
+ case syscall.AF_UNIX:
+ switch fd.sotype {
+ case syscall.SOCK_STREAM:
+ return sockaddrToUnix
+ case syscall.SOCK_DGRAM:
+ return sockaddrToUnixgram
+ case syscall.SOCK_SEQPACKET:
+ return sockaddrToUnixpacket
+ }
+ }
+ return func(syscall.Sockaddr) Addr { return nil }
+}
+
+func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time) error {
var err error
var lsa syscall.Sockaddr
if laddr != nil {
@@ -123,14 +147,14 @@ func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, toAddr func(sys
}
lsa, _ = syscall.Getsockname(fd.sysfd)
if rsa, _ = syscall.Getpeername(fd.sysfd); rsa != nil {
- fd.setAddr(toAddr(lsa), toAddr(rsa))
+ fd.setAddr(fd.addrFunc()(lsa), fd.addrFunc()(rsa))
} else {
- fd.setAddr(toAddr(lsa), raddr)
+ fd.setAddr(fd.addrFunc()(lsa), raddr)
}
return nil
}
-func (fd *netFD) listenStream(laddr sockaddr, backlog int, toAddr func(syscall.Sockaddr) Addr) error {
+func (fd *netFD) listenStream(laddr sockaddr, backlog int) error {
if err := setDefaultListenerSockopts(fd.sysfd); err != nil {
return err
}
@@ -148,11 +172,11 @@ func (fd *netFD) listenStream(laddr sockaddr, backlog int, toAddr func(syscall.S
return err
}
lsa, _ := syscall.Getsockname(fd.sysfd)
- fd.setAddr(toAddr(lsa), nil)
+ fd.setAddr(fd.addrFunc()(lsa), nil)
return nil
}
-func (fd *netFD) listenDatagram(laddr sockaddr, toAddr func(syscall.Sockaddr) Addr) error {
+func (fd *netFD) listenDatagram(laddr sockaddr) error {
switch addr := laddr.(type) {
case *UDPAddr:
// We provide a socket that listens to a wildcard
@@ -187,6 +211,6 @@ func (fd *netFD) listenDatagram(laddr sockaddr, toAddr func(syscall.Sockaddr) Ad
return err
}
lsa, _ := syscall.Getsockname(fd.sysfd)
- fd.setAddr(toAddr(lsa), nil)
+ fd.setAddr(fd.addrFunc()(lsa), nil)
return nil
}
diff --git a/libgo/go/net/sock_solaris.go b/libgo/go/net/sock_stub.go
index 90fe9de894c..ed6b0894893 100644
--- a/libgo/go/net/sock_solaris.go
+++ b/libgo/go/net/sock_stub.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 nacl solaris
+
package net
import "syscall"
diff --git a/libgo/go/net/sockopt_bsd.go b/libgo/go/net/sockopt_bsd.go
index 2fa3b6f1d36..d5b3621c526 100644
--- a/libgo/go/net/sockopt_bsd.go
+++ b/libgo/go/net/sockopt_bsd.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 dragonfly freebsd nacl netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
@@ -17,7 +17,7 @@ func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
// On DragonFly BSD, we adjust the ephemeral port
// range because unlike other BSD systems its default
// port range doesn't conform to IANA recommendation
- // as described in RFC 6355 and is pretty narrow.
+ // as described in RFC 6056 and is pretty narrow.
switch family {
case syscall.AF_INET:
syscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_PORTRANGE, syscall.IP_PORTRANGE_HIGH)
diff --git a/libgo/go/net/sockopt_posix.go b/libgo/go/net/sockopt_posix.go
index 921918c37f5..1654d1b85e4 100644
--- a/libgo/go/net/sockopt_posix.go
+++ b/libgo/go/net/sockopt_posix.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 dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package net
diff --git a/libgo/go/net/sockopt_stub.go b/libgo/go/net/sockopt_stub.go
new file mode 100644
index 00000000000..de5ee0bb63c
--- /dev/null
+++ b/libgo/go/net/sockopt_stub.go
@@ -0,0 +1,37 @@
+// 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 nacl
+
+package net
+
+import "syscall"
+
+func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
+ return nil
+}
+
+func setDefaultListenerSockopts(s int) error {
+ return nil
+}
+
+func setDefaultMulticastSockopts(s int) error {
+ return nil
+}
+
+func setReadBuffer(fd *netFD, bytes int) error {
+ return syscall.ENOPROTOOPT
+}
+
+func setWriteBuffer(fd *netFD, bytes int) error {
+ return syscall.ENOPROTOOPT
+}
+
+func setKeepAlive(fd *netFD, keepalive bool) error {
+ return syscall.ENOPROTOOPT
+}
+
+func setLinger(fd *netFD, sec int) error {
+ return syscall.ENOPROTOOPT
+}
diff --git a/libgo/go/net/sockoptip_bsd.go b/libgo/go/net/sockoptip_bsd.go
index 87132f0f461..2199e480d42 100644
--- a/libgo/go/net/sockoptip_bsd.go
+++ b/libgo/go/net/sockoptip_bsd.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 dragonfly freebsd nacl netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
package net
diff --git a/libgo/go/net/sockoptip_posix.go b/libgo/go/net/sockoptip_posix.go
index b5c80e44909..c2579be9114 100644
--- a/libgo/go/net/sockoptip_posix.go
+++ b/libgo/go/net/sockoptip_posix.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 dragonfly freebsd linux nacl netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
package net
diff --git a/libgo/go/net/sockoptip_stub.go b/libgo/go/net/sockoptip_stub.go
index dcd3a22b57d..32ec5ddb859 100644
--- a/libgo/go/net/sockoptip_stub.go
+++ b/libgo/go/net/sockoptip_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 solaris
+// +build nacl solaris
package net
@@ -10,30 +10,30 @@ import "syscall"
func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
// See golang.org/issue/7399.
- return syscall.EINVAL
+ return syscall.ENOPROTOOPT
}
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
// See golang.org/issue/7399.
- return syscall.EINVAL
+ return syscall.ENOPROTOOPT
}
func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
// See golang.org/issue/7399.
- return syscall.EINVAL
+ return syscall.ENOPROTOOPT
}
func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
// See golang.org/issue/7399.
- return syscall.EINVAL
+ return syscall.ENOPROTOOPT
}
func setIPv6MulticastLoopback(fd *netFD, v bool) error {
// See golang.org/issue/7399.
- return syscall.EINVAL
+ return syscall.ENOPROTOOPT
}
func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
// See golang.org/issue/7399.
- return syscall.EINVAL
+ return syscall.ENOPROTOOPT
}
diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go
index b79b115ca5b..dd78aefa773 100644
--- a/libgo/go/net/tcpsock_posix.go
+++ b/libgo/go/net/tcpsock_posix.go
@@ -153,7 +153,7 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
}
func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
- fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial")
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@@ -183,7 +183,7 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, e
if err == nil {
fd.Close()
}
- fd, err = internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ fd, err = internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial")
}
if err != nil {
@@ -231,7 +231,7 @@ func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
- fd, err := l.fd.accept(sockaddrToTCP)
+ fd, err := l.fd.accept()
if err != nil {
return nil, err
}
@@ -291,7 +291,7 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
if laddr == nil {
laddr = &TCPAddr{}
}
- fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
+ fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_STREAM, 0, "listen")
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
}
diff --git a/libgo/go/net/tcpsockopt_darwin.go b/libgo/go/net/tcpsockopt_darwin.go
index 33140849c95..1f1609088ba 100644
--- a/libgo/go/net/tcpsockopt_darwin.go
+++ b/libgo/go/net/tcpsockopt_darwin.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.
-// TCP socket options for darwin
-
package net
import (
@@ -12,16 +10,20 @@ import (
"time"
)
-// Set keep alive period.
+const sysTCP_KEEPINTVL = 0x101
+
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
-
// The kernel expects seconds so round to next highest second.
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
-
+ switch err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err {
+ case nil, syscall.ENOPROTOOPT: // OS X 10.7 and earlier don't support this option
+ default:
+ return os.NewSyscallError("setsockopt", err)
+ }
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
}
diff --git a/libgo/go/net/tcpsockopt_dragonfly.go b/libgo/go/net/tcpsockopt_dragonfly.go
index d10a77773d8..0aa213239d1 100644
--- a/libgo/go/net/tcpsockopt_dragonfly.go
+++ b/libgo/go/net/tcpsockopt_dragonfly.go
@@ -10,20 +10,17 @@ import (
"time"
)
-// Set keep alive period.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
-
- // The kernel expects milliseconds so round to next highest millisecond.
+ // The kernel expects milliseconds so round to next highest
+ // millisecond.
d += (time.Millisecond - time.Nanosecond)
- msecs := int(time.Duration(d.Nanoseconds()) / time.Millisecond)
-
- err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, msecs))
- if err != nil {
- return err
+ msecs := int(d / time.Millisecond)
+ if err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, msecs); err != nil {
+ return os.NewSyscallError("setsockopt", err)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, msecs))
}
diff --git a/libgo/go/net/tcpsockopt_openbsd.go b/libgo/go/net/tcpsockopt_openbsd.go
index 3480f932c80..041e1786a92 100644
--- a/libgo/go/net/tcpsockopt_openbsd.go
+++ b/libgo/go/net/tcpsockopt_openbsd.go
@@ -2,26 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TCP socket options for openbsd
-
package net
import (
- "os"
"syscall"
"time"
)
-// Set keep alive period.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
-
- // The kernel expects seconds so round to next highest second.
- d += (time.Second - time.Nanosecond)
- secs := int(d.Seconds())
-
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.SO_KEEPALIVE, secs))
+ // OpenBSD has no user-settable per-socket TCP keepalive
+ // options.
+ return syscall.ENOPROTOOPT
}
diff --git a/libgo/go/net/tcpsockopt_posix.go b/libgo/go/net/tcpsockopt_posix.go
index 6484bad4b45..0abf3f97f6b 100644
--- a/libgo/go/net/tcpsockopt_posix.go
+++ b/libgo/go/net/tcpsockopt_posix.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 dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package net
diff --git a/libgo/go/net/tcpsockopt_solaris.go b/libgo/go/net/tcpsockopt_solaris.go
deleted file mode 100644
index eaab6b6787b..00000000000
--- a/libgo/go/net/tcpsockopt_solaris.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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.
-
-// TCP socket options for solaris
-
-package net
-
-import (
- "os"
- "syscall"
- "time"
-)
-
-// Set keep alive period.
-func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
-
- // The kernel expects seconds so round to next highest second.
- d += (time.Second - time.Nanosecond)
- secs := int(d.Seconds())
-
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.SO_KEEPALIVE, secs))
-}
diff --git a/libgo/go/net/tcpsockopt_stub.go b/libgo/go/net/tcpsockopt_stub.go
new file mode 100644
index 00000000000..b413a764d82
--- /dev/null
+++ b/libgo/go/net/tcpsockopt_stub.go
@@ -0,0 +1,20 @@
+// 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 nacl
+
+package net
+
+import (
+ "syscall"
+ "time"
+)
+
+func setNoDelay(fd *netFD, noDelay bool) error {
+ return syscall.ENOPROTOOPT
+}
+
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+ return syscall.ENOPROTOOPT
+}
diff --git a/libgo/go/net/tcpsockopt_unix.go b/libgo/go/net/tcpsockopt_unix.go
index 2693a541d20..c9f604cad7b 100644
--- a/libgo/go/net/tcpsockopt_unix.go
+++ b/libgo/go/net/tcpsockopt_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 freebsd linux nacl netbsd
+// +build freebsd linux netbsd solaris
package net
@@ -12,20 +12,16 @@ import (
"time"
)
-// Set keep alive period.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
-
// The kernel expects seconds so round to next highest second.
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
-
- err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs))
- if err != nil {
- return err
+ if err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs); err != nil {
+ return os.NewSyscallError("setsockopt", err)
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs))
}
diff --git a/libgo/go/net/tcpsockopt_windows.go b/libgo/go/net/tcpsockopt_windows.go
index 8ef1407977f..091f5233f20 100644
--- a/libgo/go/net/tcpsockopt_windows.go
+++ b/libgo/go/net/tcpsockopt_windows.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.
-// TCP socket options for windows
-
package net
import (
@@ -18,14 +16,14 @@ func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
return err
}
defer fd.decref()
-
- // Windows expects milliseconds so round to next highest millisecond.
+ // The kernel expects milliseconds so round to next highest
+ // millisecond.
d += (time.Millisecond - time.Nanosecond)
- millis := uint32(d / time.Millisecond)
+ msecs := uint32(d / time.Millisecond)
ka := syscall.TCPKeepalive{
OnOff: 1,
- Time: millis,
- Interval: millis,
+ Time: msecs,
+ Interval: msecs,
}
ret := uint32(0)
size := uint32(unsafe.Sizeof(ka))
diff --git a/libgo/go/net/testdata/domain-resolv.conf b/libgo/go/net/testdata/domain-resolv.conf
new file mode 100644
index 00000000000..ff269180f43
--- /dev/null
+++ b/libgo/go/net/testdata/domain-resolv.conf
@@ -0,0 +1,5 @@
+# /etc/resolv.conf
+
+search test invalid
+domain localdomain
+nameserver 8.8.8.8
diff --git a/libgo/go/net/testdata/empty-resolv.conf b/libgo/go/net/testdata/empty-resolv.conf
new file mode 100644
index 00000000000..c4b2b576549
--- /dev/null
+++ b/libgo/go/net/testdata/empty-resolv.conf
@@ -0,0 +1 @@
+# /etc/resolv.conf
diff --git a/libgo/go/net/testdata/resolv.conf b/libgo/go/net/testdata/resolv.conf
index 3841bbf9044..04e87eed03f 100644
--- a/libgo/go/net/testdata/resolv.conf
+++ b/libgo/go/net/testdata/resolv.conf
@@ -1,6 +1,8 @@
# /etc/resolv.conf
-domain Home
-nameserver 192.168.1.1
+domain localdomain
+nameserver 8.8.8.8
+nameserver 2001:4860:4860::8888
+nameserver fe80::1%lo0
options ndots:5 timeout:10 attempts:3 rotate
options attempts 3
diff --git a/libgo/go/net/testdata/search-resolv.conf b/libgo/go/net/testdata/search-resolv.conf
new file mode 100644
index 00000000000..1c846bfaffc
--- /dev/null
+++ b/libgo/go/net/testdata/search-resolv.conf
@@ -0,0 +1,5 @@
+# /etc/resolv.conf
+
+domain localdomain
+search test invalid
+nameserver 8.8.8.8
diff --git a/libgo/go/net/udp_test.go b/libgo/go/net/udp_test.go
index e1778779cf5..125bbca6c40 100644
--- a/libgo/go/net/udp_test.go
+++ b/libgo/go/net/udp_test.go
@@ -9,6 +9,7 @@ import (
"runtime"
"strings"
"testing"
+ "time"
)
func TestResolveUDPAddr(t *testing.T) {
@@ -34,6 +35,46 @@ func TestResolveUDPAddr(t *testing.T) {
}
}
+func TestReadFromUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("skipping test on %q, see issue 8916", runtime.GOOS)
+ }
+
+ ra, err := ResolveUDPAddr("udp", "127.0.0.1:7")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ la, err := ResolveUDPAddr("udp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ c, err := ListenUDP("udp", la)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ _, err = c.WriteToUDP([]byte("a"), ra)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ if err != nil {
+ t.Fatal(err)
+ }
+ b := make([]byte, 1)
+ _, _, err = c.ReadFromUDP(b)
+ if err == nil {
+ t.Fatal("ReadFromUDP should fail")
+ } else if !isTimeout(err) {
+ t.Fatal(err)
+ }
+}
+
func TestWriteToUDP(t *testing.T) {
switch runtime.GOOS {
case "plan9":
diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go
index 5dfba94e9a6..a0533366a42 100644
--- a/libgo/go/net/udpsock_posix.go
+++ b/libgo/go/net/udpsock_posix.go
@@ -175,7 +175,7 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
}
func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
- fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_DGRAM, 0, "dial")
if err != nil {
return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err}
}
@@ -198,7 +198,7 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
if laddr == nil {
laddr = &UDPAddr{}
}
- fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err}
}
@@ -218,7 +218,7 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e
if gaddr == nil || gaddr.IP == nil {
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress}
}
- fd, err := internetSocket(net, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: gaddr, Err: err}
}
diff --git a/libgo/go/net/unicast_posix_test.go b/libgo/go/net/unicast_posix_test.go
index 452ac925428..ab7ef40a758 100644
--- a/libgo/go/net/unicast_posix_test.go
+++ b/libgo/go/net/unicast_posix_test.go
@@ -204,6 +204,9 @@ func TestDualStackTCPListener(t *testing.T) {
// to a test listener with various address families, differnet
// listening address and same port.
func TestDualStackUDPListener(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in -short mode, see issue 5001")
+ }
switch runtime.GOOS {
case "plan9":
t.Skipf("skipping test on %q", runtime.GOOS)
diff --git a/libgo/go/net/unix_test.go b/libgo/go/net/unix_test.go
index 05643ddf9ae..1cdff3908c1 100644
--- a/libgo/go/net/unix_test.go
+++ b/libgo/go/net/unix_test.go
@@ -256,8 +256,11 @@ func TestUnixConnLocalAndRemoteNames(t *testing.T) {
t.Fatalf("UnixConn.Write failed: %v", err)
}
- if runtime.GOOS == "linux" && laddr == "" {
- laddr = "@" // autobind feature
+ switch runtime.GOOS {
+ case "android", "linux":
+ if laddr == "" {
+ laddr = "@" // autobind feature
+ }
}
var connAddrs = [3]struct{ got, want Addr }{
{ln.Addr(), ta},
@@ -308,9 +311,13 @@ func TestUnixgramConnLocalAndRemoteNames(t *testing.T) {
}
}()
- if runtime.GOOS == "linux" && laddr == "" {
- laddr = "@" // autobind feature
+ switch runtime.GOOS {
+ case "android", "linux":
+ if laddr == "" {
+ laddr = "@" // autobind feature
+ }
}
+
var connAddrs = [4]struct{ got, want Addr }{
{c1.LocalAddr(), ta},
{c1.RemoteAddr(), nil},
diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go
index 2610779bfd2..3c2e78bdca3 100644
--- a/libgo/go/net/unixsock_posix.go
+++ b/libgo/go/net/unixsock_posix.go
@@ -42,14 +42,7 @@ func unixSocket(net string, laddr, raddr sockaddr, mode string, deadline time.Ti
return nil, errors.New("unknown mode: " + mode)
}
- f := sockaddrToUnix
- if sotype == syscall.SOCK_DGRAM {
- f = sockaddrToUnixgram
- } else if sotype == syscall.SOCK_SEQPACKET {
- f = sockaddrToUnixpacket
- }
-
- fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, deadline, f)
+ fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, deadline)
if err != nil {
return nil, err
}
@@ -286,11 +279,7 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
- toAddr := sockaddrToUnix
- if l.fd.sotype == syscall.SOCK_SEQPACKET {
- toAddr = sockaddrToUnixpacket
- }
- fd, err := l.fd.accept(toAddr)
+ fd, err := l.fd.accept()
if err != nil {
return nil, err
}
diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go
index 75f650a2756..f167408faba 100644
--- a/libgo/go/net/url/url.go
+++ b/libgo/go/net/url/url.go
@@ -64,7 +64,6 @@ func (e EscapeError) Error() string {
// Return true if the specified character should be escaped when
// appearing in a URL string, according to RFC 3986.
-// When 'all' is true the full range of reserved characters are matched.
func shouldEscape(c byte, mode encoding) bool {
// §2.3 Unreserved characters (alphanum)
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
@@ -86,10 +85,12 @@ func shouldEscape(c byte, mode encoding) bool {
// last two as well. That leaves only ? to escape.
return c == '?'
- case encodeUserPassword: // §3.2.2
- // The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /.
- // The parsing of userinfo treats : as special so we must escape that too.
- return c == '@' || c == '/' || c == ':'
+ case encodeUserPassword: // §3.2.1
+ // The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
+ // userinfo, so we must escape only '@', '/', and '?'.
+ // The parsing of userinfo treats ':' as special so we must escape
+ // that too.
+ return c == '@' || c == '/' || c == '?' || c == ':'
case encodeQueryComponent: // §3.4
// The RFC reserves (so we must escape) everything.
@@ -440,6 +441,24 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
}
// String reassembles the URL into a valid URL string.
+// The general form of the result is one of:
+//
+// scheme:opaque
+// scheme://userinfo@host/path?query#fragment
+//
+// If u.Opaque is non-empty, String uses the first form;
+// otherwise it uses the second form.
+//
+// In the second form, the following rules apply:
+// - if u.Scheme is empty, scheme: is omitted.
+// - if u.User is nil, userinfo@ is omitted.
+// - if u.Host is empty, host/ is omitted.
+// - if u.Scheme and u.Host are empty and u.User is nil,
+// the entire scheme://userinfo@host/ is omitted.
+// - if u.Host is non-empty and u.Path begins with a /,
+// the form host/path does not add its own /.
+// - if u.RawQuery is empty, ?query is omitted.
+// - if u.Fragment is empty, #fragment is omitted.
func (u *URL) String() string {
var buf bytes.Buffer
if u.Scheme != "" {
diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go
index cad758f2385..d8b19d805d0 100644
--- a/libgo/go/net/url/url_test.go
+++ b/libgo/go/net/url/url_test.go
@@ -279,6 +279,16 @@ var urltests = []URLTest{
},
"a/b/c",
},
+ // escaped '?' in username and password
+ {
+ "http://%3Fam:pa%3Fsword@google.com",
+ &URL{
+ Scheme: "http",
+ User: UserPassword("?am", "pa?sword"),
+ Host: "google.com",
+ },
+ "",
+ },
}
// more useful string for debugging than fmt's struct printer
@@ -903,3 +913,49 @@ func TestParseFailure(t *testing.T) {
t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
}
}
+
+type shouldEscapeTest struct {
+ in byte
+ mode encoding
+ escape bool
+}
+
+var shouldEscapeTests = []shouldEscapeTest{
+ // Unreserved characters (§2.3)
+ {'a', encodePath, false},
+ {'a', encodeUserPassword, false},
+ {'a', encodeQueryComponent, false},
+ {'a', encodeFragment, false},
+ {'z', encodePath, false},
+ {'A', encodePath, false},
+ {'Z', encodePath, false},
+ {'0', encodePath, false},
+ {'9', encodePath, false},
+ {'-', encodePath, false},
+ {'-', encodeUserPassword, false},
+ {'-', encodeQueryComponent, false},
+ {'-', encodeFragment, false},
+ {'.', encodePath, false},
+ {'_', encodePath, false},
+ {'~', encodePath, false},
+
+ // User information (§3.2.1)
+ {':', encodeUserPassword, true},
+ {'/', encodeUserPassword, true},
+ {'?', encodeUserPassword, true},
+ {'@', encodeUserPassword, true},
+ {'$', encodeUserPassword, false},
+ {'&', encodeUserPassword, false},
+ {'+', encodeUserPassword, false},
+ {',', encodeUserPassword, false},
+ {';', encodeUserPassword, false},
+ {'=', encodeUserPassword, false},
+}
+
+func TestShouldEscape(t *testing.T) {
+ for _, tt := range shouldEscapeTests {
+ if shouldEscape(tt.in, tt.mode) != tt.escape {
+ t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape)
+ }
+ }
+}
diff --git a/libgo/go/net/z_last_test.go b/libgo/go/net/z_last_test.go
index 4f6a54a560a..716c103db26 100644
--- a/libgo/go/net/z_last_test.go
+++ b/libgo/go/net/z_last_test.go
@@ -8,6 +8,7 @@ import (
"flag"
"fmt"
"testing"
+ "time"
)
var testDNSFlood = flag.Bool("dnsflood", false, "whether to test dns query flooding")
@@ -35,3 +36,64 @@ func TestDNSThreadLimit(t *testing.T) {
// If we're still here, it worked.
}
+
+func TestLookupIPDeadline(t *testing.T) {
+ if !*testDNSFlood {
+ t.Skip("test disabled; use -dnsflood to enable")
+ }
+
+ const N = 5000
+ const timeout = 3 * time.Second
+ c := make(chan error, 2*N)
+ for i := 0; i < N; i++ {
+ name := fmt.Sprintf("%d.net-test.golang.org", i)
+ go func() {
+ _, err := lookupIPDeadline(name, time.Now().Add(timeout/2))
+ c <- err
+ }()
+ go func() {
+ _, err := lookupIPDeadline(name, time.Now().Add(timeout))
+ c <- err
+ }()
+ }
+ qstats := struct {
+ succeeded, failed int
+ timeout, temporary, other int
+ unknown int
+ }{}
+ deadline := time.After(timeout + time.Second)
+ for i := 0; i < 2*N; i++ {
+ select {
+ case <-deadline:
+ t.Fatal("deadline exceeded")
+ case err := <-c:
+ switch err := err.(type) {
+ case nil:
+ qstats.succeeded++
+ case Error:
+ qstats.failed++
+ if err.Timeout() {
+ qstats.timeout++
+ }
+ if err.Temporary() {
+ qstats.temporary++
+ }
+ if !err.Timeout() && !err.Temporary() {
+ qstats.other++
+ }
+ default:
+ qstats.failed++
+ qstats.unknown++
+ }
+ }
+ }
+
+ // A high volume of DNS queries for sub-domain of golang.org
+ // would be coordinated by authoritative or recursive server,
+ // or stub resolver which implements query-response rate
+ // limitation, so we can expect some query successes and more
+ // failures including timeout, temporary and other here.
+ // As a rule, unknown must not be shown but it might possibly
+ // happen due to issue 4856 for now.
+ t.Logf("%v succeeded, %v failed (%v timeout, %v temporary, %v other, %v unknown)", qstats.succeeded, qstats.failed, qstats.timeout, qstats.temporary, qstats.other, qstats.unknown)
+}