Merge lp:~pedronis/ubuntu-push/update-http13 into lp:ubuntu-push/automatic

Proposed by Samuele Pedroni
Status: Merged
Approved by: Samuele Pedroni
Approved revision: 187
Merged at revision: 186
Proposed branch: lp:~pedronis/ubuntu-push/update-http13
Merge into: lp:ubuntu-push/automatic
Diff against target: 4164 lines (+1453/-1014)
20 files modified
debian/changelog (+1/-0)
http13client/Makefile (+2/-1)
http13client/_patches/empty_server.patch (+118/-42)
http13client/_patches/fix_code.patch (+73/-65)
http13client/_patches/fix_status.patch (+33/-20)
http13client/_patches/fix_tests.patch (+338/-384)
http13client/_using.txt (+3/-3)
http13client/client.go (+11/-3)
http13client/client_test.go (+92/-0)
http13client/cookie.go (+19/-37)
http13client/cookie_test.go (+0/-304)
http13client/request.go (+62/-50)
http13client/request_test.go (+48/-8)
http13client/response.go (+52/-5)
http13client/response_test.go (+4/-0)
http13client/responsewrite_test.go (+117/-1)
http13client/server.go (+11/-5)
http13client/transfer.go (+59/-27)
http13client/transport.go (+114/-42)
http13client/transport_test.go (+296/-17)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/update-http13
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Review via email: mp+223916@code.launchpad.net

Commit message

update http13client from the actual go1.3 release

Description of the change

update http13client from the actual go1.3 release

To post a comment you must log in.
Revision history for this message
John Lenton (chipaca) wrote :

LGTM.

review: Approve
187. By Samuele Pedroni

