diff options
Diffstat (limited to 'src/pkg/net/http')
-rw-r--r-- | src/pkg/net/http/client_test.go | 6 | ||||
-rw-r--r-- | src/pkg/net/http/request.go | 6 | ||||
-rw-r--r-- | src/pkg/net/http/request_test.go | 19 | ||||
-rw-r--r-- | src/pkg/net/http/server.go | 2 | ||||
-rw-r--r-- | src/pkg/net/http/transport.go | 2 | ||||
-rw-r--r-- | src/pkg/net/http/transport_test.go | 26 | ||||
-rw-r--r-- | src/pkg/net/http/triv.go | 53 |
7 files changed, 76 insertions, 38 deletions
diff --git a/src/pkg/net/http/client_test.go b/src/pkg/net/http/client_test.go index aa0bf4be6..e00b62e59 100644 --- a/src/pkg/net/http/client_test.go +++ b/src/pkg/net/http/client_test.go @@ -238,9 +238,9 @@ func TestRedirects(t *testing.T) { } var expectedCookies = []*Cookie{ - &Cookie{Name: "ChocolateChip", Value: "tasty"}, - &Cookie{Name: "First", Value: "Hit"}, - &Cookie{Name: "Second", Value: "Hit"}, + {Name: "ChocolateChip", Value: "tasty"}, + {Name: "First", Value: "Hit"}, + {Name: "Second", Value: "Hit"}, } var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request) { diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go index 527765780..f5bc6eb91 100644 --- a/src/pkg/net/http/request.go +++ b/src/pkg/net/http/request.go @@ -455,11 +455,13 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) { // First line: GET /index.html HTTP/1.0 var s string if s, err = tp.ReadLine(); err != nil { + return nil, err + } + defer func() { if err == io.EOF { err = io.ErrUnexpectedEOF } - return nil, err - } + }() var f []string if f = strings.SplitN(s, " ", 3); len(f) < 3 { diff --git a/src/pkg/net/http/request_test.go b/src/pkg/net/http/request_test.go index 7a3556d03..6e00b9bfd 100644 --- a/src/pkg/net/http/request_test.go +++ b/src/pkg/net/http/request_test.go @@ -5,6 +5,7 @@ package http_test import ( + "bufio" "bytes" "fmt" "io" @@ -177,6 +178,24 @@ func TestRequestMultipartCallOrder(t *testing.T) { } } +var readRequestErrorTests = []struct { + in string + err error +}{ + {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", nil}, + {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF}, + {"", io.EOF}, +} + +func TestReadRequestErrors(t *testing.T) { + for i, tt := range readRequestErrorTests { + _, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in))) + if err != tt.err { + t.Errorf("%d. got error = %v; want %v", i, err, tt.err) + } + } +} + func testMissingFile(t *testing.T, req *Request) { f, fh, err := req.FormFile("missing") if f != nil { diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go index fa0df54a2..228ac4019 100644 --- a/src/pkg/net/http/server.go +++ b/src/pkg/net/http/server.go @@ -601,7 +601,7 @@ func (c *conn) serve() { // while they're still writing their // request. Undefined behavior. msg = "413 Request Entity Too Large" - } else if err == io.ErrUnexpectedEOF { + } else if err == io.EOF { break // Don't reply } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() { break // Don't reply diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go index 09579f8a0..024975946 100644 --- a/src/pkg/net/http/transport.go +++ b/src/pkg/net/http/transport.go @@ -196,7 +196,7 @@ func (t *Transport) CloseIdleConnections() { pconn.close() } } - t.idleConn = nil + t.idleConn = make(map[string][]*persistConn) } // diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go index cbb3884f9..a9e401de5 100644 --- a/src/pkg/net/http/transport_test.go +++ b/src/pkg/net/http/transport_test.go @@ -698,6 +698,32 @@ func TestTransportPersistConnLeak(t *testing.T) { } } +// This used to crash; http://golang.org/issue/3266 +func TestTransportIdleConnCrash(t *testing.T) { + tr := &Transport{} + c := &Client{Transport: tr} + + unblockCh := make(chan bool, 1) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + <-unblockCh + tr.CloseIdleConnections() + })) + defer ts.Close() + + didreq := make(chan bool) + go func() { + res, err := c.Get(ts.URL) + if err != nil { + t.Error(err) + } else { + res.Body.Close() // returns idle conn + } + didreq <- true + }() + unblockCh <- true + <-didreq +} + type fooProto struct{} func (fooProto) RoundTrip(req *Request) (*Response, error) { diff --git a/src/pkg/net/http/triv.go b/src/pkg/net/http/triv.go index 269af0ca3..232d65089 100644 --- a/src/pkg/net/http/triv.go +++ b/src/pkg/net/http/triv.go @@ -15,7 +15,9 @@ import ( "log" "net/http" "os" + "os/exec" "strconv" + "sync" ) // hello world, the web server @@ -28,14 +30,21 @@ func HelloServer(w http.ResponseWriter, req *http.Request) { // Simple counter server. POSTing to it will set the value. type Counter struct { - n int + mu sync.Mutex // protects n + n int } // This makes Counter satisfy the expvar.Var interface, so we can export // it directly. -func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) } +func (ctr *Counter) String() string { + ctr.mu.Lock() + defer ctr.mu.Unlock() + return fmt.Sprintf("%d", ctr.n) +} func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { + ctr.mu.Lock() + defer ctr.mu.Unlock() switch req.Method { case "GET": ctr.n++ @@ -95,54 +104,36 @@ func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { // exec a program, redirecting output func DateServer(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("Content-Type", "text/plain; charset=utf-8") - r, w, err := os.Pipe() - if err != nil { - fmt.Fprintf(rw, "pipe: %s\n", err) - return - } - p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}}) - defer r.Close() - w.Close() - if err != nil { - fmt.Fprintf(rw, "fork/exec: %s\n", err) - return - } - io.Copy(rw, r) - wait, err := p.Wait(0) + date, err := exec.Command("/bin/date").Output() if err != nil { - fmt.Fprintf(rw, "wait: %s\n", err) - return - } - if !wait.Exited() || wait.ExitStatus() != 0 { - fmt.Fprintf(rw, "date: %v\n", wait) + http.Error(rw, err.Error(), 500) return } + rw.Write(date) } func Logger(w http.ResponseWriter, req *http.Request) { - log.Print(req.URL.Raw) - w.WriteHeader(404) - w.Write([]byte("oops")) + log.Print(req.URL) + http.Error(w, "oops", 404) } -var webroot = flag.String("root", "/home/rsc", "web root directory") +var webroot = flag.String("root", os.Getenv("HOME"), "web root directory") func main() { flag.Parse() // The counter is published as a variable directly. ctr := new(Counter) - http.Handle("/counter", ctr) expvar.Publish("counter", ctr) - + http.Handle("/counter", ctr) http.Handle("/", http.HandlerFunc(Logger)) http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot)))) - http.Handle("/flags", http.HandlerFunc(FlagServer)) - http.Handle("/args", http.HandlerFunc(ArgServer)) - http.Handle("/go/hello", http.HandlerFunc(HelloServer)) http.Handle("/chan", ChanCreate()) - http.Handle("/date", http.HandlerFunc(DateServer)) + http.HandleFunc("/flags", FlagServer) + http.HandleFunc("/args", ArgServer) + http.HandleFunc("/go/hello", HelloServer) + http.HandleFunc("/date", DateServer) err := http.ListenAndServe(":12345", nil) if err != nil { log.Panicln("ListenAndServe:", err) |