diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-12-22 01:15:33 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-12-22 01:15:33 +0000 |
commit | 409a5e7eb4cca107037fafa4a7eea92603edb83d (patch) | |
tree | 06f36bbef6fae78278f799194ad0df8ba2dabaa1 /libgo/go/net | |
parent | 7e9268b4cf01ab87d9b602f592ed2e2facfadda9 (diff) | |
download | gcc-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.go | 14 | ||||
-rw-r--r-- | libgo/go/net/cgo_unix.go | 2 | ||||
-rw-r--r-- | libgo/go/net/conn_test.go | 16 | ||||
-rw-r--r-- | libgo/go/net/dial.go | 2 | ||||
-rw-r--r-- | libgo/go/net/dial_test.go | 3 | ||||
-rw-r--r-- | libgo/go/net/http/client.go | 34 | ||||
-rw-r--r-- | libgo/go/net/http/client_test.go | 118 | ||||
-rw-r--r-- | libgo/go/net/http/pprof/pprof.go | 5 | ||||
-rw-r--r-- | libgo/go/net/http/readrequest_test.go | 48 | ||||
-rw-r--r-- | libgo/go/net/http/serve_test.go | 70 | ||||
-rw-r--r-- | libgo/go/net/http/server.go | 44 | ||||
-rw-r--r-- | libgo/go/net/http/transport.go | 4 | ||||
-rw-r--r-- | libgo/go/net/http/transport_test.go | 53 | ||||
-rw-r--r-- | libgo/go/net/packetconn_test.go | 21 | ||||
-rw-r--r-- | libgo/go/net/protoconn_test.go | 43 | ||||
-rw-r--r-- | libgo/go/net/smtp/smtp.go | 65 | ||||
-rw-r--r-- | libgo/go/net/smtp/smtp_test.go | 229 | ||||
-rw-r--r-- | libgo/go/net/unixsock_plan9.go | 21 | ||||
-rw-r--r-- | libgo/go/net/unixsock_posix.go | 147 | ||||
-rw-r--r-- | libgo/go/net/url/url.go | 19 | ||||
-rw-r--r-- | libgo/go/net/url/url_test.go | 14 |
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"}, |