aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-12-22 01:15:33 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-12-22 01:15:33 +0000
commit409a5e7eb4cca107037fafa4a7eea92603edb83d (patch)
tree06f36bbef6fae78278f799194ad0df8ba2dabaa1 /libgo/go/net
parent7e9268b4cf01ab87d9b602f592ed2e2facfadda9 (diff)
downloadgcc-409a5e7eb4cca107037fafa4a7eea92603edb83d.zip
gcc-409a5e7eb4cca107037fafa4a7eea92603edb83d.tar.gz
gcc-409a5e7eb4cca107037fafa4a7eea92603edb83d.tar.bz2
libgo: Update to revision 15193:6fdc1974457c of master library.
From-SVN: r194692
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/cgo_openbsd.go14
-rw-r--r--libgo/go/net/cgo_unix.go2
-rw-r--r--libgo/go/net/conn_test.go16
-rw-r--r--libgo/go/net/dial.go2
-rw-r--r--libgo/go/net/dial_test.go3
-rw-r--r--libgo/go/net/http/client.go34
-rw-r--r--libgo/go/net/http/client_test.go118
-rw-r--r--libgo/go/net/http/pprof/pprof.go5
-rw-r--r--libgo/go/net/http/readrequest_test.go48
-rw-r--r--libgo/go/net/http/serve_test.go70
-rw-r--r--libgo/go/net/http/server.go44
-rw-r--r--libgo/go/net/http/transport.go4
-rw-r--r--libgo/go/net/http/transport_test.go53
-rw-r--r--libgo/go/net/packetconn_test.go21
-rw-r--r--libgo/go/net/protoconn_test.go43
-rw-r--r--libgo/go/net/smtp/smtp.go65
-rw-r--r--libgo/go/net/smtp/smtp_test.go229
-rw-r--r--libgo/go/net/unixsock_plan9.go21
-rw-r--r--libgo/go/net/unixsock_posix.go147
-rw-r--r--libgo/go/net/url/url.go19
-rw-r--r--libgo/go/net/url/url_test.go14
21 files changed, 806 insertions, 166 deletions
diff --git a/libgo/go/net/cgo_openbsd.go b/libgo/go/net/cgo_openbsd.go
new file mode 100644
index 0000000..aeaf8e5
--- /dev/null
+++ b/libgo/go/net/cgo_openbsd.go
@@ -0,0 +1,14 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+/*
+#include <netdb.h>
+*/
+import "C"
+
+func cgoAddrInfoFlags() C.int {
+ return C.AI_CANONNAME
+}
diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go
index 69daedc..a4d96a8 100644
--- a/libgo/go/net/cgo_unix.go
+++ b/libgo/go/net/cgo_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd
+// +build darwin freebsd linux netbsd openbsd
package net
diff --git a/libgo/go/net/conn_test.go b/libgo/go/net/conn_test.go
index 037ce80..f733a81 100644
--- a/libgo/go/net/conn_test.go
+++ b/libgo/go/net/conn_test.go
@@ -17,8 +17,8 @@ var connTests = []struct {
addr string
}{
{"tcp", "127.0.0.1:0"},
- {"unix", "/tmp/gotest.net"},
- {"unixpacket", "/tmp/gotest.net"},
+ {"unix", "/tmp/gotest.net1"},
+ {"unixpacket", "/tmp/gotest.net2"},
}
func TestConnAndListener(t *testing.T) {
@@ -41,7 +41,13 @@ func TestConnAndListener(t *testing.T) {
return
}
ln.Addr()
- defer ln.Close()
+ defer func(ln net.Listener, net, addr string) {
+ ln.Close()
+ switch net {
+ case "unix", "unixpacket":
+ os.Remove(addr)
+ }
+ }(ln, tt.net, tt.addr)
done := make(chan int)
go transponder(t, ln, done)
@@ -68,10 +74,6 @@ func TestConnAndListener(t *testing.T) {
}
<-done
- switch tt.net {
- case "unix", "unixpacket":
- os.Remove(tt.addr)
- }
}
}
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go
index 0c46084..c1eb983 100644
--- a/libgo/go/net/dial.go
+++ b/libgo/go/net/dial.go
@@ -238,7 +238,7 @@ func ListenPacket(net, laddr string) (PacketConn, error) {
if a != nil {
la = a.(*UnixAddr)
}
- return DialUnix(net, la, nil)
+ return ListenUnixgram(net, la)
}
return nil, UnknownNetworkError(net)
}
diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go
index 865dd62..f30dee3 100644
--- a/libgo/go/net/dial_test.go
+++ b/libgo/go/net/dial_test.go
@@ -240,7 +240,8 @@ func TestDialTimeoutFDLeak(t *testing.T) {
err error
}
dials := listenerBacklog + 100
- maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows?
+ // used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
+ maxGoodConnect := listenerBacklog + runtime.NumCPU()*10
resc := make(chan connErr)
for i := 0; i < dials; i++ {
go func() {
diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go
index 2f957d2..5ee0804 100644
--- a/libgo/go/net/http/client.go
+++ b/libgo/go/net/http/client.go
@@ -98,7 +98,9 @@ func (c *Client) send(req *Request) (*Response, error) {
return nil, err
}
if c.Jar != nil {
- c.Jar.SetCookies(req.URL, resp.Cookies())
+ if rc := resp.Cookies(); len(rc) > 0 {
+ c.Jar.SetCookies(req.URL, rc)
+ }
}
return resp, err
}
@@ -120,7 +122,10 @@ func (c *Client) send(req *Request) (*Response, error) {
// Generally Get, Post, or PostForm will be used instead of Do.
func (c *Client) Do(req *Request) (resp *Response, err error) {
if req.Method == "GET" || req.Method == "HEAD" {
- return c.doFollowingRedirects(req)
+ return c.doFollowingRedirects(req, shouldRedirectGet)
+ }
+ if req.Method == "POST" || req.Method == "PUT" {
+ return c.doFollowingRedirects(req, shouldRedirectPost)
}
return c.send(req)
}
@@ -166,7 +171,7 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
-func shouldRedirect(statusCode int) bool {
+func shouldRedirectGet(statusCode int) bool {
switch statusCode {
case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
return true
@@ -174,6 +179,16 @@ func shouldRedirect(statusCode int) bool {
return false
}
+// True if the specified HTTP status code is one for which the Post utility should
+// automatically redirect.
+func shouldRedirectPost(statusCode int) bool {
+ switch statusCode {
+ case StatusFound, StatusSeeOther:
+ return true
+ }
+ return false
+}
+
// Get issues a GET to the specified URL. If the response is one of the following
// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
//
@@ -214,12 +229,10 @@ func (c *Client) Get(url string) (resp *Response, err error) {
if err != nil {
return nil, err
}
- return c.doFollowingRedirects(req)
+ return c.doFollowingRedirects(req, shouldRedirectGet)
}
-func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error) {
- // TODO: if/when we add cookie support, the redirected request shouldn't
- // necessarily supply the same cookies as the original.
+func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
var base *url.URL
redirectChecker := c.CheckRedirect
if redirectChecker == nil {
@@ -238,6 +251,9 @@ func (c *Client) doFollowingRedirects(ireq *Request) (resp *Response, err error)
if redirect != 0 {
req = new(Request)
req.Method = ireq.Method
+ if ireq.Method == "POST" || ireq.Method == "PUT" {
+ req.Method = "GET"
+ }
req.Header = make(Header)
req.URL, err = base.Parse(urlStr)
if err != nil {
@@ -321,7 +337,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Respon
return nil, err
}
req.Header.Set("Content-Type", bodyType)
- return c.send(req)
+ return c.doFollowingRedirects(req, shouldRedirectPost)
}
// PostForm issues a POST to the specified URL, with data's keys and
@@ -371,5 +387,5 @@ func (c *Client) Head(url string) (resp *Response, err error) {
if err != nil {
return nil, err
}
- return c.doFollowingRedirects(req)
+ return c.doFollowingRedirects(req, shouldRedirectGet)
}
diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go
index f4ba6a9..9514a4b 100644
--- a/libgo/go/net/http/client_test.go
+++ b/libgo/go/net/http/client_test.go
@@ -7,6 +7,7 @@
package http_test
import (
+ "bytes"
"crypto/tls"
"crypto/x509"
"errors"
@@ -246,6 +247,52 @@ func TestRedirects(t *testing.T) {
}
}
+func TestPostRedirects(t *testing.T) {
+ var log struct {
+ sync.Mutex
+ bytes.Buffer
+ }
+ var ts *httptest.Server
+ ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ log.Lock()
+ fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
+ log.Unlock()
+ if v := r.URL.Query().Get("code"); v != "" {
+ code, _ := strconv.Atoi(v)
+ if code/100 == 3 {
+ w.Header().Set("Location", ts.URL)
+ }
+ w.WriteHeader(code)
+ }
+ }))
+ tests := []struct {
+ suffix string
+ want int // response code
+ }{
+ {"/", 200},
+ {"/?code=301", 301},
+ {"/?code=302", 200},
+ {"/?code=303", 200},
+ {"/?code=404", 404},
+ }
+ for _, tt := range tests {
+ res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.StatusCode != tt.want {
+ t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want)
+ }
+ }
+ log.Lock()
+ got := log.String()
+ log.Unlock()
+ want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
+ if got != want {
+ t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want)
+ }
+}
+
var expectedCookies = []*Cookie{
{Name: "ChocolateChip", Value: "tasty"},
{Name: "First", Value: "Hit"},
@@ -304,6 +351,9 @@ type TestJar struct {
func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) {
j.m.Lock()
defer j.m.Unlock()
+ if j.perURL == nil {
+ j.perURL = make(map[string][]*Cookie)
+ }
j.perURL[u.Host] = cookies
}
@@ -334,8 +384,9 @@ 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)}
+ c := &Client{
+ Jar: new(TestJar),
+ }
u, _ := url.Parse(ts.URL)
c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
resp, err := c.Get(ts.URL)
@@ -364,6 +415,69 @@ func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
}
}
+func TestJarCalls(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ pathSuffix := r.RequestURI[1:]
+ if r.RequestURI == "/nosetcookie" {
+ return // dont set cookies for this path
+ }
+ SetCookie(w, &Cookie{Name: "name" + pathSuffix, Value: "val" + pathSuffix})
+ if r.RequestURI == "/" {
+ Redirect(w, r, "http://secondhost.fake/secondpath", 302)
+ }
+ }))
+ defer ts.Close()
+ jar := new(RecordingJar)
+ c := &Client{
+ Jar: jar,
+ Transport: &Transport{
+ Dial: func(_ string, _ string) (net.Conn, error) {
+ return net.Dial("tcp", ts.Listener.Addr().String())
+ },
+ },
+ }
+ _, err := c.Get("http://firsthost.fake/")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = c.Get("http://firsthost.fake/nosetcookie")
+ if err != nil {
+ t.Fatal(err)
+ }
+ got := jar.log.String()
+ want := `Cookies("http://firsthost.fake/")
+SetCookie("http://firsthost.fake/", [name=val])
+Cookies("http://secondhost.fake/secondpath")
+SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath])
+Cookies("http://firsthost.fake/nosetcookie")
+`
+ if got != want {
+ t.Errorf("Got Jar calls:\n%s\nWant:\n%s", got, want)
+ }
+}
+
+// RecordingJar keeps a log of calls made to it, without
+// tracking any cookies.
+type RecordingJar struct {
+ mu sync.Mutex
+ log bytes.Buffer
+}
+
+func (j *RecordingJar) SetCookies(u *url.URL, cookies []*Cookie) {
+ j.logf("SetCookie(%q, %v)\n", u, cookies)
+}
+
+func (j *RecordingJar) Cookies(u *url.URL) []*Cookie {
+ j.logf("Cookies(%q)\n", u)
+ return nil
+}
+
+func (j *RecordingJar) logf(format string, args ...interface{}) {
+ j.mu.Lock()
+ defer j.mu.Unlock()
+ fmt.Fprintf(&j.log, format, args...)
+}
+
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/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go
index d70bf4e..0c03e5b 100644
--- a/libgo/go/net/http/pprof/pprof.go
+++ b/libgo/go/net/http/pprof/pprof.go
@@ -34,9 +34,8 @@
//
// go tool pprof http://localhost:6060/debug/pprof/block
//
-// Or to view all available profiles:
-//
-// go tool pprof http://localhost:6060/debug/pprof/
+// To view all available profiles, open http://localhost:6060/debug/pprof/
+// in your browser.
//
// For a study of the facility in action, visit
//
diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go
index 2e03c65..ffdd6a8 100644
--- a/libgo/go/net/http/readrequest_test.go
+++ b/libgo/go/net/http/readrequest_test.go
@@ -247,6 +247,54 @@ var reqTests = []reqTest{
noTrailer,
noError,
},
+
+ // SSDP Notify request. golang.org/issue/3692
+ {
+ "NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
+ &Request{
+ Method: "NOTIFY",
+ URL: &url.URL{
+ Path: "*",
+ },
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{
+ "Server": []string{"foo"},
+ },
+ Close: false,
+ ContentLength: 0,
+ RequestURI: "*",
+ },
+
+ noBody,
+ noTrailer,
+ noError,
+ },
+
+ // OPTIONS request. Similar to golang.org/issue/3692
+ {
+ "OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
+ &Request{
+ Method: "OPTIONS",
+ URL: &url.URL{
+ Path: "*",
+ },
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{
+ "Server": []string{"foo"},
+ },
+ Close: false,
+ ContentLength: 0,
+ RequestURI: "*",
+ },
+
+ noBody,
+ noTrailer,
+ noError,
+ },
}
func TestReadRequest(t *testing.T) {
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index 8ca227f..1de4171 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -918,15 +918,19 @@ func TestZeroLengthPostAndResponse(t *testing.T) {
}
}
+func TestHandlerPanicNil(t *testing.T) {
+ testHandlerPanic(t, false, nil)
+}
+
func TestHandlerPanic(t *testing.T) {
- testHandlerPanic(t, false)
+ testHandlerPanic(t, false, "intentional death for testing")
}
func TestHandlerPanicWithHijack(t *testing.T) {
- testHandlerPanic(t, true)
+ testHandlerPanic(t, true, "intentional death for testing")
}
-func testHandlerPanic(t *testing.T, withHijack bool) {
+func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) {
// Unlike the other tests that set the log output to ioutil.Discard
// to quiet the output, this test uses a pipe. The pipe serves three
// purposes:
@@ -955,7 +959,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
}
defer rwc.Close()
}
- panic("intentional death for testing")
+ panic(panicValue)
}))
defer ts.Close()
@@ -968,7 +972,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
_, err := pr.Read(buf)
pr.Close()
if err != nil {
- t.Fatal(err)
+ t.Error(err)
}
done <- true
}()
@@ -978,6 +982,10 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
t.Logf("expected an error")
}
+ if panicValue == nil {
+ return
+ }
+
select {
case <-done:
return
@@ -1288,6 +1296,58 @@ For:
ts.Close()
}
+func TestOptions(t *testing.T) {
+ uric := make(chan string, 2) // only expect 1, but leave space for 2
+ mux := NewServeMux()
+ mux.HandleFunc("/", func(w ResponseWriter, r *Request) {
+ uric <- r.RequestURI
+ })
+ ts := httptest.NewServer(mux)
+ defer ts.Close()
+
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+
+ // An OPTIONS * request should succeed.
+ _, err = conn.Write([]byte("OPTIONS * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ br := bufio.NewReader(conn)
+ res, err := ReadResponse(br, &Request{Method: "OPTIONS"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.StatusCode != 200 {
+ t.Errorf("Got non-200 response to OPTIONS *: %#v", res)
+ }
+
+ // A GET * request on a ServeMux should fail.
+ _, err = conn.Write([]byte("GET * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err = ReadResponse(br, &Request{Method: "GET"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.StatusCode != 400 {
+ t.Errorf("Got non-400 response to GET *: %#v", res)
+ }
+
+ res, err = Get(ts.URL + "/second")
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ if got := <-uric; got != "/second" {
+ t.Errorf("Handler saw request for %q; want /second", got)
+ }
+}
+
// goTimeout runs f, failing t if f takes more than ns to complete.
func goTimeout(t *testing.T, d time.Duration, f func()) {
ch := make(chan bool, 2)
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index c4ddbec..89a46f0 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -702,24 +702,19 @@ func (c *conn) closeWriteAndWait() {
// Serve a new connection.
func (c *conn) serve() {
defer func() {
- err := recover()
- if err == nil {
- return
+ if err := recover(); err != nil {
+ const size = 4096
+ buf := make([]byte, size)
+ buf = buf[:runtime.Stack(buf, false)]
+ log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
}
-
- const size = 4096
- buf := make([]byte, size)
- buf = buf[:runtime.Stack(buf, false)]
- log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
-
- if c.rwc != nil { // may be nil if connection hijacked
- c.rwc.Close()
+ if !c.hijacked() {
+ c.close()
}
}()
if tlsConn, ok := c.rwc.(*tls.Conn); ok {
if err := tlsConn.Handshake(); err != nil {
- c.close()
return
}
c.tlsState = new(tls.ConnectionState)
@@ -770,6 +765,9 @@ func (c *conn) serve() {
if handler == nil {
handler = DefaultServeMux
}
+ if req.RequestURI == "*" && req.Method == "OPTIONS" {
+ handler = globalOptionsHandler{}
+ }
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
@@ -788,7 +786,6 @@ func (c *conn) serve() {
break
}
}
- c.close()
}
func (w *response) sendExpectationFailed() {
@@ -1085,6 +1082,11 @@ func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
+ if r.RequestURI == "*" {
+ w.Header().Set("Connection", "close")
+ w.WriteHeader(StatusBadRequest)
+ return
+ }
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
@@ -1408,6 +1410,22 @@ func (tw *timeoutWriter) WriteHeader(code int) {
tw.w.WriteHeader(code)
}
+// globalOptionsHandler responds to "OPTIONS *" requests.
+type globalOptionsHandler struct{}
+
+func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
+ w.Header().Set("Content-Length", "0")
+ if r.ContentLength != 0 {
+ // Read up to 4KB of OPTIONS body (as mentioned in the
+ // spec as being reserved for future use), but anything
+ // over that is considered a waste of server resources
+ // (or an attack) and we abort and close the connection,
+ // courtesy of MaxBytesReader's EOF behavior.
+ mb := MaxBytesReader(w, r.Body, 4<<10)
+ io.Copy(ioutil.Discard, mb)
+ }
+}
+
// loggingConn is used for debugging.
type loggingConn struct {
name string
diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go
index 7b4afeb..d0505bf 100644
--- a/libgo/go/net/http/transport.go
+++ b/libgo/go/net/http/transport.go
@@ -144,6 +144,9 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
}
return rt.RoundTrip(req)
}
+ if req.URL.Host == "" {
+ return nil, errors.New("http: no Host in request URL")
+ }
treq := &transportRequest{Request: req}
cm, err := t.connectMethodForRequest(treq)
if err != nil {
@@ -739,6 +742,7 @@ WaitResponse:
case err := <-writeErrCh:
if err != nil {
re = responseAndError{nil, err}
+ pc.close()
break WaitResponse
}
case <-pconnDeadCh:
diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go
index f1d4158..c37ef13 100644
--- a/libgo/go/net/http/transport_test.go
+++ b/libgo/go/net/http/transport_test.go
@@ -778,6 +778,45 @@ func TestTransportPersistConnLeak(t *testing.T) {
}
}
+// golang.org/issue/4531: Transport leaks goroutines when
+// request.ContentLength is explicitly short
+func TestTransportPersistConnLeakShortBody(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ }))
+ defer ts.Close()
+
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+
+ n0 := runtime.NumGoroutine()
+ body := []byte("Hello")
+ for i := 0; i < 20; i++ {
+ req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
+ if err != nil {
+ t.Fatal(err)
+ }
+ req.ContentLength = int64(len(body) - 2) // explicitly short
+ _, err = c.Do(req)
+ if err == nil {
+ t.Fatal("Expect an error from writing too long of a body.")
+ }
+ }
+ nhigh := runtime.NumGoroutine()
+ tr.CloseIdleConnections()
+ time.Sleep(50 * time.Millisecond)
+ runtime.GC()
+ nfinal := runtime.NumGoroutine()
+
+ growth := nfinal - n0
+
+ // We expect 0 or 1 extra goroutine, empirically. Allow up to 5.
+ // Previously we were leaking one per numReq.
+ t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
+ if int(growth) > 5 {
+ t.Error("too many new goroutines")
+ }
+}
+
// This used to crash; http://golang.org/issue/3266
func TestTransportIdleConnCrash(t *testing.T) {
tr := &Transport{}
@@ -1062,6 +1101,20 @@ func TestTransportAltProto(t *testing.T) {
}
}
+func TestTransportNoHost(t *testing.T) {
+ tr := &Transport{}
+ _, err := tr.RoundTrip(&Request{
+ Header: make(Header),
+ URL: &url.URL{
+ Scheme: "http",
+ },
+ })
+ want := "http: no Host in request URL"
+ if got := fmt.Sprint(err); got != want {
+ t.Errorf("error = %v; want %q", err, want)
+ }
+}
+
var proxyFromEnvTests = []struct {
env string
wanturl string
diff --git a/libgo/go/net/packetconn_test.go b/libgo/go/net/packetconn_test.go
index 5075baa..ff29e24 100644
--- a/libgo/go/net/packetconn_test.go
+++ b/libgo/go/net/packetconn_test.go
@@ -24,6 +24,15 @@ var packetConnTests = []struct {
}
func TestPacketConn(t *testing.T) {
+ closer := func(c net.PacketConn, net, addr1, addr2 string) {
+ c.Close()
+ switch net {
+ case "unixgram":
+ os.Remove(addr1)
+ os.Remove(addr2)
+ }
+ }
+
for _, tt := range packetConnTests {
var wb []byte
netstr := strings.Split(tt.net, ":")
@@ -39,7 +48,7 @@ func TestPacketConn(t *testing.T) {
continue
}
id := os.Getpid() & 0xffff
- wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST "))
+ wb = newICMPEchoRequest(id, 1, 128, []byte("IP PACKETCONN TEST"))
case "unixgram":
switch runtime.GOOS {
case "plan9", "windows":
@@ -60,7 +69,7 @@ func TestPacketConn(t *testing.T) {
c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
- defer c1.Close()
+ defer closer(c1, netstr[0], tt.addr1, tt.addr2)
c2, err := net.ListenPacket(tt.net, tt.addr2)
if err != nil {
@@ -70,7 +79,7 @@ func TestPacketConn(t *testing.T) {
c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
- defer c2.Close()
+ defer closer(c2, netstr[0], tt.addr1, tt.addr2)
if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil {
t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
@@ -86,12 +95,6 @@ func TestPacketConn(t *testing.T) {
if _, _, err := c1.ReadFrom(rb1); err != nil {
t.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
}
-
- switch netstr[0] {
- case "unixgram":
- os.Remove(tt.addr1)
- os.Remove(tt.addr2)
- }
}
}
diff --git a/libgo/go/net/protoconn_test.go b/libgo/go/net/protoconn_test.go
index f249372..d99de3f 100644
--- a/libgo/go/net/protoconn_test.go
+++ b/libgo/go/net/protoconn_test.go
@@ -263,9 +263,10 @@ func TestUnixConnSpecificMethods(t *testing.T) {
return
}
- p1, p2 := "/tmp/gotest.net1", "/tmp/gotest.net2"
+ p1, p2, p3 := "/tmp/gotest.net1", "/tmp/gotest.net2", "/tmp/gotest.net3"
os.Remove(p1)
os.Remove(p2)
+ os.Remove(p3)
a1, err := net.ResolveUnixAddr("unixgram", p1)
if err != nil {
@@ -305,9 +306,30 @@ func TestUnixConnSpecificMethods(t *testing.T) {
defer c2.Close()
defer os.Remove(p2)
+ a3, err := net.ResolveUnixAddr("unixgram", p3)
+ if err != nil {
+ t.Errorf("net.ResolveUnixAddr failed: %v", err)
+ return
+ }
+ c3, err := net.ListenUnixgram("unixgram", a3)
+ if err != nil {
+ t.Errorf("net.ListenUnixgram failed: %v", err)
+ return
+ }
+ c3.LocalAddr()
+ c3.RemoteAddr()
+ c3.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c3.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c3.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+ c3.SetReadBuffer(2048)
+ c3.SetWriteBuffer(2048)
+ defer c3.Close()
+ defer os.Remove(p3)
+
wb := []byte("UNIXCONN TEST")
rb1 := make([]byte, 128)
rb2 := make([]byte, 128)
+ rb3 := make([]byte, 128)
if _, _, err := c1.WriteMsgUnix(wb, nil, a2); err != nil {
t.Errorf("net.UnixConn.WriteMsgUnix failed: %v", err)
return
@@ -324,9 +346,22 @@ func TestUnixConnSpecificMethods(t *testing.T) {
t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
return
}
-
- // TODO: http://golang.org/issue/3875
- net.ListenUnixgram("unixgram", nil)
+ if _, err := c3.WriteToUnix(wb, a1); err != nil {
+ t.Errorf("net.UnixConn.WriteToUnix failed: %v", err)
+ return
+ }
+ if _, _, err := c1.ReadFromUnix(rb1); err != nil {
+ t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
+ return
+ }
+ if _, err := c2.WriteToUnix(wb, a3); err != nil {
+ t.Errorf("net.UnixConn.WriteToUnix failed: %v", err)
+ return
+ }
+ if _, _, err := c3.ReadFromUnix(rb3); err != nil {
+ t.Errorf("net.UnixConn.ReadFromUnix failed: %v", err)
+ return
+ }
if f, err := c1.File(); err != nil {
t.Errorf("net.UnixConn.File failed: %v", err)
diff --git a/libgo/go/net/smtp/smtp.go b/libgo/go/net/smtp/smtp.go
index 59f6449..4b91778 100644
--- a/libgo/go/net/smtp/smtp.go
+++ b/libgo/go/net/smtp/smtp.go
@@ -13,6 +13,7 @@ package smtp
import (
"crypto/tls"
"encoding/base64"
+ "errors"
"io"
"net"
"net/textproto"
@@ -33,7 +34,10 @@ type Client struct {
// map of supported extensions
ext map[string]string
// supported auth mechanisms
- auth []string
+ auth []string
+ localName string // the name to use in HELO/EHLO
+ didHello bool // whether we've said HELO/EHLO
+ helloError error // the error from the hello
}
// Dial returns a new Client connected to an SMTP server at addr.
@@ -55,12 +59,33 @@ func NewClient(conn net.Conn, host string) (*Client, error) {
text.Close()
return nil, err
}
- c := &Client{Text: text, conn: conn, serverName: host}
- err = c.ehlo()
- if err != nil {
- err = c.helo()
+ c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
+ return c, nil
+}
+
+// hello runs a hello exchange if needed.
+func (c *Client) hello() error {
+ if !c.didHello {
+ c.didHello = true
+ err := c.ehlo()
+ if err != nil {
+ c.helloError = c.helo()
+ }
+ }
+ return c.helloError
+}
+
+// Hello sends a HELO or EHLO to the server as the given host name.
+// Calling this method is only necessary if the client needs control
+// over the host name used. The client will introduce itself as "localhost"
+// automatically otherwise. If Hello is called, it must be called before
+// any of the other methods.
+func (c *Client) Hello(localName string) error {
+ if c.didHello {
+ return errors.New("smtp: Hello called after other methods")
}
- return c, err
+ c.localName = localName
+ return c.hello()
}
// cmd is a convenience function that sends a command and returns the response
@@ -79,14 +104,14 @@ func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, s
// server does not support ehlo.
func (c *Client) helo() error {
c.ext = nil
- _, _, err := c.cmd(250, "HELO localhost")
+ _, _, err := c.cmd(250, "HELO %s", c.localName)
return err
}
// ehlo sends the EHLO (extended hello) greeting to the server. It
// should be the preferred greeting for servers that support it.
func (c *Client) ehlo() error {
- _, msg, err := c.cmd(250, "EHLO localhost")
+ _, msg, err := c.cmd(250, "EHLO %s", c.localName)
if err != nil {
return err
}
@@ -113,6 +138,9 @@ func (c *Client) ehlo() error {
// StartTLS sends the STARTTLS command and encrypts all further communication.
// Only servers that advertise the STARTTLS extension support this function.
func (c *Client) StartTLS(config *tls.Config) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(220, "STARTTLS")
if err != nil {
return err
@@ -128,6 +156,9 @@ func (c *Client) StartTLS(config *tls.Config) error {
// does not necessarily indicate an invalid address. Many servers
// will not verify addresses for security reasons.
func (c *Client) Verify(addr string) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(250, "VRFY %s", addr)
return err
}
@@ -136,6 +167,9 @@ func (c *Client) Verify(addr string) error {
// A failed authentication closes the connection.
// Only servers that advertise the AUTH extension support this function.
func (c *Client) Auth(a Auth) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
encoding := base64.StdEncoding
mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth})
if err != nil {
@@ -178,6 +212,9 @@ func (c *Client) Auth(a Auth) error {
// parameter.
// This initiates a mail transaction and is followed by one or more Rcpt calls.
func (c *Client) Mail(from string) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
cmdStr := "MAIL FROM:<%s>"
if c.ext != nil {
if _, ok := c.ext["8BITMIME"]; ok {
@@ -227,6 +264,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
if err != nil {
return err
}
+ if err := c.hello(); err != nil {
+ return err
+ }
if ok, _ := c.Extension("STARTTLS"); ok {
if err = c.StartTLS(nil); err != nil {
return err
@@ -267,6 +307,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
// Extension also returns a string that contains any parameters the
// server specifies for the extension.
func (c *Client) Extension(ext string) (bool, string) {
+ if err := c.hello(); err != nil {
+ return false, ""
+ }
if c.ext == nil {
return false, ""
}
@@ -278,12 +321,18 @@ func (c *Client) Extension(ext string) (bool, string) {
// Reset sends the RSET command to the server, aborting the current mail
// transaction.
func (c *Client) Reset() error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(250, "RSET")
return err
}
// Quit sends the QUIT command and closes the connection to the server.
func (c *Client) Quit() error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(221, "QUIT")
if err != nil {
return err
diff --git a/libgo/go/net/smtp/smtp_test.go b/libgo/go/net/smtp/smtp_test.go
index c315d18..8317428 100644
--- a/libgo/go/net/smtp/smtp_test.go
+++ b/libgo/go/net/smtp/smtp_test.go
@@ -69,14 +69,14 @@ func (f faker) SetReadDeadline(time.Time) error { return nil }
func (f faker) SetWriteDeadline(time.Time) error { return nil }
func TestBasic(t *testing.T) {
- basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n")
- basicClient = strings.Join(strings.Split(basicClient, "\n"), "\r\n")
+ server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
+ client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
var fake faker
- fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(basicServer)), bcmdbuf)
- c := &Client{Text: textproto.NewConn(fake)}
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
+ c := &Client{Text: textproto.NewConn(fake), localName: "localhost"}
if err := c.helo(); err != nil {
t.Fatalf("HELO failed: %s", err)
@@ -88,6 +88,7 @@ func TestBasic(t *testing.T) {
t.Fatalf("Second EHLO failed: %s", err)
}
+ c.didHello = true
if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
t.Fatalf("Expected AUTH supported")
}
@@ -143,8 +144,8 @@ Goodbye.`
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
- if basicClient != actualcmds {
- t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, basicClient)
+ if client != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@@ -187,8 +188,8 @@ QUIT
`
func TestNewClient(t *testing.T) {
- newClientServer = strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
- newClientClient = strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
+ server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
+ client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
@@ -197,7 +198,7 @@ func TestNewClient(t *testing.T) {
return cmdbuf.String()
}
var fake faker
- fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClientServer)), bcmdbuf)
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c, err := NewClient(fake, "fake.host")
if err != nil {
t.Fatalf("NewClient: %v\n(after %v)", err, out())
@@ -213,8 +214,8 @@ func TestNewClient(t *testing.T) {
}
actualcmds := out()
- if newClientClient != actualcmds {
- t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClientClient)
+ if client != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@@ -231,13 +232,13 @@ QUIT
`
func TestNewClient2(t *testing.T) {
- newClient2Server = strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
- newClient2Client = strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
+ server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
+ client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
var fake faker
- fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClient2Server)), bcmdbuf)
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c, err := NewClient(fake, "fake.host")
if err != nil {
t.Fatalf("NewClient: %v", err)
@@ -251,8 +252,8 @@ func TestNewClient2(t *testing.T) {
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
- if newClient2Client != actualcmds {
- t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClient2Client)
+ if client != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@@ -269,3 +270,199 @@ var newClient2Client = `EHLO localhost
HELO localhost
QUIT
`
+
+func TestHello(t *testing.T) {
+
+ if len(helloServer) != len(helloClient) {
+ t.Fatalf("Hello server and client size mismatch")
+ }
+
+ for i := 0; i < len(helloServer); i++ {
+ server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n")
+ client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n")
+ var cmdbuf bytes.Buffer
+ bcmdbuf := bufio.NewWriter(&cmdbuf)
+ var fake faker
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
+ c, err := NewClient(fake, "fake.host")
+ if err != nil {
+ t.Fatalf("NewClient: %v", err)
+ }
+ c.localName = "customhost"
+ err = nil
+
+ switch i {
+ case 0:
+ err = c.Hello("customhost")
+ case 1:
+ err = c.StartTLS(nil)
+ if err.Error() == "502 Not implemented" {
+ err = nil
+ }
+ case 2:
+ err = c.Verify("test@example.com")
+ case 3:
+ c.tls = true
+ c.serverName = "smtp.google.com"
+ err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
+ case 4:
+ err = c.Mail("test@example.com")
+ case 5:
+ ok, _ := c.Extension("feature")
+ if ok {
+ t.Errorf("Expected FEATURE not to be supported")
+ }
+ case 6:
+ err = c.Reset()
+ case 7:
+ err = c.Quit()
+ case 8:
+ err = c.Verify("test@example.com")
+ if err != nil {
+ err = c.Hello("customhost")
+ if err != nil {
+ t.Errorf("Want error, got none")
+ }
+ }
+ default:
+ t.Fatalf("Unhandled command")
+ }
+
+ if err != nil {
+ t.Errorf("Command %d failed: %v", i, err)
+ }
+
+ bcmdbuf.Flush()
+ actualcmds := cmdbuf.String()
+ if client != actualcmds {
+ t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
+ }
+ }
+}
+
+var baseHelloServer = `220 hello world
+502 EH?
+250-mx.google.com at your service
+250 FEATURE
+`
+
+var helloServer = []string{
+ "",
+ "502 Not implemented\n",
+ "250 User is valid\n",
+ "235 Accepted\n",
+ "250 Sender ok\n",
+ "",
+ "250 Reset ok\n",
+ "221 Goodbye\n",
+ "250 Sender ok\n",
+}
+
+var baseHelloClient = `EHLO customhost
+HELO customhost
+`
+
+var helloClient = []string{
+ "",
+ "STARTTLS\n",
+ "VRFY test@example.com\n",
+ "AUTH PLAIN AHVzZXIAcGFzcw==\n",
+ "MAIL FROM:<test@example.com>\n",
+ "",
+ "RSET\n",
+ "QUIT\n",
+ "VRFY test@example.com\n",
+}
+
+func TestSendMail(t *testing.T) {
+ server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n")
+ client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n")
+ var cmdbuf bytes.Buffer
+ bcmdbuf := bufio.NewWriter(&cmdbuf)
+ l, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("Unable to to create listener: %v", err)
+ }
+ defer l.Close()
+
+ // prevent data race on bcmdbuf
+ var done = make(chan struct{})
+ go func(data []string) {
+
+ defer close(done)
+
+ conn, err := l.Accept()
+ if err != nil {
+ t.Errorf("Accept error: %v", err)
+ return
+ }
+ defer conn.Close()
+
+ tc := textproto.NewConn(conn)
+ for i := 0; i < len(data) && data[i] != ""; i++ {
+ tc.PrintfLine(data[i])
+ for len(data[i]) >= 4 && data[i][3] == '-' {
+ i++
+ tc.PrintfLine(data[i])
+ }
+ if data[i] == "221 Goodbye" {
+ return
+ }
+ read := false
+ for !read || data[i] == "354 Go ahead" {
+ msg, err := tc.ReadLine()
+ bcmdbuf.Write([]byte(msg + "\r\n"))
+ read = true
+ if err != nil {
+ t.Errorf("Read error: %v", err)
+ return
+ }
+ if data[i] == "354 Go ahead" && msg == "." {
+ break
+ }
+ }
+ }
+ }(strings.Split(server, "\r\n"))
+
+ err = SendMail(l.Addr().String(), nil, "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com
+To: other@example.com
+Subject: SendMail test
+
+SendMail is working for me.
+`, "\n", "\r\n", -1)))
+
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+
+ <-done
+ bcmdbuf.Flush()
+ actualcmds := cmdbuf.String()
+ if client != actualcmds {
+ t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
+ }
+}
+
+var sendMailServer = `220 hello world
+502 EH?
+250 mx.google.com at your service
+250 Sender ok
+250 Receiver ok
+354 Go ahead
+250 Data ok
+221 Goodbye
+`
+
+var sendMailClient = `EHLO localhost
+HELO localhost
+MAIL FROM:<test@example.com>
+RCPT TO:<other@example.com>
+DATA
+From: test@example.com
+To: other@example.com
+Subject: SendMail test
+
+SendMail is working for me.
+.
+QUIT
+`
diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go
index f7be5d2..713820c 100644
--- a/libgo/go/net/unixsock_plan9.go
+++ b/libgo/go/net/unixsock_plan9.go
@@ -64,21 +64,21 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
return 0, 0, syscall.EPLAN9
}
-// CloseRead shuts down the reading side of the Unix domain
-// connection. Most callers should just use Close.
+// CloseRead shuts down the reading side of the Unix domain connection.
+// Most callers should just use Close.
func (c *UnixConn) CloseRead() error {
return syscall.EPLAN9
}
-// CloseWrite shuts down the writing side of the Unix domain
-// connection. Most callers should just use Close.
+// CloseWrite shuts down the writing side of the Unix domain connection.
+// Most callers should just use Close.
func (c *UnixConn) CloseWrite() error {
return syscall.EPLAN9
}
// DialUnix connects to the remote address raddr on the network net,
-// which must be "unix" or "unixgram". If laddr is not nil, it is
-// used as the local address for the connection.
+// which must be "unix", "unixgram" or "unixpacket". If laddr is not
+// nil, it is used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
return dialUnix(net, laddr, raddr, noDeadline)
}
@@ -93,7 +93,8 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
type UnixListener struct{}
// ListenUnix announces on the Unix domain socket laddr and returns a
-// Unix listener. Net must be "unix" (stream sockets).
+// Unix listener. The network net must be "unix", "unixgram" or
+// "unixpacket".
func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
return nil, syscall.EPLAN9
}
@@ -134,8 +135,8 @@ func (l *UnixListener) File() (*os.File, error) {
// ListenUnixgram listens for incoming Unix datagram packets addressed
// to the local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP packets
-// with per-packet addressing. The network net must be "unixgram".
-func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
+// and WriteTo methods can be used to receive and send packets with
+// per-packet addressing. The network net must be "unixgram".
+func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go
index 16ebd58..653190c 100644
--- a/libgo/go/net/unixsock_posix.go
+++ b/libgo/go/net/unixsock_posix.go
@@ -9,29 +9,27 @@
package net
import (
+ "errors"
"os"
"syscall"
"time"
)
-func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (fd *netFD, err error) {
+func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) {
var sotype int
switch net {
- default:
- return nil, UnknownNetworkError(net)
case "unix":
sotype = syscall.SOCK_STREAM
case "unixgram":
sotype = syscall.SOCK_DGRAM
case "unixpacket":
sotype = syscall.SOCK_SEQPACKET
+ default:
+ return nil, UnknownNetworkError(net)
}
var la, ra syscall.Sockaddr
switch mode {
- default:
- panic("unixSocket mode " + mode)
-
case "dial":
if laddr != nil {
la = &syscall.SockaddrUnix{Name: laddr.Name}
@@ -41,15 +39,10 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
} else if sotype != syscall.SOCK_DGRAM || laddr == nil {
return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress}
}
-
case "listen":
- if laddr == nil {
- return nil, &OpError{mode, net, nil, errMissingAddress}
- }
la = &syscall.SockaddrUnix{Name: laddr.Name}
- if raddr != nil {
- return nil, &OpError{Op: mode, Net: net, Addr: raddr, Err: &AddrError{Err: "unexpected remote address", Addr: raddr.String()}}
- }
+ default:
+ return nil, errors.New("unknown mode: " + mode)
}
f := sockaddrToUnix
@@ -59,15 +52,16 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T
f = sockaddrToUnixpacket
}
- fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
+ fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
if err != nil {
- goto Error
+ goto error
}
return fd, nil
-Error:
+error:
addr := raddr
- if mode == "listen" {
+ switch mode {
+ case "listen":
addr = laddr
}
return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err}
@@ -108,21 +102,21 @@ func sotypeToNet(sotype int) string {
return ""
}
-// UnixConn is an implementation of the Conn interface
-// for connections to Unix domain sockets.
+// UnixConn is an implementation of the Conn interface for connections
+// to Unix domain sockets.
type UnixConn struct {
conn
}
func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} }
-// ReadFromUnix reads a packet from c, copying the payload into b.
-// It returns the number of bytes copied into b and the source address
-// of the packet.
+// ReadFromUnix reads a packet from c, copying the payload into b. It
+// returns the number of bytes copied into b and the source address of
+// the packet.
//
-// ReadFromUnix can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetReadDeadline.
+// ReadFromUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
@@ -144,12 +138,28 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
return n, uaddr.toAddr(), err
}
+// ReadMsgUnix reads a packet from c, copying the payload into b and
+// the associated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet, and the source address of the packet.
+func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
+ if !c.ok() {
+ return 0, 0, 0, nil, syscall.EINVAL
+ }
+ n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
+ switch sa := sa.(type) {
+ case *syscall.SockaddrUnix:
+ addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
+ }
+ return
+}
+
// WriteToUnix writes a packet to addr via c, copying the payload from b.
//
-// WriteToUnix can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
if !c.ok() {
return 0, syscall.EINVAL
@@ -173,26 +183,9 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
return c.WriteToUnix(b, a)
}
-// ReadMsgUnix reads a packet from c, copying the payload into b
-// and the associated out-of-band data into oob.
-// It returns the number of bytes copied into b, the number of
-// bytes copied into oob, the flags that were set on the packet,
-// and the source address of the packet.
-func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
- if !c.ok() {
- return 0, 0, 0, nil, syscall.EINVAL
- }
- n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
- switch sa := sa.(type) {
- case *syscall.SockaddrUnix:
- addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
- }
- return
-}
-
-// WriteMsgUnix writes a packet to addr via c, copying the payload from b
-// and the associated out-of-band data from oob. It returns the number
-// of payload and out-of-band bytes written.
+// WriteMsgUnix writes a packet to addr via c, copying the payload
+// from b and the associated out-of-band data from oob. It returns
+// the number of payload and out-of-band bytes written.
func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
if !c.ok() {
return 0, 0, syscall.EINVAL
@@ -226,13 +219,18 @@ func (c *UnixConn) CloseWrite() error {
}
// DialUnix connects to the remote address raddr on the network net,
-// which must be "unix" or "unixgram". If laddr is not nil, it is used
-// as the local address for the connection.
+// which must be "unix", "unixgram" or "unixpacket". If laddr is not
+// nil, it is used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
return dialUnix(net, laddr, raddr, noDeadline)
}
func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
+ switch net {
+ case "unix", "unixgram", "unixpacket":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
if err != nil {
return nil, err
@@ -240,22 +238,25 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
return newUnixConn(fd), nil
}
-// UnixListener is a Unix domain socket listener.
-// Clients should typically use variables of type Listener
-// instead of assuming Unix domain sockets.
+// UnixListener is a Unix domain socket listener. Clients should
+// typically use variables of type Listener instead of assuming Unix
+// domain sockets.
type UnixListener struct {
fd *netFD
path string
}
-// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
-// Net must be "unix" (stream sockets).
+// ListenUnix announces on the Unix domain socket laddr and returns a
+// Unix listener. The network net must be "unix", "unixgram" or
+// "unixpacket".
func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
- if net != "unix" && net != "unixgram" && net != "unixpacket" {
+ switch net {
+ case "unix", "unixgram", "unixpacket":
+ default:
return nil, UnknownNetworkError(net)
}
- if laddr != nil {
- laddr = &UnixAddr{laddr.Name, net} // make our own copy
+ if laddr == nil {
+ return nil, &OpError{"listen", net, nil, errMissingAddress}
}
fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
@@ -269,8 +270,8 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
return &UnixListener{fd, laddr.Name}, nil
}
-// AcceptUnix accepts the next incoming call and returns the new connection
-// and the remote address.
+// AcceptUnix accepts the next incoming call and returns the new
+// connection and the remote address.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
@@ -283,8 +284,8 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
return c, nil
}
-// Accept implements the Accept method in the Listener interface;
-// it waits for the next call and returns a generic Conn.
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
func (l *UnixListener) Accept() (c Conn, err error) {
c1, err := l.AcceptUnix()
if err != nil {
@@ -293,8 +294,8 @@ func (l *UnixListener) Accept() (c Conn, err error) {
return c1, nil
}
-// Close stops listening on the Unix address.
-// Already accepted connections are not closed.
+// Close stops listening on the Unix address. Already accepted
+// connections are not closed.
func (l *UnixListener) Close() error {
if l == nil || l.fd == nil {
return syscall.EINVAL
@@ -328,16 +329,16 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
return setDeadline(l.fd, t)
}
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
+// File returns a copy of the underlying os.File, set to blocking
+// mode. It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() }
-// ListenUnixgram listens for incoming Unix datagram packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP
-// packets with per-packet addressing. The network net must be "unixgram".
-func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
+// ListenUnixgram listens for incoming Unix datagram packets addressed
+// to the local address laddr. The returned connection c's ReadFrom
+// and WriteTo methods can be used to receive and send packets with
+// per-packet addressing. The network net must be "unixgram".
+func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
switch net {
case "unixgram":
default:
@@ -350,5 +351,5 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
if err != nil {
return nil, err
}
- return newUDPConn(fd), nil
+ return newUnixConn(fd), nil
}
diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go
index 692a7fd..71758fe 100644
--- a/libgo/go/net/url/url.go
+++ b/libgo/go/net/url/url.go
@@ -361,6 +361,11 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
}
url = new(URL)
+ if rawurl == "*" {
+ url.Path = "*"
+ return
+ }
+
// Split off possible leading "http:", "mailto:", etc.
// Cannot contain escaped characters.
if url.Scheme, rest, err = getscheme(rawurl); err != nil {
@@ -572,23 +577,33 @@ func resolvePath(basepath string, refpath string) string {
if len(base) == 0 {
base = []string{""}
}
+
+ rm := true
for idx, ref := range refs {
switch {
case ref == ".":
- base[len(base)-1] = ""
+ if idx == 0 {
+ base[len(base)-1] = ""
+ rm = true
+ } else {
+ rm = false
+ }
case ref == "..":
newLen := len(base) - 1
if newLen < 1 {
newLen = 1
}
base = base[0:newLen]
- base[len(base)-1] = ""
+ if rm {
+ base[len(base)-1] = ""
+ }
default:
if idx == 0 || base[len(base)-1] == "" {
base[len(base)-1] = ref
} else {
base = append(base, ref)
}
+ rm = false
}
}
return strings.Join(base, "/")
diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go
index 64f1170..4d3545d 100644
--- a/libgo/go/net/url/url_test.go
+++ b/libgo/go/net/url/url_test.go
@@ -277,7 +277,7 @@ func TestParse(t *testing.T) {
const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
-var parseRequestUrlTests = []struct {
+var parseRequestURLTests = []struct {
url string
expectedValid bool
}{
@@ -289,10 +289,11 @@ var parseRequestUrlTests = []struct {
{"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
{"foo.html", false},
{"../dir/", false},
+ {"*", true},
}
func TestParseRequestURI(t *testing.T) {
- for _, test := range parseRequestUrlTests {
+ for _, test := range parseRequestURLTests {
_, err := ParseRequestURI(test.url)
valid := err == nil
if valid != test.expectedValid {
@@ -536,6 +537,15 @@ var resolveReferenceTests = []struct {
{"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
{"http://foo.com/bar", "..", "http://foo.com/"},
{"http://foo.com/bar/baz", "./..", "http://foo.com/"},
+ // ".." in the middle (issue 3560)
+ {"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot"},
// "." and ".." in the base aren't special
{"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"},