changelog

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/changelog'
--- debian/changelog 2014-06-20 12:33:03 +0000
+++ debian/changelog 2014-06-20 13:24:39 +0000
@@ -3,6 +3,7 @@
3 [ Samuele Pedroni ]3 [ Samuele Pedroni ]
4 * Support registering tokens and sending notifications with a token4 * Support registering tokens and sending notifications with a token
5 * Register script and scripts unicast support5 * Register script and scripts unicast support
6 * Update http13client from the actual go1.3 release
67
7 [ Roberto Alsina ]8 [ Roberto Alsina ]
8 * Make signing-helper generate a HTTP header instead of a querystring,9 * Make signing-helper generate a HTTP header instead of a querystring,
910
=== modified file 'http13client/Makefile'
--- http13client/Makefile 2014-03-20 12:15:36 +0000
+++ http13client/Makefile 2014-06-20 13:24:39 +0000
@@ -20,7 +20,8 @@
2020
21prune:21prune:
22 rm -rf example_test.go filetransport*.go fs*.go race.go range_test.go \22 rm -rf example_test.go filetransport*.go fs*.go race.go range_test.go \
23 sniff*.go httptest httputil testdata triv.go jar.go status.go23 sniff*.go httptest httputil testdata triv.go jar.go status.go \
24 cookie_test.go
24 sed -i -e 's+"launchpad.net/ubuntu-push/http13client/+"net/http/+' *.go25 sed -i -e 's+"launchpad.net/ubuntu-push/http13client/+"net/http/+' *.go
2526
26fix:27fix:
2728
=== modified file 'http13client/_patches/empty_server.patch'
--- http13client/_patches/empty_server.patch 2014-03-19 23:13:58 +0000
+++ http13client/_patches/empty_server.patch 2014-06-20 13:24:39 +0000
@@ -1,6 +1,6 @@
1=== modified file 'http13client/serve_test.go'1=== modified file 'http13client/serve_test.go'
2--- http13client/serve_test.go 2014-03-19 21:38:56 +00002--- http13client/serve_test.go 2014-06-20 11:00:47 +0000
3+++ http13client/serve_test.go 2014-03-19 22:27:37 +00003+++ http13client/serve_test.go 2014-06-20 12:00:22 +0000
4@@ -2,60 +2,15 @@4@@ -2,60 +2,15 @@
5 // Use of this source code is governed by a BSD-style5 // Use of this source code is governed by a BSD-style
6 // license that can be found in the LICENSE file.6 // license that can be found in the LICENSE file.
@@ -62,7 +62,7 @@
62 62
63 func (a dummyAddr) Network() string {63 func (a dummyAddr) Network() string {
64 return string(a)64 return string(a)
65@@ -93,1289 +48,6 @@65@@ -93,1325 +48,6 @@
66 return nil66 return nil
67 }67 }
68 68
@@ -906,31 +906,50 @@
906-}906-}
907-907-
908-type serverExpectTest struct {908-type serverExpectTest struct {
909- contentLength int // of request body909- contentLength int // of request body
910- chunked bool
910- expectation string // e.g. "100-continue"911- expectation string // e.g. "100-continue"
911- readBody bool // whether handler should read the body (if false, sends StatusUnauthorized)912- readBody bool // whether handler should read the body (if false, sends StatusUnauthorized)
912- expectedResponse string // expected substring in first line of http response913- expectedResponse string // expected substring in first line of http response
913-}914-}
914-915-
916-func expectTest(contentLength int, expectation string, readBody bool, expectedResponse string) serverExpectTest {
917- return serverExpectTest{
918- contentLength: contentLength,
919- expectation: expectation,
920- readBody: readBody,
921- expectedResponse: expectedResponse,
922- }
923-}
924-
915-var serverExpectTests = []serverExpectTest{925-var serverExpectTests = []serverExpectTest{
916- // Normal 100-continues, case-insensitive.926- // Normal 100-continues, case-insensitive.
917- {100, "100-continue", true, "100 Continue"},927- expectTest(100, "100-continue", true, "100 Continue"),
918- {100, "100-cOntInUE", true, "100 Continue"},928- expectTest(100, "100-cOntInUE", true, "100 Continue"),
919-929-
920- // No 100-continue.930- // No 100-continue.
921- {100, "", true, "200 OK"},931- expectTest(100, "", true, "200 OK"),
922-932-
923- // 100-continue but requesting client to deny us,933- // 100-continue but requesting client to deny us,
924- // so it never reads the body.934- // so it never reads the body.
925- {100, "100-continue", false, "401 Unauthorized"},935- expectTest(100, "100-continue", false, "401 Unauthorized"),
926- // Likewise without 100-continue:936- // Likewise without 100-continue:
927- {100, "", false, "401 Unauthorized"},937- expectTest(100, "", false, "401 Unauthorized"),
928-938-
929- // Non-standard expectations are failures939- // Non-standard expectations are failures
930- {0, "a-pony", false, "417 Expectation Failed"},940- expectTest(0, "a-pony", false, "417 Expectation Failed"),
931-941-
932- // Expect-100 requested but no body942- // Expect-100 requested but no body (is apparently okay: Issue 7625)
933- {0, "100-continue", true, "400 Bad Request"},943- expectTest(0, "100-continue", true, "200 OK"),
944- // Expect-100 requested but handler doesn't read the body
945- expectTest(0, "100-continue", false, "401 Unauthorized"),
946- // Expect-100 continue with no body, but a chunked body.
947- {
948- expectation: "100-continue",
949- readBody: true,
950- chunked: true,
951- expectedResponse: "100 Continue",
952- },
934-}953-}
935-954-
936-// Tests that the server responds to the "Expect" request header955-// Tests that the server responds to the "Expect" request header
@@ -959,21 +978,38 @@
959-978-
960- // Only send the body immediately if we're acting like an HTTP client979- // Only send the body immediately if we're acting like an HTTP client
961- // that doesn't send 100-continue expectations.980- // that doesn't send 100-continue expectations.
962- writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue"981- writeBody := test.contentLength != 0 && strings.ToLower(test.expectation) != "100-continue"
963-982-
964- go func() {983- go func() {
984- contentLen := fmt.Sprintf("Content-Length: %d", test.contentLength)
985- if test.chunked {
986- contentLen = "Transfer-Encoding: chunked"
987- }
965- _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+988- _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+
966- "Connection: close\r\n"+989- "Connection: close\r\n"+
967- "Content-Length: %d\r\n"+990- "%s\r\n"+
968- "Expect: %s\r\nHost: foo\r\n\r\n",991- "Expect: %s\r\nHost: foo\r\n\r\n",
969- test.readBody, test.contentLength, test.expectation)992- test.readBody, contentLen, test.expectation)
970- if err != nil {993- if err != nil {
971- t.Errorf("On test %#v, error writing request headers: %v", test, err)994- t.Errorf("On test %#v, error writing request headers: %v", test, err)
972- return995- return
973- }996- }
974- if writeBody {997- if writeBody {
998- var targ io.WriteCloser = struct {
999- io.Writer
1000- io.Closer
1001- }{
1002- conn,
1003- ioutil.NopCloser(nil),
1004- }
1005- if test.chunked {
1006- targ = httputil.NewChunkedWriter(conn)
1007- }
975- body := strings.Repeat("A", test.contentLength)1008- body := strings.Repeat("A", test.contentLength)
976- _, err = fmt.Fprint(conn, body)1009- _, err = fmt.Fprint(targ, body)
1010- if err == nil {
1011- err = targ.Close()
1012- }
977- if err != nil {1013- if err != nil {
978- if !test.readBody {1014- if !test.readBody {
979- // Server likely already hung up on us.1015- // Server likely already hung up on us.
@@ -1352,7 +1388,7 @@
1352 type neverEnding byte1388 type neverEnding byte
1353 1389
1354 func (b neverEnding) Read(p []byte) (n int, err error) {1390 func (b neverEnding) Read(p []byte) (n int, err error) {
1355@@ -1384,1344 +56,3 @@1391@@ -1420,1392 +56,3 @@
1356 }1392 }
1357 return len(p), nil1393 return len(p), nil
1358 }1394 }
@@ -2080,7 +2116,7 @@
2080- got := ht.rawResponse(req)2116- got := ht.rawResponse(req)
2081- wantStatus := fmt.Sprintf("%d %s", code, StatusText(code))2117- wantStatus := fmt.Sprintf("%d %s", code, StatusText(code))
2082- if !strings.Contains(got, wantStatus) {2118- if !strings.Contains(got, wantStatus) {
2083- t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, req, got)2119- t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, wantStatus, req, got)
2084- } else if strings.Contains(got, "Content-Length") {2120- } else if strings.Contains(got, "Content-Length") {
2085- t.Errorf("Code %d: Got a Content-Length from %q: %s", code, req, got)2121- t.Errorf("Code %d: Got a Content-Length from %q: %s", code, req, got)
2086- } else if strings.Contains(got, "stuff") {2122- } else if strings.Contains(got, "stuff") {
@@ -2090,6 +2126,21 @@
2090- }2126- }
2091-}2127-}
2092-2128-
2129-func TestContentTypeOkayOn204(t *testing.T) {
2130- ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
2131- w.Header().Set("Content-Length", "123") // suppressed
2132- w.Header().Set("Content-Type", "foo/bar")
2133- w.WriteHeader(204)
2134- }))
2135- got := ht.rawResponse("GET / HTTP/1.1")
2136- if !strings.Contains(got, "Content-Type: foo/bar") {
2137- t.Errorf("Response = %q; want Content-Type: foo/bar", got)
2138- }
2139- if strings.Contains(got, "Content-Length: 123") {
2140- t.Errorf("Response = %q; don't want a Content-Length", got)
2141- }
2142-}
2143-
2093-// Issue 69952144-// Issue 6995
2094-// A server Handler can receive a Request, and then turn around and2145-// A server Handler can receive a Request, and then turn around and
2095-// give a copy of that Request.Body out to the Transport (e.g. any2146-// give a copy of that Request.Body out to the Transport (e.g. any
@@ -2261,7 +2312,7 @@
2261- ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)2312- ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
2262- ts.Config.ConnState = func(c net.Conn, state ConnState) {2313- ts.Config.ConnState = func(c net.Conn, state ConnState) {
2263- if c == nil {2314- if c == nil {
2264- t.Error("nil conn seen in state %s", state)2315- t.Errorf("nil conn seen in state %s", state)
2265- return2316- return
2266- }2317- }
2267- mu.Lock()2318- mu.Lock()
@@ -2397,6 +2448,39 @@
2397- }2448- }
2398-}2449-}
2399-2450-
2451-// golang.org/issue/7856
2452-func TestServerEmptyBodyRace(t *testing.T) {
2453- defer afterTest(t)
2454- var n int32
2455- ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
2456- atomic.AddInt32(&n, 1)
2457- }))
2458- defer ts.Close()
2459- var wg sync.WaitGroup
2460- const reqs = 20
2461- for i := 0; i < reqs; i++ {
2462- wg.Add(1)
2463- go func() {
2464- defer wg.Done()
2465- res, err := Get(ts.URL)
2466- if err != nil {
2467- t.Error(err)
2468- return
2469- }
2470- defer res.Body.Close()
2471- _, err = io.Copy(ioutil.Discard, res.Body)
2472- if err != nil {
2473- t.Error(err)
2474- return
2475- }
2476- }()
2477- }
2478- wg.Wait()
2479- if got := atomic.LoadInt32(&n); got != reqs {
2480- t.Errorf("handler ran %d times; want %d", got, reqs)
2481- }
2482-}
2483-
2400-func TestServerConnStateNew(t *testing.T) {2484-func TestServerConnStateNew(t *testing.T) {
2401- sawNew := false // if the test is buggy, we'll race on this variable.2485- sawNew := false // if the test is buggy, we'll race on this variable.
2402- srv := &Server{2486- srv := &Server{
@@ -2699,9 +2783,9 @@
2699-}2783-}
27002784
2701=== modified file 'http13client/server.go'2785=== modified file 'http13client/server.go'
2702--- http13client/server.go 2014-03-19 20:20:19 +00002786--- http13client/server.go 2014-06-20 11:00:47 +0000
2703+++ http13client/server.go 2014-03-19 22:27:37 +00002787+++ http13client/server.go 2014-06-20 12:05:53 +0000
2704@@ -2,1984 +2,17 @@2788@@ -2,1976 +2,16 @@
2705 // Use of this source code is governed by a BSD-style2789 // Use of this source code is governed by a BSD-style
2706 // license that can be found in the LICENSE file.2790 // license that can be found in the LICENSE file.
2707 2791
@@ -2723,7 +2807,7 @@
2723- "path"2807- "path"
2724- "runtime"2808- "runtime"
2725- "strconv"2809- "strconv"
2726 "strings"2810- "strings"
2727 "sync"2811 "sync"
2728- "sync/atomic"2812- "sync/atomic"
2729- "time"2813- "time"
@@ -3506,18 +3590,16 @@
3506- }3590- }
3507-3591-
3508- code := w.status3592- code := w.status
3509- if !bodyAllowedForStatus(code) {3593- if bodyAllowedForStatus(code) {
3510- // Must not have body.
3511- // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
3512- for _, k := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
3513- delHeader(k)
3514- }
3515- } else {
3516- // If no content type, apply sniffing algorithm to body.3594- // If no content type, apply sniffing algorithm to body.
3517- _, haveType := header["Content-Type"]3595- _, haveType := header["Content-Type"]
3518- if !haveType {3596- if !haveType {
3519- setHeader.contentType = DetectContentType(p)3597- setHeader.contentType = DetectContentType(p)
3520- }3598- }
3599- } else {
3600- for _, k := range suppressedHeaders(code) {
3601- delHeader(k)
3602- }
3521- }3603- }
3522-3604-
3523- if _, ok := header["Date"]; !ok {3605- if _, ok := header["Date"]; !ok {
@@ -3865,16 +3947,10 @@
3865- // Expect 100 Continue support3947- // Expect 100 Continue support
3866- req := w.req3948- req := w.req
3867- if req.expectsContinue() {3949- if req.expectsContinue() {
3868- if req.ProtoAtLeast(1, 1) {3950- if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
3869- // Wrap the Body reader with one that replies on the connection3951- // Wrap the Body reader with one that replies on the connection
3870- req.Body = &expectContinueReader{readCloser: req.Body, resp: w}3952- req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
3871- }3953- }
3872- if req.ContentLength == 0 {
3873- w.Header().Set("Connection", "close")
3874- w.WriteHeader(StatusBadRequest)
3875- w.finishRequest()
3876- break
3877- }
3878- req.Header.Del("Expect")3954- req.Header.Del("Expect")
3879- } else if req.Header.get("Expect") != "" {3955- } else if req.Header.get("Expect") != "" {
3880- w.sendExpectationFailed()3956- w.sendExpectationFailed()
@@ -4685,11 +4761,11 @@
4685-}4761-}
4686+)4762+)
4687 4763
4688 // eofReader is a non-nil io.ReadCloser that always returns EOF.4764 type eofReaderWithWriteTo struct{}
4689 // It embeds a *strings.Reader so it still has a WriteTo method4765
4690@@ -1992,28 +25,6 @@4766@@ -1991,28 +31,6 @@
4691 ioutil.NopCloser(nil),4767 // Verify that an io.Copy from an eofReader won't require a buffer.
4692 }4768 var _ io.WriterTo = eofReader
4693 4769
4694-// initNPNRequest is an HTTP handler that initializes certain4770-// initNPNRequest is an HTTP handler that initializes certain
4695-// uninitialized fields in its *Request. Such partially-initialized4771-// uninitialized fields in its *Request. Such partially-initialized
46964772
=== modified file 'http13client/_patches/fix_code.patch'
--- http13client/_patches/fix_code.patch 2014-03-19 23:13:58 +0000
+++ http13client/_patches/fix_code.patch 2014-06-20 13:24:39 +0000
@@ -1,6 +1,6 @@
1=== modified file 'http13client/client.go'1=== modified file 'http13client/client.go'
2--- http13client/client.go 2014-03-19 20:20:19 +00002--- http13client/client.go 2014-06-20 11:00:47 +0000
3+++ http13client/client.go 2014-03-19 22:27:37 +00003+++ http13client/client.go 2014-06-20 12:05:53 +0000
4@@ -17,6 +17,7 @@4@@ -17,6 +17,7 @@
5 "io/ioutil"5 "io/ioutil"
6 "log"6 "log"
@@ -18,7 +18,7 @@
18 18
19 // Timeout specifies a time limit for requests made by this19 // Timeout specifies a time limit for requests made by this
20 // Client. The timeout includes connection time, any20 // Client. The timeout includes connection time, any
21@@ -177,7 +178,7 @@21@@ -184,7 +185,7 @@
22 // Headers, leaving it uninitialized. We guarantee to the22 // Headers, leaving it uninitialized. We guarantee to the
23 // Transport that this has been initialized, though.23 // Transport that this has been initialized, though.
24 if req.Header == nil {24 if req.Header == nil {
@@ -27,7 +27,7 @@
27 }27 }
28 28
29 if u := req.URL.User; u != nil {29 if u := req.URL.User; u != nil {
30@@ -308,7 +309,7 @@30@@ -316,7 +317,7 @@
31 if ireq.Method == "POST" || ireq.Method == "PUT" {31 if ireq.Method == "POST" || ireq.Method == "PUT" {
32 nreq.Method = "GET"32 nreq.Method = "GET"
33 }33 }
@@ -38,8 +38,8 @@
38 break38 break
3939
40=== modified file 'http13client/cookie.go'40=== modified file 'http13client/cookie.go'
41--- http13client/cookie.go 2014-03-19 20:20:19 +000041--- http13client/cookie.go 2014-06-20 11:00:47 +0000
42+++ http13client/cookie.go 2014-03-19 22:27:37 +000042+++ http13client/cookie.go 2014-06-20 12:05:53 +0000
43@@ -5,10 +5,9 @@43@@ -5,10 +5,9 @@
44 package http44 package http
45 45
@@ -94,7 +94,7 @@
94 Name: name,94 Name: name,
95 Value: value,95 Value: value,
96 Raw: line,96 Raw: line,
97@@ -129,59 +108,12 @@97@@ -125,59 +104,12 @@
98 return cookies98 return cookies
99 }99 }
100 100
@@ -156,7 +156,7 @@
156 lines, ok := h["Cookie"]156 lines, ok := h["Cookie"]
157 if !ok {157 if !ok {
158 return cookies158 return cookies
159@@ -213,7 +145,7 @@159@@ -209,7 +141,7 @@
160 if !success {160 if !success {
161 continue161 continue
162 }162 }
@@ -167,8 +167,8 @@
167 }167 }
168168
169=== modified file 'http13client/header.go'169=== modified file 'http13client/header.go'
170--- http13client/header.go 2014-03-19 20:20:19 +0000170--- http13client/header.go 2014-06-20 11:00:47 +0000
171+++ http13client/header.go 2014-03-19 22:27:37 +0000171+++ http13client/header.go 2014-06-20 12:00:22 +0000
172@@ -5,176 +5,9 @@172@@ -5,176 +5,9 @@
173 package http173 package http
174 174
@@ -348,8 +348,8 @@
348 // token must be all lowercase.348 // token must be all lowercase.
349349
350=== modified file 'http13client/request.go'350=== modified file 'http13client/request.go'
351--- http13client/request.go 2014-03-19 20:20:19 +0000351--- http13client/request.go 2014-06-20 11:00:47 +0000
352+++ http13client/request.go 2014-03-19 22:27:37 +0000352+++ http13client/request.go 2014-06-20 12:05:53 +0000
353@@ -16,6 +16,7 @@353@@ -16,6 +16,7 @@
354 "io/ioutil"354 "io/ioutil"
355 "mime"355 "mime"
@@ -358,25 +358,25 @@
358 "net/textproto"358 "net/textproto"
359 "net/url"359 "net/url"
360 "strconv"360 "strconv"
361@@ -103,7 +104,7 @@361@@ -121,7 +122,7 @@
362 // The request parser implements this by canonicalizing the362 // added and may override values in Header.
363 // name, making the first character and any characters363 //
364 // following a hyphen uppercase and the rest lowercase.364 // See the documentation for the Request.Write method.
365- Header Header365- Header Header
366+ Header http.Header366+ Header http.Header
367 367
368 // Body is the request's body.368 // Body is the request's body.
369 //369 //
370@@ -164,7 +165,7 @@370@@ -199,7 +200,7 @@
371 // For server requests, Trailer is only populated after Body has been371 // not mutate Trailer.
372 // closed or fully consumed.372 //
373 // Trailer support is only partially complete.373 // Few HTTP clients, servers, or proxies support HTTP trailers.
374- Trailer Header374- Trailer Header
375+ Trailer http.Header375+ Trailer http.Header
376 376
377 // RemoteAddr allows HTTP servers and other software to record377 // RemoteAddr allows HTTP servers and other software to record
378 // the network address that sent the request, usually for378 // the network address that sent the request, usually for
379@@ -204,7 +205,7 @@379@@ -239,7 +240,7 @@
380 }380 }
381 381
382 // Cookies parses and returns the HTTP cookies sent with the request.382 // Cookies parses and returns the HTTP cookies sent with the request.
@@ -385,7 +385,7 @@
385 return readCookies(r.Header, "")385 return readCookies(r.Header, "")
386 }386 }
387 387
388@@ -212,7 +213,7 @@388@@ -247,7 +248,7 @@
389 389
390 // Cookie returns the named cookie provided in the request or390 // Cookie returns the named cookie provided in the request or
391 // ErrNoCookie if not found.391 // ErrNoCookie if not found.
@@ -394,7 +394,7 @@
394 for _, c := range readCookies(r.Header, name) {394 for _, c := range readCookies(r.Header, name) {
395 return c, nil395 return c, nil
396 }396 }
397@@ -223,7 +224,7 @@397@@ -258,7 +259,7 @@
398 // AddCookie does not attach more than one Cookie header field. That398 // AddCookie does not attach more than one Cookie header field. That
399 // means all cookies, if any, are written into the same line,399 // means all cookies, if any, are written into the same line,
400 // separated by semicolon.400 // separated by semicolon.
@@ -403,7 +403,7 @@
403 s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))403 s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
404 if c := r.Header.Get("Cookie"); c != "" {404 if c := r.Header.Get("Cookie"); c != "" {
405 r.Header.Set("Cookie", c+"; "+s)405 r.Header.Set("Cookie", c+"; "+s)
406@@ -326,7 +327,7 @@406@@ -361,7 +362,7 @@
407 }407 }
408 408
409 // extraHeaders may be nil409 // extraHeaders may be nil
@@ -412,7 +412,7 @@
412 host := req.Host412 host := req.Host
413 if host == "" {413 if host == "" {
414 if req.URL == nil {414 if req.URL == nil {
415@@ -456,7 +457,7 @@415@@ -490,7 +491,7 @@
416 Proto: "HTTP/1.1",416 Proto: "HTTP/1.1",
417 ProtoMajor: 1,417 ProtoMajor: 1,
418 ProtoMinor: 1,418 ProtoMinor: 1,
@@ -421,7 +421,7 @@
421 Body: rc,421 Body: rc,
422 Host: u.Host,422 Host: u.Host,
423 }423 }
424@@ -571,7 +572,7 @@424@@ -605,7 +606,7 @@
425 if err != nil {425 if err != nil {
426 return nil, err426 return nil, err
427 }427 }
@@ -430,7 +430,7 @@
430 430
431 // RFC2616: Must treat431 // RFC2616: Must treat
432 // GET /index.html HTTP/1.1432 // GET /index.html HTTP/1.1
433@@ -582,7 +583,7 @@433@@ -616,7 +617,7 @@
434 // the same. In the second case, any Host line is ignored.434 // the same. In the second case, any Host line is ignored.
435 req.Host = req.URL.Host435 req.Host = req.URL.Host
436 if req.Host == "" {436 if req.Host == "" {
@@ -439,7 +439,7 @@
439 }439 }
440 delete(req.Header, "Host")440 delete(req.Header, "Host")
441 441
442@@ -630,12 +631,12 @@442@@ -638,12 +639,12 @@
443 //443 //
444 // MaxBytesReader prevents clients from accidentally or maliciously444 // MaxBytesReader prevents clients from accidentally or maliciously
445 // sending a large request and wasting server resources.445 // sending a large request and wasting server resources.
@@ -454,7 +454,7 @@
454 r io.ReadCloser // underlying reader454 r io.ReadCloser // underlying reader
455 n int64 // max bytes remaining455 n int64 // max bytes remaining
456 stopped bool456 stopped bool
457@@ -645,9 +646,6 @@457@@ -653,9 +654,6 @@
458 if l.n <= 0 {458 if l.n <= 0 {
459 if !l.stopped {459 if !l.stopped {
460 l.stopped = true460 l.stopped = true
@@ -464,7 +464,7 @@
464 }464 }
465 return 0, errors.New("http: request body too large")465 return 0, errors.New("http: request body too large")
466 }466 }
467@@ -852,16 +850,16 @@467@@ -858,18 +856,18 @@
468 }468 }
469 469
470 func (r *Request) expectsContinue() bool {470 func (r *Request) expectsContinue() bool {
@@ -484,11 +484,13 @@
484- return hasToken(r.Header.get("Connection"), "close")484- return hasToken(r.Header.get("Connection"), "close")
485+ return hasToken(r.Header.Get("Connection"), "close")485+ return hasToken(r.Header.Get("Connection"), "close")
486 }486 }
487
488 func (r *Request) closeBody() {
487489
488=== modified file 'http13client/response.go'490=== modified file 'http13client/response.go'
489--- http13client/response.go 2014-03-19 20:20:19 +0000491--- http13client/response.go 2014-06-20 11:00:47 +0000
490+++ http13client/response.go 2014-03-19 22:27:37 +0000492+++ http13client/response.go 2014-06-20 12:05:53 +0000
491@@ -11,6 +11,7 @@493@@ -12,6 +12,7 @@
492 "crypto/tls"494 "crypto/tls"
493 "errors"495 "errors"
494 "io"496 "io"
@@ -496,7 +498,7 @@
496 "net/textproto"498 "net/textproto"
497 "net/url"499 "net/url"
498 "strconv"500 "strconv"
499@@ -40,7 +41,7 @@501@@ -41,7 +42,7 @@
500 // omitted from Header.502 // omitted from Header.
501 //503 //
502 // Keys in the map are canonicalized (see CanonicalHeaderKey).504 // Keys in the map are canonicalized (see CanonicalHeaderKey).
@@ -505,7 +507,7 @@
505 507
506 // Body represents the response body.508 // Body represents the response body.
507 //509 //
508@@ -69,7 +70,7 @@510@@ -71,7 +72,7 @@
509 511
510 // Trailer maps trailer keys to values, in the same512 // Trailer maps trailer keys to values, in the same
511 // format as the header.513 // format as the header.
@@ -514,7 +516,7 @@
514 516
515 // The Request that was sent to obtain this Response.517 // The Request that was sent to obtain this Response.
516 // Request's Body is nil (having already been consumed).518 // Request's Body is nil (having already been consumed).
517@@ -84,7 +85,7 @@519@@ -86,7 +87,7 @@
518 }520 }
519 521
520 // Cookies parses and returns the cookies set in the Set-Cookie headers.522 // Cookies parses and returns the cookies set in the Set-Cookie headers.
@@ -523,7 +525,7 @@
523 return readSetCookies(r.Header)525 return readSetCookies(r.Header)
524 }526 }
525 527
526@@ -153,7 +154,7 @@528@@ -155,7 +156,7 @@
527 }529 }
528 return nil, err530 return nil, err
529 }531 }
@@ -532,7 +534,7 @@
532 534
533 fixPragmaCacheControl(resp.Header)535 fixPragmaCacheControl(resp.Header)
534 536
535@@ -169,7 +170,7 @@537@@ -171,7 +172,7 @@
536 // Pragma: no-cache538 // Pragma: no-cache
537 // like539 // like
538 // Cache-Control: no-cache540 // Cache-Control: no-cache
@@ -543,17 +545,17 @@
543 header["Cache-Control"] = []string{"no-cache"}545 header["Cache-Control"] = []string{"no-cache"}
544546
545=== modified file 'http13client/transfer.go'547=== modified file 'http13client/transfer.go'
546--- http13client/transfer.go 2014-03-19 20:20:19 +0000548--- http13client/transfer.go 2014-06-20 11:00:47 +0000
547+++ http13client/transfer.go 2014-03-19 22:27:37 +0000549+++ http13client/transfer.go 2014-06-20 12:05:53 +0000
548@@ -11,6 +11,7 @@550@@ -11,6 +11,7 @@
549 "fmt"551 "fmt"
550 "io"552 "io"
551 "io/ioutil"553 "io/ioutil"
552+ "net/http"554+ "net/http"
553 "net/textproto"555 "net/textproto"
556 "sort"
554 "strconv"557 "strconv"
555 "strings"558@@ -37,7 +38,7 @@
556@@ -36,7 +37,7 @@
557 ContentLength int64 // -1 means unknown, 0 means exactly none559 ContentLength int64 // -1 means unknown, 0 means exactly none
558 Close bool560 Close bool
559 TransferEncoding []string561 TransferEncoding []string
@@ -562,16 +564,16 @@
562 }564 }
563 565
564 func newTransferWriter(r interface{}) (t *transferWriter, err error) {566 func newTransferWriter(r interface{}) (t *transferWriter, err error) {
565@@ -174,7 +175,7 @@567@@ -171,7 +172,7 @@
566 io.WriteString(w, "Trailer: ")568 if t.Trailer != nil {
567 needComma := false569 keys := make([]string, 0, len(t.Trailer))
568 for k := range t.Trailer {570 for k := range t.Trailer {
569- k = CanonicalHeaderKey(k)571- k = CanonicalHeaderKey(k)
570+ k = http.CanonicalHeaderKey(k)572+ k = http.CanonicalHeaderKey(k)
571 switch k {573 switch k {
572 case "Transfer-Encoding", "Trailer", "Content-Length":574 case "Transfer-Encoding", "Trailer", "Content-Length":
573 return &badStringError{"invalid Trailer key", k}575 return &badStringError{"invalid Trailer key", k}
574@@ -237,7 +238,7 @@576@@ -243,7 +244,7 @@
575 577
576 type transferReader struct {578 type transferReader struct {
577 // Input579 // Input
@@ -580,7 +582,7 @@
580 StatusCode int582 StatusCode int
581 RequestMethod string583 RequestMethod string
582 ProtoMajor int584 ProtoMajor int
583@@ -247,7 +248,7 @@585@@ -253,7 +254,7 @@
584 ContentLength int64586 ContentLength int64
585 TransferEncoding []string587 TransferEncoding []string
586 Close bool588 Close bool
@@ -589,7 +591,7 @@
589 }591 }
590 592
591 // bodyAllowedForStatus reports whether a given response status code593 // bodyAllowedForStatus reports whether a given response status code
592@@ -308,7 +309,7 @@594@@ -330,7 +331,7 @@
593 return err595 return err
594 }596 }
595 if isResponse && t.RequestMethod == "HEAD" {597 if isResponse && t.RequestMethod == "HEAD" {
@@ -598,7 +600,7 @@
598 return err600 return err
599 } else {601 } else {
600 t.ContentLength = n602 t.ContentLength = n
601@@ -386,7 +387,7 @@603@@ -408,7 +409,7 @@
602 func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }604 func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
603 605
604 // Sanitize transfer encoding606 // Sanitize transfer encoding
@@ -607,7 +609,7 @@
607 raw, present := header["Transfer-Encoding"]609 raw, present := header["Transfer-Encoding"]
608 if !present {610 if !present {
609 return nil, nil611 return nil, nil
610@@ -429,7 +430,7 @@612@@ -451,7 +452,7 @@
611 // Determine the expected body length, using RFC 2616 Section 4.4. This613 // Determine the expected body length, using RFC 2616 Section 4.4. This
612 // function is not a method, because ultimately it should be shared by614 // function is not a method, because ultimately it should be shared by
613 // ReadResponse and ReadRequest.615 // ReadResponse and ReadRequest.
@@ -616,7 +618,7 @@
616 618
617 // Logic based on response type or status619 // Logic based on response type or status
618 if noBodyExpected(requestMethod) {620 if noBodyExpected(requestMethod) {
619@@ -449,7 +450,7 @@621@@ -471,7 +472,7 @@
620 }622 }
621 623
622 // Logic based on Content-Length624 // Logic based on Content-Length
@@ -625,7 +627,7 @@
625 if cl != "" {627 if cl != "" {
626 n, err := parseContentLength(cl)628 n, err := parseContentLength(cl)
627 if err != nil {629 if err != nil {
628@@ -475,18 +476,18 @@630@@ -497,18 +498,18 @@
629 // Determine whether to hang up after sending a request and body, or631 // Determine whether to hang up after sending a request and body, or
630 // receiving a response and body632 // receiving a response and body
631 // 'header' is the request headers633 // 'header' is the request headers
@@ -647,7 +649,7 @@
647 header.Del("Connection")649 header.Del("Connection")
648 return true650 return true
649 }651 }
650@@ -495,17 +496,17 @@652@@ -517,17 +518,17 @@
651 }653 }
652 654
653 // Parse the trailer header655 // Parse the trailer header
@@ -669,22 +671,28 @@
669 switch key {671 switch key {
670 case "Transfer-Encoding", "Trailer", "Content-Length":672 case "Transfer-Encoding", "Trailer", "Content-Length":
671 return nil, &badStringError{"bad trailer key", key}673 return nil, &badStringError{"bad trailer key", key}
672@@ -642,9 +643,9 @@674@@ -664,14 +665,14 @@
673 }675 }
674 switch rr := b.hdr.(type) {676 switch rr := b.hdr.(type) {
675 case *Request:677 case *Request:
676- rr.Trailer = Header(hdr)678- mergeSetHeader(&rr.Trailer, Header(hdr))
677+ rr.Trailer = http.Header(hdr)679+ mergeSetHeader(&rr.Trailer, http.Header(hdr))
678 case *Response:680 case *Response:
679- rr.Trailer = Header(hdr)681- mergeSetHeader(&rr.Trailer, Header(hdr))
680+ rr.Trailer = http.Header(hdr)682+ mergeSetHeader(&rr.Trailer, http.Header(hdr))
681 }683 }
682 return nil684 return nil
683 }685 }
686
687-func mergeSetHeader(dst *Header, src Header) {
688+func mergeSetHeader(dst *http.Header, src http.Header) {
689 if *dst == nil {
690 *dst = src
691 return
684692
685=== modified file 'http13client/transport.go'693=== modified file 'http13client/transport.go'
686--- http13client/transport.go 2014-03-19 20:20:19 +0000694--- http13client/transport.go 2014-06-20 11:00:47 +0000
687+++ http13client/transport.go 2014-03-19 22:27:37 +0000695+++ http13client/transport.go 2014-06-20 12:05:53 +0000
688@@ -18,6 +18,7 @@696@@ -18,6 +18,7 @@
689 "io"697 "io"
690 "log"698 "log"
@@ -693,7 +701,7 @@
693 "net/url"701 "net/url"
694 "os"702 "os"
695 "strings"703 "strings"
696@@ -144,12 +145,12 @@704@@ -147,12 +148,12 @@
697 // optional extra headers to write.705 // optional extra headers to write.
698 type transportRequest struct {706 type transportRequest struct {
699 *Request // original request, not to be mutated707 *Request // original request, not to be mutated
@@ -709,7 +717,7 @@
709 }717 }
710 return tr.extra718 return tr.extra
711 }719 }
712@@ -512,7 +513,7 @@720@@ -519,7 +520,7 @@
713 case cm.targetScheme == "http":721 case cm.targetScheme == "http":
714 pconn.isProxy = true722 pconn.isProxy = true
715 if pa != "" {723 if pa != "" {
@@ -718,7 +726,7 @@
718 h.Set("Proxy-Authorization", pa)726 h.Set("Proxy-Authorization", pa)
719 }727 }
720 }728 }
721@@ -521,7 +522,7 @@729@@ -528,7 +529,7 @@
722 Method: "CONNECT",730 Method: "CONNECT",
723 URL: &url.URL{Opaque: cm.targetAddr},731 URL: &url.URL{Opaque: cm.targetAddr},
724 Host: cm.targetAddr,732 Host: cm.targetAddr,
@@ -727,7 +735,7 @@
727 }735 }
728 if pa != "" {736 if pa != "" {
729 connectReq.Header.Set("Proxy-Authorization", pa)737 connectReq.Header.Set("Proxy-Authorization", pa)
730@@ -735,7 +736,7 @@738@@ -748,7 +749,7 @@
731 // mutateHeaderFunc is an optional func to modify extra739 // mutateHeaderFunc is an optional func to modify extra
732 // headers on each outbound request before it's written. (the740 // headers on each outbound request before it's written. (the
733 // original Request given to RoundTrip is not modified)741 // original Request given to RoundTrip is not modified)
@@ -735,5 +743,5 @@
735+ mutateHeaderFunc func(http.Header)743+ mutateHeaderFunc func(http.Header)
736 }744 }
737 745
738 func (pc *persistConn) isBroken() bool {746 // isBroken reports whether this connection is in a known broken state.
739747
740748
=== modified file 'http13client/_patches/fix_status.patch'
--- http13client/_patches/fix_status.patch 2014-03-19 23:43:25 +0000
+++ http13client/_patches/fix_status.patch 2014-06-20 13:24:39 +0000
@@ -1,7 +1,7 @@
1=== modified file 'http13client/client.go'1=== modified file 'http13client/client.go'
2--- http13client/client.go 2014-03-19 23:13:58 +00002--- http13client/client.go 2014-06-20 12:46:25 +0000
3+++ http13client/client.go 2014-03-19 23:38:11 +00003+++ http13client/client.go 2014-06-20 12:46:45 +0000
4@@ -210,7 +210,7 @@4@@ -217,7 +217,7 @@
5 // automatically redirect.5 // automatically redirect.
6 func shouldRedirectGet(statusCode int) bool {6 func shouldRedirectGet(statusCode int) bool {
7 switch statusCode {7 switch statusCode {
@@ -10,7 +10,7 @@
10 return true10 return true
11 }11 }
12 return false12 return false
13@@ -220,7 +220,7 @@13@@ -227,7 +227,7 @@
14 // automatically redirect.14 // automatically redirect.
15 func shouldRedirectPost(statusCode int) bool {15 func shouldRedirectPost(statusCode int) bool {
16 switch statusCode {16 switch statusCode {
@@ -21,9 +21,9 @@
21 return false21 return false
2222
23=== modified file 'http13client/client_test.go'23=== modified file 'http13client/client_test.go'
24--- http13client/client_test.go 2014-03-19 23:13:58 +000024--- http13client/client_test.go 2014-06-20 12:46:25 +0000
25+++ http13client/client_test.go 2014-03-19 23:39:48 +000025+++ http13client/client_test.go 2014-06-20 12:46:45 +0000
26@@ -202,7 +202,7 @@26@@ -204,7 +204,7 @@
27 }27 }
28 }28 }
29 if n < 15 {29 if n < 15 {
@@ -32,7 +32,7 @@
32 return32 return
33 }33 }
34 fmt.Fprintf(w, "n=%d", n)34 fmt.Fprintf(w, "n=%d", n)
35@@ -324,7 +324,7 @@35@@ -326,7 +326,7 @@
36 }36 }
37 if r.URL.Path == "/" {37 if r.URL.Path == "/" {
38 http.SetCookie(w, expectedCookies[1])38 http.SetCookie(w, expectedCookies[1])
@@ -41,7 +41,7 @@
41 } else {41 } else {
42 http.SetCookie(w, expectedCookies[2])42 http.SetCookie(w, expectedCookies[2])
43 w.Write([]byte("hello"))43 w.Write([]byte("hello"))
44@@ -783,7 +783,7 @@44@@ -785,7 +785,7 @@
45 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {45 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46 if r.URL.Path == "/" {46 if r.URL.Path == "/" {
47 sawRoot <- true47 sawRoot <- true
@@ -50,7 +50,7 @@
50 return50 return
51 }51 }
52 if r.URL.Path == "/slow" {52 if r.URL.Path == "/slow" {
53@@ -844,7 +844,7 @@53@@ -846,7 +846,7 @@
54 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {54 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
55 saw <- r.RemoteAddr55 saw <- r.RemoteAddr
56 if r.URL.Path == "/" {56 if r.URL.Path == "/" {
@@ -61,9 +61,9 @@
61 defer ts.Close()61 defer ts.Close()
6262
63=== modified file 'http13client/request_test.go'63=== modified file 'http13client/request_test.go'
64--- http13client/request_test.go 2014-03-19 23:13:58 +000064--- http13client/request_test.go 2014-06-20 12:46:25 +0000
65+++ http13client/request_test.go 2014-03-19 23:40:12 +000065+++ http13client/request_test.go 2014-06-20 12:46:45 +0000
66@@ -164,11 +164,11 @@66@@ -182,11 +182,11 @@
67 switch r.URL.Path {67 switch r.URL.Path {
68 case "/":68 case "/":
69 w.Header().Set("Location", "/foo/")69 w.Header().Set("Location", "/foo/")
@@ -79,9 +79,9 @@
79 defer ts.Close()79 defer ts.Close()
8080
81=== modified file 'http13client/response.go'81=== modified file 'http13client/response.go'
82--- http13client/response.go 2014-03-19 23:13:58 +000082--- http13client/response.go 2014-06-20 12:46:25 +0000
83+++ http13client/response.go 2014-03-19 23:38:56 +000083+++ http13client/response.go 2014-06-20 12:46:45 +0000
84@@ -204,9 +204,8 @@84@@ -205,9 +205,8 @@
85 // Status line85 // Status line
86 text := r.Status86 text := r.Status
87 if text == "" {87 if text == "" {
@@ -94,10 +94,23 @@
94 }94 }
95 }95 }
9696
97=== modified file 'http13client/responsewrite_test.go'
98--- http13client/responsewrite_test.go 2014-06-20 12:46:25 +0000
99+++ http13client/responsewrite_test.go 2014-06-20 12:47:05 +0000
100@@ -197,7 +197,7 @@
101 // there were two.
102 {
103 Response{
104- StatusCode: StatusOK,
105+ StatusCode: http.StatusOK,
106 ProtoMajor: 1,
107 ProtoMinor: 1,
108 Request: &Request{Method: "POST"},
109
97=== modified file 'http13client/transport_test.go'110=== modified file 'http13client/transport_test.go'
98--- http13client/transport_test.go 2014-03-19 23:13:58 +0000111--- http13client/transport_test.go 2014-06-20 12:46:25 +0000
99+++ http13client/transport_test.go 2014-03-19 23:40:39 +0000112+++ http13client/transport_test.go 2014-06-20 12:46:45 +0000
100@@ -968,7 +968,7 @@113@@ -1004,7 +1004,7 @@
101 defer afterTest(t)114 defer afterTest(t)
102 const deniedMsg = "sorry, denied."115 const deniedMsg = "sorry, denied."
103 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {116 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -106,7 +119,7 @@
106 }))119 }))
107 defer ts.Close()120 defer ts.Close()
108 tr := &Transport{}121 tr := &Transport{}
109@@ -992,7 +992,7 @@122@@ -1028,7 +1028,7 @@
110 func TestChunkedNoContent(t *testing.T) {123 func TestChunkedNoContent(t *testing.T) {
111 defer afterTest(t)124 defer afterTest(t)
112 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {125 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
113126
=== modified file 'http13client/_patches/fix_tests.patch'
--- http13client/_patches/fix_tests.patch 2014-03-19 23:13:58 +0000
+++ http13client/_patches/fix_tests.patch 2014-06-20 13:24:39 +0000
@@ -1,6 +1,6 @@
1=== modified file 'http13client/client_test.go'1=== modified file 'http13client/client_test.go'
2--- http13client/client_test.go 2014-03-19 21:38:56 +00002--- http13client/client_test.go 2014-06-20 11:00:47 +0000
3+++ http13client/client_test.go 2014-03-19 22:27:37 +00003+++ http13client/client_test.go 2014-06-20 12:05:53 +0000
4@@ -15,8 +15,8 @@4@@ -15,8 +15,8 @@
5 "fmt"5 "fmt"
6 "io"6 "io"
@@ -11,7 +11,7 @@
11 . "launchpad.net/ubuntu-push/http13client"11 . "launchpad.net/ubuntu-push/http13client"
12 "net/http/httptest"12 "net/http/httptest"
13 "net/url"13 "net/url"
14@@ -27,7 +27,7 @@14@@ -29,7 +29,7 @@
15 "time"15 "time"
16 )16 )
17 17
@@ -20,7 +20,7 @@
20 w.Header().Set("Last-Modified", "sometime")20 w.Header().Set("Last-Modified", "sometime")
21 fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")21 fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
22 })22 })
23@@ -193,7 +193,7 @@23@@ -195,7 +195,7 @@
24 func TestClientRedirects(t *testing.T) {24 func TestClientRedirects(t *testing.T) {
25 defer afterTest(t)25 defer afterTest(t)
26 var ts *httptest.Server26 var ts *httptest.Server
@@ -29,7 +29,7 @@
29 n, _ := strconv.Atoi(r.FormValue("n"))29 n, _ := strconv.Atoi(r.FormValue("n"))
30 // Test Referer header. (7 is arbitrary position to test at)30 // Test Referer header. (7 is arbitrary position to test at)
31 if n == 7 {31 if n == 7 {
32@@ -202,7 +202,7 @@32@@ -204,7 +204,7 @@
33 }33 }
34 }34 }
35 if n < 15 {35 if n < 15 {
@@ -38,7 +38,7 @@
38 return38 return
39 }39 }
40 fmt.Fprintf(w, "n=%d", n)40 fmt.Fprintf(w, "n=%d", n)
41@@ -271,7 +271,7 @@41@@ -273,7 +273,7 @@
42 bytes.Buffer42 bytes.Buffer
43 }43 }
44 var ts *httptest.Server44 var ts *httptest.Server
@@ -47,7 +47,7 @@
47 log.Lock()47 log.Lock()
48 fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)48 fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
49 log.Unlock()49 log.Unlock()
50@@ -312,21 +312,21 @@50@@ -314,21 +314,21 @@
51 }51 }
52 }52 }
53 53
@@ -75,7 +75,7 @@
75 w.Write([]byte("hello"))75 w.Write([]byte("hello"))
76 }76 }
77 })77 })
78@@ -334,7 +334,7 @@78@@ -336,7 +336,7 @@
79 func TestClientSendsCookieFromJar(t *testing.T) {79 func TestClientSendsCookieFromJar(t *testing.T) {
80 tr := &recordingTransport{}80 tr := &recordingTransport{}
81 client := &Client{Transport: tr}81 client := &Client{Transport: tr}
@@ -84,7 +84,7 @@
84 us := "http://dummy.faketld/"84 us := "http://dummy.faketld/"
85 u, _ := url.Parse(us)85 u, _ := url.Parse(us)
86 client.Jar.SetCookies(u, expectedCookies)86 client.Jar.SetCookies(u, expectedCookies)
87@@ -364,19 +364,19 @@87@@ -366,19 +366,19 @@
88 // scope of all cookies.88 // scope of all cookies.
89 type TestJar struct {89 type TestJar struct {
90 m sync.Mutex90 m sync.Mutex
@@ -108,7 +108,7 @@
108 j.m.Lock()108 j.m.Lock()
109 defer j.m.Unlock()109 defer j.m.Unlock()
110 return j.perURL[u.Host]110 return j.perURL[u.Host]
111@@ -391,7 +391,7 @@111@@ -393,7 +393,7 @@
112 Jar: new(TestJar),112 Jar: new(TestJar),
113 }113 }
114 u, _ := url.Parse(ts.URL)114 u, _ := url.Parse(ts.URL)
@@ -117,7 +117,7 @@
117 resp, err := c.Get(ts.URL)117 resp, err := c.Get(ts.URL)
118 if err != nil {118 if err != nil {
119 t.Fatalf("Get: %v", err)119 t.Fatalf("Get: %v", err)
120@@ -400,7 +400,7 @@120@@ -402,7 +402,7 @@
121 matchReturnedCookies(t, expectedCookies, resp.Cookies())121 matchReturnedCookies(t, expectedCookies, resp.Cookies())
122 }122 }
123 123
@@ -126,7 +126,7 @@
126 if len(given) != len(expected) {126 if len(given) != len(expected) {
127 t.Logf("Received cookies: %v", given)127 t.Logf("Received cookies: %v", given)
128 t.Errorf("Expected %d cookies, got %d", len(expected), len(given))128 t.Errorf("Expected %d cookies, got %d", len(expected), len(given))
129@@ -421,14 +421,14 @@129@@ -423,14 +423,14 @@
130 130
131 func TestJarCalls(t *testing.T) {131 func TestJarCalls(t *testing.T) {
132 defer afterTest(t)132 defer afterTest(t)
@@ -144,7 +144,7 @@
144 }144 }
145 }))145 }))
146 defer ts.Close()146 defer ts.Close()
147@@ -468,11 +468,11 @@147@@ -470,11 +470,11 @@
148 log bytes.Buffer148 log bytes.Buffer
149 }149 }
150 150
@@ -158,7 +158,7 @@
158 j.logf("Cookies(%q)\n", u)158 j.logf("Cookies(%q)\n", u)
159 return nil159 return nil
160 }160 }
161@@ -486,11 +486,11 @@161@@ -488,11 +488,11 @@
162 func TestStreamingGet(t *testing.T) {162 func TestStreamingGet(t *testing.T) {
163 defer afterTest(t)163 defer afterTest(t)
164 say := make(chan string)164 say := make(chan string)
@@ -173,7 +173,7 @@
173 }173 }
174 }))174 }))
175 defer ts.Close()175 defer ts.Close()
176@@ -536,7 +536,7 @@176@@ -538,7 +538,7 @@
177 // don't send a TCP packet per line of the http request + body.177 // don't send a TCP packet per line of the http request + body.
178 func TestClientWrites(t *testing.T) {178 func TestClientWrites(t *testing.T) {
179 defer afterTest(t)179 defer afterTest(t)
@@ -182,7 +182,7 @@
182 }))182 }))
183 defer ts.Close()183 defer ts.Close()
184 184
185@@ -568,46 +568,6 @@185@@ -570,46 +570,6 @@
186 }186 }
187 }187 }
188 188
@@ -229,7 +229,7 @@
229 func TestClientErrorWithRequestURI(t *testing.T) {229 func TestClientErrorWithRequestURI(t *testing.T) {
230 defer afterTest(t)230 defer afterTest(t)
231 req, _ := NewRequest("GET", "http://localhost:1234/", nil)231 req, _ := NewRequest("GET", "http://localhost:1234/", nil)
232@@ -639,7 +599,7 @@232@@ -641,7 +601,7 @@
233 233
234 func TestClientWithCorrectTLSServerName(t *testing.T) {234 func TestClientWithCorrectTLSServerName(t *testing.T) {
235 defer afterTest(t)235 defer afterTest(t)
@@ -238,7 +238,7 @@
238 if r.TLS.ServerName != "127.0.0.1" {238 if r.TLS.ServerName != "127.0.0.1" {
239 t.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r.TLS.ServerName)239 t.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r.TLS.ServerName)
240 }240 }
241@@ -652,33 +612,6 @@241@@ -654,33 +614,6 @@
242 }242 }
243 }243 }
244 244
@@ -272,7 +272,7 @@
272 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName272 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
273 // when not empty.273 // when not empty.
274 //274 //
275@@ -690,7 +623,7 @@275@@ -692,7 +625,7 @@
276 // The httptest.Server has a cert with "example.com" as its name.276 // The httptest.Server has a cert with "example.com" as its name.
277 func TestTransportUsesTLSConfigServerName(t *testing.T) {277 func TestTransportUsesTLSConfigServerName(t *testing.T) {
278 defer afterTest(t)278 defer afterTest(t)
@@ -281,7 +281,7 @@
281 w.Write([]byte("Hello"))281 w.Write([]byte("Hello"))
282 }))282 }))
283 defer ts.Close()283 defer ts.Close()
284@@ -711,7 +644,7 @@284@@ -713,7 +646,7 @@
285 285
286 func TestResponseSetsTLSConnectionState(t *testing.T) {286 func TestResponseSetsTLSConnectionState(t *testing.T) {
287 defer afterTest(t)287 defer afterTest(t)
@@ -290,7 +290,7 @@
290 w.Write([]byte("Hello"))290 w.Write([]byte("Hello"))
291 }))291 }))
292 defer ts.Close()292 defer ts.Close()
293@@ -739,7 +672,7 @@293@@ -741,7 +674,7 @@
294 // Verify Response.ContentLength is populated. http://golang.org/issue/4126294 // Verify Response.ContentLength is populated. http://golang.org/issue/4126
295 func TestClientHeadContentLength(t *testing.T) {295 func TestClientHeadContentLength(t *testing.T) {
296 defer afterTest(t)296 defer afterTest(t)
@@ -299,7 +299,7 @@
299 if v := r.FormValue("cl"); v != "" {299 if v := r.FormValue("cl"); v != "" {
300 w.Header().Set("Content-Length", v)300 w.Header().Set("Content-Length", v)
301 }301 }
302@@ -775,7 +708,7 @@302@@ -777,7 +710,7 @@
303 func TestEmptyPasswordAuth(t *testing.T) {303 func TestEmptyPasswordAuth(t *testing.T) {
304 defer afterTest(t)304 defer afterTest(t)
305 gopher := "gopher"305 gopher := "gopher"
@@ -308,7 +308,7 @@
308 auth := r.Header.Get("Authorization")308 auth := r.Header.Get("Authorization")
309 if strings.HasPrefix(auth, "Basic ") {309 if strings.HasPrefix(auth, "Basic ") {
310 encoded := auth[6:]310 encoded := auth[6:]
311@@ -847,15 +780,15 @@311@@ -849,15 +782,15 @@
312 defer afterTest(t)312 defer afterTest(t)
313 sawRoot := make(chan bool, 1)313 sawRoot := make(chan bool, 1)
314 sawSlow := make(chan bool, 1)314 sawSlow := make(chan bool, 1)
@@ -327,7 +327,7 @@
327 sawSlow <- true327 sawSlow <- true
328 time.Sleep(2 * time.Second)328 time.Sleep(2 * time.Second)
329 return329 return
330@@ -908,10 +841,10 @@330@@ -910,10 +843,10 @@
331 func TestClientRedirectEatsBody(t *testing.T) {331 func TestClientRedirectEatsBody(t *testing.T) {
332 defer afterTest(t)332 defer afterTest(t)
333 saw := make(chan string, 2)333 saw := make(chan string, 2)
@@ -340,233 +340,50 @@
340 }340 }
341 }))341 }))
342 defer ts.Close()342 defer ts.Close()
343343@@ -957,7 +890,7 @@
344=== modified file 'http13client/cookie_test.go'344
345--- http13client/cookie_test.go 2014-03-19 20:20:19 +0000345 func TestClientTrailers(t *testing.T) {
346+++ http13client/cookie_test.go 2014-03-19 22:27:37 +0000346 defer afterTest(t)
347@@ -9,6 +9,7 @@347- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
348 "encoding/json"348+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
349 "fmt"349 w.Header().Set("Connection", "close")
350 "log"350 w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
351+ "net/http"351 w.Header().Add("Trailer", "Server-Trailer-C")
352 "os"352@@ -992,9 +925,9 @@
353 "reflect"353 // trailers to be sent, if and only if they were
354 "strings"354 // previously declared with w.Header().Set("Trailer",
355@@ -17,39 +18,39 @@355 // ..keys..)
356 )356- w.(Flusher).Flush()
357 357- conn, buf, _ := w.(Hijacker).Hijack()
358 var writeSetCookiesTests = []struct {358- t := Header{}
359- Cookie *Cookie359+ w.(http.Flusher).Flush()
360+ Cookie *http.Cookie360+ conn, buf, _ := w.(http.Hijacker).Hijack()
361 Raw string361+ t := http.Header{}
362 }{362 t.Set("Server-Trailer-A", "valuea")
363 {363 t.Set("Server-Trailer-C", "valuec") // skipping B
364- &Cookie{Name: "cookie-1", Value: "v$1"},364 buf.WriteString("0\r\n") // eof
365+ &http.Cookie{Name: "cookie-1", Value: "v$1"},365@@ -1015,7 +948,7 @@
366 "cookie-1=v$1",366 req.Trailer["Client-Trailer-B"] = []string{"valueb"}
367 },367 }),
368 {368 ))
369- &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},369- req.Trailer = Header{
370+ &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},370+ req.Trailer = http.Header{
371 "cookie-2=two; Max-Age=3600",371 "Client-Trailer-A": nil, // to be set later
372 },372 "Client-Trailer-B": nil, // to be set later
373 {373 }
374- &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},374@@ -1027,7 +960,7 @@
375+ &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},375 if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil {
376 "cookie-3=three; Domain=example.com",376 t.Error(err)
377 },377 }
378 {378- want := Header{
379- &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},379+ want := http.Header{
380+ &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},380 "Server-Trailer-A": []string{"valuea"},
381 "cookie-4=four; Path=/restricted/",381 "Server-Trailer-B": nil,
382 },382 "Server-Trailer-C": []string{"valuec"},
383 {
384- &Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
385+ &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
386 "cookie-5=five",
387 },
388 {
389- &Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
390+ &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
391 "cookie-6=six",
392 },
393 {
394- &Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
395+ &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
396 "cookie-7=seven; Domain=127.0.0.1",
397 },
398 {
399- &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
400+ &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
401 "cookie-8=eight",
402 },
403 }
404@@ -71,10 +72,10 @@
405 }
406 }
407
408-type headerOnlyResponseWriter Header
409+type headerOnlyResponseWriter http.Header
410
411-func (ho headerOnlyResponseWriter) Header() Header {
412- return Header(ho)
413+func (ho headerOnlyResponseWriter) Header() http.Header {
414+ return http.Header(ho)
415 }
416
417 func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
418@@ -86,9 +87,9 @@
419 }
420
421 func TestSetCookie(t *testing.T) {
422- m := make(Header)
423- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
424- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
425+ m := make(http.Header)
426+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
427+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
428 if l := len(m["Set-Cookie"]); l != 2 {
429 t.Fatalf("expected %d cookies, got %d", 2, l)
430 }
431@@ -101,19 +102,19 @@
432 }
433
434 var addCookieTests = []struct {
435- Cookies []*Cookie
436+ Cookies []*http.Cookie
437 Raw string
438 }{
439 {
440- []*Cookie{},
441+ []*http.Cookie{},
442 "",
443 },
444 {
445- []*Cookie{{Name: "cookie-1", Value: "v$1"}},
446+ []*http.Cookie{{Name: "cookie-1", Value: "v$1"}},
447 "cookie-1=v$1",
448 },
449 {
450- []*Cookie{
451+ []*http.Cookie{
452 {Name: "cookie-1", Value: "v$1"},
453 {Name: "cookie-2", Value: "v$2"},
454 {Name: "cookie-3", Value: "v$3"},
455@@ -136,16 +137,16 @@
456 }
457
458 var readSetCookiesTests = []struct {
459- Header Header
460- Cookies []*Cookie
461+ Header http.Header
462+ Cookies []*http.Cookie
463 }{
464 {
465- Header{"Set-Cookie": {"Cookie-1=v$1"}},
466- []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
467+ http.Header{"Set-Cookie": {"Cookie-1=v$1"}},
468+ []*http.Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
469 },
470 {
471- Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
472- []*Cookie{{
473+ http.Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
474+ []*http.Cookie{{
475 Name: "NID",
476 Value: "99=YsDT5i3E-CXax-",
477 Path: "/",
478@@ -157,8 +158,8 @@
479 }},
480 },
481 {
482- Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
483- []*Cookie{{
484+ http.Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
485+ []*http.Cookie{{
486 Name: ".ASPXAUTH",
487 Value: "7E3AA",
488 Path: "/",
489@@ -169,8 +170,8 @@
490 }},
491 },
492 {
493- Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
494- []*Cookie{{
495+ http.Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
496+ []*http.Cookie{{
497 Name: "ASP.NET_SessionId",
498 Value: "foo",
499 Path: "/",
500@@ -207,37 +208,37 @@
501 }
502
503 var readCookiesTests = []struct {
504- Header Header
505+ Header http.Header
506 Filter string
507- Cookies []*Cookie
508+ Cookies []*http.Cookie
509 }{
510 {
511- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
512- "",
513- []*Cookie{
514- {Name: "Cookie-1", Value: "v$1"},
515- {Name: "c2", Value: "v2"},
516- },
517- },
518- {
519- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
520- "c2",
521- []*Cookie{
522- {Name: "c2", Value: "v2"},
523- },
524- },
525- {
526- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
527- "",
528- []*Cookie{
529- {Name: "Cookie-1", Value: "v$1"},
530- {Name: "c2", Value: "v2"},
531- },
532- },
533- {
534- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
535- "c2",
536- []*Cookie{
537+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
538+ "",
539+ []*http.Cookie{
540+ {Name: "Cookie-1", Value: "v$1"},
541+ {Name: "c2", Value: "v2"},
542+ },
543+ },
544+ {
545+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
546+ "c2",
547+ []*http.Cookie{
548+ {Name: "c2", Value: "v2"},
549+ },
550+ },
551+ {
552+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
553+ "",
554+ []*http.Cookie{
555+ {Name: "Cookie-1", Value: "v$1"},
556+ {Name: "c2", Value: "v2"},
557+ },
558+ },
559+ {
560+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
561+ "c2",
562+ []*http.Cookie{
563 {Name: "c2", Value: "v2"},
564 },
565 },
566383
567=== modified file 'http13client/export_test.go'384=== modified file 'http13client/export_test.go'
568--- http13client/export_test.go 2014-03-19 20:20:19 +0000385--- http13client/export_test.go 2014-06-20 11:00:47 +0000
569+++ http13client/export_test.go 2014-03-19 22:27:37 +0000386+++ http13client/export_test.go 2014-06-20 12:00:22 +0000
570@@ -9,15 +9,12 @@387@@ -9,15 +9,12 @@
571 388
572 import (389 import (
@@ -599,8 +416,8 @@
599 noProxyEnv.reset()416 noProxyEnv.reset()
600417
601=== modified file 'http13client/header_test.go'418=== modified file 'http13client/header_test.go'
602--- http13client/header_test.go 2014-03-19 20:20:19 +0000419--- http13client/header_test.go 2014-06-20 11:00:47 +0000
603+++ http13client/header_test.go 2014-03-19 22:27:37 +0000420+++ http13client/header_test.go 2014-06-20 12:00:22 +0000
604@@ -6,19 +6,20 @@421@@ -6,19 +6,20 @@
605 422
606 import (423 import (
@@ -731,8 +548,8 @@
731 }548 }
732549
733=== modified file 'http13client/npn_test.go'550=== modified file 'http13client/npn_test.go'
734--- http13client/npn_test.go 2014-03-19 21:38:56 +0000551--- http13client/npn_test.go 2014-06-20 11:00:47 +0000
735+++ http13client/npn_test.go 2014-03-19 22:27:37 +0000552+++ http13client/npn_test.go 2014-06-20 12:05:53 +0000
736@@ -11,13 +11,14 @@553@@ -11,13 +11,14 @@
737 "io"554 "io"
738 "io/ioutil"555 "io/ioutil"
@@ -792,8 +609,8 @@
792 func (w http09Writer) WriteHeader(int) {} // no headers609 func (w http09Writer) WriteHeader(int) {} // no headers
793610
794=== modified file 'http13client/readrequest_test.go'611=== modified file 'http13client/readrequest_test.go'
795--- http13client/readrequest_test.go 2014-03-19 20:20:19 +0000612--- http13client/readrequest_test.go 2014-06-20 11:00:47 +0000
796+++ http13client/readrequest_test.go 2014-03-19 22:27:37 +0000613+++ http13client/readrequest_test.go 2014-06-20 12:00:22 +0000
797@@ -9,6 +9,7 @@614@@ -9,6 +9,7 @@
798 "bytes"615 "bytes"
799 "fmt"616 "fmt"
@@ -909,8 +726,8 @@
909 Close: false,726 Close: false,
910727
911=== modified file 'http13client/request_test.go'728=== modified file 'http13client/request_test.go'
912--- http13client/request_test.go 2014-03-19 21:38:56 +0000729--- http13client/request_test.go 2014-06-20 11:00:47 +0000
913+++ http13client/request_test.go 2014-03-19 22:27:37 +0000730+++ http13client/request_test.go 2014-06-20 12:05:53 +0000
914@@ -12,6 +12,7 @@731@@ -12,6 +12,7 @@
915 "io/ioutil"732 "io/ioutil"
916 "mime/multipart"733 "mime/multipart"
@@ -945,8 +762,26 @@
945+ req.Header = http.Header{"Content-Type": {"text/plain"}}762+ req.Header = http.Header{"Content-Type": {"text/plain"}}
946 multipart, err = req.MultipartReader()763 multipart, err = req.MultipartReader()
947 if multipart != nil {764 if multipart != nil {
948 t.Errorf("unexpected multipart for text/plain")765 t.Error("unexpected multipart for text/plain")
949@@ -159,7 +160,7 @@766@@ -161,7 +162,7 @@
767 func TestParseMultipartForm(t *testing.T) {
768 req := &Request{
769 Method: "POST",
770- Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
771+ Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
772 Body: ioutil.NopCloser(new(bytes.Buffer)),
773 }
774 err := req.ParseMultipartForm(25)
775@@ -169,7 +170,7 @@
776 t.Error("expected multipart EOF, got nil")
777 }
778
779- req.Header = Header{"Content-Type": {"text/plain"}}
780+ req.Header = http.Header{"Content-Type": {"text/plain"}}
781 err = req.ParseMultipartForm(25)
782 if err != ErrNotMultipart {
783 t.Error("expected ErrNotMultipart for text/plain")
784@@ -177,7 +178,7 @@
950 }785 }
951 786
952 func TestRedirect(t *testing.T) {787 func TestRedirect(t *testing.T) {
@@ -957,8 +792,8 @@
957 w.Header().Set("Location", "/foo/")792 w.Header().Set("Location", "/foo/")
958793
959=== modified file 'http13client/requestwrite_test.go'794=== modified file 'http13client/requestwrite_test.go'
960--- http13client/requestwrite_test.go 2014-03-19 20:20:19 +0000795--- http13client/requestwrite_test.go 2014-06-20 11:00:47 +0000
961+++ http13client/requestwrite_test.go 2014-03-19 22:27:37 +0000796+++ http13client/requestwrite_test.go 2014-06-20 12:00:22 +0000
962@@ -10,6 +10,7 @@797@@ -10,6 +10,7 @@
963 "fmt"798 "fmt"
964 "io"799 "io"
@@ -1068,8 +903,8 @@
1068 var braw bytes.Buffer903 var braw bytes.Buffer
1069904
1070=== modified file 'http13client/response_test.go'905=== modified file 'http13client/response_test.go'
1071--- http13client/response_test.go 2014-03-19 20:20:19 +0000906--- http13client/response_test.go 2014-06-20 11:00:47 +0000
1072+++ http13client/response_test.go 2014-03-19 22:27:37 +0000907+++ http13client/response_test.go 2014-06-20 12:05:53 +0000
1073@@ -12,6 +12,7 @@908@@ -12,6 +12,7 @@
1074 "fmt"909 "fmt"
1075 "io"910 "io"
@@ -1078,7 +913,7 @@
1078 "net/url"913 "net/url"
1079 "reflect"914 "reflect"
1080 "regexp"915 "regexp"
1081@@ -44,7 +45,7 @@916@@ -48,7 +49,7 @@
1082 ProtoMajor: 1,917 ProtoMajor: 1,
1083 ProtoMinor: 0,918 ProtoMinor: 0,
1084 Request: dummyReq("GET"),919 Request: dummyReq("GET"),
@@ -1087,7 +922,7 @@
1087 "Connection": {"close"}, // TODO(rsc): Delete?922 "Connection": {"close"}, // TODO(rsc): Delete?
1088 },923 },
1089 Close: true,924 Close: true,
1090@@ -67,7 +68,7 @@925@@ -71,7 +72,7 @@
1091 Proto: "HTTP/1.1",926 Proto: "HTTP/1.1",
1092 ProtoMajor: 1,927 ProtoMajor: 1,
1093 ProtoMinor: 1,928 ProtoMinor: 1,
@@ -1096,7 +931,7 @@
1096 Request: dummyReq("GET"),931 Request: dummyReq("GET"),
1097 Close: true,932 Close: true,
1098 ContentLength: -1,933 ContentLength: -1,
1099@@ -88,7 +89,7 @@934@@ -92,7 +93,7 @@
1100 Proto: "HTTP/1.1",935 Proto: "HTTP/1.1",
1101 ProtoMajor: 1,936 ProtoMajor: 1,
1102 ProtoMinor: 1,937 ProtoMinor: 1,
@@ -1105,7 +940,7 @@
1105 Request: dummyReq("GET"),940 Request: dummyReq("GET"),
1106 Close: false,941 Close: false,
1107 ContentLength: 0,942 ContentLength: 0,
1108@@ -112,7 +113,7 @@943@@ -116,7 +117,7 @@
1109 ProtoMajor: 1,944 ProtoMajor: 1,
1110 ProtoMinor: 0,945 ProtoMinor: 0,
1111 Request: dummyReq("GET"),946 Request: dummyReq("GET"),
@@ -1114,61 +949,61 @@
1114 "Connection": {"close"},949 "Connection": {"close"},
1115 "Content-Length": {"10"},950 "Content-Length": {"10"},
1116 },951 },
1117@@ -142,7 +143,7 @@952@@ -146,7 +147,7 @@
1118 ProtoMajor: 1,953 ProtoMajor: 1,
1119 ProtoMinor: 1,954 ProtoMinor: 1,
1120 Request: dummyReq("GET"),955 Request: dummyReq("GET"),
1121- Header: Header{},956- Header: Header{},
1122+ Header: http.Header{},957+ Header: http.Header{},
1123 Close: false,958 Close: false,
1124 ContentLength: -1,959 ContentLength: -1,
1125 TransferEncoding: []string{"chunked"},960 TransferEncoding: []string{"chunked"},
1126@@ -169,7 +170,7 @@961@@ -173,7 +174,7 @@
1127 ProtoMajor: 1,962 ProtoMajor: 1,
1128 ProtoMinor: 1,963 ProtoMinor: 1,
1129 Request: dummyReq("GET"),964 Request: dummyReq("GET"),
1130- Header: Header{},965- Header: Header{},
1131+ Header: http.Header{},966+ Header: http.Header{},
1132 Close: false,967 Close: false,
1133 ContentLength: -1,968 ContentLength: -1,
1134 TransferEncoding: []string{"chunked"},969 TransferEncoding: []string{"chunked"},
1135@@ -191,7 +192,7 @@970@@ -195,7 +196,7 @@
1136 ProtoMajor: 1,971 ProtoMajor: 1,
1137 ProtoMinor: 1,972 ProtoMinor: 1,
1138 Request: dummyReq("HEAD"),973 Request: dummyReq("HEAD"),
1139- Header: Header{},974- Header: Header{},
1140+ Header: http.Header{},975+ Header: http.Header{},
1141 TransferEncoding: []string{"chunked"},976 TransferEncoding: []string{"chunked"},
1142 Close: false,977 Close: false,
1143 ContentLength: -1,978 ContentLength: -1,
1144@@ -213,7 +214,7 @@979@@ -217,7 +218,7 @@
1145 ProtoMajor: 1,980 ProtoMajor: 1,
1146 ProtoMinor: 0,981 ProtoMinor: 0,
1147 Request: dummyReq("HEAD"),982 Request: dummyReq("HEAD"),
1148- Header: Header{"Content-Length": {"256"}},983- Header: Header{"Content-Length": {"256"}},
1149+ Header: http.Header{"Content-Length": {"256"}},984+ Header: http.Header{"Content-Length": {"256"}},
1150 TransferEncoding: nil,985 TransferEncoding: nil,
1151 Close: true,986 Close: true,
1152 ContentLength: 256,987 ContentLength: 256,
1153@@ -235,7 +236,7 @@988@@ -239,7 +240,7 @@
1154 ProtoMajor: 1,989 ProtoMajor: 1,
1155 ProtoMinor: 1,990 ProtoMinor: 1,
1156 Request: dummyReq("HEAD"),991 Request: dummyReq("HEAD"),
1157- Header: Header{"Content-Length": {"256"}},992- Header: Header{"Content-Length": {"256"}},
1158+ Header: http.Header{"Content-Length": {"256"}},993+ Header: http.Header{"Content-Length": {"256"}},
1159 TransferEncoding: nil,994 TransferEncoding: nil,
1160 Close: false,995 Close: false,
1161 ContentLength: 256,996 ContentLength: 256,
1162@@ -256,7 +257,7 @@997@@ -260,7 +261,7 @@
1163 ProtoMajor: 1,998 ProtoMajor: 1,
1164 ProtoMinor: 0,999 ProtoMinor: 0,
1165 Request: dummyReq("HEAD"),1000 Request: dummyReq("HEAD"),
1166- Header: Header{},1001- Header: Header{},
1167+ Header: http.Header{},1002+ Header: http.Header{},
1168 TransferEncoding: nil,1003 TransferEncoding: nil,
1169 Close: true,1004 Close: true,
1170 ContentLength: -1,1005 ContentLength: -1,
1171@@ -278,7 +279,7 @@1006@@ -282,7 +283,7 @@
1172 ProtoMajor: 1,1007 ProtoMajor: 1,
1173 ProtoMinor: 1,1008 ProtoMinor: 1,
1174 Request: dummyReq("GET"),1009 Request: dummyReq("GET"),
@@ -1177,25 +1012,25 @@
1177 "Content-Length": {"0"},1012 "Content-Length": {"0"},
1178 },1013 },
1179 Close: false,1014 Close: false,
1180@@ -299,7 +300,7 @@1015@@ -303,7 +304,7 @@
1181 ProtoMajor: 1,1016 ProtoMajor: 1,
1182 ProtoMinor: 0,1017 ProtoMinor: 0,
1183 Request: dummyReq("GET"),1018 Request: dummyReq("GET"),
1184- Header: Header{},1019- Header: Header{},
1185+ Header: http.Header{},1020+ Header: http.Header{},
1186 Close: true,1021 Close: true,
1187 ContentLength: -1,1022 ContentLength: -1,
1188 },1023 },
1189@@ -318,7 +319,7 @@1024@@ -322,7 +323,7 @@
1190 ProtoMajor: 1,1025 ProtoMajor: 1,
1191 ProtoMinor: 0,1026 ProtoMinor: 0,
1192 Request: dummyReq("GET"),1027 Request: dummyReq("GET"),
1193- Header: Header{},1028- Header: Header{},
1194+ Header: http.Header{},1029+ Header: http.Header{},
1195 Close: true,1030 Close: true,
1196 ContentLength: -1,1031 ContentLength: -1,
1197 },1032 },
1198@@ -340,7 +341,7 @@1033@@ -344,7 +345,7 @@
1199 ProtoMajor: 1,1034 ProtoMajor: 1,
1200 ProtoMinor: 1,1035 ProtoMinor: 1,
1201 Request: dummyReq("GET"),1036 Request: dummyReq("GET"),
@@ -1204,7 +1039,7 @@
1204 "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},1039 "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},
1205 },1040 },
1206 Close: true,1041 Close: true,
1207@@ -363,7 +364,7 @@1042@@ -367,7 +368,7 @@
1208 Proto: "HTTP/1.0",1043 Proto: "HTTP/1.0",
1209 ProtoMajor: 1,1044 ProtoMajor: 1,
1210 ProtoMinor: 0,1045 ProtoMinor: 0,
@@ -1213,7 +1048,7 @@
1213 "Connection": {"close"}, // TODO(rsc): Delete?1048 "Connection": {"close"}, // TODO(rsc): Delete?
1214 },1049 },
1215 Close: true,1050 Close: true,
1216@@ -545,7 +546,7 @@1051@@ -549,7 +550,7 @@
1217 func TestLocationResponse(t *testing.T) {1052 func TestLocationResponse(t *testing.T) {
1218 for i, tt := range responseLocationTests {1053 for i, tt := range responseLocationTests {
1219 res := new(Response)1054 res := new(Response)
@@ -1222,7 +1057,7 @@
1222 res.Header.Set("Location", tt.location)1057 res.Header.Set("Location", tt.location)
1223 if tt.requrl != "" {1058 if tt.requrl != "" {
1224 res.Request = &Request{}1059 res.Request = &Request{}
1225@@ -626,16 +627,3 @@1060@@ -630,16 +631,3 @@
1226 t.Errorf("ReadResponse = %v; want io.ErrUnexpectedEOF", err)1061 t.Errorf("ReadResponse = %v; want io.ErrUnexpectedEOF", err)
1227 }1062 }
1228 }1063 }
@@ -1241,8 +1076,8 @@
1241-}1076-}
12421077
1243=== modified file 'http13client/responsewrite_test.go'1078=== modified file 'http13client/responsewrite_test.go'
1244--- http13client/responsewrite_test.go 2014-03-19 20:20:19 +00001079--- http13client/responsewrite_test.go 2014-06-20 11:00:47 +0000
1245+++ http13client/responsewrite_test.go 2014-03-19 22:27:37 +00001080+++ http13client/responsewrite_test.go 2014-06-20 12:05:53 +0000
1246@@ -7,6 +7,7 @@1081@@ -7,6 +7,7 @@
1247 import (1082 import (
1248 "bytes"1083 "bytes"
@@ -1257,7 +1092,7 @@
1257 Request: dummyReq("GET"),1092 Request: dummyReq("GET"),
1258- Header: Header{},1093- Header: Header{},
1259+ Header: http.Header{},1094+ Header: http.Header{},
1260 Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),1095 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1261 ContentLength: 6,1096 ContentLength: 6,
1262 },1097 },
1263@@ -41,7 +42,7 @@1098@@ -41,7 +42,7 @@
@@ -1270,6 +1105,60 @@
1270 ContentLength: -1,1105 ContentLength: -1,
1271 },1106 },
1272@@ -56,7 +57,7 @@1107@@ -56,7 +57,7 @@
1108 ProtoMajor: 1,
1109 ProtoMinor: 1,
1110 Request: dummyReq("GET"),
1111- Header: Header{},
1112+ Header: http.Header{},
1113 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1114 ContentLength: -1,
1115 Close: true,
1116@@ -73,7 +74,7 @@
1117 ProtoMajor: 1,
1118 ProtoMinor: 1,
1119 Request: dummyReq11("GET"),
1120- Header: Header{},
1121+ Header: http.Header{},
1122 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1123 ContentLength: -1,
1124 Close: false,
1125@@ -91,7 +92,7 @@
1126 ProtoMajor: 1,
1127 ProtoMinor: 1,
1128 Request: dummyReq11("GET"),
1129- Header: Header{},
1130+ Header: http.Header{},
1131 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1132 ContentLength: -1,
1133 TransferEncoding: []string{"chunked"},
1134@@ -108,7 +109,7 @@
1135 ProtoMajor: 1,
1136 ProtoMinor: 1,
1137 Request: dummyReq11("GET"),
1138- Header: Header{},
1139+ Header: http.Header{},
1140 Body: nil,
1141 ContentLength: 0,
1142 Close: false,
1143@@ -124,7 +125,7 @@
1144 ProtoMajor: 1,
1145 ProtoMinor: 1,
1146 Request: dummyReq11("GET"),
1147- Header: Header{},
1148+ Header: http.Header{},
1149 Body: ioutil.NopCloser(strings.NewReader("")),
1150 ContentLength: 0,
1151 Close: false,
1152@@ -140,7 +141,7 @@
1153 ProtoMajor: 1,
1154 ProtoMinor: 1,
1155 Request: dummyReq11("GET"),
1156- Header: Header{},
1157+ Header: http.Header{},
1158 Body: ioutil.NopCloser(strings.NewReader("foo")),
1159 ContentLength: 0,
1160 Close: false,
1161@@ -156,7 +157,7 @@
1273 ProtoMajor: 1,1162 ProtoMajor: 1,
1274 ProtoMinor: 1,1163 ProtoMinor: 1,
1275 Request: dummyReq("GET"),1164 Request: dummyReq("GET"),
@@ -1278,7 +1167,7 @@
1278 Body: ioutil.NopCloser(strings.NewReader("abcdef")),1167 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1279 ContentLength: 6,1168 ContentLength: 6,
1280 TransferEncoding: []string{"chunked"},1169 TransferEncoding: []string{"chunked"},
1281@@ -77,7 +78,7 @@1170@@ -177,7 +178,7 @@
1282 ProtoMajor: 1,1171 ProtoMajor: 1,
1283 ProtoMinor: 1,1172 ProtoMinor: 1,
1284 Request: dummyReq("GET"),1173 Request: dummyReq("GET"),
@@ -1287,11 +1176,20 @@
1287 "Foo": []string{" Bar\nBaz "},1176 "Foo": []string{" Bar\nBaz "},
1288 },1177 },
1289 Body: nil,1178 Body: nil,
1179@@ -200,7 +201,7 @@
1180 ProtoMajor: 1,
1181 ProtoMinor: 1,
1182 Request: &Request{Method: "POST"},
1183- Header: Header{},
1184+ Header: http.Header{},
1185 ContentLength: 0,
1186 TransferEncoding: nil,
1187 Body: nil,
12901188
1291=== modified file 'http13client/transport_test.go'1189=== modified file 'http13client/transport_test.go'
1292--- http13client/transport_test.go 2014-03-19 21:38:56 +00001190--- http13client/transport_test.go 2014-06-20 11:00:47 +0000
1293+++ http13client/transport_test.go 2014-03-19 22:27:37 +00001191+++ http13client/transport_test.go 2014-06-20 12:05:53 +0000
1294@@ -17,8 +17,8 @@1192@@ -18,8 +18,8 @@
1295 "io/ioutil"1193 "io/ioutil"
1296 "log"1194 "log"
1297 "net"1195 "net"
@@ -1301,7 +1199,7 @@
1301 "net/http/httptest"1199 "net/http/httptest"
1302 "net/url"1200 "net/url"
1303 "os"1201 "os"
1304@@ -34,7 +34,7 @@1202@@ -35,7 +35,7 @@
1305 // and then verify that the final 2 responses get errors back.1203 // and then verify that the final 2 responses get errors back.
1306 1204
1307 // hostPortHandler writes back the client's "host:port".1205 // hostPortHandler writes back the client's "host:port".
@@ -1310,7 +1208,7 @@
1310 if r.FormValue("close") == "true" {1208 if r.FormValue("close") == "true" {
1311 w.Header().Set("Connection", "close")1209 w.Header().Set("Connection", "close")
1312 }1210 }
1313@@ -280,7 +280,7 @@1211@@ -289,7 +289,7 @@
1314 const msg = "foobar"1212 const msg = "foobar"
1315 1213
1316 var addrSeen map[string]int1214 var addrSeen map[string]int
@@ -1319,7 +1217,7 @@
1319 addrSeen[r.RemoteAddr]++1217 addrSeen[r.RemoteAddr]++
1320 if r.URL.Path == "/chunked/" {1218 if r.URL.Path == "/chunked/" {
1321 w.WriteHeader(200)1219 w.WriteHeader(200)
1322@@ -299,7 +299,7 @@1220@@ -308,7 +308,7 @@
1323 wantLen := []int{len(msg), -1}[pi]1221 wantLen := []int{len(msg), -1}[pi]
1324 addrSeen = make(map[string]int)1222 addrSeen = make(map[string]int)
1325 for i := 0; i < 3; i++ {1223 for i := 0; i < 3; i++ {
@@ -1328,7 +1226,7 @@
1328 if err != nil {1226 if err != nil {
1329 t.Errorf("Get %s: %v", path, err)1227 t.Errorf("Get %s: %v", path, err)
1330 continue1228 continue
1331@@ -329,7 +329,7 @@1229@@ -338,7 +338,7 @@
1332 defer afterTest(t)1230 defer afterTest(t)
1333 resch := make(chan string)1231 resch := make(chan string)
1334 gotReq := make(chan bool)1232 gotReq := make(chan bool)
@@ -1337,7 +1235,7 @@
1337 gotReq <- true1235 gotReq <- true
1338 msg := <-resch1236 msg := <-resch
1339 _, err := w.Write([]byte(msg))1237 _, err := w.Write([]byte(msg))
1340@@ -457,12 +457,12 @@1238@@ -466,12 +466,12 @@
1341 if testing.Short() {1239 if testing.Short() {
1342 t.Skip("skipping test in short mode")1240 t.Skip("skipping test in short mode")
1343 }1241 }
@@ -1353,7 +1251,7 @@
1353 buf.Flush()1251 buf.Flush()
1354 conn.Close()1252 conn.Close()
1355 }))1253 }))
1356@@ -510,7 +510,7 @@1254@@ -519,7 +519,7 @@
1357 // with no bodies properly1255 // with no bodies properly
1358 func TestTransportHeadResponses(t *testing.T) {1256 func TestTransportHeadResponses(t *testing.T) {
1359 defer afterTest(t)1257 defer afterTest(t)
@@ -1362,7 +1260,7 @@
1362 if r.Method != "HEAD" {1260 if r.Method != "HEAD" {
1363 panic("expected HEAD; got " + r.Method)1261 panic("expected HEAD; got " + r.Method)
1364 }1262 }
1365@@ -545,7 +545,7 @@1263@@ -554,7 +554,7 @@
1366 // on responses to HEAD requests.1264 // on responses to HEAD requests.
1367 func TestTransportHeadChunkedResponse(t *testing.T) {1265 func TestTransportHeadChunkedResponse(t *testing.T) {
1368 defer afterTest(t)1266 defer afterTest(t)
@@ -1371,7 +1269,7 @@
1371 if r.Method != "HEAD" {1269 if r.Method != "HEAD" {
1372 panic("expected HEAD; got " + r.Method)1270 panic("expected HEAD; got " + r.Method)
1373 }1271 }
1374@@ -588,7 +588,7 @@1272@@ -597,7 +597,7 @@
1375 func TestRoundTripGzip(t *testing.T) {1273 func TestRoundTripGzip(t *testing.T) {
1376 defer afterTest(t)1274 defer afterTest(t)
1377 const responseBody = "test response body"1275 const responseBody = "test response body"
@@ -1380,7 +1278,7 @@
1380 accept := req.Header.Get("Accept-Encoding")1278 accept := req.Header.Get("Accept-Encoding")
1381 if expect := req.FormValue("expect_accept"); accept != expect {1279 if expect := req.FormValue("expect_accept"); accept != expect {
1382 t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",1280 t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",
1383@@ -647,7 +647,7 @@1281@@ -656,7 +656,7 @@
1384 defer afterTest(t)1282 defer afterTest(t)
1385 const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"1283 const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1386 const nRandBytes = 1024 * 10241284 const nRandBytes = 1024 * 1024
@@ -1389,7 +1287,7 @@
1389 if req.Method == "HEAD" {1287 if req.Method == "HEAD" {
1390 if g := req.Header.Get("Accept-Encoding"); g != "" {1288 if g := req.Header.Get("Accept-Encoding"); g != "" {
1391 t.Errorf("HEAD request sent with Accept-Encoding of %q; want none", g)1289 t.Errorf("HEAD request sent with Accept-Encoding of %q; want none", g)
1392@@ -742,11 +742,11 @@1290@@ -751,11 +751,11 @@
1393 func TestTransportProxy(t *testing.T) {1291 func TestTransportProxy(t *testing.T) {
1394 defer afterTest(t)1292 defer afterTest(t)
1395 ch := make(chan string, 1)1293 ch := make(chan string, 1)
@@ -1403,7 +1301,7 @@
1403 ch <- "proxy for " + r.URL.String()1301 ch <- "proxy for " + r.URL.String()
1404 }))1302 }))
1405 defer proxy.Close()1303 defer proxy.Close()
1406@@ -770,7 +770,7 @@1304@@ -779,7 +779,7 @@
1407 // Content-Encoding is removed.1305 // Content-Encoding is removed.
1408 func TestTransportGzipRecursive(t *testing.T) {1306 func TestTransportGzipRecursive(t *testing.T) {
1409 defer afterTest(t)1307 defer afterTest(t)
@@ -1412,7 +1310,16 @@
1412 w.Header().Set("Content-Encoding", "gzip")1310 w.Header().Set("Content-Encoding", "gzip")
1413 w.Write(rgz)1311 w.Write(rgz)
1414 }))1312 }))
1415@@ -802,7 +802,7 @@1313@@ -807,7 +807,7 @@
1314 // a short gzip body
1315 func TestTransportGzipShort(t *testing.T) {
1316 defer afterTest(t)
1317- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1318+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1319 w.Header().Set("Content-Encoding", "gzip")
1320 w.Write([]byte{0x1f, 0x8b})
1321 }))
1322@@ -838,7 +838,7 @@
1416 defer afterTest(t)1323 defer afterTest(t)
1417 gotReqCh := make(chan bool)1324 gotReqCh := make(chan bool)
1418 unblockCh := make(chan bool)1325 unblockCh := make(chan bool)
@@ -1421,7 +1328,7 @@
1421 gotReqCh <- true1328 gotReqCh <- true
1422 <-unblockCh1329 <-unblockCh
1423 w.Header().Set("Content-Length", "0")1330 w.Header().Set("Content-Length", "0")
1424@@ -869,7 +869,7 @@1331@@ -905,7 +905,7 @@
1425 t.Skip("skipping test; see http://golang.org/issue/7237")1332 t.Skip("skipping test; see http://golang.org/issue/7237")
1426 }1333 }
1427 defer afterTest(t)1334 defer afterTest(t)
@@ -1430,7 +1337,7 @@
1430 }))1337 }))
1431 defer ts.Close()1338 defer ts.Close()
1432 1339
1433@@ -912,7 +912,7 @@1340@@ -948,7 +948,7 @@
1434 c := &Client{Transport: tr}1341 c := &Client{Transport: tr}
1435 1342
1436 unblockCh := make(chan bool, 1)1343 unblockCh := make(chan bool, 1)
@@ -1439,7 +1346,7 @@
1439 <-unblockCh1346 <-unblockCh
1440 tr.CloseIdleConnections()1347 tr.CloseIdleConnections()
1441 }))1348 }))
1442@@ -939,7 +939,7 @@1349@@ -975,7 +975,7 @@
1443 func TestIssue3644(t *testing.T) {1350 func TestIssue3644(t *testing.T) {
1444 defer afterTest(t)1351 defer afterTest(t)
1445 const numFoos = 50001352 const numFoos = 5000
@@ -1448,7 +1355,7 @@
1448 w.Header().Set("Connection", "close")1355 w.Header().Set("Connection", "close")
1449 for i := 0; i < numFoos; i++ {1356 for i := 0; i < numFoos; i++ {
1450 w.Write([]byte("foo "))1357 w.Write([]byte("foo "))
1451@@ -967,8 +967,8 @@1358@@ -1003,8 +1003,8 @@
1452 func TestIssue3595(t *testing.T) {1359 func TestIssue3595(t *testing.T) {
1453 defer afterTest(t)1360 defer afterTest(t)
1454 const deniedMsg = "sorry, denied."1361 const deniedMsg = "sorry, denied."
@@ -1459,7 +1366,7 @@
1459 }))1366 }))
1460 defer ts.Close()1367 defer ts.Close()
1461 tr := &Transport{}1368 tr := &Transport{}
1462@@ -991,7 +991,7 @@1369@@ -1027,7 +1027,7 @@
1463 // "client fails to handle requests with no body and chunked encoding"1370 // "client fails to handle requests with no body and chunked encoding"
1464 func TestChunkedNoContent(t *testing.T) {1371 func TestChunkedNoContent(t *testing.T) {
1465 defer afterTest(t)1372 defer afterTest(t)
@@ -1468,7 +1375,7 @@
1468 w.WriteHeader(StatusNoContent)1375 w.WriteHeader(StatusNoContent)
1469 }))1376 }))
1470 defer ts.Close()1377 defer ts.Close()
1471@@ -1019,7 +1019,7 @@1378@@ -1055,7 +1055,7 @@
1472 maxProcs, numReqs = 4, 501379 maxProcs, numReqs = 4, 50
1473 }1380 }
1474 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))1381 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
@@ -1477,7 +1384,7 @@
1477 fmt.Fprintf(w, "%v", r.FormValue("echo"))1384 fmt.Fprintf(w, "%v", r.FormValue("echo"))
1478 }))1385 }))
1479 defer ts.Close()1386 defer ts.Close()
1480@@ -1080,8 +1080,8 @@1387@@ -1116,8 +1116,8 @@
1481 }1388 }
1482 defer afterTest(t)1389 defer afterTest(t)
1483 const debug = false1390 const debug = false
@@ -1488,7 +1395,7 @@
1488 io.Copy(w, neverEnding('a'))1395 io.Copy(w, neverEnding('a'))
1489 })1396 })
1490 ts := httptest.NewServer(mux)1397 ts := httptest.NewServer(mux)
1491@@ -1144,11 +1144,11 @@1398@@ -1180,11 +1180,11 @@
1492 }1399 }
1493 defer afterTest(t)1400 defer afterTest(t)
1494 const debug = false1401 const debug = false
@@ -1503,20 +1410,22 @@
1503 defer r.Body.Close()1410 defer r.Body.Close()
1504 io.Copy(ioutil.Discard, r.Body)1411 io.Copy(ioutil.Discard, r.Body)
1505 })1412 })
1506@@ -1214,9 +1214,9 @@1413@@ -1251,11 +1251,11 @@
1507 if testing.Short() {
1508 t.Skip("skipping timeout test in -short mode")1414 t.Skip("skipping timeout test in -short mode")
1509 }1415 }
1416 inHandler := make(chan bool, 1)
1510- mux := NewServeMux()1417- mux := NewServeMux()
1511- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {})1418- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {
1419+ mux := http.NewServeMux()
1420+ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {
1421 inHandler <- true
1422 })
1512- mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {1423- mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {
1513+ mux := http.NewServeMux()
1514+ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {})
1515+ mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {1424+ mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
1425 inHandler <- true
1516 time.Sleep(2 * time.Second)1426 time.Sleep(2 * time.Second)
1517 })1427 })
1518 ts := httptest.NewServer(mux)1428@@ -1322,9 +1322,9 @@
1519@@ -1276,9 +1276,9 @@
1520 t.Skip("skipping test in -short mode")1429 t.Skip("skipping test in -short mode")
1521 }1430 }
1522 unblockc := make(chan bool)1431 unblockc := make(chan bool)
@@ -1528,7 +1437,7 @@
1528 <-unblockc1437 <-unblockc
1529 }))1438 }))
1530 defer ts.Close()1439 defer ts.Close()
1531@@ -1386,14 +1386,14 @@1440@@ -1431,14 +1431,14 @@
1532 defer afterTest(t)1441 defer afterTest(t)
1533 writeErr := make(chan error, 1)1442 writeErr := make(chan error, 1)
1534 msg := []byte("young\n")1443 msg := []byte("young\n")
@@ -1545,7 +1454,7 @@
1545 }1454 }
1546 }))1455 }))
1547 defer ts.Close()1456 defer ts.Close()
1548@@ -1449,7 +1449,7 @@1457@@ -1494,7 +1494,7 @@
1549 res := &Response{1458 res := &Response{
1550 Status: "200 OK",1459 Status: "200 OK",
1551 StatusCode: 200,1460 StatusCode: 200,
@@ -1554,7 +1463,7 @@
1554 Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),1463 Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),
1555 }1464 }
1556 return res, nil1465 return res, nil
1557@@ -1478,7 +1478,7 @@1466@@ -1523,7 +1523,7 @@
1558 defer afterTest(t)1467 defer afterTest(t)
1559 tr := &Transport{}1468 tr := &Transport{}
1560 _, err := tr.RoundTrip(&Request{1469 _, err := tr.RoundTrip(&Request{
@@ -1563,7 +1472,7 @@
1563 URL: &url.URL{1472 URL: &url.URL{
1564 Scheme: "http",1473 Scheme: "http",
1565 },1474 },
1566@@ -1492,14 +1492,14 @@1475@@ -1537,14 +1537,14 @@
1567 func TestTransportSocketLateBinding(t *testing.T) {1476 func TestTransportSocketLateBinding(t *testing.T) {
1568 defer afterTest(t)1477 defer afterTest(t)
1569 1478
@@ -1582,7 +1491,7 @@
1582 w.Header().Set("bar-ipport", r.RemoteAddr)1491 w.Header().Set("bar-ipport", r.RemoteAddr)
1583 })1492 })
1584 ts := httptest.NewServer(mux)1493 ts := httptest.NewServer(mux)
1585@@ -1720,7 +1720,7 @@1494@@ -1767,7 +1767,7 @@
1586 var mu sync.Mutex1495 var mu sync.Mutex
1587 var n int1496 var n int
1588 1497
@@ -1591,7 +1500,7 @@
1591 mu.Lock()1500 mu.Lock()
1592 n++1501 n++
1593 mu.Unlock()1502 mu.Unlock()
1594@@ -1756,7 +1756,7 @@1503@@ -1803,7 +1803,7 @@
1595 // then closes it.1504 // then closes it.
1596 func TestTransportClosesRequestBody(t *testing.T) {1505 func TestTransportClosesRequestBody(t *testing.T) {
1597 defer afterTest(t)1506 defer afterTest(t)
@@ -1600,4 +1509,49 @@
1600 io.Copy(ioutil.Discard, r.Body)1509 io.Copy(ioutil.Discard, r.Body)
1601 }))1510 }))
1602 defer ts.Close()1511 defer ts.Close()
1512@@ -1890,9 +1890,9 @@
1513 t.Skip("skipping flaky test on Windows; golang.org/issue/7634")
1514 }
1515 closedc := make(chan bool, 1)
1516- ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1517+ ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1518 if strings.Contains(r.URL.Path, "/keep-alive-then-die") {
1519- conn, _, _ := w.(Hijacker).Hijack()
1520+ conn, _, _ := w.(http.Hijacker).Hijack()
1521 conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
1522 conn.Close()
1523 closedc <- true
1524@@ -1994,12 +1994,12 @@
1525 }
1526 defer closeConn()
1527
1528- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1529+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1530 if r.Method == "GET" {
1531 io.WriteString(w, "bar")
1532 return
1533 }
1534- conn, _, _ := w.(Hijacker).Hijack()
1535+ conn, _, _ := w.(http.Hijacker).Hijack()
1536 sconn.Lock()
1537 sconn.c = conn
1538 sconn.Unlock()
1539@@ -2056,7 +2056,7 @@
1540 }
1541 defer afterTest(t)
1542 readBody := make(chan error, 1)
1543- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1544+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1545 _, err := ioutil.ReadAll(r.Body)
1546 readBody <- err
1547 }))
1548@@ -2098,7 +2098,7 @@
1549 }
1550 }
1551
1552-func wantBody(res *http.Response, err error, want string) error {
1553+func wantBody(res *Response, err error, want string) error {
1554 if err != nil {
1555 return err
1556 }
16031557
16041558
=== modified file 'http13client/_using.txt'
--- http13client/_using.txt 2014-03-20 12:20:01 +0000
+++ http13client/_using.txt 2014-06-20 13:24:39 +0000
@@ -1,5 +1,5 @@
1parent: 19512:32c32aef2a41 tip1parent: 20169:9895f9e36435 go1.3 release
2 test: enable bug385_32 test on amd64p32.2 go1.3
3branch: default3branch: release-branch.go1.3
4commit: (clean)4commit: (clean)
5update: (current)5update: (current)
66
=== modified file 'http13client/client.go'
--- http13client/client.go 2014-03-20 09:26:28 +0000
+++ http13client/client.go 2014-06-20 13:24:39 +0000
@@ -92,8 +92,9 @@
92 // authentication, or cookies.92 // authentication, or cookies.
93 //93 //
94 // RoundTrip should not modify the request, except for94 // RoundTrip should not modify the request, except for
95 // consuming and closing the Body. The request's URL and95 // consuming and closing the Body, including on errors. The
96 // Header fields are guaranteed to be initialized.96 // request's URL and Header fields are guaranteed to be
97 // initialized.
97 RoundTrip(*Request) (*Response, error)98 RoundTrip(*Request) (*Response, error)
98}99}
99100
@@ -141,6 +142,9 @@
141// (typically Transport) may not be able to re-use a persistent TCP142// (typically Transport) may not be able to re-use a persistent TCP
142// connection to the server for a subsequent "keep-alive" request.143// connection to the server for a subsequent "keep-alive" request.
143//144//
145// The request Body, if non-nil, will be closed by the underlying
146// Transport, even on errors.
147//
144// Generally Get, Post, or PostForm will be used instead of Do.148// Generally Get, Post, or PostForm will be used instead of Do.
145func (c *Client) Do(req *Request) (resp *Response, err error) {149func (c *Client) Do(req *Request) (resp *Response, err error) {
146 if req.Method == "GET" || req.Method == "HEAD" {150 if req.Method == "GET" || req.Method == "HEAD" {
@@ -163,14 +167,17 @@
163// Caller should close resp.Body when done reading from it.167// Caller should close resp.Body when done reading from it.
164func send(req *Request, t RoundTripper) (resp *Response, err error) {168func send(req *Request, t RoundTripper) (resp *Response, err error) {
165 if t == nil {169 if t == nil {
170 req.closeBody()
166 return nil, errors.New("http: no Client.Transport or DefaultTransport")171 return nil, errors.New("http: no Client.Transport or DefaultTransport")
167 }172 }
168173
169 if req.URL == nil {174 if req.URL == nil {
175 req.closeBody()
170 return nil, errors.New("http: nil Request.URL")176 return nil, errors.New("http: nil Request.URL")
171 }177 }
172178
173 if req.RequestURI != "" {179 if req.RequestURI != "" {
180 req.closeBody()
174 return nil, errors.New("http: Request.RequestURI can't be set in client requests.")181 return nil, errors.New("http: Request.RequestURI can't be set in client requests.")
175 }182 }
176183
@@ -278,6 +285,7 @@
278 var via []*Request285 var via []*Request
279286
280 if ireq.URL == nil {287 if ireq.URL == nil {
288 ireq.closeBody()
281 return nil, errors.New("http: nil Request.URL")289 return nil, errors.New("http: nil Request.URL")
282 }290 }
283291
@@ -400,7 +408,7 @@
400// Caller should close resp.Body when done reading from it.408// Caller should close resp.Body when done reading from it.
401//409//
402// If the provided body is also an io.Closer, it is closed after the410// If the provided body is also an io.Closer, it is closed after the
403// body is successfully written to the server.411// request.
404func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {412func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
405 req, err := NewRequest("POST", url, body)413 req, err := NewRequest("POST", url, body)
406 if err != nil {414 if err != nil {
407415
=== modified file 'http13client/client_test.go'
--- http13client/client_test.go 2014-03-20 09:26:28 +0000
+++ http13client/client_test.go 2014-06-20 13:24:39 +0000
@@ -20,6 +20,8 @@
20 "net/http"20 "net/http"
21 "net/http/httptest"21 "net/http/httptest"
22 "net/url"22 "net/url"
23 "reflect"
24 "sort"
23 "strconv"25 "strconv"
24 "strings"26 "strings"
25 "sync"27 "sync"
@@ -877,3 +879,93 @@
877 t.Fatal("server saw different client ports before & after the redirect")879 t.Fatal("server saw different client ports before & after the redirect")
878 }880 }
879}881}
882
883// eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF.
884type eofReaderFunc func()
885
886func (f eofReaderFunc) Read(p []byte) (n int, err error) {
887 f()
888 return 0, io.EOF
889}
890
891func TestClientTrailers(t *testing.T) {
892 defer afterTest(t)
893 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
894 w.Header().Set("Connection", "close")
895 w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
896 w.Header().Add("Trailer", "Server-Trailer-C")
897
898 var decl []string
899 for k := range r.Trailer {
900 decl = append(decl, k)
901 }
902 sort.Strings(decl)
903
904 slurp, err := ioutil.ReadAll(r.Body)
905 if err != nil {
906 t.Errorf("Server reading request body: %v", err)
907 }
908 if string(slurp) != "foo" {
909 t.Errorf("Server read request body %q; want foo", slurp)
910 }
911 if r.Trailer == nil {
912 io.WriteString(w, "nil Trailer")
913 } else {
914 fmt.Fprintf(w, "decl: %v, vals: %s, %s",
915 decl,
916 r.Trailer.Get("Client-Trailer-A"),
917 r.Trailer.Get("Client-Trailer-B"))
918 }
919
920 // TODO: golang.org/issue/7759: there's no way yet for
921 // the server to set trailers without hijacking, so do
922 // that for now, just to test the client. Later, in
923 // Go 1.4, it should be implicit that any mutations
924 // to w.Header() after the initial write are the
925 // trailers to be sent, if and only if they were
926 // previously declared with w.Header().Set("Trailer",
927 // ..keys..)
928 w.(http.Flusher).Flush()
929 conn, buf, _ := w.(http.Hijacker).Hijack()
930 t := http.Header{}
931 t.Set("Server-Trailer-A", "valuea")
932 t.Set("Server-Trailer-C", "valuec") // skipping B
933 buf.WriteString("0\r\n") // eof
934 t.Write(buf)
935 buf.WriteString("\r\n") // end of trailers
936 buf.Flush()
937 conn.Close()
938 }))
939 defer ts.Close()
940
941 var req *Request
942 req, _ = NewRequest("POST", ts.URL, io.MultiReader(
943 eofReaderFunc(func() {
944 req.Trailer["Client-Trailer-A"] = []string{"valuea"}
945 }),
946 strings.NewReader("foo"),
947 eofReaderFunc(func() {
948 req.Trailer["Client-Trailer-B"] = []string{"valueb"}
949 }),
950 ))
951 req.Trailer = http.Header{
952 "Client-Trailer-A": nil, // to be set later
953 "Client-Trailer-B": nil, // to be set later
954 }
955 req.ContentLength = -1
956 res, err := DefaultClient.Do(req)
957 if err != nil {
958 t.Fatal(err)
959 }
960 if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil {
961 t.Error(err)
962 }
963 want := http.Header{
964 "Server-Trailer-A": []string{"valuea"},
965 "Server-Trailer-B": nil,
966 "Server-Trailer-C": []string{"valuec"},
967 }
968 if !reflect.DeepEqual(res.Trailer, want) {
969 t.Errorf("Response trailers = %#v; want %#v", res.Trailer, want)
970 }
971}
880972
=== modified file 'http13client/cookie.go'
--- http13client/cookie.go 2014-03-19 23:13:58 +0000
+++ http13client/cookie.go 2014-06-20 13:24:39 +0000
@@ -55,11 +55,7 @@
55 attr, val = attr[:j], attr[j+1:]55 attr, val = attr[:j], attr[j+1:]
56 }56 }
57 lowerAttr := strings.ToLower(attr)57 lowerAttr := strings.ToLower(attr)
58 parseCookieValueFn := parseCookieValue58 val, success = parseCookieValue(val)
59 if lowerAttr == "expires" {
60 parseCookieValueFn = parseCookieExpiresValue
61 }
62 val, success = parseCookieValueFn(val)
63 if !success {59 if !success {
64 c.Unparsed = append(c.Unparsed, parts[i])60 c.Unparsed = append(c.Unparsed, parts[i])
65 continue61 continue
@@ -230,12 +226,23 @@
230// ; US-ASCII characters excluding CTLs,226// ; US-ASCII characters excluding CTLs,
231// ; whitespace DQUOTE, comma, semicolon,227// ; whitespace DQUOTE, comma, semicolon,
232// ; and backslash228// ; and backslash
229// We loosen this as spaces and commas are common in cookie values
230// but we produce a quoted cookie-value in when value starts or ends
231// with a comma or space.
232// See http://golang.org/issue/7243 for the discussion.
233func sanitizeCookieValue(v string) string {233func sanitizeCookieValue(v string) string {
234 return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)234 v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
235 if len(v) == 0 {
236 return v
237 }
238 if v[0] == ' ' || v[0] == ',' || v[len(v)-1] == ' ' || v[len(v)-1] == ',' {
239 return `"` + v + `"`
240 }
241 return v
235}242}
236243
237func validCookieValueByte(b byte) bool {244func validCookieValueByte(b byte) bool {
238 return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\'245 return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
239}246}
240247
241// path-av = "Path=" path-value248// path-av = "Path=" path-value
@@ -270,38 +277,13 @@
270 return string(buf)277 return string(buf)
271}278}
272279
273func unquoteCookieValue(v string) string {
274 if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' {
275 return v[1 : len(v)-1]
276 }
277 return v
278}
279
280func isCookieByte(c byte) bool {
281 switch {
282 case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
283 0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
284 return true
285 }
286 return false
287}
288
289func isCookieExpiresByte(c byte) (ok bool) {
290 return isCookieByte(c) || c == ',' || c == ' '
291}
292
293func parseCookieValue(raw string) (string, bool) {280func parseCookieValue(raw string) (string, bool) {
294 return parseCookieValueUsing(raw, isCookieByte)281 // Strip the quotes, if present.
295}282 if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
296283 raw = raw[1 : len(raw)-1]
297func parseCookieExpiresValue(raw string) (string, bool) {284 }
298 return parseCookieValueUsing(raw, isCookieExpiresByte)
299}
300
301func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) {
302 raw = unquoteCookieValue(raw)
303 for i := 0; i < len(raw); i++ {285 for i := 0; i < len(raw); i++ {
304 if !validByte(raw[i]) {286 if !validCookieValueByte(raw[i]) {
305 return "", false287 return "", false
306 }288 }
307 }289 }
308290
=== removed file 'http13client/cookie_test.go'
--- http13client/cookie_test.go 2014-03-19 23:13:58 +0000
+++ http13client/cookie_test.go 1970-01-01 00:00:00 +0000
@@ -1,304 +0,0 @@
1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package http
6
7import (
8 "bytes"
9 "encoding/json"
10 "fmt"
11 "log"
12 "net/http"
13 "os"
14 "reflect"
15 "strings"
16 "testing"
17 "time"
18)
19
20var writeSetCookiesTests = []struct {
21 Cookie *http.Cookie
22 Raw string
23}{
24 {
25 &http.Cookie{Name: "cookie-1", Value: "v$1"},
26 "cookie-1=v$1",
27 },
28 {
29 &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
30 "cookie-2=two; Max-Age=3600",
31 },
32 {
33 &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
34 "cookie-3=three; Domain=example.com",
35 },
36 {
37 &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
38 "cookie-4=four; Path=/restricted/",
39 },
40 {
41 &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
42 "cookie-5=five",
43 },
44 {
45 &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
46 "cookie-6=six",
47 },
48 {
49 &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
50 "cookie-7=seven; Domain=127.0.0.1",
51 },
52 {
53 &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
54 "cookie-8=eight",
55 },
56}
57
58func TestWriteSetCookies(t *testing.T) {
59 defer log.SetOutput(os.Stderr)
60 var logbuf bytes.Buffer
61 log.SetOutput(&logbuf)
62
63 for i, tt := range writeSetCookiesTests {
64 if g, e := tt.Cookie.String(), tt.Raw; g != e {
65 t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g)
66 continue
67 }
68 }
69
70 if got, sub := logbuf.String(), "dropping domain attribute"; !strings.Contains(got, sub) {
71 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
72 }
73}
74
75type headerOnlyResponseWriter http.Header
76
77func (ho headerOnlyResponseWriter) Header() http.Header {
78 return http.Header(ho)
79}
80
81func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
82 panic("NOIMPL")
83}
84
85func (ho headerOnlyResponseWriter) WriteHeader(int) {
86 panic("NOIMPL")
87}
88
89func TestSetCookie(t *testing.T) {
90 m := make(http.Header)
91 http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
92 http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
93 if l := len(m["Set-Cookie"]); l != 2 {
94 t.Fatalf("expected %d cookies, got %d", 2, l)
95 }
96 if g, e := m["Set-Cookie"][0], "cookie-1=one; Path=/restricted/"; g != e {
97 t.Errorf("cookie #1: want %q, got %q", e, g)
98 }
99 if g, e := m["Set-Cookie"][1], "cookie-2=two; Max-Age=3600"; g != e {
100 t.Errorf("cookie #2: want %q, got %q", e, g)
101 }
102}
103
104var addCookieTests = []struct {
105 Cookies []*http.Cookie
106 Raw string
107}{
108 {
109 []*http.Cookie{},
110 "",
111 },
112 {
113 []*http.Cookie{{Name: "cookie-1", Value: "v$1"}},
114 "cookie-1=v$1",
115 },
116 {
117 []*http.Cookie{
118 {Name: "cookie-1", Value: "v$1"},
119 {Name: "cookie-2", Value: "v$2"},
120 {Name: "cookie-3", Value: "v$3"},
121 },
122 "cookie-1=v$1; cookie-2=v$2; cookie-3=v$3",
123 },
124}
125
126func TestAddCookie(t *testing.T) {
127 for i, tt := range addCookieTests {
128 req, _ := NewRequest("GET", "http://example.com/", nil)
129 for _, c := range tt.Cookies {
130 req.AddCookie(c)
131 }
132 if g := req.Header.Get("Cookie"); g != tt.Raw {
133 t.Errorf("Test %d:\nwant: %s\n got: %s\n", i, tt.Raw, g)
134 continue
135 }
136 }
137}
138
139var readSetCookiesTests = []struct {
140 Header http.Header
141 Cookies []*http.Cookie
142}{
143 {
144 http.Header{"Set-Cookie": {"Cookie-1=v$1"}},
145 []*http.Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
146 },
147 {
148 http.Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
149 []*http.Cookie{{
150 Name: "NID",
151 Value: "99=YsDT5i3E-CXax-",
152 Path: "/",
153 Domain: ".google.ch",
154 HttpOnly: true,
155 Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
156 RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
157 Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
158 }},
159 },
160 {
161 http.Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
162 []*http.Cookie{{
163 Name: ".ASPXAUTH",
164 Value: "7E3AA",
165 Path: "/",
166 Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
167 RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
168 HttpOnly: true,
169 Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
170 }},
171 },
172 {
173 http.Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
174 []*http.Cookie{{
175 Name: "ASP.NET_SessionId",
176 Value: "foo",
177 Path: "/",
178 HttpOnly: true,
179 Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly",
180 }},
181 },
182
183 // TODO(bradfitz): users have reported seeing this in the
184 // wild, but do browsers handle it? RFC 6265 just says "don't
185 // do that" (section 3) and then never mentions header folding
186 // again.
187 // Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
188}
189
190func toJSON(v interface{}) string {
191 b, err := json.Marshal(v)
192 if err != nil {
193 return fmt.Sprintf("%#v", v)
194 }
195 return string(b)
196}
197
198func TestReadSetCookies(t *testing.T) {
199 for i, tt := range readSetCookiesTests {
200 for n := 0; n < 2; n++ { // to verify readSetCookies doesn't mutate its input
201 c := readSetCookies(tt.Header)
202 if !reflect.DeepEqual(c, tt.Cookies) {
203 t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
204 continue
205 }
206 }
207 }
208}
209
210var readCookiesTests = []struct {
211 Header http.Header
212 Filter string
213 Cookies []*http.Cookie
214}{
215 {
216 http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
217 "",
218 []*http.Cookie{
219 {Name: "Cookie-1", Value: "v$1"},
220 {Name: "c2", Value: "v2"},
221 },
222 },
223 {
224 http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
225 "c2",
226 []*http.Cookie{
227 {Name: "c2", Value: "v2"},
228 },
229 },
230 {
231 http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
232 "",
233 []*http.Cookie{
234 {Name: "Cookie-1", Value: "v$1"},
235 {Name: "c2", Value: "v2"},
236 },
237 },
238 {
239 http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
240 "c2",
241 []*http.Cookie{
242 {Name: "c2", Value: "v2"},
243 },
244 },
245}
246
247func TestReadCookies(t *testing.T) {
248 for i, tt := range readCookiesTests {
249 for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
250 c := readCookies(tt.Header, tt.Filter)
251 if !reflect.DeepEqual(c, tt.Cookies) {
252 t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
253 continue
254 }
255 }
256 }
257}
258
259func TestCookieSanitizeValue(t *testing.T) {
260 defer log.SetOutput(os.Stderr)
261 var logbuf bytes.Buffer
262 log.SetOutput(&logbuf)
263
264 tests := []struct {
265 in, want string
266 }{
267 {"foo", "foo"},
268 {"foo bar", "foobar"},
269 {"\x00\x7e\x7f\x80", "\x7e"},
270 {`"withquotes"`, "withquotes"},
271 }
272 for _, tt := range tests {
273 if got := sanitizeCookieValue(tt.in); got != tt.want {
274 t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want)
275 }
276 }
277
278 if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
279 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
280 }
281}
282
283func TestCookieSanitizePath(t *testing.T) {
284 defer log.SetOutput(os.Stderr)
285 var logbuf bytes.Buffer
286 log.SetOutput(&logbuf)
287
288 tests := []struct {
289 in, want string
290 }{
291 {"/path", "/path"},
292 {"/path with space/", "/path with space/"},
293 {"/just;no;semicolon\x00orstuff/", "/justnosemicolonorstuff/"},
294 }
295 for _, tt := range tests {
296 if got := sanitizeCookiePath(tt.in); got != tt.want {
297 t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want)
298 }
299 }
300
301 if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
302 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
303 }
304}
3050
=== modified file 'http13client/request.go'
--- http13client/request.go 2014-03-19 23:13:58 +0000
+++ http13client/request.go 2014-06-20 13:24:39 +0000
@@ -69,18 +69,31 @@
6969
70// A Request represents an HTTP request received by a server70// A Request represents an HTTP request received by a server
71// or to be sent by a client.71// or to be sent by a client.
72//
73// The field semantics differ slightly between client and server
74// usage. In addition to the notes on the fields below, see the
75// documentation for Request.Write and RoundTripper.
72type Request struct {76type Request struct {
73 Method string // GET, POST, PUT, etc.77 // Method specifies the HTTP method (GET, POST, PUT, etc.).
78 // For client requests an empty string means GET.
79 Method string
7480
75 // URL is created from the URI supplied on the Request-Line81 // URL specifies either the URI being requested (for server
76 // as stored in RequestURI.82 // requests) or the URL to access (for client requests).
77 //83 //
78 // For most requests, fields other than Path and RawQuery84 // For server requests the URL is parsed from the URI
79 // will be empty. (See RFC 2616, Section 5.1.2)85 // supplied on the Request-Line as stored in RequestURI. For
86 // most requests, fields other than Path and RawQuery will be
87 // empty. (See RFC 2616, Section 5.1.2)
88 //
89 // For client requests, the URL's Host specifies the server to
90 // connect to, while the Request's Host field optionally
91 // specifies the Host header value to send in the HTTP
92 // request.
80 URL *url.URL93 URL *url.URL
8194
82 // The protocol version for incoming requests.95 // The protocol version for incoming requests.
83 // Outgoing requests always use HTTP/1.1.96 // Client requests always use HTTP/1.1.
84 Proto string // "HTTP/1.0"97 Proto string // "HTTP/1.0"
85 ProtoMajor int // 198 ProtoMajor int // 1
86 ProtoMinor int // 099 ProtoMinor int // 0
@@ -104,15 +117,20 @@
104 // The request parser implements this by canonicalizing the117 // The request parser implements this by canonicalizing the
105 // name, making the first character and any characters118 // name, making the first character and any characters
106 // following a hyphen uppercase and the rest lowercase.119 // following a hyphen uppercase and the rest lowercase.
120 //
121 // For client requests certain headers are automatically
122 // added and may override values in Header.
123 //
124 // See the documentation for the Request.Write method.
107 Header http.Header125 Header http.Header
108126
109 // Body is the request's body.127 // Body is the request's body.
110 //128 //
111 // For client requests, a nil body means the request has no129 // For client requests a nil body means the request has no
112 // body, such as a GET request. The HTTP Client's Transport130 // body, such as a GET request. The HTTP Client's Transport
113 // is responsible for calling the Close method.131 // is responsible for calling the Close method.
114 //132 //
115 // For server requests, the Request Body is always non-nil133 // For server requests the Request Body is always non-nil
116 // but will return EOF immediately when no body is present.134 // but will return EOF immediately when no body is present.
117 // The Server will close the request body. The ServeHTTP135 // The Server will close the request body. The ServeHTTP
118 // Handler does not need to.136 // Handler does not need to.
@@ -122,7 +140,7 @@
122 // The value -1 indicates that the length is unknown.140 // The value -1 indicates that the length is unknown.
123 // Values >= 0 indicate that the given number of bytes may141 // Values >= 0 indicate that the given number of bytes may
124 // be read from Body.142 // be read from Body.
125 // For outgoing requests, a value of 0 means unknown if Body is not nil.143 // For client requests, a value of 0 means unknown if Body is not nil.
126 ContentLength int64144 ContentLength int64
127145
128 // TransferEncoding lists the transfer encodings from outermost to146 // TransferEncoding lists the transfer encodings from outermost to
@@ -133,13 +151,18 @@
133 TransferEncoding []string151 TransferEncoding []string
134152
135 // Close indicates whether to close the connection after153 // Close indicates whether to close the connection after
136 // replying to this request.154 // replying to this request (for servers) or after sending
155 // the request (for clients).
137 Close bool156 Close bool
138157
139 // The host on which the URL is sought.158 // For server requests Host specifies the host on which the
140 // Per RFC 2616, this is either the value of the Host: header159 // URL is sought. Per RFC 2616, this is either the value of
141 // or the host name given in the URL itself.160 // the "Host" header or the host name given in the URL itself.
142 // It may be of the form "host:port".161 // It may be of the form "host:port".
162 //
163 // For client requests Host optionally overrides the Host
164 // header to send. If empty, the Request.Write method uses
165 // the value of URL.Host.
143 Host string166 Host string
144167
145 // Form contains the parsed form data, including both the URL168 // Form contains the parsed form data, including both the URL
@@ -159,12 +182,24 @@
159 // The HTTP client ignores MultipartForm and uses Body instead.182 // The HTTP client ignores MultipartForm and uses Body instead.
160 MultipartForm *multipart.Form183 MultipartForm *multipart.Form
161184
162 // Trailer maps trailer keys to values. Like for Header, if the185 // Trailer specifies additional headers that are sent after the request
163 // response has multiple trailer lines with the same key, they will be186 // body.
164 // concatenated, delimited by commas.187 //
165 // For server requests, Trailer is only populated after Body has been188 // For server requests the Trailer map initially contains only the
166 // closed or fully consumed.189 // trailer keys, with nil values. (The client declares which trailers it
167 // Trailer support is only partially complete.190 // will later send.) While the handler is reading from Body, it must
191 // not reference Trailer. After reading from Body returns EOF, Trailer
192 // can be read again and will contain non-nil values, if they were sent
193 // by the client.
194 //
195 // For client requests Trailer must be initialized to a map containing
196 // the trailer keys to later send. The values may be nil or their final
197 // values. The ContentLength must be 0 or -1, to send a chunked request.
198 // After the HTTP request is sent the map values can be updated while
199 // the request body is read. Once the body returns EOF, the caller must
200 // not mutate Trailer.
201 //
202 // Few HTTP clients, servers, or proxies support HTTP trailers.
168 Trailer http.Header203 Trailer http.Header
169204
170 // RemoteAddr allows HTTP servers and other software to record205 // RemoteAddr allows HTTP servers and other software to record
@@ -382,7 +417,6 @@
382 return err417 return err
383 }418 }
384419
385 // TODO: split long values? (If so, should share code with Conn.Write)
386 err = req.Header.WriteSubset(w, reqWriteExcludeHeader)420 err = req.Header.WriteSubset(w, reqWriteExcludeHeader)
387 if err != nil {421 if err != nil {
388 return err422 return err
@@ -589,32 +623,6 @@
589623
590 fixPragmaCacheControl(req.Header)624 fixPragmaCacheControl(req.Header)
591625
592 // TODO: Parse specific header values:
593 // Accept
594 // Accept-Encoding
595 // Accept-Language
596 // Authorization
597 // Cache-Control
598 // Connection
599 // Date
600 // Expect
601 // From
602 // If-Match
603 // If-Modified-Since
604 // If-None-Match
605 // If-Range
606 // If-Unmodified-Since
607 // Max-Forwards
608 // Proxy-Authorization
609 // Referer [sic]
610 // TE (transfer-codings)
611 // Trailer
612 // Transfer-Encoding
613 // Upgrade
614 // User-Agent
615 // Via
616 // Warning
617
618 err = readTransfer(req, b)626 err = readTransfer(req, b)
619 if err != nil {627 if err != nil {
620 return nil, err628 return nil, err
@@ -783,9 +791,7 @@
783 }791 }
784792
785 mr, err := r.multipartReader()793 mr, err := r.multipartReader()
786 if err == ErrNotMultipart {794 if err != nil {
787 return nil
788 } else if err != nil {
789 return err795 return err
790 }796 }
791797
@@ -863,3 +869,9 @@
863func (r *Request) wantsClose() bool {869func (r *Request) wantsClose() bool {
864 return hasToken(r.Header.Get("Connection"), "close")870 return hasToken(r.Header.Get("Connection"), "close")
865}871}
872
873func (r *Request) closeBody() {
874 if r.Body != nil {
875 r.Body.Close()
876 }
877}
866878
=== modified file 'http13client/request_test.go'
--- http13client/request_test.go 2014-03-20 09:26:28 +0000
+++ http13client/request_test.go 2014-06-20 13:24:39 +0000
@@ -155,7 +155,25 @@
155 req.Header = http.Header{"Content-Type": {"text/plain"}}155 req.Header = http.Header{"Content-Type": {"text/plain"}}
156 multipart, err = req.MultipartReader()156 multipart, err = req.MultipartReader()
157 if multipart != nil {157 if multipart != nil {
158 t.Errorf("unexpected multipart for text/plain")158 t.Error("unexpected multipart for text/plain")
159 }
160}
161
162func TestParseMultipartForm(t *testing.T) {
163 req := &Request{
164 Method: "POST",
165 Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
166 Body: ioutil.NopCloser(new(bytes.Buffer)),
167 }
168 err := req.ParseMultipartForm(25)
169 if err == nil {
170 t.Error("expected multipart EOF, got nil")
171 }
172
173 req.Header = http.Header{"Content-Type": {"text/plain"}}
174 err = req.ParseMultipartForm(25)
175 if err != ErrNotMultipart {
176 t.Error("expected ErrNotMultipart for text/plain")
159 }177 }
160}178}
161179
@@ -221,16 +239,38 @@
221 validateTestMultipartContents(t, req, true)239 validateTestMultipartContents(t, req, true)
222}240}
223241
224func TestEmptyMultipartRequest(t *testing.T) {242func TestMissingFileMultipartRequest(t *testing.T) {
225 // Test that FormValue and FormFile automatically invoke243 // Test that FormFile returns an error if
226 // ParseMultipartForm and return the right values.244 // the named file is missing.
227 req, err := NewRequest("GET", "/", nil)245 req := newTestMultipartRequest(t)
228 if err != nil {
229 t.Errorf("NewRequest err = %q", err)
230 }
231 testMissingFile(t, req)246 testMissingFile(t, req)
232}247}
233248
249// Test that FormValue invokes ParseMultipartForm.
250func TestFormValueCallsParseMultipartForm(t *testing.T) {
251 req, _ := NewRequest("POST", "http://www.google.com/", strings.NewReader("z=post"))
252 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
253 if req.Form != nil {
254 t.Fatal("Unexpected request Form, want nil")
255 }
256 req.FormValue("z")
257 if req.Form == nil {
258 t.Fatal("ParseMultipartForm not called by FormValue")
259 }
260}
261
262// Test that FormFile invokes ParseMultipartForm.
263func TestFormFileCallsParseMultipartForm(t *testing.T) {
264 req := newTestMultipartRequest(t)
265 if req.Form != nil {
266 t.Fatal("Unexpected request Form, want nil")
267 }
268 req.FormFile("")
269 if req.Form == nil {
270 t.Fatal("ParseMultipartForm not called by FormFile")
271 }
272}
273
234// Test that ParseMultipartForm errors if called274// Test that ParseMultipartForm errors if called
235// after MultipartReader on the same request.275// after MultipartReader on the same request.
236func TestParseMultipartFormOrder(t *testing.T) {276func TestParseMultipartFormOrder(t *testing.T) {
237277
=== modified file 'http13client/response.go'
--- http13client/response.go 2014-03-19 23:43:25 +0000
+++ http13client/response.go 2014-06-20 13:24:39 +0000
@@ -8,6 +8,7 @@
88
9import (9import (
10 "bufio"10 "bufio"
11 "bytes"
11 "crypto/tls"12 "crypto/tls"
12 "errors"13 "errors"
13 "io"14 "io"
@@ -47,7 +48,8 @@
47 //48 //
48 // The http Client and Transport guarantee that Body is always49 // The http Client and Transport guarantee that Body is always
49 // non-nil, even on responses without a body or responses with50 // non-nil, even on responses without a body or responses with
50 // a zero-lengthed body.51 // a zero-length body. It is the caller's responsibility to
52 // close Body.
51 //53 //
52 // The Body is automatically dechunked if the server replied54 // The Body is automatically dechunked if the server replied
53 // with a "chunked" Transfer-Encoding.55 // with a "chunked" Transfer-Encoding.
@@ -200,7 +202,6 @@
200//202//
201// Body is closed after it is sent.203// Body is closed after it is sent.
202func (r *Response) Write(w io.Writer) error {204func (r *Response) Write(w io.Writer) error {
203
204 // Status line205 // Status line
205 text := r.Status206 text := r.Status
206 if text == "" {207 if text == "" {
@@ -212,10 +213,45 @@
212 protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)213 protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
213 statusCode := strconv.Itoa(r.StatusCode) + " "214 statusCode := strconv.Itoa(r.StatusCode) + " "
214 text = strings.TrimPrefix(text, statusCode)215 text = strings.TrimPrefix(text, statusCode)
215 io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")216 if _, err := io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n"); err != nil {
217 return err
218 }
219
220 // Clone it, so we can modify r1 as needed.
221 r1 := new(Response)
222 *r1 = *r
223 if r1.ContentLength == 0 && r1.Body != nil {
224 // Is it actually 0 length? Or just unknown?
225 var buf [1]byte
226 n, err := r1.Body.Read(buf[:])
227 if err != nil && err != io.EOF {
228 return err
229 }
230 if n == 0 {
231 // Reset it to a known zero reader, in case underlying one
232 // is unhappy being read repeatedly.
233 r1.Body = eofReader
234 } else {
235 r1.ContentLength = -1
236 r1.Body = struct {
237 io.Reader
238 io.Closer
239 }{
240 io.MultiReader(bytes.NewReader(buf[:1]), r.Body),
241 r.Body,
242 }
243 }
244 }
245 // If we're sending a non-chunked HTTP/1.1 response without a
246 // content-length, the only way to do that is the old HTTP/1.0
247 // way, by noting the EOF with a connection close, so we need
248 // to set Close.
249 if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !chunked(r1.TransferEncoding) {
250 r1.Close = true
251 }
216252
217 // Process Body,ContentLength,Close,Trailer253 // Process Body,ContentLength,Close,Trailer
218 tw, err := newTransferWriter(r)254 tw, err := newTransferWriter(r1)
219 if err != nil {255 if err != nil {
220 return err256 return err
221 }257 }
@@ -230,8 +266,19 @@
230 return err266 return err
231 }267 }
232268
269 // contentLengthAlreadySent may have been already sent for
270 // POST/PUT requests, even if zero length. See Issue 8180.
271 contentLengthAlreadySent := tw.shouldSendContentLength()
272 if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent {
273 if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
274 return err
275 }
276 }
277
233 // End-of-header278 // End-of-header
234 io.WriteString(w, "\r\n")279 if _, err := io.WriteString(w, "\r\n"); err != nil {
280 return err
281 }
235282
236 // Write body and trailer283 // Write body and trailer
237 err = tw.WriteBody(w)284 err = tw.WriteBody(w)
238285
=== modified file 'http13client/response_test.go'
--- http13client/response_test.go 2014-03-19 23:13:58 +0000
+++ http13client/response_test.go 2014-06-20 13:24:39 +0000
@@ -30,6 +30,10 @@
30 return &Request{Method: method}30 return &Request{Method: method}
31}31}
3232
33func dummyReq11(method string) *Request {
34 return &Request{Method: method, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1}
35}
36
33var respTests = []respTest{37var respTests = []respTest{
34 // Unchunked response without Content-Length.38 // Unchunked response without Content-Length.
35 {39 {
3640
=== modified file 'http13client/responsewrite_test.go'
--- http13client/responsewrite_test.go 2014-03-19 23:13:58 +0000
+++ http13client/responsewrite_test.go 2014-06-20 13:24:39 +0000
@@ -27,7 +27,7 @@
27 ProtoMinor: 0,27 ProtoMinor: 0,
28 Request: dummyReq("GET"),28 Request: dummyReq("GET"),
29 Header: http.Header{},29 Header: http.Header{},
30 Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),30 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
31 ContentLength: 6,31 ContentLength: 6,
32 },32 },
3333
@@ -50,6 +50,106 @@
50 "\r\n" +50 "\r\n" +
51 "abcdef",51 "abcdef",
52 },52 },
53 // HTTP/1.1 response with unknown length and Connection: close
54 {
55 Response{
56 StatusCode: 200,
57 ProtoMajor: 1,
58 ProtoMinor: 1,
59 Request: dummyReq("GET"),
60 Header: http.Header{},
61 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
62 ContentLength: -1,
63 Close: true,
64 },
65 "HTTP/1.1 200 OK\r\n" +
66 "Connection: close\r\n" +
67 "\r\n" +
68 "abcdef",
69 },
70 // HTTP/1.1 response with unknown length and not setting connection: close
71 {
72 Response{
73 StatusCode: 200,
74 ProtoMajor: 1,
75 ProtoMinor: 1,
76 Request: dummyReq11("GET"),
77 Header: http.Header{},
78 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
79 ContentLength: -1,
80 Close: false,
81 },
82 "HTTP/1.1 200 OK\r\n" +
83 "Connection: close\r\n" +
84 "\r\n" +
85 "abcdef",
86 },
87 // HTTP/1.1 response with unknown length and not setting connection: close, but
88 // setting chunked.
89 {
90 Response{
91 StatusCode: 200,
92 ProtoMajor: 1,
93 ProtoMinor: 1,
94 Request: dummyReq11("GET"),
95 Header: http.Header{},
96 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
97 ContentLength: -1,
98 TransferEncoding: []string{"chunked"},
99 Close: false,
100 },
101 "HTTP/1.1 200 OK\r\n" +
102 "Transfer-Encoding: chunked\r\n\r\n" +
103 "6\r\nabcdef\r\n0\r\n\r\n",
104 },
105 // HTTP/1.1 response 0 content-length, and nil body
106 {
107 Response{
108 StatusCode: 200,
109 ProtoMajor: 1,
110 ProtoMinor: 1,
111 Request: dummyReq11("GET"),
112 Header: http.Header{},
113 Body: nil,
114 ContentLength: 0,
115 Close: false,
116 },
117 "HTTP/1.1 200 OK\r\n" +
118 "Content-Length: 0\r\n" +
119 "\r\n",
120 },
121 // HTTP/1.1 response 0 content-length, and non-nil empty body
122 {
123 Response{
124 StatusCode: 200,
125 ProtoMajor: 1,
126 ProtoMinor: 1,
127 Request: dummyReq11("GET"),
128 Header: http.Header{},
129 Body: ioutil.NopCloser(strings.NewReader("")),
130 ContentLength: 0,
131 Close: false,
132 },
133 "HTTP/1.1 200 OK\r\n" +
134 "Content-Length: 0\r\n" +
135 "\r\n",
136 },
137 // HTTP/1.1 response 0 content-length, and non-nil non-empty body
138 {
139 Response{
140 StatusCode: 200,
141 ProtoMajor: 1,
142 ProtoMinor: 1,
143 Request: dummyReq11("GET"),
144 Header: http.Header{},
145 Body: ioutil.NopCloser(strings.NewReader("foo")),
146 ContentLength: 0,
147 Close: false,
148 },
149 "HTTP/1.1 200 OK\r\n" +
150 "Connection: close\r\n" +
151 "\r\nfoo",
152 },
53 // HTTP/1.1, chunked coding; empty trailer; close153 // HTTP/1.1, chunked coding; empty trailer; close
54 {154 {
55 Response{155 Response{
@@ -92,6 +192,22 @@
92 "Foo: Bar Baz\r\n" +192 "Foo: Bar Baz\r\n" +
93 "\r\n",193 "\r\n",
94 },194 },
195
196 // Want a single Content-Length header. Fixing issue 8180 where
197 // there were two.
198 {
199 Response{
200 StatusCode: http.StatusOK,
201 ProtoMajor: 1,
202 ProtoMinor: 1,
203 Request: &Request{Method: "POST"},
204 Header: http.Header{},
205 ContentLength: 0,
206 TransferEncoding: nil,
207 Body: nil,
208 },
209 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
210 },
95 }211 }
96212
97 for i := range respWriteTests {213 for i := range respWriteTests {
98214
=== modified file 'http13client/server.go'
--- http13client/server.go 2014-03-19 23:13:58 +0000
+++ http13client/server.go 2014-06-20 13:24:39 +0000
@@ -10,21 +10,27 @@
10 "io/ioutil"10 "io/ioutil"
11 "log"11 "log"
12 "net"12 "net"
13 "strings"
14 "sync"13 "sync"
15)14)
1615
16type eofReaderWithWriteTo struct{}
17
18func (eofReaderWithWriteTo) WriteTo(io.Writer) (int64, error) { return 0, nil }
19func (eofReaderWithWriteTo) Read([]byte) (int, error) { return 0, io.EOF }
20
17// eofReader is a non-nil io.ReadCloser that always returns EOF.21// eofReader is a non-nil io.ReadCloser that always returns EOF.
18// It embeds a *strings.Reader so it still has a WriteTo method22// It has a WriteTo method so io.Copy won't need a buffer.
19// and io.Copy won't need a buffer.
20var eofReader = &struct {23var eofReader = &struct {
21 *strings.Reader24 eofReaderWithWriteTo
22 io.Closer25 io.Closer
23}{26}{
24 strings.NewReader(""),27 eofReaderWithWriteTo{},
25 ioutil.NopCloser(nil),28 ioutil.NopCloser(nil),
26}29}
2730
31// Verify that an io.Copy from an eofReader won't require a buffer.
32var _ io.WriterTo = eofReader
33
28// loggingConn is used for debugging.34// loggingConn is used for debugging.
29type loggingConn struct {35type loggingConn struct {
30 name string36 name string
3137
=== modified file 'http13client/transfer.go'
--- http13client/transfer.go 2014-03-19 23:13:58 +0000
+++ http13client/transfer.go 2014-06-20 13:24:39 +0000
@@ -13,6 +13,7 @@
13 "io/ioutil"13 "io/ioutil"
14 "net/http"14 "net/http"
15 "net/textproto"15 "net/textproto"
16 "sort"
16 "strconv"17 "strconv"
17 "strings"18 "strings"
18 "sync"19 "sync"
@@ -144,11 +145,10 @@
144 return false145 return false
145}146}
146147
147func (t *transferWriter) WriteHeader(w io.Writer) (err error) {148func (t *transferWriter) WriteHeader(w io.Writer) error {
148 if t.Close {149 if t.Close {
149 _, err = io.WriteString(w, "Connection: close\r\n")150 if _, err := io.WriteString(w, "Connection: close\r\n"); err != nil {
150 if err != nil {151 return err
151 return
152 }152 }
153 }153 }
154154
@@ -156,43 +156,44 @@
156 // function of the sanitized field triple (Body, ContentLength,156 // function of the sanitized field triple (Body, ContentLength,
157 // TransferEncoding)157 // TransferEncoding)
158 if t.shouldSendContentLength() {158 if t.shouldSendContentLength() {
159 io.WriteString(w, "Content-Length: ")159 if _, err := io.WriteString(w, "Content-Length: "); err != nil {
160 _, err = io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n")160 return err
161 if err != nil {161 }
162 return162 if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil {
163 return err
163 }164 }
164 } else if chunked(t.TransferEncoding) {165 } else if chunked(t.TransferEncoding) {
165 _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")166 if _, err := io.WriteString(w, "Transfer-Encoding: chunked\r\n"); err != nil {
166 if err != nil {167 return err
167 return
168 }168 }
169 }169 }
170170
171 // Write Trailer header171 // Write Trailer header
172 if t.Trailer != nil {172 if t.Trailer != nil {
173 // TODO: At some point, there should be a generic mechanism for173 keys := make([]string, 0, len(t.Trailer))
174 // writing long headers, using HTTP line splitting
175 io.WriteString(w, "Trailer: ")
176 needComma := false
177 for k := range t.Trailer {174 for k := range t.Trailer {
178 k = http.CanonicalHeaderKey(k)175 k = http.CanonicalHeaderKey(k)
179 switch k {176 switch k {
180 case "Transfer-Encoding", "Trailer", "Content-Length":177 case "Transfer-Encoding", "Trailer", "Content-Length":
181 return &badStringError{"invalid Trailer key", k}178 return &badStringError{"invalid Trailer key", k}
182 }179 }
183 if needComma {180 keys = append(keys, k)
184 io.WriteString(w, ",")181 }
182 if len(keys) > 0 {
183 sort.Strings(keys)
184 // TODO: could do better allocation-wise here, but trailers are rare,
185 // so being lazy for now.
186 if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil {
187 return err
185 }188 }
186 io.WriteString(w, k)
187 needComma = true
188 }189 }
189 _, err = io.WriteString(w, "\r\n")
190 }190 }
191191
192 return192 return nil
193}193}
194194
195func (t *transferWriter) WriteBody(w io.Writer) (err error) {195func (t *transferWriter) WriteBody(w io.Writer) error {
196 var err error
196 var ncopy int64197 var ncopy int64
197198
198 // Write body199 // Write body
@@ -229,11 +230,16 @@
229230
230 // TODO(petar): Place trailer writer code here.231 // TODO(petar): Place trailer writer code here.
231 if chunked(t.TransferEncoding) {232 if chunked(t.TransferEncoding) {
233 // Write Trailer header
234 if t.Trailer != nil {
235 if err := t.Trailer.Write(w); err != nil {
236 return err
237 }
238 }
232 // Last chunk, empty trailer239 // Last chunk, empty trailer
233 _, err = io.WriteString(w, "\r\n")240 _, err = io.WriteString(w, "\r\n")
234 }241 }
235242 return err
236 return
237}243}
238244
239type transferReader struct {245type transferReader struct {
@@ -265,6 +271,22 @@
265 return true271 return true
266}272}
267273
274var (
275 suppressedHeaders304 = []string{"Content-Type", "Content-Length", "Transfer-Encoding"}
276 suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"}
277)
278
279func suppressedHeaders(status int) []string {
280 switch {
281 case status == 304:
282 // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
283 return suppressedHeaders304
284 case !bodyAllowedForStatus(status):
285 return suppressedHeadersNoBody
286 }
287 return nil
288}
289
268// msg is *Request or *Response.290// msg is *Request or *Response.
269func readTransfer(msg interface{}, r *bufio.Reader) (err error) {291func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
270 t := &transferReader{RequestMethod: "GET"}292 t := &transferReader{RequestMethod: "GET"}
@@ -511,7 +533,7 @@
511 case "Transfer-Encoding", "Trailer", "Content-Length":533 case "Transfer-Encoding", "Trailer", "Content-Length":
512 return nil, &badStringError{"bad trailer key", key}534 return nil, &badStringError{"bad trailer key", key}
513 }535 }
514 trailer.Del(key)536 trailer[key] = nil
515 }537 }
516 if len(trailer) == 0 {538 if len(trailer) == 0 {
517 return nil, nil539 return nil, nil
@@ -643,13 +665,23 @@
643 }665 }
644 switch rr := b.hdr.(type) {666 switch rr := b.hdr.(type) {
645 case *Request:667 case *Request:
646 rr.Trailer = http.Header(hdr)668 mergeSetHeader(&rr.Trailer, http.Header(hdr))
647 case *Response:669 case *Response:
648 rr.Trailer = http.Header(hdr)670 mergeSetHeader(&rr.Trailer, http.Header(hdr))
649 }671 }
650 return nil672 return nil
651}673}
652674
675func mergeSetHeader(dst *http.Header, src http.Header) {
676 if *dst == nil {
677 *dst = src
678 return
679 }
680 for k, vv := range src {
681 (*dst)[k] = vv
682 }
683}
684
653func (b *body) Close() error {685func (b *body) Close() error {
654 b.mu.Lock()686 b.mu.Lock()
655 defer b.mu.Unlock()687 defer b.mu.Unlock()
656688
=== modified file 'http13client/transport.go'
--- http13client/transport.go 2014-03-20 09:26:28 +0000
+++ http13client/transport.go 2014-06-20 13:24:39 +0000
@@ -110,6 +110,9 @@
110// An error is returned if the proxy environment is invalid.110// An error is returned if the proxy environment is invalid.
111// A nil URL and nil error are returned if no proxy is defined in the111// A nil URL and nil error are returned if no proxy is defined in the
112// environment, or a proxy should not be used for the given request.112// environment, or a proxy should not be used for the given request.
113//
114// As a special case, if req.URL.Host is "localhost" (with or without
115// a port number), then a nil URL and nil error will be returned.
113func ProxyFromEnvironment(req *Request) (*url.URL, error) {116func ProxyFromEnvironment(req *Request) (*url.URL, error) {
114 proxy := httpProxyEnv.Get()117 proxy := httpProxyEnv.Get()
115 if proxy == "" {118 if proxy == "" {
@@ -161,9 +164,11 @@
161// and redirects), see Get, Post, and the Client type.164// and redirects), see Get, Post, and the Client type.
162func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {165func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
163 if req.URL == nil {166 if req.URL == nil {
167 req.closeBody()
164 return nil, errors.New("http: nil Request.URL")168 return nil, errors.New("http: nil Request.URL")
165 }169 }
166 if req.Header == nil {170 if req.Header == nil {
171 req.closeBody()
167 return nil, errors.New("http: nil Request.Header")172 return nil, errors.New("http: nil Request.Header")
168 }173 }
169 if req.URL.Scheme != "http" && req.URL.Scheme != "https" {174 if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
@@ -174,16 +179,19 @@
174 }179 }
175 t.altMu.RUnlock()180 t.altMu.RUnlock()
176 if rt == nil {181 if rt == nil {
182 req.closeBody()
177 return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}183 return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
178 }184 }
179 return rt.RoundTrip(req)185 return rt.RoundTrip(req)
180 }186 }
181 if req.URL.Host == "" {187 if req.URL.Host == "" {
188 req.closeBody()
182 return nil, errors.New("http: no Host in request URL")189 return nil, errors.New("http: no Host in request URL")
183 }190 }
184 treq := &transportRequest{Request: req}191 treq := &transportRequest{Request: req}
185 cm, err := t.connectMethodForRequest(treq)192 cm, err := t.connectMethodForRequest(treq)
186 if err != nil {193 if err != nil {
194 req.closeBody()
187 return nil, err195 return nil, err
188 }196 }
189197
@@ -194,6 +202,7 @@
194 pconn, err := t.getConn(req, cm)202 pconn, err := t.getConn(req, cm)
195 if err != nil {203 if err != nil {
196 t.setReqCanceler(req, nil)204 t.setReqCanceler(req, nil)
205 req.closeBody()
197 return nil, err206 return nil, err
198 }207 }
199208
@@ -231,9 +240,6 @@
231 t.idleConn = nil240 t.idleConn = nil
232 t.idleConnCh = nil241 t.idleConnCh = nil
233 t.idleMu.Unlock()242 t.idleMu.Unlock()
234 if m == nil {
235 return
236 }
237 for _, conns := range m {243 for _, conns := range m {
238 for _, pconn := range conns {244 for _, pconn := range conns {
239 pconn.close()245 pconn.close()
@@ -499,12 +505,13 @@
499 pa := cm.proxyAuth()505 pa := cm.proxyAuth()
500506
501 pconn := &persistConn{507 pconn := &persistConn{
502 t: t,508 t: t,
503 cacheKey: cm.key(),509 cacheKey: cm.key(),
504 conn: conn,510 conn: conn,
505 reqch: make(chan requestAndChan, 50),511 reqch: make(chan requestAndChan, 1),
506 writech: make(chan writeRequest, 50),512 writech: make(chan writeRequest, 1),
507 closech: make(chan struct{}),513 closech: make(chan struct{}),
514 writeErrCh: make(chan error, 1),
508 }515 }
509516
510 switch {517 switch {
@@ -589,7 +596,7 @@
589 pconn.conn = tlsConn596 pconn.conn = tlsConn
590 }597 }
591598
592 pconn.br = bufio.NewReader(pconn.conn)599 pconn.br = bufio.NewReader(noteEOFReader{pconn.conn, &pconn.sawEOF})
593 pconn.bw = bufio.NewWriter(pconn.conn)600 pconn.bw = bufio.NewWriter(pconn.conn)
594 go pconn.readLoop()601 go pconn.readLoop()
595 go pconn.writeLoop()602 go pconn.writeLoop()
@@ -722,16 +729,22 @@
722 cacheKey connectMethodKey729 cacheKey connectMethodKey
723 conn net.Conn730 conn net.Conn
724 tlsState *tls.ConnectionState731 tlsState *tls.ConnectionState
725 closed bool // whether conn has been closed
726 br *bufio.Reader // from conn732 br *bufio.Reader // from conn
733 sawEOF bool // whether we've seen EOF from conn; owned by readLoop
727 bw *bufio.Writer // to conn734 bw *bufio.Writer // to conn
728 reqch chan requestAndChan // written by roundTrip; read by readLoop735 reqch chan requestAndChan // written by roundTrip; read by readLoop
729 writech chan writeRequest // written by roundTrip; read by writeLoop736 writech chan writeRequest // written by roundTrip; read by writeLoop
730 closech chan struct{} // broadcast close when readLoop (TCP connection) closes737 closech chan struct{} // closed when conn closed
731 isProxy bool738 isProxy bool
739 // writeErrCh passes the request write error (usually nil)
740 // from the writeLoop goroutine to the readLoop which passes
741 // it off to the res.Body reader, which then uses it to decide
742 // whether or not a connection can be reused. Issue 7569.
743 writeErrCh chan error
732744
733 lk sync.Mutex // guards following 3 fields745 lk sync.Mutex // guards following fields
734 numExpectedResponses int746 numExpectedResponses int
747 closed bool // whether conn has been closed
735 broken bool // an error has happened on this connection; marked broken so it's not reused.748 broken bool // an error has happened on this connection; marked broken so it's not reused.
736 // mutateHeaderFunc is an optional func to modify extra749 // mutateHeaderFunc is an optional func to modify extra
737 // headers on each outbound request before it's written. (the750 // headers on each outbound request before it's written. (the
@@ -739,6 +752,7 @@
739 mutateHeaderFunc func(http.Header)752 mutateHeaderFunc func(http.Header)
740}753}
741754
755// isBroken reports whether this connection is in a known broken state.
742func (pc *persistConn) isBroken() bool {756func (pc *persistConn) isBroken() bool {
743 pc.lk.Lock()757 pc.lk.Lock()
744 b := pc.broken758 b := pc.broken
@@ -763,7 +777,6 @@
763}777}
764778
765func (pc *persistConn) readLoop() {779func (pc *persistConn) readLoop() {
766 defer close(pc.closech)
767 alive := true780 alive := true
768781
769 for alive {782 for alive {
@@ -771,12 +784,14 @@
771784
772 pc.lk.Lock()785 pc.lk.Lock()
773 if pc.numExpectedResponses == 0 {786 if pc.numExpectedResponses == 0 {
774 pc.closeLocked()787 if !pc.closed {
788 pc.closeLocked()
789 if len(pb) > 0 {
790 log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
791 string(pb), err)
792 }
793 }
775 pc.lk.Unlock()794 pc.lk.Unlock()
776 if len(pb) > 0 {
777 log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
778 string(pb), err)
779 }
780 return795 return
781 }796 }
782 pc.lk.Unlock()797 pc.lk.Unlock()
@@ -809,13 +824,7 @@
809 resp.Header.Del("Content-Encoding")824 resp.Header.Del("Content-Encoding")
810 resp.Header.Del("Content-Length")825 resp.Header.Del("Content-Length")
811 resp.ContentLength = -1826 resp.ContentLength = -1
812 gzReader, zerr := gzip.NewReader(resp.Body)827 resp.Body = &gzipReader{body: resp.Body}
813 if zerr != nil {
814 pc.close()
815 err = zerr
816 } else {
817 resp.Body = &readerAndCloser{gzReader, resp.Body}
818 }
819 }828 }
820 resp.Body = &bodyEOFSignal{body: resp.Body}829 resp.Body = &bodyEOFSignal{body: resp.Body}
821 }830 }
@@ -838,24 +847,18 @@
838 return nil847 return nil
839 }848 }
840 resp.Body.(*bodyEOFSignal).fn = func(err error) {849 resp.Body.(*bodyEOFSignal).fn = func(err error) {
841 alive1 := alive850 waitForBodyRead <- alive &&
842 if err != nil {851 err == nil &&
843 alive1 = false852 !pc.sawEOF &&
844 }853 pc.wroteRequest() &&
845 if alive1 && !pc.t.putIdleConn(pc) {854 pc.t.putIdleConn(pc)
846 alive1 = false
847 }
848 if !alive1 || pc.isBroken() {
849 pc.close()
850 }
851 waitForBodyRead <- alive1
852 }855 }
853 }856 }
854857
855 if alive && !hasBody {858 if alive && !hasBody {
856 if !pc.t.putIdleConn(pc) {859 alive = !pc.sawEOF &&
857 alive = false860 pc.wroteRequest() &&
858 }861 pc.t.putIdleConn(pc)
859 }862 }
860863
861 rc.ch <- responseAndError{resp, err}864 rc.ch <- responseAndError{resp, err}
@@ -863,7 +866,11 @@
863 // Wait for the just-returned response body to be fully consumed866 // Wait for the just-returned response body to be fully consumed
864 // before we race and peek on the underlying bufio reader.867 // before we race and peek on the underlying bufio reader.
865 if waitForBodyRead != nil {868 if waitForBodyRead != nil {
866 alive = <-waitForBodyRead869 select {
870 case alive = <-waitForBodyRead:
871 case <-pc.closech:
872 alive = false
873 }
867 }874 }
868875
869 pc.t.setReqCanceler(rc.req, nil)876 pc.t.setReqCanceler(rc.req, nil)
@@ -888,14 +895,44 @@
888 }895 }
889 if err != nil {896 if err != nil {
890 pc.markBroken()897 pc.markBroken()
898 wr.req.Request.closeBody()
891 }899 }
892 wr.ch <- err900 pc.writeErrCh <- err // to the body reader, which might recycle us
901 wr.ch <- err // to the roundTrip function
893 case <-pc.closech:902 case <-pc.closech:
894 return903 return
895 }904 }
896 }905 }
897}906}
898907
908// wroteRequest is a check before recycling a connection that the previous write
909// (from writeLoop above) happened and was successful.
910func (pc *persistConn) wroteRequest() bool {
911 select {
912 case err := <-pc.writeErrCh:
913 // Common case: the write happened well before the response, so
914 // avoid creating a timer.
915 return err == nil
916 default:
917 // Rare case: the request was written in writeLoop above but
918 // before it could send to pc.writeErrCh, the reader read it
919 // all, processed it, and called us here. In this case, give the
920 // write goroutine a bit of time to finish its send.
921 //
922 // Less rare case: We also get here in the legitimate case of
923 // Issue 7569, where the writer is still writing (or stalled),
924 // but the server has already replied. In this case, we don't
925 // want to wait too long, and we want to return false so this
926 // connection isn't re-used.
927 select {
928 case err := <-pc.writeErrCh:
929 return err == nil
930 case <-time.After(50 * time.Millisecond):
931 return false
932 }
933 }
934}
935
899type responseAndError struct {936type responseAndError struct {
900 res *Response937 res *Response
901 err error938 err error
@@ -1043,6 +1080,7 @@
1043 if !pc.closed {1080 if !pc.closed {
1044 pc.conn.Close()1081 pc.conn.Close()
1045 pc.closed = true1082 pc.closed = true
1083 close(pc.closech)
1046 }1084 }
1047 pc.mutateHeaderFunc = nil1085 pc.mutateHeaderFunc = nil
1048}1086}
@@ -1125,6 +1163,27 @@
1125 es.fn = nil1163 es.fn = nil
1126}1164}
11271165
1166// gzipReader wraps a response body so it can lazily
1167// call gzip.NewReader on the first call to Read
1168type gzipReader struct {
1169 body io.ReadCloser // underlying Response.Body
1170 zr io.Reader // lazily-initialized gzip reader
1171}
1172
1173func (gz *gzipReader) Read(p []byte) (n int, err error) {
1174 if gz.zr == nil {
1175 gz.zr, err = gzip.NewReader(gz.body)
1176 if err != nil {
1177 return 0, err
1178 }
1179 }
1180 return gz.zr.Read(p)
1181}
1182
1183func (gz *gzipReader) Close() error {
1184 return gz.body.Close()
1185}
1186
1128type readerAndCloser struct {1187type readerAndCloser struct {
1129 io.Reader1188 io.Reader
1130 io.Closer1189 io.Closer
@@ -1135,3 +1194,16 @@
1135func (tlsHandshakeTimeoutError) Timeout() bool { return true }1194func (tlsHandshakeTimeoutError) Timeout() bool { return true }
1136func (tlsHandshakeTimeoutError) Temporary() bool { return true }1195func (tlsHandshakeTimeoutError) Temporary() bool { return true }
1137func (tlsHandshakeTimeoutError) Error() string { return "net/http: TLS handshake timeout" }1196func (tlsHandshakeTimeoutError) Error() string { return "net/http: TLS handshake timeout" }
1197
1198type noteEOFReader struct {
1199 r io.Reader
1200 sawEOF *bool
1201}
1202
1203func (nr noteEOFReader) Read(p []byte) (n int, err error) {
1204 n, err = nr.r.Read(p)
1205 if err == io.EOF {
1206 *nr.sawEOF = true
1207 }
1208 return
1209}
11381210
=== modified file 'http13client/transport_test.go'
--- http13client/transport_test.go 2014-03-20 09:26:28 +0000
+++ http13client/transport_test.go 2014-06-20 13:24:39 +0000
@@ -11,6 +11,7 @@
11 "bytes"11 "bytes"
12 "compress/gzip"12 "compress/gzip"
13 "crypto/rand"13 "crypto/rand"
14 "crypto/tls"
14 "errors"15 "errors"
15 "fmt"16 "fmt"
16 "io"17 "io"
@@ -56,21 +57,21 @@
56// been closed.57// been closed.
57type testConnSet struct {58type testConnSet struct {
58 t *testing.T59 t *testing.T
60 mu sync.Mutex // guards closed and list
59 closed map[net.Conn]bool61 closed map[net.Conn]bool
60 list []net.Conn // in order created62 list []net.Conn // in order created
61 mutex sync.Mutex
62}63}
6364
64func (tcs *testConnSet) insert(c net.Conn) {65func (tcs *testConnSet) insert(c net.Conn) {
65 tcs.mutex.Lock()66 tcs.mu.Lock()
66 defer tcs.mutex.Unlock()67 defer tcs.mu.Unlock()
67 tcs.closed[c] = false68 tcs.closed[c] = false
68 tcs.list = append(tcs.list, c)69 tcs.list = append(tcs.list, c)
69}70}
7071
71func (tcs *testConnSet) remove(c net.Conn) {72func (tcs *testConnSet) remove(c net.Conn) {
72 tcs.mutex.Lock()73 tcs.mu.Lock()
73 defer tcs.mutex.Unlock()74 defer tcs.mu.Unlock()
74 tcs.closed[c] = true75 tcs.closed[c] = true
75}76}
7677
@@ -93,11 +94,19 @@
93}94}
9495
95func (tcs *testConnSet) check(t *testing.T) {96func (tcs *testConnSet) check(t *testing.T) {
96 tcs.mutex.Lock()97 tcs.mu.Lock()
97 defer tcs.mutex.Unlock()98 defer tcs.mu.Unlock()
9899 for i := 4; i >= 0; i-- {
99 for i, c := range tcs.list {100 for i, c := range tcs.list {
100 if !tcs.closed[c] {101 if tcs.closed[c] {
102 continue
103 }
104 if i != 0 {
105 tcs.mu.Unlock()
106 time.Sleep(50 * time.Millisecond)
107 tcs.mu.Lock()
108 continue
109 }
101 t.Errorf("TCP connection #%d, %p (of %d total) was not closed", i+1, c, len(tcs.list))110 t.Errorf("TCP connection #%d, %p (of %d total) was not closed", i+1, c, len(tcs.list))
102 }111 }
103 }112 }
@@ -794,6 +803,33 @@
794 }803 }
795}804}
796805
806// golang.org/issue/7750: request fails when server replies with
807// a short gzip body
808func TestTransportGzipShort(t *testing.T) {
809 defer afterTest(t)
810 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
811 w.Header().Set("Content-Encoding", "gzip")
812 w.Write([]byte{0x1f, 0x8b})
813 }))
814 defer ts.Close()
815
816 tr := &Transport{}
817 defer tr.CloseIdleConnections()
818 c := &Client{Transport: tr}
819 res, err := c.Get(ts.URL)
820 if err != nil {
821 t.Fatal(err)
822 }
823 defer res.Body.Close()
824 _, err = ioutil.ReadAll(res.Body)
825 if err == nil {
826 t.Fatal("Expect an error from reading a body.")
827 }
828 if err != io.ErrUnexpectedEOF {
829 t.Errorf("ReadAll error = %v; want io.ErrUnexpectedEOF", err)
830 }
831}
832
797// tests that persistent goroutine connections shut down when no longer desired.833// tests that persistent goroutine connections shut down when no longer desired.
798func TestTransportPersistConnLeak(t *testing.T) {834func TestTransportPersistConnLeak(t *testing.T) {
799 if runtime.GOOS == "plan9" {835 if runtime.GOOS == "plan9" {
@@ -1214,9 +1250,13 @@
1214 if testing.Short() {1250 if testing.Short() {
1215 t.Skip("skipping timeout test in -short mode")1251 t.Skip("skipping timeout test in -short mode")
1216 }1252 }
1253 inHandler := make(chan bool, 1)
1217 mux := http.NewServeMux()1254 mux := http.NewServeMux()
1218 mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {})1255 mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {
1256 inHandler <- true
1257 })
1219 mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {1258 mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
1259 inHandler <- true
1220 time.Sleep(2 * time.Second)1260 time.Sleep(2 * time.Second)
1221 })1261 })
1222 ts := httptest.NewServer(mux)1262 ts := httptest.NewServer(mux)
@@ -1239,6 +1279,12 @@
1239 }1279 }
1240 for i, tt := range tests {1280 for i, tt := range tests {
1241 res, err := c.Get(ts.URL + tt.path)1281 res, err := c.Get(ts.URL + tt.path)
1282 select {
1283 case <-inHandler:
1284 case <-time.After(5 * time.Second):
1285 t.Errorf("never entered handler for test index %d, %s", i, tt.path)
1286 continue
1287 }
1242 if err != nil {1288 if err != nil {
1243 uerr, ok := err.(*url.Error)1289 uerr, ok := err.(*url.Error)
1244 if !ok {1290 if !ok {
@@ -1366,7 +1412,6 @@
1366 case <-gotres:1412 case <-gotres:
1367 case <-time.After(5 * time.Second):1413 case <-time.After(5 * time.Second):
1368 panic("hang. events are: " + logbuf.String())1414 panic("hang. events are: " + logbuf.String())
1369 t.Fatal("timeout; cancel didn't work?")
1370 }1415 }
13711416
1372 got := logbuf.String()1417 got := logbuf.String()
@@ -1508,8 +1553,10 @@
1508 dialGate := make(chan bool, 1)1553 dialGate := make(chan bool, 1)
1509 tr := &Transport{1554 tr := &Transport{
1510 Dial: func(n, addr string) (net.Conn, error) {1555 Dial: func(n, addr string) (net.Conn, error) {
1511 <-dialGate1556 if <-dialGate {
1512 return net.Dial(n, addr)1557 return net.Dial(n, addr)
1558 }
1559 return nil, errors.New("manually closed")
1513 },1560 },
1514 DisableKeepAlives: false,1561 DisableKeepAlives: false,
1515 }1562 }
@@ -1544,7 +1591,7 @@
1544 t.Fatalf("/foo came from conn %q; /bar came from %q instead", fooAddr, barAddr)1591 t.Fatalf("/foo came from conn %q; /bar came from %q instead", fooAddr, barAddr)
1545 }1592 }
1546 barRes.Body.Close()1593 barRes.Body.Close()
1547 dialGate <- true1594 dialGate <- false
1548}1595}
15491596
1550// Issue 21841597// Issue 2184
@@ -1823,10 +1870,10 @@
1823 return1870 return
1824 }1871 }
1825 if !ne.Timeout() {1872 if !ne.Timeout() {
1826 t.Error("expected timeout error; got %v", err)1873 t.Errorf("expected timeout error; got %v", err)
1827 }1874 }
1828 if !strings.Contains(err.Error(), "handshake timeout") {1875 if !strings.Contains(err.Error(), "handshake timeout") {
1829 t.Error("expected 'handshake timeout' in error; got %v", err)1876 t.Errorf("expected 'handshake timeout' in error; got %v", err)
1830 }1877 }
1831 }()1878 }()
1832 select {1879 select {
@@ -1836,6 +1883,238 @@
1836 }1883 }
1837}1884}
18381885
1886// Trying to repro golang.org/issue/3514
1887func TestTLSServerClosesConnection(t *testing.T) {
1888 defer afterTest(t)
1889 if runtime.GOOS == "windows" {
1890 t.Skip("skipping flaky test on Windows; golang.org/issue/7634")
1891 }
1892 closedc := make(chan bool, 1)
1893 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1894 if strings.Contains(r.URL.Path, "/keep-alive-then-die") {
1895 conn, _, _ := w.(http.Hijacker).Hijack()
1896 conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
1897 conn.Close()
1898 closedc <- true
1899 return
1900 }
1901 fmt.Fprintf(w, "hello")
1902 }))
1903 defer ts.Close()
1904 tr := &Transport{
1905 TLSClientConfig: &tls.Config{
1906 InsecureSkipVerify: true,
1907 },
1908 }
1909 defer tr.CloseIdleConnections()
1910 client := &Client{Transport: tr}
1911
1912 var nSuccess = 0
1913 var errs []error
1914 const trials = 20
1915 for i := 0; i < trials; i++ {
1916 tr.CloseIdleConnections()
1917 res, err := client.Get(ts.URL + "/keep-alive-then-die")
1918 if err != nil {
1919 t.Fatal(err)
1920 }
1921 <-closedc
1922 slurp, err := ioutil.ReadAll(res.Body)
1923 if err != nil {
1924 t.Fatal(err)
1925 }
1926 if string(slurp) != "foo" {
1927 t.Errorf("Got %q, want foo", slurp)
1928 }
1929
1930 // Now try again and see if we successfully
1931 // pick a new connection.
1932 res, err = client.Get(ts.URL + "/")
1933 if err != nil {
1934 errs = append(errs, err)
1935 continue
1936 }
1937 slurp, err = ioutil.ReadAll(res.Body)
1938 if err != nil {
1939 errs = append(errs, err)
1940 continue
1941 }
1942 nSuccess++
1943 }
1944 if nSuccess > 0 {
1945 t.Logf("successes = %d of %d", nSuccess, trials)
1946 } else {
1947 t.Errorf("All runs failed:")
1948 }
1949 for _, err := range errs {
1950 t.Logf(" err: %v", err)
1951 }
1952}
1953
1954// byteFromChanReader is an io.Reader that reads a single byte at a
1955// time from the channel. When the channel is closed, the reader
1956// returns io.EOF.
1957type byteFromChanReader chan byte
1958
1959func (c byteFromChanReader) Read(p []byte) (n int, err error) {
1960 if len(p) == 0 {
1961 return
1962 }
1963 b, ok := <-c
1964 if !ok {
1965 return 0, io.EOF
1966 }
1967 p[0] = b
1968 return 1, nil
1969}
1970
1971// Verifies that the Transport doesn't reuse a connection in the case
1972// where the server replies before the request has been fully
1973// written. We still honor that reply (see TestIssue3595), but don't
1974// send future requests on the connection because it's then in a
1975// questionable state.
1976// golang.org/issue/7569
1977func TestTransportNoReuseAfterEarlyResponse(t *testing.T) {
1978 defer afterTest(t)
1979 var sconn struct {
1980 sync.Mutex
1981 c net.Conn
1982 }
1983 var getOkay bool
1984 closeConn := func() {
1985 sconn.Lock()
1986 defer sconn.Unlock()
1987 if sconn.c != nil {
1988 sconn.c.Close()
1989 sconn.c = nil
1990 if !getOkay {
1991 t.Logf("Closed server connection")
1992 }
1993 }
1994 }
1995 defer closeConn()
1996
1997 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1998 if r.Method == "GET" {
1999 io.WriteString(w, "bar")
2000 return
2001 }
2002 conn, _, _ := w.(http.Hijacker).Hijack()
2003 sconn.Lock()
2004 sconn.c = conn
2005 sconn.Unlock()
2006 conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo")) // keep-alive
2007 go io.Copy(ioutil.Discard, conn)
2008 }))
2009 defer ts.Close()
2010 tr := &Transport{}
2011 defer tr.CloseIdleConnections()
2012 client := &Client{Transport: tr}
2013
2014 const bodySize = 256 << 10
2015 finalBit := make(byteFromChanReader, 1)
2016 req, _ := NewRequest("POST", ts.URL, io.MultiReader(io.LimitReader(neverEnding('x'), bodySize-1), finalBit))
2017 req.ContentLength = bodySize
2018 res, err := client.Do(req)
2019 if err := wantBody(res, err, "foo"); err != nil {
2020 t.Errorf("POST response: %v", err)
2021 }
2022 donec := make(chan bool)
2023 go func() {
2024 defer close(donec)
2025 res, err = client.Get(ts.URL)
2026 if err := wantBody(res, err, "bar"); err != nil {
2027 t.Errorf("GET response: %v", err)
2028 return
2029 }
2030 getOkay = true // suppress test noise
2031 }()
2032 time.AfterFunc(5*time.Second, closeConn)
2033 select {
2034 case <-donec:
2035 finalBit <- 'x' // unblock the writeloop of the first Post
2036 close(finalBit)
2037 case <-time.After(7 * time.Second):
2038 t.Fatal("timeout waiting for GET request to finish")
2039 }
2040}
2041
2042type errorReader struct {
2043 err error
2044}
2045
2046func (e errorReader) Read(p []byte) (int, error) { return 0, e.err }
2047
2048type closerFunc func() error
2049
2050func (f closerFunc) Close() error { return f() }
2051
2052// Issue 6981
2053func TestTransportClosesBodyOnError(t *testing.T) {
2054 if runtime.GOOS == "plan9" {
2055 t.Skip("skipping test; see http://golang.org/issue/7782")
2056 }
2057 defer afterTest(t)
2058 readBody := make(chan error, 1)
2059 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2060 _, err := ioutil.ReadAll(r.Body)
2061 readBody <- err
2062 }))
2063 defer ts.Close()
2064 fakeErr := errors.New("fake error")
2065 didClose := make(chan bool, 1)
2066 req, _ := NewRequest("POST", ts.URL, struct {
2067 io.Reader
2068 io.Closer
2069 }{
2070 io.MultiReader(io.LimitReader(neverEnding('x'), 1<<20), errorReader{fakeErr}),
2071 closerFunc(func() error {
2072 select {
2073 case didClose <- true:
2074 default:
2075 }
2076 return nil
2077 }),
2078 })
2079 res, err := DefaultClient.Do(req)
2080 if res != nil {
2081 defer res.Body.Close()
2082 }
2083 if err == nil || !strings.Contains(err.Error(), fakeErr.Error()) {
2084 t.Fatalf("Do error = %v; want something containing %q", err, fakeErr.Error())
2085 }
2086 select {
2087 case err := <-readBody:
2088 if err == nil {
2089 t.Errorf("Unexpected success reading request body from handler; want 'unexpected EOF reading trailer'")
2090 }
2091 case <-time.After(5 * time.Second):
2092 t.Error("timeout waiting for server handler to complete")
2093 }
2094 select {
2095 case <-didClose:
2096 default:
2097 t.Errorf("didn't see Body.Close")
2098 }
2099}
2100
2101func wantBody(res *Response, err error, want string) error {
2102 if err != nil {
2103 return err
2104 }
2105 slurp, err := ioutil.ReadAll(res.Body)
2106 if err != nil {
2107 return fmt.Errorf("error reading body: %v", err)
2108 }
2109 if string(slurp) != want {
2110 return fmt.Errorf("body = %q; want %q", slurp, want)
2111 }
2112 if err := res.Body.Close(); err != nil {
2113 return fmt.Errorf("body Close = %v", err)
2114 }
2115 return nil
2116}
2117
1839func newLocalListener(t *testing.T) net.Listener {2118func newLocalListener(t *testing.T) net.Listener {
1840 ln, err := net.Listen("tcp", "127.0.0.1:0")2119 ln, err := net.Listen("tcp", "127.0.0.1:0")
1841 if err != nil {2120 if err != nil {

Subscribers

People subscribed via source and target branches