diff options
Diffstat (limited to 'libgo/go/net')
46 files changed, 891 insertions, 807 deletions
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go index 07e72cc..18c3936 100644 --- a/libgo/go/net/dnsclient_unix.go +++ b/libgo/go/net/dnsclient_unix.go @@ -45,7 +45,11 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error return nil, err } - c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds + if cfg.timeout == 0 { + c.SetReadDeadline(time.Time{}) + } else { + c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second)) + } buf := make([]byte, 2000) // More than enough. n, err = c.Read(buf) diff --git a/libgo/go/net/doc.go b/libgo/go/net/doc.go new file mode 100644 index 0000000..3a44e52 --- /dev/null +++ b/libgo/go/net/doc.go @@ -0,0 +1,59 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +// LookupHost looks up the given host using the local resolver. +// It returns an array of that host's addresses. +func LookupHost(host string) (addrs []string, err error) { + return lookupHost(host) +} + +// LookupIP looks up host using the local resolver. +// It returns an array of that host's IPv4 and IPv6 addresses. +func LookupIP(host string) (addrs []IP, err error) { + return lookupIP(host) +} + +// LookupPort looks up the port for the given network and service. +func LookupPort(network, service string) (port int, err error) { + return lookupPort(network, service) +} + +// LookupCNAME returns the canonical DNS host for the given name. +// Callers that do not care about the canonical name can call +// LookupHost or LookupIP directly; both take care of resolving +// the canonical name as part of the lookup. +func LookupCNAME(name string) (cname string, err error) { + return lookupCNAME(name) +} + +// LookupSRV tries to resolve an SRV query of the given service, +// protocol, and domain name. The proto is "tcp" or "udp". +// The returned records are sorted by priority and randomized +// by weight within a priority. +// +// LookupSRV constructs the DNS name to look up following RFC 2782. +// That is, it looks up _service._proto.name. To accommodate services +// publishing SRV records under non-standard names, if both service +// and proto are empty strings, LookupSRV looks up name directly. +func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { + return lookupSRV(service, proto, name) +} + +// LookupMX returns the DNS MX records for the given domain name sorted by preference. +func LookupMX(name string) (mx []*MX, err error) { + return lookupMX(name) +} + +// LookupTXT returns the DNS TXT records for the given domain name. +func LookupTXT(name string) (txt []string, err error) { + return lookupTXT(name) +} + +// LookupAddr performs a reverse lookup for the given address, returning a list +// of names mapping to that address. +func LookupAddr(addr string) (name []string, err error) { + return lookupAddr(addr) +} diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go index 3dec9f4..7ecd135 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -24,7 +24,7 @@ type netFD struct { // immutable until Close sysfd int family int - proto int + sotype int sysfile *os.File cr chan bool cw chan bool @@ -33,12 +33,10 @@ type netFD struct { raddr Addr // owned by client - rdeadline_delta int64 - rdeadline int64 - rio sync.Mutex - wdeadline_delta int64 - wdeadline int64 - wio sync.Mutex + rdeadline int64 + rio sync.Mutex + wdeadline int64 + wio sync.Mutex // owned by fd wait server ncr, ncw int @@ -276,7 +274,7 @@ func startServer() { pollserver = p } -func newFD(fd, family, proto int, net string) (f *netFD, err error) { +func newFD(fd, family, sotype int, net string) (f *netFD, err error) { onceStartServer.Do(startServer) if e := syscall.SetNonblock(fd, true); e != nil { return nil, e @@ -284,7 +282,7 @@ func newFD(fd, family, proto int, net string) (f *netFD, err error) { f = &netFD{ sysfd: fd, family: family, - proto: proto, + sotype: sotype, net: net, } f.cr = make(chan bool, 1) @@ -388,11 +386,6 @@ func (fd *netFD) Read(p []byte) (n int, err error) { if fd.sysfile == nil { return 0, os.EINVAL } - if fd.rdeadline_delta > 0 { - fd.rdeadline = pollserver.Now() + fd.rdeadline_delta - } else { - fd.rdeadline = 0 - } for { n, err = syscall.Read(fd.sysfile.Fd(), p) if err == syscall.EAGAIN { @@ -404,7 +397,7 @@ func (fd *netFD) Read(p []byte) (n int, err error) { } if err != nil { n = 0 - } else if n == 0 && err == nil && fd.proto != syscall.SOCK_DGRAM { + } else if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM { err = io.EOF } break @@ -423,11 +416,6 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { defer fd.rio.Unlock() fd.incref() defer fd.decref() - if fd.rdeadline_delta > 0 { - fd.rdeadline = pollserver.Now() + fd.rdeadline_delta - } else { - fd.rdeadline = 0 - } for { n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) if err == syscall.EAGAIN { @@ -456,11 +444,6 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S defer fd.rio.Unlock() fd.incref() defer fd.decref() - if fd.rdeadline_delta > 0 { - fd.rdeadline = pollserver.Now() + fd.rdeadline_delta - } else { - fd.rdeadline = 0 - } for { n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) if err == syscall.EAGAIN { @@ -493,11 +476,6 @@ func (fd *netFD) Write(p []byte) (n int, err error) { if fd.sysfile == nil { return 0, os.EINVAL } - if fd.wdeadline_delta > 0 { - fd.wdeadline = pollserver.Now() + fd.wdeadline_delta - } else { - fd.wdeadline = 0 - } nn := 0 for { @@ -539,11 +517,6 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { defer fd.wio.Unlock() fd.incref() defer fd.decref() - if fd.wdeadline_delta > 0 { - fd.wdeadline = pollserver.Now() + fd.wdeadline_delta - } else { - fd.wdeadline = 0 - } for { err = syscall.Sendto(fd.sysfd, p, 0, sa) if err == syscall.EAGAIN { @@ -571,11 +544,6 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob defer fd.wio.Unlock() fd.incref() defer fd.decref() - if fd.wdeadline_delta > 0 { - fd.wdeadline = pollserver.Now() + fd.wdeadline_delta - } else { - fd.wdeadline = 0 - } for { err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) if err == syscall.EAGAIN { @@ -603,11 +571,6 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err fd.incref() defer fd.decref() - if fd.rdeadline_delta > 0 { - fd.rdeadline = pollserver.Now() + fd.rdeadline_delta - } else { - fd.rdeadline = 0 - } // See ../syscall/exec.go for description of ForkLock. // It is okay to hold the lock across syscall.Accept @@ -636,7 +599,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() - if nfd, err = newFD(s, fd.family, fd.proto, fd.net); err != nil { + if nfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil { syscall.Close(s) return nil, err } diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index 7bffd1c..6e37b4e 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -150,12 +150,13 @@ func (s *ioSrv) ProcessRemoteIO() { } // ExecIO executes a single io operation. It either executes it -// inline, or, if timeouts are employed, passes the request onto +// inline, or, if a deadline is employed, passes the request onto // a special goroutine and waits for completion or cancels request. -func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { +// deadline is unix nanos. +func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (n int, err error) { var e error o := oi.Op() - if deadline_delta > 0 { + if deadline != 0 { // Send request to a special dedicated thread, // so it can stop the io with CancelIO later. s.submchan <- oi @@ -172,12 +173,17 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e} } // Wait for our request to complete. - // TODO(rsc): This should stop the timer. var r ioResult - if deadline_delta > 0 { + if deadline != 0 { + dt := deadline - time.Now().UnixNano() + if dt < 1 { + dt = 1 + } + timer := time.NewTimer(time.Duration(dt) * time.Nanosecond) + defer timer.Stop() select { case r = <-o.resultc: - case <-time.After(time.Duration(deadline_delta) * time.Nanosecond): + case <-timer.C: s.canchan <- oi <-o.errnoc r = <-o.resultc @@ -224,7 +230,7 @@ type netFD struct { // immutable until Close sysfd syscall.Handle family int - proto int + sotype int net string laddr Addr raddr Addr @@ -232,19 +238,17 @@ type netFD struct { errnoc [2]chan error // read/write submit or cancel operation errors // owned by client - rdeadline_delta int64 - rdeadline int64 - rio sync.Mutex - wdeadline_delta int64 - wdeadline int64 - wio sync.Mutex + rdeadline int64 + rio sync.Mutex + wdeadline int64 + wio sync.Mutex } -func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) { +func allocFD(fd syscall.Handle, family, sotype int, net string) (f *netFD) { f = &netFD{ sysfd: fd, family: family, - proto: proto, + sotype: sotype, net: net, } runtime.SetFinalizer(f, (*netFD).Close) @@ -357,7 +361,7 @@ func (fd *netFD) Read(buf []byte) (n int, err error) { } var o readOp o.Init(fd, buf, 'r') - n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) + n, err = iosrv.ExecIO(&o, fd.rdeadline) if err == nil && n == 0 { err = io.EOF } @@ -398,7 +402,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { var o readFromOp o.Init(fd, buf, 'r') o.rsan = int32(unsafe.Sizeof(o.rsa)) - n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) + n, err = iosrv.ExecIO(&o, fd.rdeadline) if err != nil { return 0, nil, err } @@ -434,7 +438,7 @@ func (fd *netFD) Write(buf []byte) (n int, err error) { } var o writeOp o.Init(fd, buf, 'w') - return iosrv.ExecIO(&o, fd.wdeadline_delta) + return iosrv.ExecIO(&o, fd.wdeadline) } // WriteTo to network. @@ -470,7 +474,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err error) { var o writeToOp o.Init(fd, buf, 'w') o.sa = sa - return iosrv.ExecIO(&o, fd.wdeadline_delta) + return iosrv.ExecIO(&o, fd.wdeadline) } // Accept new network connections. @@ -502,7 +506,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err // Get new socket. // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock() - s, e := syscall.Socket(fd.family, fd.proto, 0) + s, e := syscall.Socket(fd.family, fd.sotype, 0) if e != nil { syscall.ForkLock.RUnlock() return nil, e @@ -542,7 +546,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err lsa, _ := lrsa.Sockaddr() rsa, _ := rrsa.Sockaddr() - nfd = allocFD(s, fd.family, fd.proto, fd.net) + nfd = allocFD(s, fd.family, fd.sotype, fd.net) nfd.setAddr(toAddr(lsa), toAddr(rsa)) return nfd, nil } diff --git a/libgo/go/net/http/cgi/host.go b/libgo/go/net/http/cgi/host.go index 615d366..73a9b6e 100644 --- a/libgo/go/net/http/cgi/host.go +++ b/libgo/go/net/http/cgi/host.go @@ -124,7 +124,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { "GATEWAY_INTERFACE=CGI/1.1", "REQUEST_METHOD=" + req.Method, "QUERY_STRING=" + req.URL.RawQuery, - "REQUEST_URI=" + req.URL.RawPath, + "REQUEST_URI=" + req.URL.RequestURI(), "PATH_INFO=" + pathInfo, "SCRIPT_NAME=" + root, "SCRIPT_FILENAME=" + h.Path, diff --git a/libgo/go/net/http/cgi/host_test.go b/libgo/go/net/http/cgi/host_test.go index 849cb00..b8dbdb4 100644 --- a/libgo/go/net/http/cgi/host_test.go +++ b/libgo/go/net/http/cgi/host_test.go @@ -364,7 +364,7 @@ func TestCopyError(t *testing.T) { conn.Close() tries := 0 - for tries < 15 && childRunning() { + for tries < 25 && childRunning() { time.Sleep(50 * time.Millisecond * time.Duration(tries)) tries++ } diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go index a4f8f19..1d70672 100644 --- a/libgo/go/net/http/client.go +++ b/libgo/go/net/http/client.go @@ -24,11 +24,13 @@ import ( // The Client's Transport typically has internal state (cached // TCP connections), so Clients should be reused instead of created as // needed. Clients are safe for concurrent use by multiple goroutines. -// -// Client is not yet very configurable. type Client struct { - Transport RoundTripper // if nil, DefaultTransport is used + // Transport specifies the mechanism by which individual + // HTTP requests are made. + // If nil, DefaultTransport is used. + Transport RoundTripper + // CheckRedirect specifies the policy for handling redirects. // If CheckRedirect is not nil, the client calls it before // following an HTTP redirect. The arguments req and via // are the upcoming request and the requests made already, @@ -121,9 +123,8 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) { req.Header = make(Header) } - info := req.URL.RawUserinfo - if len(info) > 0 { - req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(info))) + if u := req.URL.User; u != nil { + req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String()))) } return t.RoundTrip(req) } @@ -213,11 +214,11 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) { break } } - for _, cookie := range jar.Cookies(req.URL) { - req.AddCookie(cookie) - } } + for _, cookie := range jar.Cookies(req.URL) { + req.AddCookie(cookie) + } urlStr = req.URL.String() if r, err = send(req, c.Transport); err != nil { break diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index 57a9dd9..c746110 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -18,6 +18,7 @@ import ( "net/url" "strconv" "strings" + "sync" "testing" ) @@ -236,6 +237,92 @@ func TestRedirects(t *testing.T) { } } +var expectedCookies = []*Cookie{ + &Cookie{Name: "ChocolateChip", Value: "tasty"}, + &Cookie{Name: "First", Value: "Hit"}, + &Cookie{Name: "Second", Value: "Hit"}, +} + +var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request) { + for _, cookie := range r.Cookies() { + SetCookie(w, cookie) + } + if r.URL.Path == "/" { + SetCookie(w, expectedCookies[1]) + Redirect(w, r, "/second", StatusMovedPermanently) + } else { + SetCookie(w, expectedCookies[2]) + w.Write([]byte("hello")) + } +}) + +// Just enough correctness for our redirect tests. Uses the URL.Host as the +// scope of all cookies. +type TestJar struct { + m sync.Mutex + perURL map[string][]*Cookie +} + +func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) { + j.m.Lock() + defer j.m.Unlock() + j.perURL[u.Host] = cookies +} + +func (j *TestJar) Cookies(u *url.URL) []*Cookie { + j.m.Lock() + defer j.m.Unlock() + return j.perURL[u.Host] +} + +func TestRedirectCookiesOnRequest(t *testing.T) { + var ts *httptest.Server + ts = httptest.NewServer(echoCookiesRedirectHandler) + defer ts.Close() + c := &Client{} + req, _ := NewRequest("GET", ts.URL, nil) + req.AddCookie(expectedCookies[0]) + // TODO: Uncomment when an implementation of a RFC6265 cookie jar lands. + _ = c + // resp, _ := c.Do(req) + // matchReturnedCookies(t, expectedCookies, resp.Cookies()) + + req, _ = NewRequest("GET", ts.URL, nil) + // resp, _ = c.Do(req) + // matchReturnedCookies(t, expectedCookies[1:], resp.Cookies()) +} + +func TestRedirectCookiesJar(t *testing.T) { + var ts *httptest.Server + ts = httptest.NewServer(echoCookiesRedirectHandler) + defer ts.Close() + c := &Client{} + c.Jar = &TestJar{perURL: make(map[string][]*Cookie)} + u, _ := url.Parse(ts.URL) + c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]}) + resp, _ := c.Get(ts.URL) + matchReturnedCookies(t, expectedCookies, resp.Cookies()) +} + +func matchReturnedCookies(t *testing.T, expected, given []*Cookie) { + t.Logf("Received cookies: %v", given) + if len(given) != len(expected) { + t.Errorf("Expected %d cookies, got %d", len(expected), len(given)) + } + for _, ec := range expected { + foundC := false + for _, c := range given { + if ec.Name == c.Name && ec.Value == c.Value { + foundC = true + break + } + } + if !foundC { + t.Errorf("Missing cookie %v", ec) + } + } +} + func TestStreamingGet(t *testing.T) { say := make(chan string) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { diff --git a/libgo/go/net/http/doc.go b/libgo/go/net/http/doc.go index 2dbcf8d..8962ed3 100644 --- a/libgo/go/net/http/doc.go +++ b/libgo/go/net/http/doc.go @@ -34,7 +34,8 @@ settings, create a Client: resp, err := client.Get("http://example.com") // ... - req := http.NewRequest("GET", "http://example.com", nil) + req, err := http.NewRequest("GET", "http://example.com", nil) + // ... req.Header.Add("If-None-Match", `W/"wyzzy"`) resp, err := client.Do(req) // ... diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go index 976ee75..85cad3e 100644 --- a/libgo/go/net/http/fs_test.go +++ b/libgo/go/net/http/fs_test.go @@ -224,16 +224,15 @@ func TestEmptyDirOpenCWD(t *testing.T) { func TestServeFileContentType(t *testing.T) { const ctype = "icecream/chocolate" - override := false ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - if override { + if r.FormValue("override") == "1" { w.Header().Set("Content-Type", ctype) } ServeFile(w, r, "testdata/file") })) defer ts.Close() - get := func(want string) { - resp, err := Get(ts.URL) + get := func(override, want string) { + resp, err := Get(ts.URL + "?override=" + override) if err != nil { t.Fatal(err) } @@ -241,9 +240,8 @@ func TestServeFileContentType(t *testing.T) { t.Errorf("Content-Type mismatch: got %q, want %q", h, want) } } - get("text/plain; charset=utf-8") - override = true - get(ctype) + get("0", "text/plain; charset=utf-8") + get("1", ctype) } func TestServeFileMimeType(t *testing.T) { diff --git a/libgo/go/net/http/httputil/dump.go b/libgo/go/net/http/httputil/dump.go index 31696ae..b8a98ee 100644 --- a/libgo/go/net/http/httputil/dump.go +++ b/libgo/go/net/http/httputil/dump.go @@ -13,6 +13,7 @@ import ( "net" "net/http" "strings" + "time" ) // One of the copies, say from b to r2, could be avoided by using a more @@ -36,12 +37,12 @@ type dumpConn struct { io.Reader } -func (c *dumpConn) Close() error { return nil } -func (c *dumpConn) LocalAddr() net.Addr { return nil } -func (c *dumpConn) RemoteAddr() net.Addr { return nil } -func (c *dumpConn) SetTimeout(nsec int64) error { return nil } -func (c *dumpConn) SetReadTimeout(nsec int64) error { return nil } -func (c *dumpConn) SetWriteTimeout(nsec int64) error { return nil } +func (c *dumpConn) Close() error { return nil } +func (c *dumpConn) LocalAddr() net.Addr { return nil } +func (c *dumpConn) RemoteAddr() net.Addr { return nil } +func (c *dumpConn) SetDeadline(t time.Time) error { return nil } +func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil } +func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil } // DumpRequestOut is like DumpRequest but includes // headers that the standard http.Transport adds, @@ -124,16 +125,8 @@ func DumpRequest(req *http.Request, body bool) (dump []byte, err error) { var b bytes.Buffer - urlStr := req.URL.Raw - if urlStr == "" { - urlStr = valueOrDefault(req.URL.EncodedPath(), "/") - if req.URL.RawQuery != "" { - urlStr += "?" + req.URL.RawQuery - } - } - - fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), urlStr, - req.ProtoMajor, req.ProtoMinor) + fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), + req.URL.RequestURI(), req.ProtoMajor, req.ProtoMinor) host := req.Host if host == "" && req.URL != nil { diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go index 1dc83e7..1072e2e 100644 --- a/libgo/go/net/http/httputil/reverseproxy.go +++ b/libgo/go/net/http/httputil/reverseproxy.go @@ -59,11 +59,6 @@ func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path) - if q := req.URL.RawQuery; q != "" { - req.URL.RawPath = req.URL.Path + "?" + q - } else { - req.URL.RawPath = req.URL.Path - } req.URL.RawQuery = target.RawQuery } return &ReverseProxy{Director: director} diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go index ad7e3c0..da3e405 100644 --- a/libgo/go/net/http/readrequest_test.go +++ b/libgo/go/net/http/readrequest_test.go @@ -44,15 +44,9 @@ var reqTests = []reqTest{ &Request{ Method: "GET", URL: &url.URL{ - Raw: "http://www.techcrunch.com/", - Scheme: "http", - RawPath: "/", - RawAuthority: "www.techcrunch.com", - RawUserinfo: "", - Host: "www.techcrunch.com", - Path: "/", - RawQuery: "", - Fragment: "", + Scheme: "http", + Host: "www.techcrunch.com", + Path: "/", }, Proto: "HTTP/1.1", ProtoMajor: 1, @@ -86,9 +80,7 @@ var reqTests = []reqTest{ &Request{ Method: "GET", URL: &url.URL{ - Raw: "/", - Path: "/", - RawPath: "/", + Path: "/", }, Proto: "HTTP/1.1", ProtoMajor: 1, @@ -113,15 +105,7 @@ var reqTests = []reqTest{ &Request{ Method: "GET", URL: &url.URL{ - Raw: "//user@host/is/actually/a/path/", - Scheme: "", - RawPath: "//user@host/is/actually/a/path/", - RawAuthority: "", - RawUserinfo: "", - Host: "", - Path: "//user@host/is/actually/a/path/", - RawQuery: "", - Fragment: "", + Path: "//user@host/is/actually/a/path/", }, Proto: "HTTP/1.1", ProtoMajor: 1, @@ -170,9 +154,7 @@ var reqTests = []reqTest{ &Request{ Method: "POST", URL: &url.URL{ - Raw: "/", - Path: "/", - RawPath: "/", + Path: "/", }, TransferEncoding: []string{"chunked"}, Proto: "HTTP/1.1", diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 2603010..5a4e739 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -302,26 +302,14 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err host = req.URL.Host } - urlStr := req.URL.RawPath - if strings.HasPrefix(urlStr, "?") { - urlStr = "/" + urlStr // Issue 2344 - } - if urlStr == "" { - urlStr = valueOrDefault(req.URL.RawPath, valueOrDefault(req.URL.EncodedPath(), "/")) - if req.URL.RawQuery != "" { - urlStr += "?" + req.URL.RawQuery - } - if usingProxy { - if urlStr == "" || urlStr[0] != '/' { - urlStr = "/" + urlStr - } - urlStr = req.URL.Scheme + "://" + host + urlStr - } + ruri := req.URL.RequestURI() + if usingProxy && req.URL.Scheme != "" && req.URL.Opaque == "" { + ruri = req.URL.Scheme + "://" + host + ruri } - // TODO(bradfitz): escape at least newlines in urlStr? + // TODO(bradfitz): escape at least newlines in ruri? bw := bufio.NewWriter(w) - fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), urlStr) + fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri) // Header lines fmt.Fprintf(bw, "Host: %s\r\n", host) diff --git a/libgo/go/net/http/requestwrite_test.go b/libgo/go/net/http/requestwrite_test.go index 8081589..fc3186f 100644 --- a/libgo/go/net/http/requestwrite_test.go +++ b/libgo/go/net/http/requestwrite_test.go @@ -32,15 +32,9 @@ var reqWriteTests = []reqWriteTest{ Req: Request{ Method: "GET", URL: &url.URL{ - Raw: "http://www.techcrunch.com/", - Scheme: "http", - RawPath: "http://www.techcrunch.com/", - RawAuthority: "www.techcrunch.com", - RawUserinfo: "", - Host: "www.techcrunch.com", - Path: "/", - RawQuery: "", - Fragment: "", + Scheme: "http", + Host: "www.techcrunch.com", + Path: "/", }, Proto: "HTTP/1.1", ProtoMajor: 1, @@ -60,7 +54,7 @@ var reqWriteTests = []reqWriteTest{ Form: map[string][]string{}, }, - WantWrite: "GET http://www.techcrunch.com/ HTTP/1.1\r\n" + + WantWrite: "GET / HTTP/1.1\r\n" + "Host: www.techcrunch.com\r\n" + "User-Agent: Fake\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + @@ -198,7 +192,7 @@ var reqWriteTests = []reqWriteTest{ "\r\n" + "abcdef", - WantProxy: "POST / HTTP/1.1\r\n" + + WantProxy: "POST http://example.com/ HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Go http package\r\n" + "Content-Length: 6\r\n" + diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index 24e6b50..147c216 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -84,15 +84,15 @@ func (c *testConn) RemoteAddr() net.Addr { return dummyAddr("remote-addr") } -func (c *testConn) SetTimeout(nsec int64) error { +func (c *testConn) SetDeadline(t time.Time) error { return nil } -func (c *testConn) SetReadTimeout(nsec int64) error { +func (c *testConn) SetReadDeadline(t time.Time) error { return nil } -func (c *testConn) SetWriteTimeout(nsec int64) error { +func (c *testConn) SetWriteDeadline(t time.Time) error { return nil } @@ -642,7 +642,7 @@ func TestServerExpect(t *testing.T) { // Note using r.FormValue("readbody") because for POST // requests that would read from r.Body, which we only // conditionally want to do. - if strings.Contains(r.URL.RawPath, "readbody=true") { + if strings.Contains(r.URL.RawQuery, "readbody=true") { ioutil.ReadAll(r.Body) w.Write([]byte("Hi")) } else { @@ -904,17 +904,13 @@ func testHandlerPanic(t *testing.T, withHijack bool) { panic("intentional death for testing") })) defer ts.Close() - _, err := Get(ts.URL) - if err == nil { - t.Logf("expected an error") - } // Do a blocking read on the log output pipe so its logging // doesn't bleed into the next test. But wait only 5 seconds // for it. - done := make(chan bool) + done := make(chan bool, 1) go func() { - buf := make([]byte, 1024) + buf := make([]byte, 4<<10) _, err := pr.Read(buf) pr.Close() if err != nil { @@ -922,6 +918,12 @@ func testHandlerPanic(t *testing.T, withHijack bool) { } done <- true }() + + _, err := Get(ts.URL) + if err == nil { + t.Logf("expected an error") + } + select { case <-done: return diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index fa90095..bad3bcb 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -569,14 +569,15 @@ func (c *conn) serve() { if err == nil { return } - if c.rwc != nil { // may be nil if connection hijacked - c.rwc.Close() - } var buf bytes.Buffer fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err) buf.Write(debug.Stack()) log.Print(buf.String()) + + if c.rwc != nil { // may be nil if connection hijacked + c.rwc.Close() + } }() if tlsConn, ok := c.rwc.(*tls.Conn); ok { @@ -954,8 +955,8 @@ func Serve(l net.Listener, handler Handler) error { type Server struct { Addr string // TCP address to listen on, ":http" if empty Handler Handler // handler to invoke, http.DefaultServeMux if nil - ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections - WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections + ReadTimeout time.Duration // maximum duration before timing out read of the request + WriteTimeout time.Duration // maximum duration before timing out write of the response MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0 } @@ -989,10 +990,10 @@ func (srv *Server) Serve(l net.Listener) error { return e } if srv.ReadTimeout != 0 { - rw.SetReadTimeout(srv.ReadTimeout.Nanoseconds()) + rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) } if srv.WriteTimeout != 0 { - rw.SetWriteTimeout(srv.WriteTimeout.Nanoseconds()) + rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout)) } c, err := srv.newConn(rw) if err != nil { diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 33ad328..1b9ad1b 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -229,9 +229,8 @@ func (cm *connectMethod) proxyAuth() string { if cm.proxyURL == nil { return "" } - proxyInfo := cm.proxyURL.RawUserinfo - if proxyInfo != "" { - return "Basic " + base64.URLEncoding.EncodeToString([]byte(proxyInfo)) + if u := cm.proxyURL.User; u != nil { + return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String())) } return "" } @@ -332,7 +331,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) { case cm.targetScheme == "https": connectReq := &Request{ Method: "CONNECT", - URL: &url.URL{RawPath: cm.targetAddr}, + URL: &url.URL{Opaque: cm.targetAddr}, Host: cm.targetAddr, Header: make(Header), } diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go index 67a4049..c74bfcd 100644 --- a/libgo/go/net/ipraw_test.go +++ b/libgo/go/net/ipraw_test.go @@ -12,6 +12,7 @@ import ( "flag" "os" "testing" + "time" ) const ICMP_ECHO_REQUEST = 8 @@ -101,7 +102,7 @@ func TestICMP(t *testing.T) { t.Fatalf(`net.WriteToIP(..., %v) = %v, %v`, raddr, n, err) } - c.SetTimeout(100e6) + c.SetDeadline(time.Now().Add(100 * time.Millisecond)) resp := make([]byte, 1024) for { n, from, err := c.ReadFrom(resp) diff --git a/libgo/go/net/iprawsock_plan9.go b/libgo/go/net/iprawsock_plan9.go index 3fd9dce..58df607 100644 --- a/libgo/go/net/iprawsock_plan9.go +++ b/libgo/go/net/iprawsock_plan9.go @@ -8,12 +8,28 @@ package net import ( "os" + "time" ) // IPConn is the implementation of the Conn and PacketConn // interfaces for IP network connections. type IPConn bool +// SetDeadline implements the net.Conn SetDeadline method. +func (c *IPConn) SetDeadline(t time.Time) error { + return os.EPLAN9 +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *IPConn) SetReadDeadline(t time.Time) error { + return os.EPLAN9 +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *IPConn) SetWriteDeadline(t time.Time) error { + return os.EPLAN9 +} + // Implementation of the Conn interface - see Conn for documentation. // Read implements the net.Conn Read method. @@ -41,21 +57,6 @@ func (c *IPConn) RemoteAddr() Addr { return nil } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *IPConn) SetTimeout(nsec int64) error { - return os.EPLAN9 -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *IPConn) SetReadTimeout(nsec int64) error { - return os.EPLAN9 -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *IPConn) SetWriteTimeout(nsec int64) error { - return os.EPLAN9 -} - // IP-specific methods. // ReadFrom implements the net.PacketConn ReadFrom method. @@ -68,7 +69,7 @@ func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { // // WriteToIP can be made to time out and return // an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. +// see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err error) { return 0, os.EPLAN9 diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go index 103c4f6..e4f755b 100644 --- a/libgo/go/net/iprawsock_posix.go +++ b/libgo/go/net/iprawsock_posix.go @@ -12,6 +12,7 @@ import ( "errors" "os" "syscall" + "time" ) func sockaddrToIP(sa syscall.Sockaddr) Addr { @@ -97,28 +98,28 @@ func (c *IPConn) RemoteAddr() Addr { return c.fd.raddr } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *IPConn) SetTimeout(nsec int64) error { +// SetDeadline implements the net.Conn SetDeadline method. +func (c *IPConn) SetDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setTimeout(c.fd, nsec) + return setDeadline(c.fd, t) } -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *IPConn) SetReadTimeout(nsec int64) error { +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *IPConn) SetReadDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setReadTimeout(c.fd, nsec) + return setReadDeadline(c.fd, t) } -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *IPConn) SetWriteTimeout(nsec int64) error { +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *IPConn) SetWriteDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setWriteTimeout(c.fd, nsec) + return setWriteDeadline(c.fd, t) } // SetReadBuffer sets the size of the operating system's @@ -146,8 +147,8 @@ func (c *IPConn) SetWriteBuffer(bytes int) error { // that was on the packet. // // ReadFromIP can be made to time out and return an error with -// Timeout() == true after a fixed time limit; see SetTimeout and -// SetReadTimeout. +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetReadDeadline. func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err error) { if !c.ok() { return 0, nil, os.EINVAL @@ -182,7 +183,7 @@ func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { // // WriteToIP can be made to time out and return // an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. +// see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err error) { if !c.ok() { diff --git a/libgo/go/net/ipsock_plan9.go b/libgo/go/net/ipsock_plan9.go index 42fb408..09d8d6b 100644 --- a/libgo/go/net/ipsock_plan9.go +++ b/libgo/go/net/ipsock_plan9.go @@ -10,6 +10,7 @@ import ( "errors" "io" "os" + "time" ) // probeIPv6Stack returns two boolean values. If the first boolean value is @@ -156,27 +157,18 @@ func (c *plan9Conn) RemoteAddr() Addr { return c.raddr } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *plan9Conn) SetTimeout(nsec int64) error { - if !c.ok() { - return os.EINVAL - } +// SetDeadline implements the net.Conn SetDeadline method. +func (c *plan9Conn) SetDeadline(t time.Time) error { return os.EPLAN9 } -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *plan9Conn) SetReadTimeout(nsec int64) error { - if !c.ok() { - return os.EINVAL - } +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *plan9Conn) SetReadDeadline(t time.Time) error { return os.EPLAN9 } -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *plan9Conn) SetWriteTimeout(nsec int64) error { - if !c.ok() { - return os.EINVAL - } +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *plan9Conn) SetWriteDeadline(t time.Time) error { return os.EPLAN9 } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index 45fe0d9..3a059f5 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -101,7 +101,7 @@ type sockaddr interface { family() int } -func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { +func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { var oserr error var la, ra syscall.Sockaddr family := favoriteAddrFamily(net, raddr, laddr, mode) @@ -115,7 +115,7 @@ func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode goto Error } } - fd, oserr = socket(net, family, socktype, proto, la, ra, toAddr) + fd, oserr = socket(net, family, sotype, proto, la, ra, toAddr) if oserr != nil { goto Error } diff --git a/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go index 027794a..645aa6d 100644 --- a/libgo/go/net/lookup_plan9.go +++ b/libgo/go/net/lookup_plan9.go @@ -69,9 +69,7 @@ func queryDNS(addr string, typ string) (res []string, err error) { return query("/net/dns", addr+" "+typ, 1024) } -// LookupHost looks up the given host using the local resolver. -// It returns an array of that host's addresses. -func LookupHost(host string) (addrs []string, err error) { +func lookupHost(host string) (addrs []string, err error) { // Use /net/cs insead of /net/dns because cs knows about // host names in local network (e.g. from /lib/ndb/local) lines, err := queryCS("tcp", host, "1") @@ -95,9 +93,7 @@ func LookupHost(host string) (addrs []string, err error) { return } -// LookupIP looks up host using the local resolver. -// It returns an array of that host's IPv4 and IPv6 addresses. -func LookupIP(host string) (ips []IP, err error) { +func lookupIP(host string) (ips []IP, err error) { addrs, err := LookupHost(host) if err != nil { return @@ -110,8 +106,7 @@ func LookupIP(host string) (ips []IP, err error) { return } -// LookupPort looks up the port for the given network and service. -func LookupPort(network, service string) (port int, err error) { +func lookupPort(network, service string) (port int, err error) { switch network { case "tcp4", "tcp6": network = "tcp" @@ -140,11 +135,7 @@ func LookupPort(network, service string) (port int, err error) { return 0, unknownPortError } -// LookupCNAME returns the canonical DNS host for the given name. -// Callers that do not care about the canonical name can call -// LookupHost or LookupIP directly; both take care of resolving -// the canonical name as part of the lookup. -func LookupCNAME(name string) (cname string, err error) { +func lookupCNAME(name string) (cname string, err error) { lines, err := queryDNS(name, "cname") if err != nil { return @@ -157,16 +148,7 @@ func LookupCNAME(name string) (cname string, err error) { return "", errors.New("net: bad response from ndb/dns") } -// LookupSRV tries to resolve an SRV query of the given service, -// protocol, and domain name. The proto is "tcp" or "udp". -// The returned records are sorted by priority and randomized -// by weight within a priority. -// -// LookupSRV constructs the DNS name to look up following RFC 2782. -// That is, it looks up _service._proto.name. To accommodate services -// publishing SRV records under non-standard names, if both service -// and proto are empty strings, LookupSRV looks up name directly. -func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { +func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { var target string if service == "" && proto == "" { target = name @@ -195,8 +177,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err return } -// LookupMX returns the DNS MX records for the given domain name sorted by preference. -func LookupMX(name string) (mx []*MX, err error) { +func lookupMX(name string) (mx []*MX, err error) { lines, err := queryDNS(name, "mx") if err != nil { return @@ -214,8 +195,7 @@ func LookupMX(name string) (mx []*MX, err error) { return } -// LookupTXT returns the DNS TXT records for the given domain name. -func LookupTXT(name string) (txt []string, err error) { +func lookupTXT(name string) (txt []string, err error) { lines, err := queryDNS(name, "txt") if err != nil { return @@ -228,9 +208,7 @@ func LookupTXT(name string) (txt []string, err error) { return } -// LookupAddr performs a reverse lookup for the given address, returning a list -// of names mapping to that address. -func LookupAddr(addr string) (name []string, err error) { +func lookupAddr(addr string) (name []string, err error) { arpa, err := reverseaddr(addr) if err != nil { return diff --git a/libgo/go/net/lookup_unix.go b/libgo/go/net/lookup_unix.go index 5c47547..d500a12 100644 --- a/libgo/go/net/lookup_unix.go +++ b/libgo/go/net/lookup_unix.go @@ -52,9 +52,7 @@ func lookupProtocol(name string) (proto int, err error) { return } -// LookupHost looks up the given host using the local resolver. -// It returns an array of that host's addresses. -func LookupHost(host string) (addrs []string, err error) { +func lookupHost(host string) (addrs []string, err error) { addrs, err, ok := cgoLookupHost(host) if !ok { addrs, err = goLookupHost(host) @@ -62,9 +60,7 @@ func LookupHost(host string) (addrs []string, err error) { return } -// LookupIP looks up host using the local resolver. -// It returns an array of that host's IPv4 and IPv6 addresses. -func LookupIP(host string) (addrs []IP, err error) { +func lookupIP(host string) (addrs []IP, err error) { addrs, err, ok := cgoLookupIP(host) if !ok { addrs, err = goLookupIP(host) @@ -72,8 +68,7 @@ func LookupIP(host string) (addrs []IP, err error) { return } -// LookupPort looks up the port for the given network and service. -func LookupPort(network, service string) (port int, err error) { +func lookupPort(network, service string) (port int, err error) { port, err, ok := cgoLookupPort(network, service) if !ok { port, err = goLookupPort(network, service) @@ -81,11 +76,7 @@ func LookupPort(network, service string) (port int, err error) { return } -// LookupCNAME returns the canonical DNS host for the given name. -// Callers that do not care about the canonical name can call -// LookupHost or LookupIP directly; both take care of resolving -// the canonical name as part of the lookup. -func LookupCNAME(name string) (cname string, err error) { +func lookupCNAME(name string) (cname string, err error) { cname, err, ok := cgoLookupCNAME(name) if !ok { cname, err = goLookupCNAME(name) @@ -93,16 +84,7 @@ func LookupCNAME(name string) (cname string, err error) { return } -// LookupSRV tries to resolve an SRV query of the given service, -// protocol, and domain name. The proto is "tcp" or "udp". -// The returned records are sorted by priority and randomized -// by weight within a priority. -// -// LookupSRV constructs the DNS name to look up following RFC 2782. -// That is, it looks up _service._proto.name. To accommodate services -// publishing SRV records under non-standard names, if both service -// and proto are empty strings, LookupSRV looks up name directly. -func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { +func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { var target string if service == "" && proto == "" { target = name @@ -123,8 +105,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err return } -// LookupMX returns the DNS MX records for the given domain name sorted by preference. -func LookupMX(name string) (mx []*MX, err error) { +func lookupMX(name string) (mx []*MX, err error) { _, records, err := lookup(name, dnsTypeMX) if err != nil { return @@ -138,8 +119,7 @@ func LookupMX(name string) (mx []*MX, err error) { return } -// LookupTXT returns the DNS TXT records for the given domain name. -func LookupTXT(name string) (txt []string, err error) { +func lookupTXT(name string) (txt []string, err error) { _, records, err := lookup(name, dnsTypeTXT) if err != nil { return @@ -151,9 +131,7 @@ func LookupTXT(name string) (txt []string, err error) { return } -// LookupAddr performs a reverse lookup for the given address, returning a list -// of names mapping to that address. -func LookupAddr(addr string) (name []string, err error) { +func lookupAddr(addr string) (name []string, err error) { name = lookupStaticAddr(addr) if len(name) > 0 { return diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go index 51afbd4..dfe2ff6 100644 --- a/libgo/go/net/lookup_windows.go +++ b/libgo/go/net/lookup_windows.go @@ -28,7 +28,7 @@ func lookupProtocol(name string) (proto int, err error) { return int(p.Proto), nil } -func LookupHost(name string) (addrs []string, err error) { +func lookupHost(name string) (addrs []string, err error) { ips, err := LookupIP(name) if err != nil { return @@ -40,7 +40,7 @@ func LookupHost(name string) (addrs []string, err error) { return } -func LookupIP(name string) (addrs []IP, err error) { +func lookupIP(name string) (addrs []IP, err error) { hostentLock.Lock() defer hostentLock.Unlock() h, e := syscall.GetHostByName(name) @@ -61,7 +61,7 @@ func LookupIP(name string) (addrs []IP, err error) { return addrs, nil } -func LookupPort(network, service string) (port int, err error) { +func lookupPort(network, service string) (port int, err error) { switch network { case "tcp4", "tcp6": network = "tcp" @@ -77,7 +77,7 @@ func LookupPort(network, service string) (port int, err error) { return int(syscall.Ntohs(s.Port)), nil } -func LookupCNAME(name string) (cname string, err error) { +func lookupCNAME(name string) (cname string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) if e != nil { @@ -91,16 +91,7 @@ func LookupCNAME(name string) (cname string, err error) { return } -// LookupSRV tries to resolve an SRV query of the given service, -// protocol, and domain name. The proto is "tcp" or "udp". -// The returned records are sorted by priority and randomized -// by weight within a priority. -// -// LookupSRV constructs the DNS name to look up following RFC 2782. -// That is, it looks up _service._proto.name. To accommodate services -// publishing SRV records under non-standard names, if both service -// and proto are empty strings, LookupSRV looks up name directly. -func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { +func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { var target string if service == "" && proto == "" { target = name @@ -122,7 +113,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err return name, addrs, nil } -func LookupMX(name string) (mx []*MX, err error) { +func lookupMX(name string) (mx []*MX, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil) if e != nil { @@ -138,7 +129,7 @@ func LookupMX(name string) (mx []*MX, err error) { return mx, nil } -func LookupTXT(name string) (txt []string, err error) { +func lookupTXT(name string) (txt []string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil) if e != nil { @@ -156,7 +147,7 @@ func LookupTXT(name string) (txt []string, err error) { return } -func LookupAddr(addr string) (name []string, err error) { +func lookupAddr(addr string) (name []string, err error) { arpa, err := reverseaddr(addr) if err != nil { return nil, err diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index b236dfd..609fee2 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -9,7 +9,10 @@ package net // TODO(rsc): // support for raw ethernet sockets -import "errors" +import ( + "errors" + "time" +) // Addr represents a network end point address. type Addr interface { @@ -21,12 +24,12 @@ type Addr interface { type Conn interface { // Read reads data from the connection. // Read can be made to time out and return a net.Error with Timeout() == true - // after a fixed time limit; see SetTimeout and SetReadTimeout. + // after a fixed time limit; see SetDeadline and SetReadDeadline. Read(b []byte) (n int, err error) // Write writes data to the connection. // Write can be made to time out and return a net.Error with Timeout() == true - // after a fixed time limit; see SetTimeout and SetWriteTimeout. + // after a fixed time limit; see SetDeadline and SetWriteDeadline. Write(b []byte) (n int, err error) // Close closes the connection. @@ -38,21 +41,23 @@ type Conn interface { // RemoteAddr returns the remote network address. RemoteAddr() Addr - // SetTimeout sets the read and write deadlines associated + // SetDeadline sets the read and write deadlines associated // with the connection. - SetTimeout(nsec int64) error - - // SetReadTimeout sets the time (in nanoseconds) that - // Read will wait for data before returning an error with Timeout() == true. - // Setting nsec == 0 (the default) disables the deadline. - SetReadTimeout(nsec int64) error - - // SetWriteTimeout sets the time (in nanoseconds) that - // Write will wait to send its data before returning an error with Timeout() == true. - // Setting nsec == 0 (the default) disables the deadline. + SetDeadline(t time.Time) error + + // SetReadDeadline sets the deadline for all Read calls to return. + // If the deadline is reached, Read will fail with a timeout + // (see type Error) instead of blocking. + // A zero value for t means Read will not time out. + SetReadDeadline(t time.Time) error + + // SetWriteDeadline sets the deadline for all Write calls to return. + // If the deadline is reached, Write will fail with a timeout + // (see type Error) instead of blocking. + // A zero value for t means Write will not time out. // Even if write times out, it may return n > 0, indicating that // some of the data was successfully written. - SetWriteTimeout(nsec int64) error + SetWriteDeadline(t time.Time) error } // An Error represents a network error. @@ -70,13 +75,13 @@ type PacketConn interface { // was on the packet. // ReadFrom can be made to time out and return // an error with Timeout() == true after a fixed time limit; - // see SetTimeout and SetReadTimeout. + // see SetDeadline and SetReadDeadline. ReadFrom(b []byte) (n int, addr Addr, err error) // WriteTo writes a packet with payload b to addr. // WriteTo can be made to time out and return // an error with Timeout() == true after a fixed time limit; - // see SetTimeout and SetWriteTimeout. + // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. WriteTo(b []byte, addr Addr) (n int, err error) @@ -86,21 +91,23 @@ type PacketConn interface { // LocalAddr returns the local network address. LocalAddr() Addr - // SetTimeout sets the read and write deadlines associated + // SetDeadline sets the read and write deadlines associated // with the connection. - SetTimeout(nsec int64) error - - // SetReadTimeout sets the time (in nanoseconds) that - // Read will wait for data before returning an error with Timeout() == true. - // Setting nsec == 0 (the default) disables the deadline. - SetReadTimeout(nsec int64) error - - // SetWriteTimeout sets the time (in nanoseconds) that - // Write will wait to send its data before returning an error with Timeout() == true. - // Setting nsec == 0 (the default) disables the deadline. + SetDeadline(t time.Time) error + + // SetReadDeadline sets the deadline for all Read calls to return. + // If the deadline is reached, Read will fail with a timeout + // (see type Error) instead of blocking. + // A zero value for t means Read will not time out. + SetReadDeadline(t time.Time) error + + // SetWriteDeadline sets the deadline for all Write calls to return. + // If the deadline is reached, Write will fail with a timeout + // (see type Error) instead of blocking. + // A zero value for t means Write will not time out. // Even if write times out, it may return n > 0, indicating that // some of the data was successfully written. - SetWriteTimeout(nsec int64) error + SetWriteDeadline(t time.Time) error } // A Listener is a generic network listener for stream-oriented protocols. diff --git a/libgo/go/net/pipe.go b/libgo/go/net/pipe.go index 0ce7ccb..f1a2eca 100644 --- a/libgo/go/net/pipe.go +++ b/libgo/go/net/pipe.go @@ -7,6 +7,7 @@ package net import ( "errors" "io" + "time" ) // Pipe creates a synchronous, in-memory, full duplex @@ -53,14 +54,14 @@ func (p *pipe) RemoteAddr() Addr { return pipeAddr(0) } -func (p *pipe) SetTimeout(nsec int64) error { - return errors.New("net.Pipe does not support timeouts") +func (p *pipe) SetDeadline(t time.Time) error { + return errors.New("net.Pipe does not support deadlines") } -func (p *pipe) SetReadTimeout(nsec int64) error { - return errors.New("net.Pipe does not support timeouts") +func (p *pipe) SetReadDeadline(t time.Time) error { + return errors.New("net.Pipe does not support deadlines") } -func (p *pipe) SetWriteTimeout(nsec int64) error { - return errors.New("net.Pipe does not support timeouts") +func (p *pipe) SetWriteDeadline(t time.Time) error { + return errors.New("net.Pipe does not support deadlines") } diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go index 350abe4..e9ab066 100644 --- a/libgo/go/net/sendfile_linux.go +++ b/libgo/go/net/sendfile_linux.go @@ -40,15 +40,6 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { defer c.wio.Unlock() c.incref() defer c.decref() - if c.wdeadline_delta > 0 { - // This is a little odd that we're setting the timeout - // for the entire file but Write has the same issue - // (if one slurps the whole file into memory and - // do one large Write). At least they're consistent. - c.wdeadline = pollserver.Now() + c.wdeadline_delta - } else { - c.wdeadline = 0 - } dst := c.sysfd src := f.Fd() diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index 5475d38..b0b546b 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -11,6 +11,7 @@ import ( "runtime" "strings" "testing" + "time" ) // Do not test empty datagrams by default. @@ -63,7 +64,7 @@ func connect(t *testing.T, network, addr string, isEmpty bool) { if err != nil { t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err) } - fd.SetReadTimeout(1e9) // 1s + fd.SetReadDeadline(time.Now().Add(1 * time.Second)) var b []byte if !isEmpty { @@ -91,7 +92,7 @@ func connect(t *testing.T, network, addr string, isEmpty bool) { } func doTest(t *testing.T, network, listenaddr, dialaddr string) { - t.Logf("Test %q %q %q\n", network, listenaddr, dialaddr) + t.Logf("Test %q %q %q", network, listenaddr, dialaddr) switch listenaddr { case "", "0.0.0.0", "[::]", "[::ffff:0.0.0.0]": if testing.Short() || avoidMacFirewall { @@ -169,10 +170,10 @@ func runPacket(t *testing.T, network, addr string, listening chan<- string, done t.Fatalf("net.ListenPacket(%q, %q) = _, %v", network, addr, err) } listening <- c.LocalAddr().String() - c.SetReadTimeout(10e6) // 10ms var buf [1000]byte Run: for { + c.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) n, addr, err := c.ReadFrom(buf[0:]) if e, ok := err.(Error); ok && e.Timeout() { select { @@ -194,7 +195,7 @@ Run: } func doTestPacket(t *testing.T, network, listenaddr, dialaddr string, isEmpty bool) { - t.Logf("TestPacket %s %s %s\n", network, listenaddr, dialaddr) + t.Logf("TestPacket %q %q %q", network, listenaddr, dialaddr) listening := make(chan string) done := make(chan int) if network == "udp" { diff --git a/libgo/go/net/smtp/auth.go b/libgo/go/net/smtp/auth.go index 6f0cde0..d401e3c 100644 --- a/libgo/go/net/smtp/auth.go +++ b/libgo/go/net/smtp/auth.go @@ -6,6 +6,7 @@ package smtp import ( "crypto/hmac" + "crypto/md5" "errors" "fmt" ) @@ -88,7 +89,7 @@ func (a *cramMD5Auth) Start(server *ServerInfo) (string, []byte, error) { func (a *cramMD5Auth) Next(fromServer []byte, more bool) ([]byte, error) { if more { - d := hmac.NewMD5([]byte(a.secret)) + d := hmac.New(md5.New, []byte(a.secret)) d.Write(fromServer) s := make([]byte, 0, d.Size()) return []byte(fmt.Sprintf("%s %x", a.username, d.Sum(s))), nil diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go index 881c922..2f3210b 100644 --- a/libgo/go/net/sock.go +++ b/libgo/go/net/sock.go @@ -17,10 +17,10 @@ import ( var listenerBacklog = maxListenerBacklog() // Generic socket creation. -func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { +func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock() - s, e := syscall.Socket(f, p, t) + s, err := syscall.Socket(f, t, p) if err != nil { syscall.ForkLock.RUnlock() return nil, err @@ -28,17 +28,17 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() - setDefaultSockopts(s, f, p) + setDefaultSockopts(s, f, t) if la != nil { - e = syscall.Bind(s, la) - if e != nil { + err = syscall.Bind(s, la) + if err != nil { closesocket(s) - return nil, e + return nil, err } } - if fd, err = newFD(s, f, p, net); err != nil { + if fd, err = newFD(s, f, t, net); err != nil { closesocket(s) return nil, err } diff --git a/libgo/go/net/sockopt.go b/libgo/go/net/sockopt.go index 7fa1052..59f9af5 100644 --- a/libgo/go/net/sockopt.go +++ b/libgo/go/net/sockopt.go @@ -12,6 +12,7 @@ import ( "bytes" "os" "syscall" + "time" ) // Boolean to int. @@ -115,21 +116,21 @@ func setWriteBuffer(fd *netFD, bytes int) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)) } -func setReadTimeout(fd *netFD, nsec int64) error { - fd.rdeadline_delta = nsec +func setReadDeadline(fd *netFD, t time.Time) error { + fd.rdeadline = t.UnixNano() return nil } -func setWriteTimeout(fd *netFD, nsec int64) error { - fd.wdeadline_delta = nsec +func setWriteDeadline(fd *netFD, t time.Time) error { + fd.wdeadline = t.UnixNano() return nil } -func setTimeout(fd *netFD, nsec int64) error { - if e := setReadTimeout(fd, nsec); e != nil { +func setDeadline(fd *netFD, t time.Time) error { + if e := setReadDeadline(fd, t); e != nil { return e } - return setWriteTimeout(fd, nsec) + return setWriteDeadline(fd, t) } func setReuseAddr(fd *netFD, reuse bool) error { diff --git a/libgo/go/net/sockopt_bsd.go b/libgo/go/net/sockopt_bsd.go index e99fb41..2093e08 100644 --- a/libgo/go/net/sockopt_bsd.go +++ b/libgo/go/net/sockopt_bsd.go @@ -12,14 +12,15 @@ import ( "syscall" ) -func setDefaultSockopts(s, f, p int) { +func setDefaultSockopts(s, f, t int) { switch f { case syscall.AF_INET6: // Allow both IP versions even if the OS default is otherwise. syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) } - if f == syscall.AF_UNIX || p == syscall.IPPROTO_TCP { + if f == syscall.AF_UNIX || + (f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM { // Allow reuse of recently-used addresses. syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) diff --git a/libgo/go/net/sockopt_linux.go b/libgo/go/net/sockopt_linux.go index 5158384..9dbb4e5 100644 --- a/libgo/go/net/sockopt_linux.go +++ b/libgo/go/net/sockopt_linux.go @@ -10,14 +10,15 @@ import ( "syscall" ) -func setDefaultSockopts(s, f, p int) { +func setDefaultSockopts(s, f, t int) { switch f { case syscall.AF_INET6: // Allow both IP versions even if the OS default is otherwise. syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) } - if f == syscall.AF_UNIX || p == syscall.IPPROTO_TCP { + if f == syscall.AF_UNIX || + (f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM { // Allow reuse of recently-used addresses. syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) } diff --git a/libgo/go/net/sockopt_windows.go b/libgo/go/net/sockopt_windows.go index 485c14a..a7b5606 100644 --- a/libgo/go/net/sockopt_windows.go +++ b/libgo/go/net/sockopt_windows.go @@ -10,7 +10,7 @@ import ( "syscall" ) -func setDefaultSockopts(s syscall.Handle, f, p int) { +func setDefaultSockopts(s syscall.Handle, f, t int) { switch f { case syscall.AF_INET6: // Allow both IP versions even if the OS default is otherwise. diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go index 69fae03..288ec05 100644 --- a/libgo/go/net/tcpsock_plan9.go +++ b/libgo/go/net/tcpsock_plan9.go @@ -8,6 +8,7 @@ package net import ( "os" + "time" ) // TCPConn is an implementation of the Conn interface @@ -16,6 +17,21 @@ type TCPConn struct { plan9Conn } +// SetDeadline implements the net.Conn SetDeadline method. +func (c *TCPConn) SetDeadline(t time.Time) error { + return os.EPLAN9 +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *TCPConn) SetReadDeadline(t time.Time) error { + return os.EPLAN9 +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *TCPConn) SetWriteDeadline(t time.Time) error { + return os.EPLAN9 +} + // CloseRead shuts down the reading side of the TCP connection. // Most callers should just use Close. func (c *TCPConn) CloseRead() error { diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index a492e61..91816fa 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -12,6 +12,7 @@ import ( "io" "os" "syscall" + "time" ) // BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for @@ -134,28 +135,28 @@ func (c *TCPConn) RemoteAddr() Addr { return c.fd.raddr } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *TCPConn) SetTimeout(nsec int64) error { +// SetDeadline implements the net.Conn SetDeadline method. +func (c *TCPConn) SetDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setTimeout(c.fd, nsec) + return setDeadline(c.fd, t) } -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *TCPConn) SetReadTimeout(nsec int64) error { +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *TCPConn) SetReadDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setReadTimeout(c.fd, nsec) + return setReadDeadline(c.fd, t) } -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *TCPConn) SetWriteTimeout(nsec int64) error { +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *TCPConn) SetWriteDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setWriteTimeout(c.fd, nsec) + return setWriteDeadline(c.fd, t) } // SetReadBuffer sets the size of the operating system's @@ -294,12 +295,13 @@ func (l *TCPListener) Close() error { // Addr returns the listener's network address, a *TCPAddr. func (l *TCPListener) Addr() Addr { return l.fd.laddr } -// SetTimeout sets the deadline associated with the listener -func (l *TCPListener) SetTimeout(nsec int64) error { +// SetDeadline sets the deadline associated with the listener. +// A zero time value disables the deadline. +func (l *TCPListener) SetDeadline(t time.Time) error { if l == nil || l.fd == nil { return os.EINVAL } - return setTimeout(l.fd, nsec) + return setDeadline(l.fd, t) } // File returns a copy of the underlying os.File, set to blocking mode. diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go index f6e5238..11db012 100644 --- a/libgo/go/net/timeout_test.go +++ b/libgo/go/net/timeout_test.go @@ -5,6 +5,7 @@ package net import ( + "fmt" "runtime" "testing" "time" @@ -17,26 +18,41 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { return } defer fd.Close() - t0 := time.Now() - fd.SetReadTimeout(1e8) // 100ms - var b [100]byte - var n int - var err1 error - if readFrom { - n, _, err1 = fd.(PacketConn).ReadFrom(b[0:]) - } else { - n, err1 = fd.Read(b[0:]) - } - t1 := time.Now() what := "Read" if readFrom { what = "ReadFrom" } - if n != 0 || err1 == nil || !err1.(Error).Timeout() { - t.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1) - } - if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 150*time.Millisecond { - t.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt) + + errc := make(chan error, 1) + go func() { + t0 := time.Now() + fd.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + var b [100]byte + var n int + var err1 error + if readFrom { + n, _, err1 = fd.(PacketConn).ReadFrom(b[0:]) + } else { + n, err1 = fd.Read(b[0:]) + } + t1 := time.Now() + if n != 0 || err1 == nil || !err1.(Error).Timeout() { + errc <- fmt.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1) + return + } + if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 250*time.Millisecond { + errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt) + return + } + errc <- nil + }() + select { + case err := <-errc: + if err != nil { + t.Error(err) + } + case <-time.After(1 * time.Second): + t.Errorf("%s on %s %s took over 1 second, expected 0.1s", what, network, addr) } } diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go index 60dec81..246c9ad 100644 --- a/libgo/go/net/udpsock_plan9.go +++ b/libgo/go/net/udpsock_plan9.go @@ -9,6 +9,7 @@ package net import ( "errors" "os" + "time" ) // UDPConn is the implementation of the Conn and PacketConn @@ -17,6 +18,21 @@ type UDPConn struct { plan9Conn } +// SetDeadline implements the net.Conn SetDeadline method. +func (c *UDPConn) SetDeadline(t time.Time) error { + return os.EPLAN9 +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *UDPConn) SetReadDeadline(t time.Time) error { + return os.EPLAN9 +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *UDPConn) SetWriteDeadline(t time.Time) error { + return os.EPLAN9 +} + // UDP-specific methods. // ReadFromUDP reads a UDP packet from c, copying the payload into b. @@ -24,7 +40,7 @@ type UDPConn struct { // that was on the packet. // // ReadFromUDP can be made to time out and return an error with Timeout() == true -// after a fixed time limit; see SetTimeout and SetReadTimeout. +// after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { if !c.ok() { return 0, nil, os.EINVAL @@ -62,7 +78,7 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { // // WriteToUDP can be made to time out and return // an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. +// see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { if !c.ok() { diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go index d0bdb14..0b5bc16 100644 --- a/libgo/go/net/udpsock_posix.go +++ b/libgo/go/net/udpsock_posix.go @@ -11,6 +11,7 @@ package net import ( "os" "syscall" + "time" ) func sockaddrToUDP(sa syscall.Sockaddr) Addr { @@ -98,28 +99,28 @@ func (c *UDPConn) RemoteAddr() Addr { return c.fd.raddr } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UDPConn) SetTimeout(nsec int64) error { +// SetDeadline implements the net.Conn SetDeadline method. +func (c *UDPConn) SetDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setTimeout(c.fd, nsec) + return setDeadline(c.fd, t) } -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UDPConn) SetReadTimeout(nsec int64) error { +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *UDPConn) SetReadDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setReadTimeout(c.fd, nsec) + return setReadDeadline(c.fd, t) } -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UDPConn) SetWriteTimeout(nsec int64) error { +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *UDPConn) SetWriteDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setWriteTimeout(c.fd, nsec) + return setWriteDeadline(c.fd, t) } // SetReadBuffer sets the size of the operating system's @@ -147,7 +148,7 @@ func (c *UDPConn) SetWriteBuffer(bytes int) error { // that was on the packet. // // ReadFromUDP can be made to time out and return an error with Timeout() == true -// after a fixed time limit; see SetTimeout and SetReadTimeout. +// after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { if !c.ok() { return 0, nil, os.EINVAL @@ -175,7 +176,7 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { // // WriteToUDP can be made to time out and return // an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. +// see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { if !c.ok() { diff --git a/libgo/go/net/unicast_test.go b/libgo/go/net/unicast_test.go index 6ed6f59..297276d 100644 --- a/libgo/go/net/unicast_test.go +++ b/libgo/go/net/unicast_test.go @@ -5,6 +5,7 @@ package net import ( + "io" "runtime" "testing" ) @@ -15,10 +16,12 @@ var unicastTests = []struct { ipv6 bool packet bool }{ - {"tcp4", "127.0.0.1:0", false, false}, - {"tcp6", "[::1]:0", true, false}, - {"udp4", "127.0.0.1:0", false, true}, - {"udp6", "[::1]:0", true, true}, + {net: "tcp4", laddr: "127.0.0.1:0"}, + {net: "tcp4", laddr: "previous"}, + {net: "tcp6", laddr: "[::1]:0", ipv6: true}, + {net: "tcp6", laddr: "previous", ipv6: true}, + {net: "udp4", laddr: "127.0.0.1:0", packet: true}, + {net: "udp6", laddr: "[::1]:0", ipv6: true, packet: true}, } func TestUnicastTCPAndUDP(t *testing.T) { @@ -26,24 +29,32 @@ func TestUnicastTCPAndUDP(t *testing.T) { return } + prevladdr := "" for _, tt := range unicastTests { if tt.ipv6 && !supportsIPv6 { continue } - var fd *netFD + var ( + fd *netFD + closer io.Closer + ) if !tt.packet { - c, err := Listen(tt.net, tt.laddr) + if tt.laddr == "previous" { + tt.laddr = prevladdr + } + l, err := Listen(tt.net, tt.laddr) if err != nil { t.Fatalf("Listen failed: %v", err) } - defer c.Close() - fd = c.(*TCPListener).fd + prevladdr = l.Addr().String() + closer = l + fd = l.(*TCPListener).fd } else { c, err := ListenPacket(tt.net, tt.laddr) if err != nil { t.Fatalf("ListenPacket failed: %v", err) } - defer c.Close() + closer = c fd = c.(*UDPConn).fd } if !tt.ipv6 { @@ -51,6 +62,7 @@ func TestUnicastTCPAndUDP(t *testing.T) { } else { testIPv6UnicastSocketOptions(t, fd) } + closer.Close() } } diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go index ac502d1..e8087d0 100644 --- a/libgo/go/net/unixsock_plan9.go +++ b/libgo/go/net/unixsock_plan9.go @@ -8,6 +8,7 @@ package net import ( "os" + "time" ) // UnixConn is an implementation of the Conn interface @@ -44,18 +45,18 @@ func (c *UnixConn) RemoteAddr() Addr { return nil } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UnixConn) SetTimeout(nsec int64) error { +// SetDeadline implements the net.Conn SetDeadline method. +func (c *UnixConn) SetDeadline(t time.Time) error { return os.EPLAN9 } -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UnixConn) SetReadTimeout(nsec int64) error { +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *UnixConn) SetReadDeadline(t time.Time) error { return os.EPLAN9 } -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UnixConn) SetWriteTimeout(nsec int64) error { +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *UnixConn) SetWriteDeadline(t time.Time) error { return os.EPLAN9 } diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 00ee016..5b8b2e4 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -11,19 +11,20 @@ package net import ( "os" "syscall" + "time" ) func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) { - var proto int + var sotype int switch net { default: return nil, UnknownNetworkError(net) case "unix": - proto = syscall.SOCK_STREAM + sotype = syscall.SOCK_STREAM case "unixgram": - proto = syscall.SOCK_DGRAM + sotype = syscall.SOCK_DGRAM case "unixpacket": - proto = syscall.SOCK_SEQPACKET + sotype = syscall.SOCK_SEQPACKET } var la, ra syscall.Sockaddr @@ -37,7 +38,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err } if raddr != nil { ra = &syscall.SockaddrUnix{Name: raddr.Name} - } else if proto != syscall.SOCK_DGRAM || laddr == nil { + } else if sotype != syscall.SOCK_DGRAM || laddr == nil { return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress} } @@ -52,13 +53,13 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err } f := sockaddrToUnix - if proto == syscall.SOCK_DGRAM { + if sotype == syscall.SOCK_DGRAM { f = sockaddrToUnixgram - } else if proto == syscall.SOCK_SEQPACKET { + } else if sotype == syscall.SOCK_SEQPACKET { f = sockaddrToUnixpacket } - fd, oserr := socket(net, syscall.AF_UNIX, proto, 0, la, ra, f) + fd, oserr := socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f) if oserr != nil { goto Error } @@ -93,8 +94,8 @@ func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr { return nil } -func protoToNet(proto int) string { - switch proto { +func sotypeToNet(sotype int) string { + switch sotype { case syscall.SOCK_STREAM: return "unix" case syscall.SOCK_SEQPACKET: @@ -102,7 +103,7 @@ func protoToNet(proto int) string { case syscall.SOCK_DGRAM: return "unixgram" default: - panic("protoToNet unknown protocol") + panic("sotypeToNet unknown socket type") } return "" } @@ -164,28 +165,28 @@ func (c *UnixConn) RemoteAddr() Addr { return c.fd.raddr } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UnixConn) SetTimeout(nsec int64) error { +// SetDeadline implements the net.Conn SetDeadline method. +func (c *UnixConn) SetDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setTimeout(c.fd, nsec) + return setDeadline(c.fd, t) } -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UnixConn) SetReadTimeout(nsec int64) error { +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (c *UnixConn) SetReadDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setReadTimeout(c.fd, nsec) + return setReadDeadline(c.fd, t) } -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UnixConn) SetWriteTimeout(nsec int64) error { +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (c *UnixConn) SetWriteDeadline(t time.Time) error { if !c.ok() { return os.EINVAL } - return setWriteTimeout(c.fd, nsec) + return setWriteDeadline(c.fd, t) } // SetReadBuffer sets the size of the operating system's @@ -212,7 +213,7 @@ func (c *UnixConn) SetWriteBuffer(bytes int) error { // // ReadFromUnix can be made to time out and return // an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetReadTimeout. +// see SetDeadline and SetReadDeadline. func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) { if !c.ok() { return 0, nil, os.EINVAL @@ -220,7 +221,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) { n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} + addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)} } return } @@ -238,13 +239,13 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) { // // WriteToUnix can be made to time out and return // an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. +// see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) { if !c.ok() { return 0, os.EINVAL } - if addr.Net != protoToNet(c.fd.proto) { + if addr.Net != sotypeToNet(c.fd.sotype) { return 0, os.EAFNOSUPPORT } sa := &syscall.SockaddrUnix{Name: addr.Name} @@ -270,7 +271,7 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) switch sa := sa.(type) { case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} + addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)} } return } @@ -280,7 +281,7 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err return 0, 0, os.EINVAL } if addr != nil { - if addr.Net != protoToNet(c.fd.proto) { + if addr.Net != sotypeToNet(c.fd.sotype) { return 0, 0, os.EAFNOSUPPORT } sa := &syscall.SockaddrUnix{Name: addr.Name} @@ -386,12 +387,13 @@ func (l *UnixListener) Close() error { // Addr returns the listener's network address. func (l *UnixListener) Addr() Addr { return l.fd.laddr } -// SetTimeout sets the deadline associated wuth the listener -func (l *UnixListener) SetTimeout(nsec int64) (err error) { +// SetDeadline sets the deadline associated with the listener. +// A zero time value disables the deadline. +func (l *UnixListener) SetDeadline(t time.Time) (err error) { if l == nil || l.fd == nil { return os.EINVAL } - return setTimeout(l.fd, nsec) + return setDeadline(l.fd, t) } // File returns a copy of the underlying os.File, set to blocking mode. diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go index 11fa189..0068e98 100644 --- a/libgo/go/net/url/url.go +++ b/libgo/go/net/url/url.go @@ -52,7 +52,6 @@ const ( encodeUserPassword encodeQueryComponent encodeFragment - encodeOpaque ) type EscapeError string @@ -69,6 +68,7 @@ func shouldEscape(c byte, mode encoding) bool { if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { return false } + // TODO: Update the character sets after RFC 3986. switch c { case '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark) return false @@ -78,12 +78,10 @@ func shouldEscape(c byte, mode encoding) bool { // the reserved characters to appear unescaped. switch mode { case encodePath: // §3.3 - // The RFC allows : @ & = + $ , but saves / ; for assigning - // meaning to individual path segments. This package + // The RFC allows : @ & = + $ but saves / ; , for assigning + // meaning to individual path segments. This package // only manipulates the path as a whole, so we allow those - // last two as well. Clients that need to distinguish between - // `/foo;y=z/bar` and `/foo%3by=z/bar` will have to re-decode RawPath. - // That leaves only ? to escape. + // last two as well. That leaves only ? to escape. return c == '?' case encodeUserPassword: // §3.2.2 @@ -99,12 +97,6 @@ func shouldEscape(c byte, mode encoding) bool { // The RFC text is silent but the grammar allows // everything, so escape nothing. return false - - case encodeOpaque: // §3 opaque_part - // The RFC allows opaque_part to use all characters - // except that the leading / must be escaped. - // (We implement that case in String.) - return false } } @@ -217,64 +209,73 @@ func escape(s string, mode encoding) string { return string(t) } -// UnescapeUserinfo parses the RawUserinfo field of a URL -// as the form user or user:password and unescapes and returns -// the two halves. +// A URL represents a parsed URL (technically, a URI reference). +// The general form represented is: // -// This functionality should only be used with legacy web sites. -// RFC 2396 warns that interpreting Userinfo this way -// ``is NOT RECOMMENDED, because the passing of authentication -// information in clear text (such as URI) has proven to be a -// security risk in almost every case where it has been used.'' -func UnescapeUserinfo(rawUserinfo string) (user, password string, err error) { - u, p := split(rawUserinfo, ':', true) - if user, err = unescape(u, encodeUserPassword); err != nil { - return "", "", err - } - if password, err = unescape(p, encodeUserPassword); err != nil { - return "", "", err - } - return +// scheme://[userinfo@]host/path[?query][#fragment] +// +// URLs that do not start with a slash after the scheme are interpreted as: +// +// scheme:opaque[?query][#fragment] +// +type URL struct { + Scheme string + Opaque string // encoded opaque data + User *Userinfo // username and password information + Host string + Path string + RawQuery string // encoded query values, without '?' + Fragment string // fragment for references, without '#' } -// EscapeUserinfo combines user and password in the form -// user:password (or just user if password is empty) and then -// escapes it for use as the URL.RawUserinfo field. -// +// User returns a Userinfo containing the provided username +// and no password set. +func User(username string) *Userinfo { + return &Userinfo{username, "", false} +} + +// UserPassword returns a Userinfo containing the provided username +// and password. // This functionality should only be used with legacy web sites. // RFC 2396 warns that interpreting Userinfo this way // ``is NOT RECOMMENDED, because the passing of authentication // information in clear text (such as URI) has proven to be a // security risk in almost every case where it has been used.'' -func EscapeUserinfo(user, password string) string { - raw := escape(user, encodeUserPassword) - if password != "" { - raw += ":" + escape(password, encodeUserPassword) +func UserPassword(username, password string) *Userinfo { + return &Userinfo{username, password, true} +} + +// The Userinfo type is an immutable encapsulation of username and +// password details for a URL. An existing Userinfo value is guaranteed +// to have a username set (potentially empty, as allowed by RFC 2396), +// and optionally a password. +type Userinfo struct { + username string + password string + passwordSet bool +} + +// Username returns the username. +func (u *Userinfo) Username() string { + return u.username +} + +// Password returns the password in case it is set, and whether it is set. +func (u *Userinfo) Password() (string, bool) { + if u.passwordSet { + return u.password, true } - return raw + return "", false } -// A URL represents a parsed URL (technically, a URI reference). -// The general form represented is: -// scheme://[userinfo@]host/path[?query][#fragment] -// The Raw, RawAuthority, RawPath, and RawQuery fields are in "wire format" -// (special characters must be hex-escaped if not meant to have special meaning). -// All other fields are logical values; '+' or '%' represent themselves. -// -// The various Raw values are supplied in wire format because -// clients typically have to split them into pieces before further -// decoding. -type URL struct { - Raw string // the original string - Scheme string // scheme - RawAuthority string // [userinfo@]host - RawUserinfo string // userinfo - Host string // host - RawPath string // /path[?query][#fragment] - Path string // /path - OpaquePath bool // path is opaque (unrooted when scheme is present) - RawQuery string // query - Fragment string // fragment +// String returns the encoded userinfo information in the standard form +// of "username[:password]". +func (u *Userinfo) String() string { + s := escape(u.username, encodeUserPassword) + if u.passwordSet { + s += ":" + escape(u.password, encodeUserPassword) + } + return s } // Maybe rawurl is of the form scheme:path. @@ -341,136 +342,112 @@ func ParseRequest(rawurl string) (url *URL, err error) { // in which case only absolute URLs or path-absolute relative URLs are allowed. // If viaRequest is false, all forms of relative URLs are allowed. func parse(rawurl string, viaRequest bool) (url *URL, err error) { - var ( - leadingSlash bool - path string - ) + var rest string if rawurl == "" { err = errors.New("empty url") goto Error } url = new(URL) - url.Raw = rawurl // Split off possible leading "http:", "mailto:", etc. // Cannot contain escaped characters. - if url.Scheme, path, err = getscheme(rawurl); err != nil { + if url.Scheme, rest, err = getscheme(rawurl); err != nil { goto Error } - leadingSlash = strings.HasPrefix(path, "/") - if url.Scheme != "" && !leadingSlash { - // RFC 2396: - // Absolute URI (has scheme) with non-rooted path - // is uninterpreted. It doesn't even have a ?query. - // This is the case that handles mailto:name@example.com. - url.RawPath = path + rest, url.RawQuery = split(rest, '?', true) - if url.Path, err = unescape(path, encodeOpaque); err != nil { - goto Error + if !strings.HasPrefix(rest, "/") { + if url.Scheme != "" { + // We consider rootless paths per RFC 3986 as opaque. + url.Opaque = rest + return url, nil } - url.OpaquePath = true - } else { - if viaRequest && !leadingSlash { + if viaRequest { err = errors.New("invalid URI for request") goto Error } + } - // Split off query before parsing path further. - url.RawPath = path - path, query := split(path, '?', false) - if len(query) > 1 { - url.RawQuery = query[1:] - } - - // Maybe path is //authority/path - if (url.Scheme != "" || !viaRequest) && - strings.HasPrefix(path, "//") && !strings.HasPrefix(path, "///") { - url.RawAuthority, path = split(path[2:], '/', false) - url.RawPath = url.RawPath[2+len(url.RawAuthority):] - } - - // Split authority into userinfo@host. - // If there's no @, split's default is wrong. Check explicitly. - var rawHost string - if strings.Index(url.RawAuthority, "@") < 0 { - rawHost = url.RawAuthority - } else { - url.RawUserinfo, rawHost = split(url.RawAuthority, '@', true) - } - - // We leave RawAuthority only in raw form because clients - // of common protocols should be using Userinfo and Host - // instead. Clients that wish to use RawAuthority will have to - // interpret it themselves: RFC 2396 does not define the meaning. - - if strings.Contains(rawHost, "%") { - // Host cannot contain escaped characters. - err = errors.New("hexadecimal escape in host") + if (url.Scheme != "" || !viaRequest) && strings.HasPrefix(rest, "//") && !strings.HasPrefix(rest, "///") { + var authority string + authority, rest = split(rest[2:], '/', false) + url.User, url.Host, err = parseAuthority(authority) + if err != nil { goto Error } - url.Host = rawHost - - if url.Path, err = unescape(path, encodePath); err != nil { + if strings.Contains(url.Host, "%") { + err = errors.New("hexadecimal escape in host") goto Error } } + if url.Path, err = unescape(rest, encodePath); err != nil { + goto Error + } return url, nil Error: return nil, &Error{"parse", rawurl, err} +} +func parseAuthority(authority string) (user *Userinfo, host string, err error) { + if strings.Index(authority, "@") < 0 { + host = authority + return + } + userinfo, host := split(authority, '@', true) + if strings.Index(userinfo, ":") < 0 { + if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil { + return + } + user = User(userinfo) + } else { + username, password := split(userinfo, ':', true) + if username, err = unescape(username, encodeUserPassword); err != nil { + return + } + if password, err = unescape(password, encodeUserPassword); err != nil { + return + } + user = UserPassword(username, password) + } + return } // ParseWithReference is like Parse but allows a trailing #fragment. func ParseWithReference(rawurlref string) (url *URL, err error) { - // Cut off #frag. - rawurl, frag := split(rawurlref, '#', false) + // Cut off #frag + rawurl, frag := split(rawurlref, '#', true) if url, err = Parse(rawurl); err != nil { return nil, err } - url.Raw += frag - url.RawPath += frag - if len(frag) > 1 { - frag = frag[1:] - if url.Fragment, err = unescape(frag, encodeFragment); err != nil { - return nil, &Error{"parse", rawurl, err} - } + if frag == "" { + return url, nil + } + if url.Fragment, err = unescape(frag, encodeFragment); err != nil { + return nil, &Error{"parse", rawurlref, err} } return url, nil } // String reassembles url into a valid URL string. -// -// There are redundant fields stored in the URL structure: -// the String method consults Scheme, Path, Host, RawUserinfo, -// RawQuery, and Fragment, but not Raw, RawPath or RawAuthority. func (url *URL) String() string { + // TODO: Rewrite to use bytes.Buffer result := "" if url.Scheme != "" { result += url.Scheme + ":" } - if url.Host != "" || url.RawUserinfo != "" { - result += "//" - if url.RawUserinfo != "" { - // hide the password, if any - info := url.RawUserinfo - if i := strings.Index(info, ":"); i >= 0 { - info = info[0:i] + ":******" + if url.Opaque != "" { + result += url.Opaque + } else { + if url.Host != "" || url.User != nil { + result += "//" + if u := url.User; u != nil { + result += u.String() + "@" } - result += info + "@" - } - result += url.Host - } - if url.OpaquePath { - path := url.Path - if strings.HasPrefix(path, "/") { - result += "%2f" - path = path[1:] + result += url.Host } - result += escape(path, encodeOpaque) - } else { result += escape(url.Path, encodePath) } if url.RawQuery != "" { @@ -630,47 +607,38 @@ func (base *URL) Parse(ref string) (*URL, error) { // base or reference. If ref is an absolute URL, then ResolveReference // ignores base and returns a copy of ref. func (base *URL) ResolveReference(ref *URL) *URL { - url := new(URL) - switch { - case ref.IsAbs(): - *url = *ref - default: - // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] - *url = *base - if ref.RawAuthority != "" { - // The "net_path" case. - url.RawAuthority = ref.RawAuthority - url.Host = ref.Host - url.RawUserinfo = ref.RawUserinfo - } - switch { - case url.OpaquePath: - url.Path = ref.Path - url.RawPath = ref.RawPath - url.RawQuery = ref.RawQuery - case strings.HasPrefix(ref.Path, "/"): - // The "abs_path" case. - url.Path = ref.Path - url.RawPath = ref.RawPath - url.RawQuery = ref.RawQuery - default: - // The "rel_path" case. - path := resolvePath(base.Path, ref.Path) - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - url.Path = path - url.RawPath = url.Path - url.RawQuery = ref.RawQuery - if ref.RawQuery != "" { - url.RawPath += "?" + url.RawQuery - } + if ref.IsAbs() { + url := *ref + return &url + } + // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] + url := *base + url.RawQuery = ref.RawQuery + url.Fragment = ref.Fragment + if ref.Opaque != "" { + url.Opaque = ref.Opaque + url.User = nil + url.Host = "" + url.Path = "" + return &url + } + if ref.Host != "" || ref.User != nil { + // The "net_path" case. + url.Host = ref.Host + url.User = ref.User + } + if strings.HasPrefix(ref.Path, "/") { + // The "abs_path" case. + url.Path = ref.Path + } else { + // The "rel_path" case. + path := resolvePath(base.Path, ref.Path) + if !strings.HasPrefix(path, "/") { + path = "/" + path } - - url.Fragment = ref.Fragment + url.Path = path } - url.Raw = url.String() - return url + return &url } // Query parses RawQuery and returns the corresponding values. @@ -679,7 +647,18 @@ func (u *URL) Query() Values { return v } -// EncodedPath returns the URL's path in "URL path encoded" form. -func (u *URL) EncodedPath() string { - return escape(u.Path, encodePath) +// RequestURI returns the encoded path?query or opaque?query +// string that would be used in an HTTP request for u. +func (u *URL) RequestURI() string { + result := u.Opaque + if result == "" { + result = escape(u.Path, encodePath) + if result == "" { + result = "/" + } + } + if u.RawQuery != "" { + result += "?" + u.RawQuery + } + return result } diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go index dab3bfa..9fe5ff8 100644 --- a/libgo/go/net/url/url_test.go +++ b/libgo/go/net/url/url_test.go @@ -21,10 +21,8 @@ var urltests = []URLTest{ { "http://www.google.com", &URL{ - Raw: "http://www.google.com", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", + Scheme: "http", + Host: "www.google.com", }, "", }, @@ -32,12 +30,9 @@ var urltests = []URLTest{ { "http://www.google.com/", &URL{ - Raw: "http://www.google.com/", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/", - Path: "/", + Scheme: "http", + Host: "www.google.com", + Path: "/", }, "", }, @@ -45,12 +40,9 @@ var urltests = []URLTest{ { "http://www.google.com/file%20one%26two", &URL{ - Raw: "http://www.google.com/file%20one%26two", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/file%20one%26two", - Path: "/file one&two", + Scheme: "http", + Host: "www.google.com", + Path: "/file one&two", }, "http://www.google.com/file%20one&two", }, @@ -58,13 +50,10 @@ var urltests = []URLTest{ { "ftp://webmaster@www.google.com/", &URL{ - Raw: "ftp://webmaster@www.google.com/", - Scheme: "ftp", - RawAuthority: "webmaster@www.google.com", - RawUserinfo: "webmaster", - Host: "www.google.com", - RawPath: "/", - Path: "/", + Scheme: "ftp", + User: User("webmaster"), + Host: "www.google.com", + Path: "/", }, "", }, @@ -72,13 +61,10 @@ var urltests = []URLTest{ { "ftp://john%20doe@www.google.com/", &URL{ - Raw: "ftp://john%20doe@www.google.com/", - Scheme: "ftp", - RawAuthority: "john%20doe@www.google.com", - RawUserinfo: "john%20doe", - Host: "www.google.com", - RawPath: "/", - Path: "/", + Scheme: "ftp", + User: User("john doe"), + Host: "www.google.com", + Path: "/", }, "ftp://john%20doe@www.google.com/", }, @@ -86,13 +72,10 @@ var urltests = []URLTest{ { "http://www.google.com/?q=go+language", &URL{ - Raw: "http://www.google.com/?q=go+language", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language", - Path: "/", - RawQuery: "q=go+language", + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go+language", }, "", }, @@ -100,13 +83,10 @@ var urltests = []URLTest{ { "http://www.google.com/?q=go%20language", &URL{ - Raw: "http://www.google.com/?q=go%20language", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go%20language", - Path: "/", - RawQuery: "q=go%20language", + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go%20language", }, "", }, @@ -114,48 +94,39 @@ var urltests = []URLTest{ { "http://www.google.com/a%20b?q=c+d", &URL{ - Raw: "http://www.google.com/a%20b?q=c+d", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/a%20b?q=c+d", - Path: "/a b", - RawQuery: "q=c+d", + Scheme: "http", + Host: "www.google.com", + Path: "/a b", + RawQuery: "q=c+d", }, "", }, - // path without leading /, so no query parsing + // path without leading /, so no parsing { "http:www.google.com/?q=go+language", &URL{ - Raw: "http:www.google.com/?q=go+language", - Scheme: "http", - RawPath: "www.google.com/?q=go+language", - Path: "www.google.com/?q=go+language", - OpaquePath: true, + Scheme: "http", + Opaque: "www.google.com/", + RawQuery: "q=go+language", }, "http:www.google.com/?q=go+language", }, - // path without leading /, so no query parsing + // path without leading /, so no parsing { "http:%2f%2fwww.google.com/?q=go+language", &URL{ - Raw: "http:%2f%2fwww.google.com/?q=go+language", - Scheme: "http", - RawPath: "%2f%2fwww.google.com/?q=go+language", - Path: "//www.google.com/?q=go+language", - OpaquePath: true, + Scheme: "http", + Opaque: "%2f%2fwww.google.com/", + RawQuery: "q=go+language", }, - "http:%2f/www.google.com/?q=go+language", + "http:%2f%2fwww.google.com/?q=go+language", }, // non-authority { "mailto:/webmaster@golang.org", &URL{ - Raw: "mailto:/webmaster@golang.org", - Scheme: "mailto", - RawPath: "/webmaster@golang.org", - Path: "/webmaster@golang.org", + Scheme: "mailto", + Path: "/webmaster@golang.org", }, "", }, @@ -163,11 +134,8 @@ var urltests = []URLTest{ { "mailto:webmaster@golang.org", &URL{ - Raw: "mailto:webmaster@golang.org", - Scheme: "mailto", - RawPath: "webmaster@golang.org", - Path: "webmaster@golang.org", - OpaquePath: true, + Scheme: "mailto", + Opaque: "webmaster@golang.org", }, "", }, @@ -175,8 +143,6 @@ var urltests = []URLTest{ { "/foo?query=http://bad", &URL{ - Raw: "/foo?query=http://bad", - RawPath: "/foo?query=http://bad", Path: "/foo", RawQuery: "query=http://bad", }, @@ -186,12 +152,7 @@ var urltests = []URLTest{ { "//foo", &URL{ - RawAuthority: "foo", - Raw: "//foo", - Host: "foo", - Scheme: "", - RawPath: "", - Path: "", + Host: "foo", }, "", }, @@ -199,14 +160,10 @@ var urltests = []URLTest{ { "//user@foo/path?a=b", &URL{ - Raw: "//user@foo/path?a=b", - RawAuthority: "user@foo", - RawUserinfo: "user", - Scheme: "", - RawPath: "/path?a=b", - Path: "/path", - RawQuery: "a=b", - Host: "foo", + User: User("user"), + Host: "foo", + Path: "/path", + RawQuery: "a=b", }, "", }, @@ -218,36 +175,18 @@ var urltests = []URLTest{ { "///threeslashes", &URL{ - RawAuthority: "", - Raw: "///threeslashes", - Host: "", - Scheme: "", - RawPath: "///threeslashes", - Path: "///threeslashes", + Path: "///threeslashes", }, "", }, { "http://user:password@google.com", &URL{ - Raw: "http://user:password@google.com", - Scheme: "http", - RawAuthority: "user:password@google.com", - RawUserinfo: "user:password", - Host: "google.com", - }, - "http://user:******@google.com", - }, - { - "http://user:longerpass@google.com", - &URL{ - Raw: "http://user:longerpass@google.com", - Scheme: "http", - RawAuthority: "user:longerpass@google.com", - RawUserinfo: "user:longerpass", - Host: "google.com", + Scheme: "http", + User: UserPassword("user", "password"), + Host: "google.com", }, - "http://user:******@google.com", + "http://user:password@google.com", }, } @@ -255,13 +194,10 @@ var urlnofragtests = []URLTest{ { "http://www.google.com/?q=go+language#foo", &URL{ - Raw: "http://www.google.com/?q=go+language#foo", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo", - Path: "/", - RawQuery: "q=go+language#foo", + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go+language#foo", }, "", }, @@ -271,28 +207,22 @@ var urlfragtests = []URLTest{ { "http://www.google.com/?q=go+language#foo", &URL{ - Raw: "http://www.google.com/?q=go+language#foo", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo", - Path: "/", - RawQuery: "q=go+language", - Fragment: "foo", + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo", }, "", }, { "http://www.google.com/?q=go+language#foo%26bar", &URL{ - Raw: "http://www.google.com/?q=go+language#foo%26bar", - Scheme: "http", - RawAuthority: "www.google.com", - Host: "www.google.com", - RawPath: "/?q=go+language#foo%26bar", - Path: "/", - RawQuery: "q=go+language", - Fragment: "foo&bar", + Scheme: "http", + Host: "www.google.com", + Path: "/", + RawQuery: "q=go+language", + Fragment: "foo&bar", }, "http://www.google.com/?q=go+language#foo&bar", }, @@ -300,9 +230,15 @@ var urlfragtests = []URLTest{ // more useful string for debugging than fmt's struct printer func ufmt(u *URL) string { - return fmt.Sprintf("raw=%q, scheme=%q, rawpath=%q, auth=%q, userinfo=%q, host=%q, path=%q, rawq=%q, frag=%q", - u.Raw, u.Scheme, u.RawPath, u.RawAuthority, u.RawUserinfo, - u.Host, u.Path, u.RawQuery, u.Fragment) + var user, pass interface{} + if u.User != nil { + user = u.User.Username() + if p, ok := u.User.Password(); ok { + pass = p + } + } + return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawq=%q, frag=%q", + u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawQuery, u.Fragment) } func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) { @@ -370,11 +306,11 @@ func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, t t.Errorf("%s(%q) returned error %s", name, tt.in, err) continue } - s := u.String() expected := tt.in if len(tt.roundtrip) > 0 { expected = tt.roundtrip } + s := u.String() if s != expected { t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected) } @@ -504,33 +440,11 @@ func TestEscape(t *testing.T) { } } -type UserinfoTest struct { - User string - Password string - Raw string -} - -var userinfoTests = []UserinfoTest{ - {"user", "password", "user:password"}, - {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./", - "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"}, -} - -func TestEscapeUserinfo(t *testing.T) { - for _, tt := range userinfoTests { - if raw := EscapeUserinfo(tt.User, tt.Password); raw != tt.Raw { - t.Errorf("EscapeUserinfo(%q, %q) = %q, want %q", tt.User, tt.Password, raw, tt.Raw) - } - } -} - -func TestUnescapeUserinfo(t *testing.T) { - for _, tt := range userinfoTests { - if user, pass, err := UnescapeUserinfo(tt.Raw); user != tt.User || pass != tt.Password || err != nil { - t.Errorf("UnescapeUserinfo(%q) = %q, %q, %v, want %q, %q, nil", tt.Raw, user, pass, err, tt.User, tt.Password) - } - } -} +//var userinfoTests = []UserinfoTest{ +// {"user", "password", "user:password"}, +// {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./", +// "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"}, +//} type EncodeQueryTest struct { m Values @@ -664,6 +578,57 @@ func TestResolveReference(t *testing.T) { t.Errorf("Expected an error from Parse wrapper parsing an empty string.") } + // Ensure Opaque resets the URL. + base = mustParse("scheme://user@foo.com/bar") + abs = base.ResolveReference(&URL{Opaque: "opaque"}) + want := mustParse("scheme:opaque") + if *abs != *want { + t.Errorf("ResolveReference failed to resolve opaque URL: want %#v, got %#v", abs, want) + } +} + +func TestResolveReferenceOpaque(t *testing.T) { + mustParse := func(url string) *URL { + u, err := ParseWithReference(url) + if err != nil { + t.Fatalf("Expected URL to parse: %q, got error: %v", url, err) + } + return u + } + for _, test := range resolveReferenceTests { + base := mustParse(test.base) + rel := mustParse(test.rel) + url := base.ResolveReference(rel) + urlStr := url.String() + if urlStr != test.expected { + t.Errorf("Resolving %q + %q != %q; got %q", test.base, test.rel, test.expected, urlStr) + } + } + + // Test that new instances are returned. + base := mustParse("http://foo.com/") + abs := base.ResolveReference(mustParse(".")) + if base == abs { + t.Errorf("Expected no-op reference to return new URL instance.") + } + barRef := mustParse("http://bar.com/") + abs = base.ResolveReference(barRef) + if abs == barRef { + t.Errorf("Expected resolution of absolute reference to return new URL instance.") + } + + // Test the convenience wrapper too + base = mustParse("http://foo.com/path/one/") + abs, _ = base.Parse("../two") + expected := "http://foo.com/path/two" + if abs.String() != expected { + t.Errorf("Parse wrapper got %q; expected %q", abs.String(), expected) + } + _, err := base.Parse("") + if err == nil { + t.Errorf("Expected an error from Parse wrapper parsing an empty string.") + } + } func TestQueryValues(t *testing.T) { @@ -747,3 +712,60 @@ func TestParseQuery(t *testing.T) { } } } + +type RequestURITest struct { + url *URL + out string +} + +var requritests = []RequestURITest{ + { + &URL{ + Scheme: "http", + Host: "example.com", + Path: "", + }, + "/", + }, + { + &URL{ + Scheme: "http", + Host: "example.com", + Path: "/a b", + }, + "/a%20b", + }, + { + &URL{ + Scheme: "http", + Host: "example.com", + Path: "/a b", + RawQuery: "q=go+language", + }, + "/a%20b?q=go+language", + }, + { + &URL{ + Scheme: "myschema", + Opaque: "opaque", + }, + "opaque", + }, + { + &URL{ + Scheme: "myschema", + Opaque: "opaque", + RawQuery: "q=go+language", + }, + "opaque?q=go+language", + }, +} + +func TestRequestURI(t *testing.T) { + for _, tt := range requritests { + s := tt.url.RequestURI() + if s != tt.out { + t.Errorf("%#v.RequestURI() == %q (expected %q)", tt.url, s, tt.out) + } + } +} |