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
1=== modified file 'debian/changelog'
2--- debian/changelog 2014-06-20 12:33:03 +0000
3+++ debian/changelog 2014-06-20 13:24:39 +0000
4@@ -3,6 +3,7 @@
5 [ Samuele Pedroni ]
6 * Support registering tokens and sending notifications with a token
7 * Register script and scripts unicast support
8+ * Update http13client from the actual go1.3 release
9
10 [ Roberto Alsina ]
11 * Make signing-helper generate a HTTP header instead of a querystring,
12
13=== modified file 'http13client/Makefile'
14--- http13client/Makefile 2014-03-20 12:15:36 +0000
15+++ http13client/Makefile 2014-06-20 13:24:39 +0000
16@@ -20,7 +20,8 @@
17
18 prune:
19 rm -rf example_test.go filetransport*.go fs*.go race.go range_test.go \
20- sniff*.go httptest httputil testdata triv.go jar.go status.go
21+ sniff*.go httptest httputil testdata triv.go jar.go status.go \
22+ cookie_test.go
23 sed -i -e 's+"launchpad.net/ubuntu-push/http13client/+"net/http/+' *.go
24
25 fix:
26
27=== modified file 'http13client/_patches/empty_server.patch'
28--- http13client/_patches/empty_server.patch 2014-03-19 23:13:58 +0000
29+++ http13client/_patches/empty_server.patch 2014-06-20 13:24:39 +0000
30@@ -1,6 +1,6 @@
31 === modified file 'http13client/serve_test.go'
32---- http13client/serve_test.go 2014-03-19 21:38:56 +0000
33-+++ http13client/serve_test.go 2014-03-19 22:27:37 +0000
34+--- http13client/serve_test.go 2014-06-20 11:00:47 +0000
35++++ http13client/serve_test.go 2014-06-20 12:00:22 +0000
36 @@ -2,60 +2,15 @@
37 // Use of this source code is governed by a BSD-style
38 // license that can be found in the LICENSE file.
39@@ -62,7 +62,7 @@
40
41 func (a dummyAddr) Network() string {
42 return string(a)
43-@@ -93,1289 +48,6 @@
44+@@ -93,1325 +48,6 @@
45 return nil
46 }
47
48@@ -906,31 +906,50 @@
49 -}
50 -
51 -type serverExpectTest struct {
52-- contentLength int // of request body
53+- contentLength int // of request body
54+- chunked bool
55 - expectation string // e.g. "100-continue"
56 - readBody bool // whether handler should read the body (if false, sends StatusUnauthorized)
57 - expectedResponse string // expected substring in first line of http response
58 -}
59 -
60+-func expectTest(contentLength int, expectation string, readBody bool, expectedResponse string) serverExpectTest {
61+- return serverExpectTest{
62+- contentLength: contentLength,
63+- expectation: expectation,
64+- readBody: readBody,
65+- expectedResponse: expectedResponse,
66+- }
67+-}
68+-
69 -var serverExpectTests = []serverExpectTest{
70 - // Normal 100-continues, case-insensitive.
71-- {100, "100-continue", true, "100 Continue"},
72-- {100, "100-cOntInUE", true, "100 Continue"},
73+- expectTest(100, "100-continue", true, "100 Continue"),
74+- expectTest(100, "100-cOntInUE", true, "100 Continue"),
75 -
76 - // No 100-continue.
77-- {100, "", true, "200 OK"},
78+- expectTest(100, "", true, "200 OK"),
79 -
80 - // 100-continue but requesting client to deny us,
81 - // so it never reads the body.
82-- {100, "100-continue", false, "401 Unauthorized"},
83+- expectTest(100, "100-continue", false, "401 Unauthorized"),
84 - // Likewise without 100-continue:
85-- {100, "", false, "401 Unauthorized"},
86+- expectTest(100, "", false, "401 Unauthorized"),
87 -
88 - // Non-standard expectations are failures
89-- {0, "a-pony", false, "417 Expectation Failed"},
90+- expectTest(0, "a-pony", false, "417 Expectation Failed"),
91 -
92-- // Expect-100 requested but no body
93-- {0, "100-continue", true, "400 Bad Request"},
94+- // Expect-100 requested but no body (is apparently okay: Issue 7625)
95+- expectTest(0, "100-continue", true, "200 OK"),
96+- // Expect-100 requested but handler doesn't read the body
97+- expectTest(0, "100-continue", false, "401 Unauthorized"),
98+- // Expect-100 continue with no body, but a chunked body.
99+- {
100+- expectation: "100-continue",
101+- readBody: true,
102+- chunked: true,
103+- expectedResponse: "100 Continue",
104+- },
105 -}
106 -
107 -// Tests that the server responds to the "Expect" request header
108@@ -959,21 +978,38 @@
109 -
110 - // Only send the body immediately if we're acting like an HTTP client
111 - // that doesn't send 100-continue expectations.
112-- writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue"
113+- writeBody := test.contentLength != 0 && strings.ToLower(test.expectation) != "100-continue"
114 -
115 - go func() {
116+- contentLen := fmt.Sprintf("Content-Length: %d", test.contentLength)
117+- if test.chunked {
118+- contentLen = "Transfer-Encoding: chunked"
119+- }
120 - _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+
121 - "Connection: close\r\n"+
122-- "Content-Length: %d\r\n"+
123+- "%s\r\n"+
124 - "Expect: %s\r\nHost: foo\r\n\r\n",
125-- test.readBody, test.contentLength, test.expectation)
126+- test.readBody, contentLen, test.expectation)
127 - if err != nil {
128 - t.Errorf("On test %#v, error writing request headers: %v", test, err)
129 - return
130 - }
131 - if writeBody {
132+- var targ io.WriteCloser = struct {
133+- io.Writer
134+- io.Closer
135+- }{
136+- conn,
137+- ioutil.NopCloser(nil),
138+- }
139+- if test.chunked {
140+- targ = httputil.NewChunkedWriter(conn)
141+- }
142 - body := strings.Repeat("A", test.contentLength)
143-- _, err = fmt.Fprint(conn, body)
144+- _, err = fmt.Fprint(targ, body)
145+- if err == nil {
146+- err = targ.Close()
147+- }
148 - if err != nil {
149 - if !test.readBody {
150 - // Server likely already hung up on us.
151@@ -1352,7 +1388,7 @@
152 type neverEnding byte
153
154 func (b neverEnding) Read(p []byte) (n int, err error) {
155-@@ -1384,1344 +56,3 @@
156+@@ -1420,1392 +56,3 @@
157 }
158 return len(p), nil
159 }
160@@ -2080,7 +2116,7 @@
161 - got := ht.rawResponse(req)
162 - wantStatus := fmt.Sprintf("%d %s", code, StatusText(code))
163 - if !strings.Contains(got, wantStatus) {
164-- t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, req, got)
165+- t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, wantStatus, req, got)
166 - } else if strings.Contains(got, "Content-Length") {
167 - t.Errorf("Code %d: Got a Content-Length from %q: %s", code, req, got)
168 - } else if strings.Contains(got, "stuff") {
169@@ -2090,6 +2126,21 @@
170 - }
171 -}
172 -
173+-func TestContentTypeOkayOn204(t *testing.T) {
174+- ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
175+- w.Header().Set("Content-Length", "123") // suppressed
176+- w.Header().Set("Content-Type", "foo/bar")
177+- w.WriteHeader(204)
178+- }))
179+- got := ht.rawResponse("GET / HTTP/1.1")
180+- if !strings.Contains(got, "Content-Type: foo/bar") {
181+- t.Errorf("Response = %q; want Content-Type: foo/bar", got)
182+- }
183+- if strings.Contains(got, "Content-Length: 123") {
184+- t.Errorf("Response = %q; don't want a Content-Length", got)
185+- }
186+-}
187+-
188 -// Issue 6995
189 -// A server Handler can receive a Request, and then turn around and
190 -// give a copy of that Request.Body out to the Transport (e.g. any
191@@ -2261,7 +2312,7 @@
192 - ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
193 - ts.Config.ConnState = func(c net.Conn, state ConnState) {
194 - if c == nil {
195-- t.Error("nil conn seen in state %s", state)
196+- t.Errorf("nil conn seen in state %s", state)
197 - return
198 - }
199 - mu.Lock()
200@@ -2397,6 +2448,39 @@
201 - }
202 -}
203 -
204+-// golang.org/issue/7856
205+-func TestServerEmptyBodyRace(t *testing.T) {
206+- defer afterTest(t)
207+- var n int32
208+- ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
209+- atomic.AddInt32(&n, 1)
210+- }))
211+- defer ts.Close()
212+- var wg sync.WaitGroup
213+- const reqs = 20
214+- for i := 0; i < reqs; i++ {
215+- wg.Add(1)
216+- go func() {
217+- defer wg.Done()
218+- res, err := Get(ts.URL)
219+- if err != nil {
220+- t.Error(err)
221+- return
222+- }
223+- defer res.Body.Close()
224+- _, err = io.Copy(ioutil.Discard, res.Body)
225+- if err != nil {
226+- t.Error(err)
227+- return
228+- }
229+- }()
230+- }
231+- wg.Wait()
232+- if got := atomic.LoadInt32(&n); got != reqs {
233+- t.Errorf("handler ran %d times; want %d", got, reqs)
234+- }
235+-}
236+-
237 -func TestServerConnStateNew(t *testing.T) {
238 - sawNew := false // if the test is buggy, we'll race on this variable.
239 - srv := &Server{
240@@ -2699,9 +2783,9 @@
241 -}
242
243 === modified file 'http13client/server.go'
244---- http13client/server.go 2014-03-19 20:20:19 +0000
245-+++ http13client/server.go 2014-03-19 22:27:37 +0000
246-@@ -2,1984 +2,17 @@
247+--- http13client/server.go 2014-06-20 11:00:47 +0000
248++++ http13client/server.go 2014-06-20 12:05:53 +0000
249+@@ -2,1976 +2,16 @@
250 // Use of this source code is governed by a BSD-style
251 // license that can be found in the LICENSE file.
252
253@@ -2723,7 +2807,7 @@
254 - "path"
255 - "runtime"
256 - "strconv"
257- "strings"
258+- "strings"
259 "sync"
260 - "sync/atomic"
261 - "time"
262@@ -3506,18 +3590,16 @@
263 - }
264 -
265 - code := w.status
266-- if !bodyAllowedForStatus(code) {
267-- // Must not have body.
268-- // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
269-- for _, k := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
270-- delHeader(k)
271-- }
272-- } else {
273+- if bodyAllowedForStatus(code) {
274 - // If no content type, apply sniffing algorithm to body.
275 - _, haveType := header["Content-Type"]
276 - if !haveType {
277 - setHeader.contentType = DetectContentType(p)
278 - }
279+- } else {
280+- for _, k := range suppressedHeaders(code) {
281+- delHeader(k)
282+- }
283 - }
284 -
285 - if _, ok := header["Date"]; !ok {
286@@ -3865,16 +3947,10 @@
287 - // Expect 100 Continue support
288 - req := w.req
289 - if req.expectsContinue() {
290-- if req.ProtoAtLeast(1, 1) {
291+- if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
292 - // Wrap the Body reader with one that replies on the connection
293 - req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
294 - }
295-- if req.ContentLength == 0 {
296-- w.Header().Set("Connection", "close")
297-- w.WriteHeader(StatusBadRequest)
298-- w.finishRequest()
299-- break
300-- }
301 - req.Header.Del("Expect")
302 - } else if req.Header.get("Expect") != "" {
303 - w.sendExpectationFailed()
304@@ -4685,11 +4761,11 @@
305 -}
306 +)
307
308- // eofReader is a non-nil io.ReadCloser that always returns EOF.
309- // It embeds a *strings.Reader so it still has a WriteTo method
310-@@ -1992,28 +25,6 @@
311- ioutil.NopCloser(nil),
312- }
313+ type eofReaderWithWriteTo struct{}
314+
315+@@ -1991,28 +31,6 @@
316+ // Verify that an io.Copy from an eofReader won't require a buffer.
317+ var _ io.WriterTo = eofReader
318
319 -// initNPNRequest is an HTTP handler that initializes certain
320 -// uninitialized fields in its *Request. Such partially-initialized
321
322=== modified file 'http13client/_patches/fix_code.patch'
323--- http13client/_patches/fix_code.patch 2014-03-19 23:13:58 +0000
324+++ http13client/_patches/fix_code.patch 2014-06-20 13:24:39 +0000
325@@ -1,6 +1,6 @@
326 === modified file 'http13client/client.go'
327---- http13client/client.go 2014-03-19 20:20:19 +0000
328-+++ http13client/client.go 2014-03-19 22:27:37 +0000
329+--- http13client/client.go 2014-06-20 11:00:47 +0000
330++++ http13client/client.go 2014-06-20 12:05:53 +0000
331 @@ -17,6 +17,7 @@
332 "io/ioutil"
333 "log"
334@@ -18,7 +18,7 @@
335
336 // Timeout specifies a time limit for requests made by this
337 // Client. The timeout includes connection time, any
338-@@ -177,7 +178,7 @@
339+@@ -184,7 +185,7 @@
340 // Headers, leaving it uninitialized. We guarantee to the
341 // Transport that this has been initialized, though.
342 if req.Header == nil {
343@@ -27,7 +27,7 @@
344 }
345
346 if u := req.URL.User; u != nil {
347-@@ -308,7 +309,7 @@
348+@@ -316,7 +317,7 @@
349 if ireq.Method == "POST" || ireq.Method == "PUT" {
350 nreq.Method = "GET"
351 }
352@@ -38,8 +38,8 @@
353 break
354
355 === modified file 'http13client/cookie.go'
356---- http13client/cookie.go 2014-03-19 20:20:19 +0000
357-+++ http13client/cookie.go 2014-03-19 22:27:37 +0000
358+--- http13client/cookie.go 2014-06-20 11:00:47 +0000
359++++ http13client/cookie.go 2014-06-20 12:05:53 +0000
360 @@ -5,10 +5,9 @@
361 package http
362
363@@ -94,7 +94,7 @@
364 Name: name,
365 Value: value,
366 Raw: line,
367-@@ -129,59 +108,12 @@
368+@@ -125,59 +104,12 @@
369 return cookies
370 }
371
372@@ -156,7 +156,7 @@
373 lines, ok := h["Cookie"]
374 if !ok {
375 return cookies
376-@@ -213,7 +145,7 @@
377+@@ -209,7 +141,7 @@
378 if !success {
379 continue
380 }
381@@ -167,8 +167,8 @@
382 }
383
384 === modified file 'http13client/header.go'
385---- http13client/header.go 2014-03-19 20:20:19 +0000
386-+++ http13client/header.go 2014-03-19 22:27:37 +0000
387+--- http13client/header.go 2014-06-20 11:00:47 +0000
388++++ http13client/header.go 2014-06-20 12:00:22 +0000
389 @@ -5,176 +5,9 @@
390 package http
391
392@@ -348,8 +348,8 @@
393 // token must be all lowercase.
394
395 === modified file 'http13client/request.go'
396---- http13client/request.go 2014-03-19 20:20:19 +0000
397-+++ http13client/request.go 2014-03-19 22:27:37 +0000
398+--- http13client/request.go 2014-06-20 11:00:47 +0000
399++++ http13client/request.go 2014-06-20 12:05:53 +0000
400 @@ -16,6 +16,7 @@
401 "io/ioutil"
402 "mime"
403@@ -358,25 +358,25 @@
404 "net/textproto"
405 "net/url"
406 "strconv"
407-@@ -103,7 +104,7 @@
408- // The request parser implements this by canonicalizing the
409- // name, making the first character and any characters
410- // following a hyphen uppercase and the rest lowercase.
411+@@ -121,7 +122,7 @@
412+ // added and may override values in Header.
413+ //
414+ // See the documentation for the Request.Write method.
415 - Header Header
416 + Header http.Header
417
418 // Body is the request's body.
419 //
420-@@ -164,7 +165,7 @@
421- // For server requests, Trailer is only populated after Body has been
422- // closed or fully consumed.
423- // Trailer support is only partially complete.
424+@@ -199,7 +200,7 @@
425+ // not mutate Trailer.
426+ //
427+ // Few HTTP clients, servers, or proxies support HTTP trailers.
428 - Trailer Header
429 + Trailer http.Header
430
431 // RemoteAddr allows HTTP servers and other software to record
432 // the network address that sent the request, usually for
433-@@ -204,7 +205,7 @@
434+@@ -239,7 +240,7 @@
435 }
436
437 // Cookies parses and returns the HTTP cookies sent with the request.
438@@ -385,7 +385,7 @@
439 return readCookies(r.Header, "")
440 }
441
442-@@ -212,7 +213,7 @@
443+@@ -247,7 +248,7 @@
444
445 // Cookie returns the named cookie provided in the request or
446 // ErrNoCookie if not found.
447@@ -394,7 +394,7 @@
448 for _, c := range readCookies(r.Header, name) {
449 return c, nil
450 }
451-@@ -223,7 +224,7 @@
452+@@ -258,7 +259,7 @@
453 // AddCookie does not attach more than one Cookie header field. That
454 // means all cookies, if any, are written into the same line,
455 // separated by semicolon.
456@@ -403,7 +403,7 @@
457 s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
458 if c := r.Header.Get("Cookie"); c != "" {
459 r.Header.Set("Cookie", c+"; "+s)
460-@@ -326,7 +327,7 @@
461+@@ -361,7 +362,7 @@
462 }
463
464 // extraHeaders may be nil
465@@ -412,7 +412,7 @@
466 host := req.Host
467 if host == "" {
468 if req.URL == nil {
469-@@ -456,7 +457,7 @@
470+@@ -490,7 +491,7 @@
471 Proto: "HTTP/1.1",
472 ProtoMajor: 1,
473 ProtoMinor: 1,
474@@ -421,7 +421,7 @@
475 Body: rc,
476 Host: u.Host,
477 }
478-@@ -571,7 +572,7 @@
479+@@ -605,7 +606,7 @@
480 if err != nil {
481 return nil, err
482 }
483@@ -430,7 +430,7 @@
484
485 // RFC2616: Must treat
486 // GET /index.html HTTP/1.1
487-@@ -582,7 +583,7 @@
488+@@ -616,7 +617,7 @@
489 // the same. In the second case, any Host line is ignored.
490 req.Host = req.URL.Host
491 if req.Host == "" {
492@@ -439,7 +439,7 @@
493 }
494 delete(req.Header, "Host")
495
496-@@ -630,12 +631,12 @@
497+@@ -638,12 +639,12 @@
498 //
499 // MaxBytesReader prevents clients from accidentally or maliciously
500 // sending a large request and wasting server resources.
501@@ -454,7 +454,7 @@
502 r io.ReadCloser // underlying reader
503 n int64 // max bytes remaining
504 stopped bool
505-@@ -645,9 +646,6 @@
506+@@ -653,9 +654,6 @@
507 if l.n <= 0 {
508 if !l.stopped {
509 l.stopped = true
510@@ -464,7 +464,7 @@
511 }
512 return 0, errors.New("http: request body too large")
513 }
514-@@ -852,16 +850,16 @@
515+@@ -858,18 +856,18 @@
516 }
517
518 func (r *Request) expectsContinue() bool {
519@@ -484,11 +484,13 @@
520 - return hasToken(r.Header.get("Connection"), "close")
521 + return hasToken(r.Header.Get("Connection"), "close")
522 }
523+
524+ func (r *Request) closeBody() {
525
526 === modified file 'http13client/response.go'
527---- http13client/response.go 2014-03-19 20:20:19 +0000
528-+++ http13client/response.go 2014-03-19 22:27:37 +0000
529-@@ -11,6 +11,7 @@
530+--- http13client/response.go 2014-06-20 11:00:47 +0000
531++++ http13client/response.go 2014-06-20 12:05:53 +0000
532+@@ -12,6 +12,7 @@
533 "crypto/tls"
534 "errors"
535 "io"
536@@ -496,7 +498,7 @@
537 "net/textproto"
538 "net/url"
539 "strconv"
540-@@ -40,7 +41,7 @@
541+@@ -41,7 +42,7 @@
542 // omitted from Header.
543 //
544 // Keys in the map are canonicalized (see CanonicalHeaderKey).
545@@ -505,7 +507,7 @@
546
547 // Body represents the response body.
548 //
549-@@ -69,7 +70,7 @@
550+@@ -71,7 +72,7 @@
551
552 // Trailer maps trailer keys to values, in the same
553 // format as the header.
554@@ -514,7 +516,7 @@
555
556 // The Request that was sent to obtain this Response.
557 // Request's Body is nil (having already been consumed).
558-@@ -84,7 +85,7 @@
559+@@ -86,7 +87,7 @@
560 }
561
562 // Cookies parses and returns the cookies set in the Set-Cookie headers.
563@@ -523,7 +525,7 @@
564 return readSetCookies(r.Header)
565 }
566
567-@@ -153,7 +154,7 @@
568+@@ -155,7 +156,7 @@
569 }
570 return nil, err
571 }
572@@ -532,7 +534,7 @@
573
574 fixPragmaCacheControl(resp.Header)
575
576-@@ -169,7 +170,7 @@
577+@@ -171,7 +172,7 @@
578 // Pragma: no-cache
579 // like
580 // Cache-Control: no-cache
581@@ -543,17 +545,17 @@
582 header["Cache-Control"] = []string{"no-cache"}
583
584 === modified file 'http13client/transfer.go'
585---- http13client/transfer.go 2014-03-19 20:20:19 +0000
586-+++ http13client/transfer.go 2014-03-19 22:27:37 +0000
587+--- http13client/transfer.go 2014-06-20 11:00:47 +0000
588++++ http13client/transfer.go 2014-06-20 12:05:53 +0000
589 @@ -11,6 +11,7 @@
590 "fmt"
591 "io"
592 "io/ioutil"
593 + "net/http"
594 "net/textproto"
595+ "sort"
596 "strconv"
597- "strings"
598-@@ -36,7 +37,7 @@
599+@@ -37,7 +38,7 @@
600 ContentLength int64 // -1 means unknown, 0 means exactly none
601 Close bool
602 TransferEncoding []string
603@@ -562,16 +564,16 @@
604 }
605
606 func newTransferWriter(r interface{}) (t *transferWriter, err error) {
607-@@ -174,7 +175,7 @@
608- io.WriteString(w, "Trailer: ")
609- needComma := false
610+@@ -171,7 +172,7 @@
611+ if t.Trailer != nil {
612+ keys := make([]string, 0, len(t.Trailer))
613 for k := range t.Trailer {
614 - k = CanonicalHeaderKey(k)
615 + k = http.CanonicalHeaderKey(k)
616 switch k {
617 case "Transfer-Encoding", "Trailer", "Content-Length":
618 return &badStringError{"invalid Trailer key", k}
619-@@ -237,7 +238,7 @@
620+@@ -243,7 +244,7 @@
621
622 type transferReader struct {
623 // Input
624@@ -580,7 +582,7 @@
625 StatusCode int
626 RequestMethod string
627 ProtoMajor int
628-@@ -247,7 +248,7 @@
629+@@ -253,7 +254,7 @@
630 ContentLength int64
631 TransferEncoding []string
632 Close bool
633@@ -589,7 +591,7 @@
634 }
635
636 // bodyAllowedForStatus reports whether a given response status code
637-@@ -308,7 +309,7 @@
638+@@ -330,7 +331,7 @@
639 return err
640 }
641 if isResponse && t.RequestMethod == "HEAD" {
642@@ -598,7 +600,7 @@
643 return err
644 } else {
645 t.ContentLength = n
646-@@ -386,7 +387,7 @@
647+@@ -408,7 +409,7 @@
648 func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
649
650 // Sanitize transfer encoding
651@@ -607,7 +609,7 @@
652 raw, present := header["Transfer-Encoding"]
653 if !present {
654 return nil, nil
655-@@ -429,7 +430,7 @@
656+@@ -451,7 +452,7 @@
657 // Determine the expected body length, using RFC 2616 Section 4.4. This
658 // function is not a method, because ultimately it should be shared by
659 // ReadResponse and ReadRequest.
660@@ -616,7 +618,7 @@
661
662 // Logic based on response type or status
663 if noBodyExpected(requestMethod) {
664-@@ -449,7 +450,7 @@
665+@@ -471,7 +472,7 @@
666 }
667
668 // Logic based on Content-Length
669@@ -625,7 +627,7 @@
670 if cl != "" {
671 n, err := parseContentLength(cl)
672 if err != nil {
673-@@ -475,18 +476,18 @@
674+@@ -497,18 +498,18 @@
675 // Determine whether to hang up after sending a request and body, or
676 // receiving a response and body
677 // 'header' is the request headers
678@@ -647,7 +649,7 @@
679 header.Del("Connection")
680 return true
681 }
682-@@ -495,17 +496,17 @@
683+@@ -517,17 +518,17 @@
684 }
685
686 // Parse the trailer header
687@@ -669,22 +671,28 @@
688 switch key {
689 case "Transfer-Encoding", "Trailer", "Content-Length":
690 return nil, &badStringError{"bad trailer key", key}
691-@@ -642,9 +643,9 @@
692+@@ -664,14 +665,14 @@
693 }
694 switch rr := b.hdr.(type) {
695 case *Request:
696-- rr.Trailer = Header(hdr)
697-+ rr.Trailer = http.Header(hdr)
698+- mergeSetHeader(&rr.Trailer, Header(hdr))
699++ mergeSetHeader(&rr.Trailer, http.Header(hdr))
700 case *Response:
701-- rr.Trailer = Header(hdr)
702-+ rr.Trailer = http.Header(hdr)
703+- mergeSetHeader(&rr.Trailer, Header(hdr))
704++ mergeSetHeader(&rr.Trailer, http.Header(hdr))
705 }
706 return nil
707 }
708+
709+-func mergeSetHeader(dst *Header, src Header) {
710++func mergeSetHeader(dst *http.Header, src http.Header) {
711+ if *dst == nil {
712+ *dst = src
713+ return
714
715 === modified file 'http13client/transport.go'
716---- http13client/transport.go 2014-03-19 20:20:19 +0000
717-+++ http13client/transport.go 2014-03-19 22:27:37 +0000
718+--- http13client/transport.go 2014-06-20 11:00:47 +0000
719++++ http13client/transport.go 2014-06-20 12:05:53 +0000
720 @@ -18,6 +18,7 @@
721 "io"
722 "log"
723@@ -693,7 +701,7 @@
724 "net/url"
725 "os"
726 "strings"
727-@@ -144,12 +145,12 @@
728+@@ -147,12 +148,12 @@
729 // optional extra headers to write.
730 type transportRequest struct {
731 *Request // original request, not to be mutated
732@@ -709,7 +717,7 @@
733 }
734 return tr.extra
735 }
736-@@ -512,7 +513,7 @@
737+@@ -519,7 +520,7 @@
738 case cm.targetScheme == "http":
739 pconn.isProxy = true
740 if pa != "" {
741@@ -718,7 +726,7 @@
742 h.Set("Proxy-Authorization", pa)
743 }
744 }
745-@@ -521,7 +522,7 @@
746+@@ -528,7 +529,7 @@
747 Method: "CONNECT",
748 URL: &url.URL{Opaque: cm.targetAddr},
749 Host: cm.targetAddr,
750@@ -727,7 +735,7 @@
751 }
752 if pa != "" {
753 connectReq.Header.Set("Proxy-Authorization", pa)
754-@@ -735,7 +736,7 @@
755+@@ -748,7 +749,7 @@
756 // mutateHeaderFunc is an optional func to modify extra
757 // headers on each outbound request before it's written. (the
758 // original Request given to RoundTrip is not modified)
759@@ -735,5 +743,5 @@
760 + mutateHeaderFunc func(http.Header)
761 }
762
763- func (pc *persistConn) isBroken() bool {
764+ // isBroken reports whether this connection is in a known broken state.
765
766
767=== modified file 'http13client/_patches/fix_status.patch'
768--- http13client/_patches/fix_status.patch 2014-03-19 23:43:25 +0000
769+++ http13client/_patches/fix_status.patch 2014-06-20 13:24:39 +0000
770@@ -1,7 +1,7 @@
771 === modified file 'http13client/client.go'
772---- http13client/client.go 2014-03-19 23:13:58 +0000
773-+++ http13client/client.go 2014-03-19 23:38:11 +0000
774-@@ -210,7 +210,7 @@
775+--- http13client/client.go 2014-06-20 12:46:25 +0000
776++++ http13client/client.go 2014-06-20 12:46:45 +0000
777+@@ -217,7 +217,7 @@
778 // automatically redirect.
779 func shouldRedirectGet(statusCode int) bool {
780 switch statusCode {
781@@ -10,7 +10,7 @@
782 return true
783 }
784 return false
785-@@ -220,7 +220,7 @@
786+@@ -227,7 +227,7 @@
787 // automatically redirect.
788 func shouldRedirectPost(statusCode int) bool {
789 switch statusCode {
790@@ -21,9 +21,9 @@
791 return false
792
793 === modified file 'http13client/client_test.go'
794---- http13client/client_test.go 2014-03-19 23:13:58 +0000
795-+++ http13client/client_test.go 2014-03-19 23:39:48 +0000
796-@@ -202,7 +202,7 @@
797+--- http13client/client_test.go 2014-06-20 12:46:25 +0000
798++++ http13client/client_test.go 2014-06-20 12:46:45 +0000
799+@@ -204,7 +204,7 @@
800 }
801 }
802 if n < 15 {
803@@ -32,7 +32,7 @@
804 return
805 }
806 fmt.Fprintf(w, "n=%d", n)
807-@@ -324,7 +324,7 @@
808+@@ -326,7 +326,7 @@
809 }
810 if r.URL.Path == "/" {
811 http.SetCookie(w, expectedCookies[1])
812@@ -41,7 +41,7 @@
813 } else {
814 http.SetCookie(w, expectedCookies[2])
815 w.Write([]byte("hello"))
816-@@ -783,7 +783,7 @@
817+@@ -785,7 +785,7 @@
818 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
819 if r.URL.Path == "/" {
820 sawRoot <- true
821@@ -50,7 +50,7 @@
822 return
823 }
824 if r.URL.Path == "/slow" {
825-@@ -844,7 +844,7 @@
826+@@ -846,7 +846,7 @@
827 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
828 saw <- r.RemoteAddr
829 if r.URL.Path == "/" {
830@@ -61,9 +61,9 @@
831 defer ts.Close()
832
833 === modified file 'http13client/request_test.go'
834---- http13client/request_test.go 2014-03-19 23:13:58 +0000
835-+++ http13client/request_test.go 2014-03-19 23:40:12 +0000
836-@@ -164,11 +164,11 @@
837+--- http13client/request_test.go 2014-06-20 12:46:25 +0000
838++++ http13client/request_test.go 2014-06-20 12:46:45 +0000
839+@@ -182,11 +182,11 @@
840 switch r.URL.Path {
841 case "/":
842 w.Header().Set("Location", "/foo/")
843@@ -79,9 +79,9 @@
844 defer ts.Close()
845
846 === modified file 'http13client/response.go'
847---- http13client/response.go 2014-03-19 23:13:58 +0000
848-+++ http13client/response.go 2014-03-19 23:38:56 +0000
849-@@ -204,9 +204,8 @@
850+--- http13client/response.go 2014-06-20 12:46:25 +0000
851++++ http13client/response.go 2014-06-20 12:46:45 +0000
852+@@ -205,9 +205,8 @@
853 // Status line
854 text := r.Status
855 if text == "" {
856@@ -94,10 +94,23 @@
857 }
858 }
859
860+=== modified file 'http13client/responsewrite_test.go'
861+--- http13client/responsewrite_test.go 2014-06-20 12:46:25 +0000
862++++ http13client/responsewrite_test.go 2014-06-20 12:47:05 +0000
863+@@ -197,7 +197,7 @@
864+ // there were two.
865+ {
866+ Response{
867+- StatusCode: StatusOK,
868++ StatusCode: http.StatusOK,
869+ ProtoMajor: 1,
870+ ProtoMinor: 1,
871+ Request: &Request{Method: "POST"},
872+
873 === modified file 'http13client/transport_test.go'
874---- http13client/transport_test.go 2014-03-19 23:13:58 +0000
875-+++ http13client/transport_test.go 2014-03-19 23:40:39 +0000
876-@@ -968,7 +968,7 @@
877+--- http13client/transport_test.go 2014-06-20 12:46:25 +0000
878++++ http13client/transport_test.go 2014-06-20 12:46:45 +0000
879+@@ -1004,7 +1004,7 @@
880 defer afterTest(t)
881 const deniedMsg = "sorry, denied."
882 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
883@@ -106,7 +119,7 @@
884 }))
885 defer ts.Close()
886 tr := &Transport{}
887-@@ -992,7 +992,7 @@
888+@@ -1028,7 +1028,7 @@
889 func TestChunkedNoContent(t *testing.T) {
890 defer afterTest(t)
891 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
892
893=== modified file 'http13client/_patches/fix_tests.patch'
894--- http13client/_patches/fix_tests.patch 2014-03-19 23:13:58 +0000
895+++ http13client/_patches/fix_tests.patch 2014-06-20 13:24:39 +0000
896@@ -1,6 +1,6 @@
897 === modified file 'http13client/client_test.go'
898---- http13client/client_test.go 2014-03-19 21:38:56 +0000
899-+++ http13client/client_test.go 2014-03-19 22:27:37 +0000
900+--- http13client/client_test.go 2014-06-20 11:00:47 +0000
901++++ http13client/client_test.go 2014-06-20 12:05:53 +0000
902 @@ -15,8 +15,8 @@
903 "fmt"
904 "io"
905@@ -11,7 +11,7 @@
906 . "launchpad.net/ubuntu-push/http13client"
907 "net/http/httptest"
908 "net/url"
909-@@ -27,7 +27,7 @@
910+@@ -29,7 +29,7 @@
911 "time"
912 )
913
914@@ -20,7 +20,7 @@
915 w.Header().Set("Last-Modified", "sometime")
916 fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
917 })
918-@@ -193,7 +193,7 @@
919+@@ -195,7 +195,7 @@
920 func TestClientRedirects(t *testing.T) {
921 defer afterTest(t)
922 var ts *httptest.Server
923@@ -29,7 +29,7 @@
924 n, _ := strconv.Atoi(r.FormValue("n"))
925 // Test Referer header. (7 is arbitrary position to test at)
926 if n == 7 {
927-@@ -202,7 +202,7 @@
928+@@ -204,7 +204,7 @@
929 }
930 }
931 if n < 15 {
932@@ -38,7 +38,7 @@
933 return
934 }
935 fmt.Fprintf(w, "n=%d", n)
936-@@ -271,7 +271,7 @@
937+@@ -273,7 +273,7 @@
938 bytes.Buffer
939 }
940 var ts *httptest.Server
941@@ -47,7 +47,7 @@
942 log.Lock()
943 fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
944 log.Unlock()
945-@@ -312,21 +312,21 @@
946+@@ -314,21 +314,21 @@
947 }
948 }
949
950@@ -75,7 +75,7 @@
951 w.Write([]byte("hello"))
952 }
953 })
954-@@ -334,7 +334,7 @@
955+@@ -336,7 +336,7 @@
956 func TestClientSendsCookieFromJar(t *testing.T) {
957 tr := &recordingTransport{}
958 client := &Client{Transport: tr}
959@@ -84,7 +84,7 @@
960 us := "http://dummy.faketld/"
961 u, _ := url.Parse(us)
962 client.Jar.SetCookies(u, expectedCookies)
963-@@ -364,19 +364,19 @@
964+@@ -366,19 +366,19 @@
965 // scope of all cookies.
966 type TestJar struct {
967 m sync.Mutex
968@@ -108,7 +108,7 @@
969 j.m.Lock()
970 defer j.m.Unlock()
971 return j.perURL[u.Host]
972-@@ -391,7 +391,7 @@
973+@@ -393,7 +393,7 @@
974 Jar: new(TestJar),
975 }
976 u, _ := url.Parse(ts.URL)
977@@ -117,7 +117,7 @@
978 resp, err := c.Get(ts.URL)
979 if err != nil {
980 t.Fatalf("Get: %v", err)
981-@@ -400,7 +400,7 @@
982+@@ -402,7 +402,7 @@
983 matchReturnedCookies(t, expectedCookies, resp.Cookies())
984 }
985
986@@ -126,7 +126,7 @@
987 if len(given) != len(expected) {
988 t.Logf("Received cookies: %v", given)
989 t.Errorf("Expected %d cookies, got %d", len(expected), len(given))
990-@@ -421,14 +421,14 @@
991+@@ -423,14 +423,14 @@
992
993 func TestJarCalls(t *testing.T) {
994 defer afterTest(t)
995@@ -144,7 +144,7 @@
996 }
997 }))
998 defer ts.Close()
999-@@ -468,11 +468,11 @@
1000+@@ -470,11 +470,11 @@
1001 log bytes.Buffer
1002 }
1003
1004@@ -158,7 +158,7 @@
1005 j.logf("Cookies(%q)\n", u)
1006 return nil
1007 }
1008-@@ -486,11 +486,11 @@
1009+@@ -488,11 +488,11 @@
1010 func TestStreamingGet(t *testing.T) {
1011 defer afterTest(t)
1012 say := make(chan string)
1013@@ -173,7 +173,7 @@
1014 }
1015 }))
1016 defer ts.Close()
1017-@@ -536,7 +536,7 @@
1018+@@ -538,7 +538,7 @@
1019 // don't send a TCP packet per line of the http request + body.
1020 func TestClientWrites(t *testing.T) {
1021 defer afterTest(t)
1022@@ -182,7 +182,7 @@
1023 }))
1024 defer ts.Close()
1025
1026-@@ -568,46 +568,6 @@
1027+@@ -570,46 +570,6 @@
1028 }
1029 }
1030
1031@@ -229,7 +229,7 @@
1032 func TestClientErrorWithRequestURI(t *testing.T) {
1033 defer afterTest(t)
1034 req, _ := NewRequest("GET", "http://localhost:1234/", nil)
1035-@@ -639,7 +599,7 @@
1036+@@ -641,7 +601,7 @@
1037
1038 func TestClientWithCorrectTLSServerName(t *testing.T) {
1039 defer afterTest(t)
1040@@ -238,7 +238,7 @@
1041 if r.TLS.ServerName != "127.0.0.1" {
1042 t.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r.TLS.ServerName)
1043 }
1044-@@ -652,33 +612,6 @@
1045+@@ -654,33 +614,6 @@
1046 }
1047 }
1048
1049@@ -272,7 +272,7 @@
1050 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
1051 // when not empty.
1052 //
1053-@@ -690,7 +623,7 @@
1054+@@ -692,7 +625,7 @@
1055 // The httptest.Server has a cert with "example.com" as its name.
1056 func TestTransportUsesTLSConfigServerName(t *testing.T) {
1057 defer afterTest(t)
1058@@ -281,7 +281,7 @@
1059 w.Write([]byte("Hello"))
1060 }))
1061 defer ts.Close()
1062-@@ -711,7 +644,7 @@
1063+@@ -713,7 +646,7 @@
1064
1065 func TestResponseSetsTLSConnectionState(t *testing.T) {
1066 defer afterTest(t)
1067@@ -290,7 +290,7 @@
1068 w.Write([]byte("Hello"))
1069 }))
1070 defer ts.Close()
1071-@@ -739,7 +672,7 @@
1072+@@ -741,7 +674,7 @@
1073 // Verify Response.ContentLength is populated. http://golang.org/issue/4126
1074 func TestClientHeadContentLength(t *testing.T) {
1075 defer afterTest(t)
1076@@ -299,7 +299,7 @@
1077 if v := r.FormValue("cl"); v != "" {
1078 w.Header().Set("Content-Length", v)
1079 }
1080-@@ -775,7 +708,7 @@
1081+@@ -777,7 +710,7 @@
1082 func TestEmptyPasswordAuth(t *testing.T) {
1083 defer afterTest(t)
1084 gopher := "gopher"
1085@@ -308,7 +308,7 @@
1086 auth := r.Header.Get("Authorization")
1087 if strings.HasPrefix(auth, "Basic ") {
1088 encoded := auth[6:]
1089-@@ -847,15 +780,15 @@
1090+@@ -849,15 +782,15 @@
1091 defer afterTest(t)
1092 sawRoot := make(chan bool, 1)
1093 sawSlow := make(chan bool, 1)
1094@@ -327,7 +327,7 @@
1095 sawSlow <- true
1096 time.Sleep(2 * time.Second)
1097 return
1098-@@ -908,10 +841,10 @@
1099+@@ -910,10 +843,10 @@
1100 func TestClientRedirectEatsBody(t *testing.T) {
1101 defer afterTest(t)
1102 saw := make(chan string, 2)
1103@@ -340,233 +340,50 @@
1104 }
1105 }))
1106 defer ts.Close()
1107-
1108-=== modified file 'http13client/cookie_test.go'
1109---- http13client/cookie_test.go 2014-03-19 20:20:19 +0000
1110-+++ http13client/cookie_test.go 2014-03-19 22:27:37 +0000
1111-@@ -9,6 +9,7 @@
1112- "encoding/json"
1113- "fmt"
1114- "log"
1115-+ "net/http"
1116- "os"
1117- "reflect"
1118- "strings"
1119-@@ -17,39 +18,39 @@
1120- )
1121-
1122- var writeSetCookiesTests = []struct {
1123-- Cookie *Cookie
1124-+ Cookie *http.Cookie
1125- Raw string
1126- }{
1127- {
1128-- &Cookie{Name: "cookie-1", Value: "v$1"},
1129-+ &http.Cookie{Name: "cookie-1", Value: "v$1"},
1130- "cookie-1=v$1",
1131- },
1132- {
1133-- &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
1134-+ &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
1135- "cookie-2=two; Max-Age=3600",
1136- },
1137- {
1138-- &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
1139-+ &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
1140- "cookie-3=three; Domain=example.com",
1141- },
1142- {
1143-- &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
1144-+ &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
1145- "cookie-4=four; Path=/restricted/",
1146- },
1147- {
1148-- &Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
1149-+ &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
1150- "cookie-5=five",
1151- },
1152- {
1153-- &Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
1154-+ &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
1155- "cookie-6=six",
1156- },
1157- {
1158-- &Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
1159-+ &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
1160- "cookie-7=seven; Domain=127.0.0.1",
1161- },
1162- {
1163-- &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
1164-+ &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
1165- "cookie-8=eight",
1166- },
1167- }
1168-@@ -71,10 +72,10 @@
1169- }
1170- }
1171-
1172--type headerOnlyResponseWriter Header
1173-+type headerOnlyResponseWriter http.Header
1174-
1175--func (ho headerOnlyResponseWriter) Header() Header {
1176-- return Header(ho)
1177-+func (ho headerOnlyResponseWriter) Header() http.Header {
1178-+ return http.Header(ho)
1179- }
1180-
1181- func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
1182-@@ -86,9 +87,9 @@
1183- }
1184-
1185- func TestSetCookie(t *testing.T) {
1186-- m := make(Header)
1187-- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
1188-- SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
1189-+ m := make(http.Header)
1190-+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
1191-+ http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
1192- if l := len(m["Set-Cookie"]); l != 2 {
1193- t.Fatalf("expected %d cookies, got %d", 2, l)
1194- }
1195-@@ -101,19 +102,19 @@
1196- }
1197-
1198- var addCookieTests = []struct {
1199-- Cookies []*Cookie
1200-+ Cookies []*http.Cookie
1201- Raw string
1202- }{
1203- {
1204-- []*Cookie{},
1205-+ []*http.Cookie{},
1206- "",
1207- },
1208- {
1209-- []*Cookie{{Name: "cookie-1", Value: "v$1"}},
1210-+ []*http.Cookie{{Name: "cookie-1", Value: "v$1"}},
1211- "cookie-1=v$1",
1212- },
1213- {
1214-- []*Cookie{
1215-+ []*http.Cookie{
1216- {Name: "cookie-1", Value: "v$1"},
1217- {Name: "cookie-2", Value: "v$2"},
1218- {Name: "cookie-3", Value: "v$3"},
1219-@@ -136,16 +137,16 @@
1220- }
1221-
1222- var readSetCookiesTests = []struct {
1223-- Header Header
1224-- Cookies []*Cookie
1225-+ Header http.Header
1226-+ Cookies []*http.Cookie
1227- }{
1228- {
1229-- Header{"Set-Cookie": {"Cookie-1=v$1"}},
1230-- []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
1231-+ http.Header{"Set-Cookie": {"Cookie-1=v$1"}},
1232-+ []*http.Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
1233- },
1234- {
1235-- Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
1236-- []*Cookie{{
1237-+ http.Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
1238-+ []*http.Cookie{{
1239- Name: "NID",
1240- Value: "99=YsDT5i3E-CXax-",
1241- Path: "/",
1242-@@ -157,8 +158,8 @@
1243- }},
1244- },
1245- {
1246-- Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
1247-- []*Cookie{{
1248-+ http.Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
1249-+ []*http.Cookie{{
1250- Name: ".ASPXAUTH",
1251- Value: "7E3AA",
1252- Path: "/",
1253-@@ -169,8 +170,8 @@
1254- }},
1255- },
1256- {
1257-- Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
1258-- []*Cookie{{
1259-+ http.Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
1260-+ []*http.Cookie{{
1261- Name: "ASP.NET_SessionId",
1262- Value: "foo",
1263- Path: "/",
1264-@@ -207,37 +208,37 @@
1265- }
1266-
1267- var readCookiesTests = []struct {
1268-- Header Header
1269-+ Header http.Header
1270- Filter string
1271-- Cookies []*Cookie
1272-+ Cookies []*http.Cookie
1273- }{
1274- {
1275-- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
1276-- "",
1277-- []*Cookie{
1278-- {Name: "Cookie-1", Value: "v$1"},
1279-- {Name: "c2", Value: "v2"},
1280-- },
1281-- },
1282-- {
1283-- Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
1284-- "c2",
1285-- []*Cookie{
1286-- {Name: "c2", Value: "v2"},
1287-- },
1288-- },
1289-- {
1290-- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
1291-- "",
1292-- []*Cookie{
1293-- {Name: "Cookie-1", Value: "v$1"},
1294-- {Name: "c2", Value: "v2"},
1295-- },
1296-- },
1297-- {
1298-- Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
1299-- "c2",
1300-- []*Cookie{
1301-+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
1302-+ "",
1303-+ []*http.Cookie{
1304-+ {Name: "Cookie-1", Value: "v$1"},
1305-+ {Name: "c2", Value: "v2"},
1306-+ },
1307-+ },
1308-+ {
1309-+ http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
1310-+ "c2",
1311-+ []*http.Cookie{
1312-+ {Name: "c2", Value: "v2"},
1313-+ },
1314-+ },
1315-+ {
1316-+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
1317-+ "",
1318-+ []*http.Cookie{
1319-+ {Name: "Cookie-1", Value: "v$1"},
1320-+ {Name: "c2", Value: "v2"},
1321-+ },
1322-+ },
1323-+ {
1324-+ http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
1325-+ "c2",
1326-+ []*http.Cookie{
1327- {Name: "c2", Value: "v2"},
1328- },
1329- },
1330+@@ -957,7 +890,7 @@
1331+
1332+ func TestClientTrailers(t *testing.T) {
1333+ defer afterTest(t)
1334+- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1335++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1336+ w.Header().Set("Connection", "close")
1337+ w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
1338+ w.Header().Add("Trailer", "Server-Trailer-C")
1339+@@ -992,9 +925,9 @@
1340+ // trailers to be sent, if and only if they were
1341+ // previously declared with w.Header().Set("Trailer",
1342+ // ..keys..)
1343+- w.(Flusher).Flush()
1344+- conn, buf, _ := w.(Hijacker).Hijack()
1345+- t := Header{}
1346++ w.(http.Flusher).Flush()
1347++ conn, buf, _ := w.(http.Hijacker).Hijack()
1348++ t := http.Header{}
1349+ t.Set("Server-Trailer-A", "valuea")
1350+ t.Set("Server-Trailer-C", "valuec") // skipping B
1351+ buf.WriteString("0\r\n") // eof
1352+@@ -1015,7 +948,7 @@
1353+ req.Trailer["Client-Trailer-B"] = []string{"valueb"}
1354+ }),
1355+ ))
1356+- req.Trailer = Header{
1357++ req.Trailer = http.Header{
1358+ "Client-Trailer-A": nil, // to be set later
1359+ "Client-Trailer-B": nil, // to be set later
1360+ }
1361+@@ -1027,7 +960,7 @@
1362+ if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil {
1363+ t.Error(err)
1364+ }
1365+- want := Header{
1366++ want := http.Header{
1367+ "Server-Trailer-A": []string{"valuea"},
1368+ "Server-Trailer-B": nil,
1369+ "Server-Trailer-C": []string{"valuec"},
1370
1371 === modified file 'http13client/export_test.go'
1372---- http13client/export_test.go 2014-03-19 20:20:19 +0000
1373-+++ http13client/export_test.go 2014-03-19 22:27:37 +0000
1374+--- http13client/export_test.go 2014-06-20 11:00:47 +0000
1375++++ http13client/export_test.go 2014-06-20 12:00:22 +0000
1376 @@ -9,15 +9,12 @@
1377
1378 import (
1379@@ -599,8 +416,8 @@
1380 noProxyEnv.reset()
1381
1382 === modified file 'http13client/header_test.go'
1383---- http13client/header_test.go 2014-03-19 20:20:19 +0000
1384-+++ http13client/header_test.go 2014-03-19 22:27:37 +0000
1385+--- http13client/header_test.go 2014-06-20 11:00:47 +0000
1386++++ http13client/header_test.go 2014-06-20 12:00:22 +0000
1387 @@ -6,19 +6,20 @@
1388
1389 import (
1390@@ -731,8 +548,8 @@
1391 }
1392
1393 === modified file 'http13client/npn_test.go'
1394---- http13client/npn_test.go 2014-03-19 21:38:56 +0000
1395-+++ http13client/npn_test.go 2014-03-19 22:27:37 +0000
1396+--- http13client/npn_test.go 2014-06-20 11:00:47 +0000
1397++++ http13client/npn_test.go 2014-06-20 12:05:53 +0000
1398 @@ -11,13 +11,14 @@
1399 "io"
1400 "io/ioutil"
1401@@ -792,8 +609,8 @@
1402 func (w http09Writer) WriteHeader(int) {} // no headers
1403
1404 === modified file 'http13client/readrequest_test.go'
1405---- http13client/readrequest_test.go 2014-03-19 20:20:19 +0000
1406-+++ http13client/readrequest_test.go 2014-03-19 22:27:37 +0000
1407+--- http13client/readrequest_test.go 2014-06-20 11:00:47 +0000
1408++++ http13client/readrequest_test.go 2014-06-20 12:00:22 +0000
1409 @@ -9,6 +9,7 @@
1410 "bytes"
1411 "fmt"
1412@@ -909,8 +726,8 @@
1413 Close: false,
1414
1415 === modified file 'http13client/request_test.go'
1416---- http13client/request_test.go 2014-03-19 21:38:56 +0000
1417-+++ http13client/request_test.go 2014-03-19 22:27:37 +0000
1418+--- http13client/request_test.go 2014-06-20 11:00:47 +0000
1419++++ http13client/request_test.go 2014-06-20 12:05:53 +0000
1420 @@ -12,6 +12,7 @@
1421 "io/ioutil"
1422 "mime/multipart"
1423@@ -945,8 +762,26 @@
1424 + req.Header = http.Header{"Content-Type": {"text/plain"}}
1425 multipart, err = req.MultipartReader()
1426 if multipart != nil {
1427- t.Errorf("unexpected multipart for text/plain")
1428-@@ -159,7 +160,7 @@
1429+ t.Error("unexpected multipart for text/plain")
1430+@@ -161,7 +162,7 @@
1431+ func TestParseMultipartForm(t *testing.T) {
1432+ req := &Request{
1433+ Method: "POST",
1434+- Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
1435++ Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
1436+ Body: ioutil.NopCloser(new(bytes.Buffer)),
1437+ }
1438+ err := req.ParseMultipartForm(25)
1439+@@ -169,7 +170,7 @@
1440+ t.Error("expected multipart EOF, got nil")
1441+ }
1442+
1443+- req.Header = Header{"Content-Type": {"text/plain"}}
1444++ req.Header = http.Header{"Content-Type": {"text/plain"}}
1445+ err = req.ParseMultipartForm(25)
1446+ if err != ErrNotMultipart {
1447+ t.Error("expected ErrNotMultipart for text/plain")
1448+@@ -177,7 +178,7 @@
1449 }
1450
1451 func TestRedirect(t *testing.T) {
1452@@ -957,8 +792,8 @@
1453 w.Header().Set("Location", "/foo/")
1454
1455 === modified file 'http13client/requestwrite_test.go'
1456---- http13client/requestwrite_test.go 2014-03-19 20:20:19 +0000
1457-+++ http13client/requestwrite_test.go 2014-03-19 22:27:37 +0000
1458+--- http13client/requestwrite_test.go 2014-06-20 11:00:47 +0000
1459++++ http13client/requestwrite_test.go 2014-06-20 12:00:22 +0000
1460 @@ -10,6 +10,7 @@
1461 "fmt"
1462 "io"
1463@@ -1068,8 +903,8 @@
1464 var braw bytes.Buffer
1465
1466 === modified file 'http13client/response_test.go'
1467---- http13client/response_test.go 2014-03-19 20:20:19 +0000
1468-+++ http13client/response_test.go 2014-03-19 22:27:37 +0000
1469+--- http13client/response_test.go 2014-06-20 11:00:47 +0000
1470++++ http13client/response_test.go 2014-06-20 12:05:53 +0000
1471 @@ -12,6 +12,7 @@
1472 "fmt"
1473 "io"
1474@@ -1078,7 +913,7 @@
1475 "net/url"
1476 "reflect"
1477 "regexp"
1478-@@ -44,7 +45,7 @@
1479+@@ -48,7 +49,7 @@
1480 ProtoMajor: 1,
1481 ProtoMinor: 0,
1482 Request: dummyReq("GET"),
1483@@ -1087,7 +922,7 @@
1484 "Connection": {"close"}, // TODO(rsc): Delete?
1485 },
1486 Close: true,
1487-@@ -67,7 +68,7 @@
1488+@@ -71,7 +72,7 @@
1489 Proto: "HTTP/1.1",
1490 ProtoMajor: 1,
1491 ProtoMinor: 1,
1492@@ -1096,7 +931,7 @@
1493 Request: dummyReq("GET"),
1494 Close: true,
1495 ContentLength: -1,
1496-@@ -88,7 +89,7 @@
1497+@@ -92,7 +93,7 @@
1498 Proto: "HTTP/1.1",
1499 ProtoMajor: 1,
1500 ProtoMinor: 1,
1501@@ -1105,7 +940,7 @@
1502 Request: dummyReq("GET"),
1503 Close: false,
1504 ContentLength: 0,
1505-@@ -112,7 +113,7 @@
1506+@@ -116,7 +117,7 @@
1507 ProtoMajor: 1,
1508 ProtoMinor: 0,
1509 Request: dummyReq("GET"),
1510@@ -1114,61 +949,61 @@
1511 "Connection": {"close"},
1512 "Content-Length": {"10"},
1513 },
1514-@@ -142,7 +143,7 @@
1515- ProtoMajor: 1,
1516- ProtoMinor: 1,
1517- Request: dummyReq("GET"),
1518-- Header: Header{},
1519-+ Header: http.Header{},
1520- Close: false,
1521- ContentLength: -1,
1522- TransferEncoding: []string{"chunked"},
1523-@@ -169,7 +170,7 @@
1524- ProtoMajor: 1,
1525- ProtoMinor: 1,
1526- Request: dummyReq("GET"),
1527-- Header: Header{},
1528-+ Header: http.Header{},
1529- Close: false,
1530- ContentLength: -1,
1531- TransferEncoding: []string{"chunked"},
1532-@@ -191,7 +192,7 @@
1533- ProtoMajor: 1,
1534- ProtoMinor: 1,
1535- Request: dummyReq("HEAD"),
1536-- Header: Header{},
1537-+ Header: http.Header{},
1538- TransferEncoding: []string{"chunked"},
1539- Close: false,
1540- ContentLength: -1,
1541-@@ -213,7 +214,7 @@
1542- ProtoMajor: 1,
1543- ProtoMinor: 0,
1544- Request: dummyReq("HEAD"),
1545-- Header: Header{"Content-Length": {"256"}},
1546-+ Header: http.Header{"Content-Length": {"256"}},
1547- TransferEncoding: nil,
1548- Close: true,
1549- ContentLength: 256,
1550-@@ -235,7 +236,7 @@
1551- ProtoMajor: 1,
1552- ProtoMinor: 1,
1553- Request: dummyReq("HEAD"),
1554-- Header: Header{"Content-Length": {"256"}},
1555-+ Header: http.Header{"Content-Length": {"256"}},
1556- TransferEncoding: nil,
1557- Close: false,
1558- ContentLength: 256,
1559-@@ -256,7 +257,7 @@
1560- ProtoMajor: 1,
1561- ProtoMinor: 0,
1562- Request: dummyReq("HEAD"),
1563-- Header: Header{},
1564-+ Header: http.Header{},
1565- TransferEncoding: nil,
1566- Close: true,
1567- ContentLength: -1,
1568-@@ -278,7 +279,7 @@
1569+@@ -146,7 +147,7 @@
1570+ ProtoMajor: 1,
1571+ ProtoMinor: 1,
1572+ Request: dummyReq("GET"),
1573+- Header: Header{},
1574++ Header: http.Header{},
1575+ Close: false,
1576+ ContentLength: -1,
1577+ TransferEncoding: []string{"chunked"},
1578+@@ -173,7 +174,7 @@
1579+ ProtoMajor: 1,
1580+ ProtoMinor: 1,
1581+ Request: dummyReq("GET"),
1582+- Header: Header{},
1583++ Header: http.Header{},
1584+ Close: false,
1585+ ContentLength: -1,
1586+ TransferEncoding: []string{"chunked"},
1587+@@ -195,7 +196,7 @@
1588+ ProtoMajor: 1,
1589+ ProtoMinor: 1,
1590+ Request: dummyReq("HEAD"),
1591+- Header: Header{},
1592++ Header: http.Header{},
1593+ TransferEncoding: []string{"chunked"},
1594+ Close: false,
1595+ ContentLength: -1,
1596+@@ -217,7 +218,7 @@
1597+ ProtoMajor: 1,
1598+ ProtoMinor: 0,
1599+ Request: dummyReq("HEAD"),
1600+- Header: Header{"Content-Length": {"256"}},
1601++ Header: http.Header{"Content-Length": {"256"}},
1602+ TransferEncoding: nil,
1603+ Close: true,
1604+ ContentLength: 256,
1605+@@ -239,7 +240,7 @@
1606+ ProtoMajor: 1,
1607+ ProtoMinor: 1,
1608+ Request: dummyReq("HEAD"),
1609+- Header: Header{"Content-Length": {"256"}},
1610++ Header: http.Header{"Content-Length": {"256"}},
1611+ TransferEncoding: nil,
1612+ Close: false,
1613+ ContentLength: 256,
1614+@@ -260,7 +261,7 @@
1615+ ProtoMajor: 1,
1616+ ProtoMinor: 0,
1617+ Request: dummyReq("HEAD"),
1618+- Header: Header{},
1619++ Header: http.Header{},
1620+ TransferEncoding: nil,
1621+ Close: true,
1622+ ContentLength: -1,
1623+@@ -282,7 +283,7 @@
1624 ProtoMajor: 1,
1625 ProtoMinor: 1,
1626 Request: dummyReq("GET"),
1627@@ -1177,25 +1012,25 @@
1628 "Content-Length": {"0"},
1629 },
1630 Close: false,
1631-@@ -299,7 +300,7 @@
1632- ProtoMajor: 1,
1633- ProtoMinor: 0,
1634- Request: dummyReq("GET"),
1635-- Header: Header{},
1636-+ Header: http.Header{},
1637- Close: true,
1638- ContentLength: -1,
1639- },
1640-@@ -318,7 +319,7 @@
1641- ProtoMajor: 1,
1642- ProtoMinor: 0,
1643- Request: dummyReq("GET"),
1644-- Header: Header{},
1645-+ Header: http.Header{},
1646- Close: true,
1647- ContentLength: -1,
1648- },
1649-@@ -340,7 +341,7 @@
1650+@@ -303,7 +304,7 @@
1651+ ProtoMajor: 1,
1652+ ProtoMinor: 0,
1653+ Request: dummyReq("GET"),
1654+- Header: Header{},
1655++ Header: http.Header{},
1656+ Close: true,
1657+ ContentLength: -1,
1658+ },
1659+@@ -322,7 +323,7 @@
1660+ ProtoMajor: 1,
1661+ ProtoMinor: 0,
1662+ Request: dummyReq("GET"),
1663+- Header: Header{},
1664++ Header: http.Header{},
1665+ Close: true,
1666+ ContentLength: -1,
1667+ },
1668+@@ -344,7 +345,7 @@
1669 ProtoMajor: 1,
1670 ProtoMinor: 1,
1671 Request: dummyReq("GET"),
1672@@ -1204,7 +1039,7 @@
1673 "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},
1674 },
1675 Close: true,
1676-@@ -363,7 +364,7 @@
1677+@@ -367,7 +368,7 @@
1678 Proto: "HTTP/1.0",
1679 ProtoMajor: 1,
1680 ProtoMinor: 0,
1681@@ -1213,7 +1048,7 @@
1682 "Connection": {"close"}, // TODO(rsc): Delete?
1683 },
1684 Close: true,
1685-@@ -545,7 +546,7 @@
1686+@@ -549,7 +550,7 @@
1687 func TestLocationResponse(t *testing.T) {
1688 for i, tt := range responseLocationTests {
1689 res := new(Response)
1690@@ -1222,7 +1057,7 @@
1691 res.Header.Set("Location", tt.location)
1692 if tt.requrl != "" {
1693 res.Request = &Request{}
1694-@@ -626,16 +627,3 @@
1695+@@ -630,16 +631,3 @@
1696 t.Errorf("ReadResponse = %v; want io.ErrUnexpectedEOF", err)
1697 }
1698 }
1699@@ -1241,8 +1076,8 @@
1700 -}
1701
1702 === modified file 'http13client/responsewrite_test.go'
1703---- http13client/responsewrite_test.go 2014-03-19 20:20:19 +0000
1704-+++ http13client/responsewrite_test.go 2014-03-19 22:27:37 +0000
1705+--- http13client/responsewrite_test.go 2014-06-20 11:00:47 +0000
1706++++ http13client/responsewrite_test.go 2014-06-20 12:05:53 +0000
1707 @@ -7,6 +7,7 @@
1708 import (
1709 "bytes"
1710@@ -1257,7 +1092,7 @@
1711 Request: dummyReq("GET"),
1712 - Header: Header{},
1713 + Header: http.Header{},
1714- Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
1715+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1716 ContentLength: 6,
1717 },
1718 @@ -41,7 +42,7 @@
1719@@ -1270,6 +1105,60 @@
1720 ContentLength: -1,
1721 },
1722 @@ -56,7 +57,7 @@
1723+ ProtoMajor: 1,
1724+ ProtoMinor: 1,
1725+ Request: dummyReq("GET"),
1726+- Header: Header{},
1727++ Header: http.Header{},
1728+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1729+ ContentLength: -1,
1730+ Close: true,
1731+@@ -73,7 +74,7 @@
1732+ ProtoMajor: 1,
1733+ ProtoMinor: 1,
1734+ Request: dummyReq11("GET"),
1735+- Header: Header{},
1736++ Header: http.Header{},
1737+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1738+ ContentLength: -1,
1739+ Close: false,
1740+@@ -91,7 +92,7 @@
1741+ ProtoMajor: 1,
1742+ ProtoMinor: 1,
1743+ Request: dummyReq11("GET"),
1744+- Header: Header{},
1745++ Header: http.Header{},
1746+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1747+ ContentLength: -1,
1748+ TransferEncoding: []string{"chunked"},
1749+@@ -108,7 +109,7 @@
1750+ ProtoMajor: 1,
1751+ ProtoMinor: 1,
1752+ Request: dummyReq11("GET"),
1753+- Header: Header{},
1754++ Header: http.Header{},
1755+ Body: nil,
1756+ ContentLength: 0,
1757+ Close: false,
1758+@@ -124,7 +125,7 @@
1759+ ProtoMajor: 1,
1760+ ProtoMinor: 1,
1761+ Request: dummyReq11("GET"),
1762+- Header: Header{},
1763++ Header: http.Header{},
1764+ Body: ioutil.NopCloser(strings.NewReader("")),
1765+ ContentLength: 0,
1766+ Close: false,
1767+@@ -140,7 +141,7 @@
1768+ ProtoMajor: 1,
1769+ ProtoMinor: 1,
1770+ Request: dummyReq11("GET"),
1771+- Header: Header{},
1772++ Header: http.Header{},
1773+ Body: ioutil.NopCloser(strings.NewReader("foo")),
1774+ ContentLength: 0,
1775+ Close: false,
1776+@@ -156,7 +157,7 @@
1777 ProtoMajor: 1,
1778 ProtoMinor: 1,
1779 Request: dummyReq("GET"),
1780@@ -1278,7 +1167,7 @@
1781 Body: ioutil.NopCloser(strings.NewReader("abcdef")),
1782 ContentLength: 6,
1783 TransferEncoding: []string{"chunked"},
1784-@@ -77,7 +78,7 @@
1785+@@ -177,7 +178,7 @@
1786 ProtoMajor: 1,
1787 ProtoMinor: 1,
1788 Request: dummyReq("GET"),
1789@@ -1287,11 +1176,20 @@
1790 "Foo": []string{" Bar\nBaz "},
1791 },
1792 Body: nil,
1793+@@ -200,7 +201,7 @@
1794+ ProtoMajor: 1,
1795+ ProtoMinor: 1,
1796+ Request: &Request{Method: "POST"},
1797+- Header: Header{},
1798++ Header: http.Header{},
1799+ ContentLength: 0,
1800+ TransferEncoding: nil,
1801+ Body: nil,
1802
1803 === modified file 'http13client/transport_test.go'
1804---- http13client/transport_test.go 2014-03-19 21:38:56 +0000
1805-+++ http13client/transport_test.go 2014-03-19 22:27:37 +0000
1806-@@ -17,8 +17,8 @@
1807+--- http13client/transport_test.go 2014-06-20 11:00:47 +0000
1808++++ http13client/transport_test.go 2014-06-20 12:05:53 +0000
1809+@@ -18,8 +18,8 @@
1810 "io/ioutil"
1811 "log"
1812 "net"
1813@@ -1301,7 +1199,7 @@
1814 "net/http/httptest"
1815 "net/url"
1816 "os"
1817-@@ -34,7 +34,7 @@
1818+@@ -35,7 +35,7 @@
1819 // and then verify that the final 2 responses get errors back.
1820
1821 // hostPortHandler writes back the client's "host:port".
1822@@ -1310,7 +1208,7 @@
1823 if r.FormValue("close") == "true" {
1824 w.Header().Set("Connection", "close")
1825 }
1826-@@ -280,7 +280,7 @@
1827+@@ -289,7 +289,7 @@
1828 const msg = "foobar"
1829
1830 var addrSeen map[string]int
1831@@ -1319,7 +1217,7 @@
1832 addrSeen[r.RemoteAddr]++
1833 if r.URL.Path == "/chunked/" {
1834 w.WriteHeader(200)
1835-@@ -299,7 +299,7 @@
1836+@@ -308,7 +308,7 @@
1837 wantLen := []int{len(msg), -1}[pi]
1838 addrSeen = make(map[string]int)
1839 for i := 0; i < 3; i++ {
1840@@ -1328,7 +1226,7 @@
1841 if err != nil {
1842 t.Errorf("Get %s: %v", path, err)
1843 continue
1844-@@ -329,7 +329,7 @@
1845+@@ -338,7 +338,7 @@
1846 defer afterTest(t)
1847 resch := make(chan string)
1848 gotReq := make(chan bool)
1849@@ -1337,7 +1235,7 @@
1850 gotReq <- true
1851 msg := <-resch
1852 _, err := w.Write([]byte(msg))
1853-@@ -457,12 +457,12 @@
1854+@@ -466,12 +466,12 @@
1855 if testing.Short() {
1856 t.Skip("skipping test in short mode")
1857 }
1858@@ -1353,7 +1251,7 @@
1859 buf.Flush()
1860 conn.Close()
1861 }))
1862-@@ -510,7 +510,7 @@
1863+@@ -519,7 +519,7 @@
1864 // with no bodies properly
1865 func TestTransportHeadResponses(t *testing.T) {
1866 defer afterTest(t)
1867@@ -1362,7 +1260,7 @@
1868 if r.Method != "HEAD" {
1869 panic("expected HEAD; got " + r.Method)
1870 }
1871-@@ -545,7 +545,7 @@
1872+@@ -554,7 +554,7 @@
1873 // on responses to HEAD requests.
1874 func TestTransportHeadChunkedResponse(t *testing.T) {
1875 defer afterTest(t)
1876@@ -1371,7 +1269,7 @@
1877 if r.Method != "HEAD" {
1878 panic("expected HEAD; got " + r.Method)
1879 }
1880-@@ -588,7 +588,7 @@
1881+@@ -597,7 +597,7 @@
1882 func TestRoundTripGzip(t *testing.T) {
1883 defer afterTest(t)
1884 const responseBody = "test response body"
1885@@ -1380,7 +1278,7 @@
1886 accept := req.Header.Get("Accept-Encoding")
1887 if expect := req.FormValue("expect_accept"); accept != expect {
1888 t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",
1889-@@ -647,7 +647,7 @@
1890+@@ -656,7 +656,7 @@
1891 defer afterTest(t)
1892 const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1893 const nRandBytes = 1024 * 1024
1894@@ -1389,7 +1287,7 @@
1895 if req.Method == "HEAD" {
1896 if g := req.Header.Get("Accept-Encoding"); g != "" {
1897 t.Errorf("HEAD request sent with Accept-Encoding of %q; want none", g)
1898-@@ -742,11 +742,11 @@
1899+@@ -751,11 +751,11 @@
1900 func TestTransportProxy(t *testing.T) {
1901 defer afterTest(t)
1902 ch := make(chan string, 1)
1903@@ -1403,7 +1301,7 @@
1904 ch <- "proxy for " + r.URL.String()
1905 }))
1906 defer proxy.Close()
1907-@@ -770,7 +770,7 @@
1908+@@ -779,7 +779,7 @@
1909 // Content-Encoding is removed.
1910 func TestTransportGzipRecursive(t *testing.T) {
1911 defer afterTest(t)
1912@@ -1412,7 +1310,16 @@
1913 w.Header().Set("Content-Encoding", "gzip")
1914 w.Write(rgz)
1915 }))
1916-@@ -802,7 +802,7 @@
1917+@@ -807,7 +807,7 @@
1918+ // a short gzip body
1919+ func TestTransportGzipShort(t *testing.T) {
1920+ defer afterTest(t)
1921+- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
1922++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1923+ w.Header().Set("Content-Encoding", "gzip")
1924+ w.Write([]byte{0x1f, 0x8b})
1925+ }))
1926+@@ -838,7 +838,7 @@
1927 defer afterTest(t)
1928 gotReqCh := make(chan bool)
1929 unblockCh := make(chan bool)
1930@@ -1421,7 +1328,7 @@
1931 gotReqCh <- true
1932 <-unblockCh
1933 w.Header().Set("Content-Length", "0")
1934-@@ -869,7 +869,7 @@
1935+@@ -905,7 +905,7 @@
1936 t.Skip("skipping test; see http://golang.org/issue/7237")
1937 }
1938 defer afterTest(t)
1939@@ -1430,7 +1337,7 @@
1940 }))
1941 defer ts.Close()
1942
1943-@@ -912,7 +912,7 @@
1944+@@ -948,7 +948,7 @@
1945 c := &Client{Transport: tr}
1946
1947 unblockCh := make(chan bool, 1)
1948@@ -1439,7 +1346,7 @@
1949 <-unblockCh
1950 tr.CloseIdleConnections()
1951 }))
1952-@@ -939,7 +939,7 @@
1953+@@ -975,7 +975,7 @@
1954 func TestIssue3644(t *testing.T) {
1955 defer afterTest(t)
1956 const numFoos = 5000
1957@@ -1448,7 +1355,7 @@
1958 w.Header().Set("Connection", "close")
1959 for i := 0; i < numFoos; i++ {
1960 w.Write([]byte("foo "))
1961-@@ -967,8 +967,8 @@
1962+@@ -1003,8 +1003,8 @@
1963 func TestIssue3595(t *testing.T) {
1964 defer afterTest(t)
1965 const deniedMsg = "sorry, denied."
1966@@ -1459,7 +1366,7 @@
1967 }))
1968 defer ts.Close()
1969 tr := &Transport{}
1970-@@ -991,7 +991,7 @@
1971+@@ -1027,7 +1027,7 @@
1972 // "client fails to handle requests with no body and chunked encoding"
1973 func TestChunkedNoContent(t *testing.T) {
1974 defer afterTest(t)
1975@@ -1468,7 +1375,7 @@
1976 w.WriteHeader(StatusNoContent)
1977 }))
1978 defer ts.Close()
1979-@@ -1019,7 +1019,7 @@
1980+@@ -1055,7 +1055,7 @@
1981 maxProcs, numReqs = 4, 50
1982 }
1983 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
1984@@ -1477,7 +1384,7 @@
1985 fmt.Fprintf(w, "%v", r.FormValue("echo"))
1986 }))
1987 defer ts.Close()
1988-@@ -1080,8 +1080,8 @@
1989+@@ -1116,8 +1116,8 @@
1990 }
1991 defer afterTest(t)
1992 const debug = false
1993@@ -1488,7 +1395,7 @@
1994 io.Copy(w, neverEnding('a'))
1995 })
1996 ts := httptest.NewServer(mux)
1997-@@ -1144,11 +1144,11 @@
1998+@@ -1180,11 +1180,11 @@
1999 }
2000 defer afterTest(t)
2001 const debug = false
2002@@ -1503,20 +1410,22 @@
2003 defer r.Body.Close()
2004 io.Copy(ioutil.Discard, r.Body)
2005 })
2006-@@ -1214,9 +1214,9 @@
2007- if testing.Short() {
2008+@@ -1251,11 +1251,11 @@
2009 t.Skip("skipping timeout test in -short mode")
2010 }
2011+ inHandler := make(chan bool, 1)
2012 - mux := NewServeMux()
2013-- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {})
2014+- mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {
2015++ mux := http.NewServeMux()
2016++ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {
2017+ inHandler <- true
2018+ })
2019 - mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {
2020-+ mux := http.NewServeMux()
2021-+ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {})
2022 + mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
2023+ inHandler <- true
2024 time.Sleep(2 * time.Second)
2025 })
2026- ts := httptest.NewServer(mux)
2027-@@ -1276,9 +1276,9 @@
2028+@@ -1322,9 +1322,9 @@
2029 t.Skip("skipping test in -short mode")
2030 }
2031 unblockc := make(chan bool)
2032@@ -1528,7 +1437,7 @@
2033 <-unblockc
2034 }))
2035 defer ts.Close()
2036-@@ -1386,14 +1386,14 @@
2037+@@ -1431,14 +1431,14 @@
2038 defer afterTest(t)
2039 writeErr := make(chan error, 1)
2040 msg := []byte("young\n")
2041@@ -1545,7 +1454,7 @@
2042 }
2043 }))
2044 defer ts.Close()
2045-@@ -1449,7 +1449,7 @@
2046+@@ -1494,7 +1494,7 @@
2047 res := &Response{
2048 Status: "200 OK",
2049 StatusCode: 200,
2050@@ -1554,7 +1463,7 @@
2051 Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),
2052 }
2053 return res, nil
2054-@@ -1478,7 +1478,7 @@
2055+@@ -1523,7 +1523,7 @@
2056 defer afterTest(t)
2057 tr := &Transport{}
2058 _, err := tr.RoundTrip(&Request{
2059@@ -1563,7 +1472,7 @@
2060 URL: &url.URL{
2061 Scheme: "http",
2062 },
2063-@@ -1492,14 +1492,14 @@
2064+@@ -1537,14 +1537,14 @@
2065 func TestTransportSocketLateBinding(t *testing.T) {
2066 defer afterTest(t)
2067
2068@@ -1582,7 +1491,7 @@
2069 w.Header().Set("bar-ipport", r.RemoteAddr)
2070 })
2071 ts := httptest.NewServer(mux)
2072-@@ -1720,7 +1720,7 @@
2073+@@ -1767,7 +1767,7 @@
2074 var mu sync.Mutex
2075 var n int
2076
2077@@ -1591,7 +1500,7 @@
2078 mu.Lock()
2079 n++
2080 mu.Unlock()
2081-@@ -1756,7 +1756,7 @@
2082+@@ -1803,7 +1803,7 @@
2083 // then closes it.
2084 func TestTransportClosesRequestBody(t *testing.T) {
2085 defer afterTest(t)
2086@@ -1600,4 +1509,49 @@
2087 io.Copy(ioutil.Discard, r.Body)
2088 }))
2089 defer ts.Close()
2090+@@ -1890,9 +1890,9 @@
2091+ t.Skip("skipping flaky test on Windows; golang.org/issue/7634")
2092+ }
2093+ closedc := make(chan bool, 1)
2094+- ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
2095++ ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2096+ if strings.Contains(r.URL.Path, "/keep-alive-then-die") {
2097+- conn, _, _ := w.(Hijacker).Hijack()
2098++ conn, _, _ := w.(http.Hijacker).Hijack()
2099+ conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
2100+ conn.Close()
2101+ closedc <- true
2102+@@ -1994,12 +1994,12 @@
2103+ }
2104+ defer closeConn()
2105+
2106+- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
2107++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2108+ if r.Method == "GET" {
2109+ io.WriteString(w, "bar")
2110+ return
2111+ }
2112+- conn, _, _ := w.(Hijacker).Hijack()
2113++ conn, _, _ := w.(http.Hijacker).Hijack()
2114+ sconn.Lock()
2115+ sconn.c = conn
2116+ sconn.Unlock()
2117+@@ -2056,7 +2056,7 @@
2118+ }
2119+ defer afterTest(t)
2120+ readBody := make(chan error, 1)
2121+- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
2122++ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2123+ _, err := ioutil.ReadAll(r.Body)
2124+ readBody <- err
2125+ }))
2126+@@ -2098,7 +2098,7 @@
2127+ }
2128+ }
2129+
2130+-func wantBody(res *http.Response, err error, want string) error {
2131++func wantBody(res *Response, err error, want string) error {
2132+ if err != nil {
2133+ return err
2134+ }
2135
2136
2137=== modified file 'http13client/_using.txt'
2138--- http13client/_using.txt 2014-03-20 12:20:01 +0000
2139+++ http13client/_using.txt 2014-06-20 13:24:39 +0000
2140@@ -1,5 +1,5 @@
2141-parent: 19512:32c32aef2a41 tip
2142- test: enable bug385_32 test on amd64p32.
2143-branch: default
2144+parent: 20169:9895f9e36435 go1.3 release
2145+ go1.3
2146+branch: release-branch.go1.3
2147 commit: (clean)
2148 update: (current)
2149
2150=== modified file 'http13client/client.go'
2151--- http13client/client.go 2014-03-20 09:26:28 +0000
2152+++ http13client/client.go 2014-06-20 13:24:39 +0000
2153@@ -92,8 +92,9 @@
2154 // authentication, or cookies.
2155 //
2156 // RoundTrip should not modify the request, except for
2157- // consuming and closing the Body. The request's URL and
2158- // Header fields are guaranteed to be initialized.
2159+ // consuming and closing the Body, including on errors. The
2160+ // request's URL and Header fields are guaranteed to be
2161+ // initialized.
2162 RoundTrip(*Request) (*Response, error)
2163 }
2164
2165@@ -141,6 +142,9 @@
2166 // (typically Transport) may not be able to re-use a persistent TCP
2167 // connection to the server for a subsequent "keep-alive" request.
2168 //
2169+// The request Body, if non-nil, will be closed by the underlying
2170+// Transport, even on errors.
2171+//
2172 // Generally Get, Post, or PostForm will be used instead of Do.
2173 func (c *Client) Do(req *Request) (resp *Response, err error) {
2174 if req.Method == "GET" || req.Method == "HEAD" {
2175@@ -163,14 +167,17 @@
2176 // Caller should close resp.Body when done reading from it.
2177 func send(req *Request, t RoundTripper) (resp *Response, err error) {
2178 if t == nil {
2179+ req.closeBody()
2180 return nil, errors.New("http: no Client.Transport or DefaultTransport")
2181 }
2182
2183 if req.URL == nil {
2184+ req.closeBody()
2185 return nil, errors.New("http: nil Request.URL")
2186 }
2187
2188 if req.RequestURI != "" {
2189+ req.closeBody()
2190 return nil, errors.New("http: Request.RequestURI can't be set in client requests.")
2191 }
2192
2193@@ -278,6 +285,7 @@
2194 var via []*Request
2195
2196 if ireq.URL == nil {
2197+ ireq.closeBody()
2198 return nil, errors.New("http: nil Request.URL")
2199 }
2200
2201@@ -400,7 +408,7 @@
2202 // Caller should close resp.Body when done reading from it.
2203 //
2204 // If the provided body is also an io.Closer, it is closed after the
2205-// body is successfully written to the server.
2206+// request.
2207 func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
2208 req, err := NewRequest("POST", url, body)
2209 if err != nil {
2210
2211=== modified file 'http13client/client_test.go'
2212--- http13client/client_test.go 2014-03-20 09:26:28 +0000
2213+++ http13client/client_test.go 2014-06-20 13:24:39 +0000
2214@@ -20,6 +20,8 @@
2215 "net/http"
2216 "net/http/httptest"
2217 "net/url"
2218+ "reflect"
2219+ "sort"
2220 "strconv"
2221 "strings"
2222 "sync"
2223@@ -877,3 +879,93 @@
2224 t.Fatal("server saw different client ports before & after the redirect")
2225 }
2226 }
2227+
2228+// eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF.
2229+type eofReaderFunc func()
2230+
2231+func (f eofReaderFunc) Read(p []byte) (n int, err error) {
2232+ f()
2233+ return 0, io.EOF
2234+}
2235+
2236+func TestClientTrailers(t *testing.T) {
2237+ defer afterTest(t)
2238+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2239+ w.Header().Set("Connection", "close")
2240+ w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
2241+ w.Header().Add("Trailer", "Server-Trailer-C")
2242+
2243+ var decl []string
2244+ for k := range r.Trailer {
2245+ decl = append(decl, k)
2246+ }
2247+ sort.Strings(decl)
2248+
2249+ slurp, err := ioutil.ReadAll(r.Body)
2250+ if err != nil {
2251+ t.Errorf("Server reading request body: %v", err)
2252+ }
2253+ if string(slurp) != "foo" {
2254+ t.Errorf("Server read request body %q; want foo", slurp)
2255+ }
2256+ if r.Trailer == nil {
2257+ io.WriteString(w, "nil Trailer")
2258+ } else {
2259+ fmt.Fprintf(w, "decl: %v, vals: %s, %s",
2260+ decl,
2261+ r.Trailer.Get("Client-Trailer-A"),
2262+ r.Trailer.Get("Client-Trailer-B"))
2263+ }
2264+
2265+ // TODO: golang.org/issue/7759: there's no way yet for
2266+ // the server to set trailers without hijacking, so do
2267+ // that for now, just to test the client. Later, in
2268+ // Go 1.4, it should be implicit that any mutations
2269+ // to w.Header() after the initial write are the
2270+ // trailers to be sent, if and only if they were
2271+ // previously declared with w.Header().Set("Trailer",
2272+ // ..keys..)
2273+ w.(http.Flusher).Flush()
2274+ conn, buf, _ := w.(http.Hijacker).Hijack()
2275+ t := http.Header{}
2276+ t.Set("Server-Trailer-A", "valuea")
2277+ t.Set("Server-Trailer-C", "valuec") // skipping B
2278+ buf.WriteString("0\r\n") // eof
2279+ t.Write(buf)
2280+ buf.WriteString("\r\n") // end of trailers
2281+ buf.Flush()
2282+ conn.Close()
2283+ }))
2284+ defer ts.Close()
2285+
2286+ var req *Request
2287+ req, _ = NewRequest("POST", ts.URL, io.MultiReader(
2288+ eofReaderFunc(func() {
2289+ req.Trailer["Client-Trailer-A"] = []string{"valuea"}
2290+ }),
2291+ strings.NewReader("foo"),
2292+ eofReaderFunc(func() {
2293+ req.Trailer["Client-Trailer-B"] = []string{"valueb"}
2294+ }),
2295+ ))
2296+ req.Trailer = http.Header{
2297+ "Client-Trailer-A": nil, // to be set later
2298+ "Client-Trailer-B": nil, // to be set later
2299+ }
2300+ req.ContentLength = -1
2301+ res, err := DefaultClient.Do(req)
2302+ if err != nil {
2303+ t.Fatal(err)
2304+ }
2305+ if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil {
2306+ t.Error(err)
2307+ }
2308+ want := http.Header{
2309+ "Server-Trailer-A": []string{"valuea"},
2310+ "Server-Trailer-B": nil,
2311+ "Server-Trailer-C": []string{"valuec"},
2312+ }
2313+ if !reflect.DeepEqual(res.Trailer, want) {
2314+ t.Errorf("Response trailers = %#v; want %#v", res.Trailer, want)
2315+ }
2316+}
2317
2318=== modified file 'http13client/cookie.go'
2319--- http13client/cookie.go 2014-03-19 23:13:58 +0000
2320+++ http13client/cookie.go 2014-06-20 13:24:39 +0000
2321@@ -55,11 +55,7 @@
2322 attr, val = attr[:j], attr[j+1:]
2323 }
2324 lowerAttr := strings.ToLower(attr)
2325- parseCookieValueFn := parseCookieValue
2326- if lowerAttr == "expires" {
2327- parseCookieValueFn = parseCookieExpiresValue
2328- }
2329- val, success = parseCookieValueFn(val)
2330+ val, success = parseCookieValue(val)
2331 if !success {
2332 c.Unparsed = append(c.Unparsed, parts[i])
2333 continue
2334@@ -230,12 +226,23 @@
2335 // ; US-ASCII characters excluding CTLs,
2336 // ; whitespace DQUOTE, comma, semicolon,
2337 // ; and backslash
2338+// We loosen this as spaces and commas are common in cookie values
2339+// but we produce a quoted cookie-value in when value starts or ends
2340+// with a comma or space.
2341+// See http://golang.org/issue/7243 for the discussion.
2342 func sanitizeCookieValue(v string) string {
2343- return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
2344+ v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
2345+ if len(v) == 0 {
2346+ return v
2347+ }
2348+ if v[0] == ' ' || v[0] == ',' || v[len(v)-1] == ' ' || v[len(v)-1] == ',' {
2349+ return `"` + v + `"`
2350+ }
2351+ return v
2352 }
2353
2354 func validCookieValueByte(b byte) bool {
2355- return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\'
2356+ return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
2357 }
2358
2359 // path-av = "Path=" path-value
2360@@ -270,38 +277,13 @@
2361 return string(buf)
2362 }
2363
2364-func unquoteCookieValue(v string) string {
2365- if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' {
2366- return v[1 : len(v)-1]
2367- }
2368- return v
2369-}
2370-
2371-func isCookieByte(c byte) bool {
2372- switch {
2373- case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
2374- 0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
2375- return true
2376- }
2377- return false
2378-}
2379-
2380-func isCookieExpiresByte(c byte) (ok bool) {
2381- return isCookieByte(c) || c == ',' || c == ' '
2382-}
2383-
2384 func parseCookieValue(raw string) (string, bool) {
2385- return parseCookieValueUsing(raw, isCookieByte)
2386-}
2387-
2388-func parseCookieExpiresValue(raw string) (string, bool) {
2389- return parseCookieValueUsing(raw, isCookieExpiresByte)
2390-}
2391-
2392-func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) {
2393- raw = unquoteCookieValue(raw)
2394+ // Strip the quotes, if present.
2395+ if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
2396+ raw = raw[1 : len(raw)-1]
2397+ }
2398 for i := 0; i < len(raw); i++ {
2399- if !validByte(raw[i]) {
2400+ if !validCookieValueByte(raw[i]) {
2401 return "", false
2402 }
2403 }
2404
2405=== removed file 'http13client/cookie_test.go'
2406--- http13client/cookie_test.go 2014-03-19 23:13:58 +0000
2407+++ http13client/cookie_test.go 1970-01-01 00:00:00 +0000
2408@@ -1,304 +0,0 @@
2409-// Copyright 2010 The Go Authors. All rights reserved.
2410-// Use of this source code is governed by a BSD-style
2411-// license that can be found in the LICENSE file.
2412-
2413-package http
2414-
2415-import (
2416- "bytes"
2417- "encoding/json"
2418- "fmt"
2419- "log"
2420- "net/http"
2421- "os"
2422- "reflect"
2423- "strings"
2424- "testing"
2425- "time"
2426-)
2427-
2428-var writeSetCookiesTests = []struct {
2429- Cookie *http.Cookie
2430- Raw string
2431-}{
2432- {
2433- &http.Cookie{Name: "cookie-1", Value: "v$1"},
2434- "cookie-1=v$1",
2435- },
2436- {
2437- &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
2438- "cookie-2=two; Max-Age=3600",
2439- },
2440- {
2441- &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
2442- "cookie-3=three; Domain=example.com",
2443- },
2444- {
2445- &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
2446- "cookie-4=four; Path=/restricted/",
2447- },
2448- {
2449- &http.Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
2450- "cookie-5=five",
2451- },
2452- {
2453- &http.Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
2454- "cookie-6=six",
2455- },
2456- {
2457- &http.Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
2458- "cookie-7=seven; Domain=127.0.0.1",
2459- },
2460- {
2461- &http.Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
2462- "cookie-8=eight",
2463- },
2464-}
2465-
2466-func TestWriteSetCookies(t *testing.T) {
2467- defer log.SetOutput(os.Stderr)
2468- var logbuf bytes.Buffer
2469- log.SetOutput(&logbuf)
2470-
2471- for i, tt := range writeSetCookiesTests {
2472- if g, e := tt.Cookie.String(), tt.Raw; g != e {
2473- t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g)
2474- continue
2475- }
2476- }
2477-
2478- if got, sub := logbuf.String(), "dropping domain attribute"; !strings.Contains(got, sub) {
2479- t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
2480- }
2481-}
2482-
2483-type headerOnlyResponseWriter http.Header
2484-
2485-func (ho headerOnlyResponseWriter) Header() http.Header {
2486- return http.Header(ho)
2487-}
2488-
2489-func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
2490- panic("NOIMPL")
2491-}
2492-
2493-func (ho headerOnlyResponseWriter) WriteHeader(int) {
2494- panic("NOIMPL")
2495-}
2496-
2497-func TestSetCookie(t *testing.T) {
2498- m := make(http.Header)
2499- http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
2500- http.SetCookie(headerOnlyResponseWriter(m), &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
2501- if l := len(m["Set-Cookie"]); l != 2 {
2502- t.Fatalf("expected %d cookies, got %d", 2, l)
2503- }
2504- if g, e := m["Set-Cookie"][0], "cookie-1=one; Path=/restricted/"; g != e {
2505- t.Errorf("cookie #1: want %q, got %q", e, g)
2506- }
2507- if g, e := m["Set-Cookie"][1], "cookie-2=two; Max-Age=3600"; g != e {
2508- t.Errorf("cookie #2: want %q, got %q", e, g)
2509- }
2510-}
2511-
2512-var addCookieTests = []struct {
2513- Cookies []*http.Cookie
2514- Raw string
2515-}{
2516- {
2517- []*http.Cookie{},
2518- "",
2519- },
2520- {
2521- []*http.Cookie{{Name: "cookie-1", Value: "v$1"}},
2522- "cookie-1=v$1",
2523- },
2524- {
2525- []*http.Cookie{
2526- {Name: "cookie-1", Value: "v$1"},
2527- {Name: "cookie-2", Value: "v$2"},
2528- {Name: "cookie-3", Value: "v$3"},
2529- },
2530- "cookie-1=v$1; cookie-2=v$2; cookie-3=v$3",
2531- },
2532-}
2533-
2534-func TestAddCookie(t *testing.T) {
2535- for i, tt := range addCookieTests {
2536- req, _ := NewRequest("GET", "http://example.com/", nil)
2537- for _, c := range tt.Cookies {
2538- req.AddCookie(c)
2539- }
2540- if g := req.Header.Get("Cookie"); g != tt.Raw {
2541- t.Errorf("Test %d:\nwant: %s\n got: %s\n", i, tt.Raw, g)
2542- continue
2543- }
2544- }
2545-}
2546-
2547-var readSetCookiesTests = []struct {
2548- Header http.Header
2549- Cookies []*http.Cookie
2550-}{
2551- {
2552- http.Header{"Set-Cookie": {"Cookie-1=v$1"}},
2553- []*http.Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
2554- },
2555- {
2556- http.Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
2557- []*http.Cookie{{
2558- Name: "NID",
2559- Value: "99=YsDT5i3E-CXax-",
2560- Path: "/",
2561- Domain: ".google.ch",
2562- HttpOnly: true,
2563- Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
2564- RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
2565- Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
2566- }},
2567- },
2568- {
2569- http.Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
2570- []*http.Cookie{{
2571- Name: ".ASPXAUTH",
2572- Value: "7E3AA",
2573- Path: "/",
2574- Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
2575- RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
2576- HttpOnly: true,
2577- Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
2578- }},
2579- },
2580- {
2581- http.Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
2582- []*http.Cookie{{
2583- Name: "ASP.NET_SessionId",
2584- Value: "foo",
2585- Path: "/",
2586- HttpOnly: true,
2587- Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly",
2588- }},
2589- },
2590-
2591- // TODO(bradfitz): users have reported seeing this in the
2592- // wild, but do browsers handle it? RFC 6265 just says "don't
2593- // do that" (section 3) and then never mentions header folding
2594- // again.
2595- // Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
2596-}
2597-
2598-func toJSON(v interface{}) string {
2599- b, err := json.Marshal(v)
2600- if err != nil {
2601- return fmt.Sprintf("%#v", v)
2602- }
2603- return string(b)
2604-}
2605-
2606-func TestReadSetCookies(t *testing.T) {
2607- for i, tt := range readSetCookiesTests {
2608- for n := 0; n < 2; n++ { // to verify readSetCookies doesn't mutate its input
2609- c := readSetCookies(tt.Header)
2610- if !reflect.DeepEqual(c, tt.Cookies) {
2611- t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
2612- continue
2613- }
2614- }
2615- }
2616-}
2617-
2618-var readCookiesTests = []struct {
2619- Header http.Header
2620- Filter string
2621- Cookies []*http.Cookie
2622-}{
2623- {
2624- http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
2625- "",
2626- []*http.Cookie{
2627- {Name: "Cookie-1", Value: "v$1"},
2628- {Name: "c2", Value: "v2"},
2629- },
2630- },
2631- {
2632- http.Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
2633- "c2",
2634- []*http.Cookie{
2635- {Name: "c2", Value: "v2"},
2636- },
2637- },
2638- {
2639- http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
2640- "",
2641- []*http.Cookie{
2642- {Name: "Cookie-1", Value: "v$1"},
2643- {Name: "c2", Value: "v2"},
2644- },
2645- },
2646- {
2647- http.Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
2648- "c2",
2649- []*http.Cookie{
2650- {Name: "c2", Value: "v2"},
2651- },
2652- },
2653-}
2654-
2655-func TestReadCookies(t *testing.T) {
2656- for i, tt := range readCookiesTests {
2657- for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
2658- c := readCookies(tt.Header, tt.Filter)
2659- if !reflect.DeepEqual(c, tt.Cookies) {
2660- t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
2661- continue
2662- }
2663- }
2664- }
2665-}
2666-
2667-func TestCookieSanitizeValue(t *testing.T) {
2668- defer log.SetOutput(os.Stderr)
2669- var logbuf bytes.Buffer
2670- log.SetOutput(&logbuf)
2671-
2672- tests := []struct {
2673- in, want string
2674- }{
2675- {"foo", "foo"},
2676- {"foo bar", "foobar"},
2677- {"\x00\x7e\x7f\x80", "\x7e"},
2678- {`"withquotes"`, "withquotes"},
2679- }
2680- for _, tt := range tests {
2681- if got := sanitizeCookieValue(tt.in); got != tt.want {
2682- t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want)
2683- }
2684- }
2685-
2686- if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
2687- t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
2688- }
2689-}
2690-
2691-func TestCookieSanitizePath(t *testing.T) {
2692- defer log.SetOutput(os.Stderr)
2693- var logbuf bytes.Buffer
2694- log.SetOutput(&logbuf)
2695-
2696- tests := []struct {
2697- in, want string
2698- }{
2699- {"/path", "/path"},
2700- {"/path with space/", "/path with space/"},
2701- {"/just;no;semicolon\x00orstuff/", "/justnosemicolonorstuff/"},
2702- }
2703- for _, tt := range tests {
2704- if got := sanitizeCookiePath(tt.in); got != tt.want {
2705- t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want)
2706- }
2707- }
2708-
2709- if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
2710- t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
2711- }
2712-}
2713
2714=== modified file 'http13client/request.go'
2715--- http13client/request.go 2014-03-19 23:13:58 +0000
2716+++ http13client/request.go 2014-06-20 13:24:39 +0000
2717@@ -69,18 +69,31 @@
2718
2719 // A Request represents an HTTP request received by a server
2720 // or to be sent by a client.
2721+//
2722+// The field semantics differ slightly between client and server
2723+// usage. In addition to the notes on the fields below, see the
2724+// documentation for Request.Write and RoundTripper.
2725 type Request struct {
2726- Method string // GET, POST, PUT, etc.
2727+ // Method specifies the HTTP method (GET, POST, PUT, etc.).
2728+ // For client requests an empty string means GET.
2729+ Method string
2730
2731- // URL is created from the URI supplied on the Request-Line
2732- // as stored in RequestURI.
2733- //
2734- // For most requests, fields other than Path and RawQuery
2735- // will be empty. (See RFC 2616, Section 5.1.2)
2736+ // URL specifies either the URI being requested (for server
2737+ // requests) or the URL to access (for client requests).
2738+ //
2739+ // For server requests the URL is parsed from the URI
2740+ // supplied on the Request-Line as stored in RequestURI. For
2741+ // most requests, fields other than Path and RawQuery will be
2742+ // empty. (See RFC 2616, Section 5.1.2)
2743+ //
2744+ // For client requests, the URL's Host specifies the server to
2745+ // connect to, while the Request's Host field optionally
2746+ // specifies the Host header value to send in the HTTP
2747+ // request.
2748 URL *url.URL
2749
2750 // The protocol version for incoming requests.
2751- // Outgoing requests always use HTTP/1.1.
2752+ // Client requests always use HTTP/1.1.
2753 Proto string // "HTTP/1.0"
2754 ProtoMajor int // 1
2755 ProtoMinor int // 0
2756@@ -104,15 +117,20 @@
2757 // The request parser implements this by canonicalizing the
2758 // name, making the first character and any characters
2759 // following a hyphen uppercase and the rest lowercase.
2760+ //
2761+ // For client requests certain headers are automatically
2762+ // added and may override values in Header.
2763+ //
2764+ // See the documentation for the Request.Write method.
2765 Header http.Header
2766
2767 // Body is the request's body.
2768 //
2769- // For client requests, a nil body means the request has no
2770+ // For client requests a nil body means the request has no
2771 // body, such as a GET request. The HTTP Client's Transport
2772 // is responsible for calling the Close method.
2773 //
2774- // For server requests, the Request Body is always non-nil
2775+ // For server requests the Request Body is always non-nil
2776 // but will return EOF immediately when no body is present.
2777 // The Server will close the request body. The ServeHTTP
2778 // Handler does not need to.
2779@@ -122,7 +140,7 @@
2780 // The value -1 indicates that the length is unknown.
2781 // Values >= 0 indicate that the given number of bytes may
2782 // be read from Body.
2783- // For outgoing requests, a value of 0 means unknown if Body is not nil.
2784+ // For client requests, a value of 0 means unknown if Body is not nil.
2785 ContentLength int64
2786
2787 // TransferEncoding lists the transfer encodings from outermost to
2788@@ -133,13 +151,18 @@
2789 TransferEncoding []string
2790
2791 // Close indicates whether to close the connection after
2792- // replying to this request.
2793+ // replying to this request (for servers) or after sending
2794+ // the request (for clients).
2795 Close bool
2796
2797- // The host on which the URL is sought.
2798- // Per RFC 2616, this is either the value of the Host: header
2799- // or the host name given in the URL itself.
2800+ // For server requests Host specifies the host on which the
2801+ // URL is sought. Per RFC 2616, this is either the value of
2802+ // the "Host" header or the host name given in the URL itself.
2803 // It may be of the form "host:port".
2804+ //
2805+ // For client requests Host optionally overrides the Host
2806+ // header to send. If empty, the Request.Write method uses
2807+ // the value of URL.Host.
2808 Host string
2809
2810 // Form contains the parsed form data, including both the URL
2811@@ -159,12 +182,24 @@
2812 // The HTTP client ignores MultipartForm and uses Body instead.
2813 MultipartForm *multipart.Form
2814
2815- // Trailer maps trailer keys to values. Like for Header, if the
2816- // response has multiple trailer lines with the same key, they will be
2817- // concatenated, delimited by commas.
2818- // For server requests, Trailer is only populated after Body has been
2819- // closed or fully consumed.
2820- // Trailer support is only partially complete.
2821+ // Trailer specifies additional headers that are sent after the request
2822+ // body.
2823+ //
2824+ // For server requests the Trailer map initially contains only the
2825+ // trailer keys, with nil values. (The client declares which trailers it
2826+ // will later send.) While the handler is reading from Body, it must
2827+ // not reference Trailer. After reading from Body returns EOF, Trailer
2828+ // can be read again and will contain non-nil values, if they were sent
2829+ // by the client.
2830+ //
2831+ // For client requests Trailer must be initialized to a map containing
2832+ // the trailer keys to later send. The values may be nil or their final
2833+ // values. The ContentLength must be 0 or -1, to send a chunked request.
2834+ // After the HTTP request is sent the map values can be updated while
2835+ // the request body is read. Once the body returns EOF, the caller must
2836+ // not mutate Trailer.
2837+ //
2838+ // Few HTTP clients, servers, or proxies support HTTP trailers.
2839 Trailer http.Header
2840
2841 // RemoteAddr allows HTTP servers and other software to record
2842@@ -382,7 +417,6 @@
2843 return err
2844 }
2845
2846- // TODO: split long values? (If so, should share code with Conn.Write)
2847 err = req.Header.WriteSubset(w, reqWriteExcludeHeader)
2848 if err != nil {
2849 return err
2850@@ -589,32 +623,6 @@
2851
2852 fixPragmaCacheControl(req.Header)
2853
2854- // TODO: Parse specific header values:
2855- // Accept
2856- // Accept-Encoding
2857- // Accept-Language
2858- // Authorization
2859- // Cache-Control
2860- // Connection
2861- // Date
2862- // Expect
2863- // From
2864- // If-Match
2865- // If-Modified-Since
2866- // If-None-Match
2867- // If-Range
2868- // If-Unmodified-Since
2869- // Max-Forwards
2870- // Proxy-Authorization
2871- // Referer [sic]
2872- // TE (transfer-codings)
2873- // Trailer
2874- // Transfer-Encoding
2875- // Upgrade
2876- // User-Agent
2877- // Via
2878- // Warning
2879-
2880 err = readTransfer(req, b)
2881 if err != nil {
2882 return nil, err
2883@@ -783,9 +791,7 @@
2884 }
2885
2886 mr, err := r.multipartReader()
2887- if err == ErrNotMultipart {
2888- return nil
2889- } else if err != nil {
2890+ if err != nil {
2891 return err
2892 }
2893
2894@@ -863,3 +869,9 @@
2895 func (r *Request) wantsClose() bool {
2896 return hasToken(r.Header.Get("Connection"), "close")
2897 }
2898+
2899+func (r *Request) closeBody() {
2900+ if r.Body != nil {
2901+ r.Body.Close()
2902+ }
2903+}
2904
2905=== modified file 'http13client/request_test.go'
2906--- http13client/request_test.go 2014-03-20 09:26:28 +0000
2907+++ http13client/request_test.go 2014-06-20 13:24:39 +0000
2908@@ -155,7 +155,25 @@
2909 req.Header = http.Header{"Content-Type": {"text/plain"}}
2910 multipart, err = req.MultipartReader()
2911 if multipart != nil {
2912- t.Errorf("unexpected multipart for text/plain")
2913+ t.Error("unexpected multipart for text/plain")
2914+ }
2915+}
2916+
2917+func TestParseMultipartForm(t *testing.T) {
2918+ req := &Request{
2919+ Method: "POST",
2920+ Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
2921+ Body: ioutil.NopCloser(new(bytes.Buffer)),
2922+ }
2923+ err := req.ParseMultipartForm(25)
2924+ if err == nil {
2925+ t.Error("expected multipart EOF, got nil")
2926+ }
2927+
2928+ req.Header = http.Header{"Content-Type": {"text/plain"}}
2929+ err = req.ParseMultipartForm(25)
2930+ if err != ErrNotMultipart {
2931+ t.Error("expected ErrNotMultipart for text/plain")
2932 }
2933 }
2934
2935@@ -221,16 +239,38 @@
2936 validateTestMultipartContents(t, req, true)
2937 }
2938
2939-func TestEmptyMultipartRequest(t *testing.T) {
2940- // Test that FormValue and FormFile automatically invoke
2941- // ParseMultipartForm and return the right values.
2942- req, err := NewRequest("GET", "/", nil)
2943- if err != nil {
2944- t.Errorf("NewRequest err = %q", err)
2945- }
2946+func TestMissingFileMultipartRequest(t *testing.T) {
2947+ // Test that FormFile returns an error if
2948+ // the named file is missing.
2949+ req := newTestMultipartRequest(t)
2950 testMissingFile(t, req)
2951 }
2952
2953+// Test that FormValue invokes ParseMultipartForm.
2954+func TestFormValueCallsParseMultipartForm(t *testing.T) {
2955+ req, _ := NewRequest("POST", "http://www.google.com/", strings.NewReader("z=post"))
2956+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
2957+ if req.Form != nil {
2958+ t.Fatal("Unexpected request Form, want nil")
2959+ }
2960+ req.FormValue("z")
2961+ if req.Form == nil {
2962+ t.Fatal("ParseMultipartForm not called by FormValue")
2963+ }
2964+}
2965+
2966+// Test that FormFile invokes ParseMultipartForm.
2967+func TestFormFileCallsParseMultipartForm(t *testing.T) {
2968+ req := newTestMultipartRequest(t)
2969+ if req.Form != nil {
2970+ t.Fatal("Unexpected request Form, want nil")
2971+ }
2972+ req.FormFile("")
2973+ if req.Form == nil {
2974+ t.Fatal("ParseMultipartForm not called by FormFile")
2975+ }
2976+}
2977+
2978 // Test that ParseMultipartForm errors if called
2979 // after MultipartReader on the same request.
2980 func TestParseMultipartFormOrder(t *testing.T) {
2981
2982=== modified file 'http13client/response.go'
2983--- http13client/response.go 2014-03-19 23:43:25 +0000
2984+++ http13client/response.go 2014-06-20 13:24:39 +0000
2985@@ -8,6 +8,7 @@
2986
2987 import (
2988 "bufio"
2989+ "bytes"
2990 "crypto/tls"
2991 "errors"
2992 "io"
2993@@ -47,7 +48,8 @@
2994 //
2995 // The http Client and Transport guarantee that Body is always
2996 // non-nil, even on responses without a body or responses with
2997- // a zero-lengthed body.
2998+ // a zero-length body. It is the caller's responsibility to
2999+ // close Body.
3000 //
3001 // The Body is automatically dechunked if the server replied
3002 // with a "chunked" Transfer-Encoding.
3003@@ -200,7 +202,6 @@
3004 //
3005 // Body is closed after it is sent.
3006 func (r *Response) Write(w io.Writer) error {
3007-
3008 // Status line
3009 text := r.Status
3010 if text == "" {
3011@@ -212,10 +213,45 @@
3012 protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
3013 statusCode := strconv.Itoa(r.StatusCode) + " "
3014 text = strings.TrimPrefix(text, statusCode)
3015- io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
3016+ if _, err := io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n"); err != nil {
3017+ return err
3018+ }
3019+
3020+ // Clone it, so we can modify r1 as needed.
3021+ r1 := new(Response)
3022+ *r1 = *r
3023+ if r1.ContentLength == 0 && r1.Body != nil {
3024+ // Is it actually 0 length? Or just unknown?
3025+ var buf [1]byte
3026+ n, err := r1.Body.Read(buf[:])
3027+ if err != nil && err != io.EOF {
3028+ return err
3029+ }
3030+ if n == 0 {
3031+ // Reset it to a known zero reader, in case underlying one
3032+ // is unhappy being read repeatedly.
3033+ r1.Body = eofReader
3034+ } else {
3035+ r1.ContentLength = -1
3036+ r1.Body = struct {
3037+ io.Reader
3038+ io.Closer
3039+ }{
3040+ io.MultiReader(bytes.NewReader(buf[:1]), r.Body),
3041+ r.Body,
3042+ }
3043+ }
3044+ }
3045+ // If we're sending a non-chunked HTTP/1.1 response without a
3046+ // content-length, the only way to do that is the old HTTP/1.0
3047+ // way, by noting the EOF with a connection close, so we need
3048+ // to set Close.
3049+ if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !chunked(r1.TransferEncoding) {
3050+ r1.Close = true
3051+ }
3052
3053 // Process Body,ContentLength,Close,Trailer
3054- tw, err := newTransferWriter(r)
3055+ tw, err := newTransferWriter(r1)
3056 if err != nil {
3057 return err
3058 }
3059@@ -230,8 +266,19 @@
3060 return err
3061 }
3062
3063+ // contentLengthAlreadySent may have been already sent for
3064+ // POST/PUT requests, even if zero length. See Issue 8180.
3065+ contentLengthAlreadySent := tw.shouldSendContentLength()
3066+ if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent {
3067+ if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
3068+ return err
3069+ }
3070+ }
3071+
3072 // End-of-header
3073- io.WriteString(w, "\r\n")
3074+ if _, err := io.WriteString(w, "\r\n"); err != nil {
3075+ return err
3076+ }
3077
3078 // Write body and trailer
3079 err = tw.WriteBody(w)
3080
3081=== modified file 'http13client/response_test.go'
3082--- http13client/response_test.go 2014-03-19 23:13:58 +0000
3083+++ http13client/response_test.go 2014-06-20 13:24:39 +0000
3084@@ -30,6 +30,10 @@
3085 return &Request{Method: method}
3086 }
3087
3088+func dummyReq11(method string) *Request {
3089+ return &Request{Method: method, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1}
3090+}
3091+
3092 var respTests = []respTest{
3093 // Unchunked response without Content-Length.
3094 {
3095
3096=== modified file 'http13client/responsewrite_test.go'
3097--- http13client/responsewrite_test.go 2014-03-19 23:13:58 +0000
3098+++ http13client/responsewrite_test.go 2014-06-20 13:24:39 +0000
3099@@ -27,7 +27,7 @@
3100 ProtoMinor: 0,
3101 Request: dummyReq("GET"),
3102 Header: http.Header{},
3103- Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
3104+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
3105 ContentLength: 6,
3106 },
3107
3108@@ -50,6 +50,106 @@
3109 "\r\n" +
3110 "abcdef",
3111 },
3112+ // HTTP/1.1 response with unknown length and Connection: close
3113+ {
3114+ Response{
3115+ StatusCode: 200,
3116+ ProtoMajor: 1,
3117+ ProtoMinor: 1,
3118+ Request: dummyReq("GET"),
3119+ Header: http.Header{},
3120+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
3121+ ContentLength: -1,
3122+ Close: true,
3123+ },
3124+ "HTTP/1.1 200 OK\r\n" +
3125+ "Connection: close\r\n" +
3126+ "\r\n" +
3127+ "abcdef",
3128+ },
3129+ // HTTP/1.1 response with unknown length and not setting connection: close
3130+ {
3131+ Response{
3132+ StatusCode: 200,
3133+ ProtoMajor: 1,
3134+ ProtoMinor: 1,
3135+ Request: dummyReq11("GET"),
3136+ Header: http.Header{},
3137+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
3138+ ContentLength: -1,
3139+ Close: false,
3140+ },
3141+ "HTTP/1.1 200 OK\r\n" +
3142+ "Connection: close\r\n" +
3143+ "\r\n" +
3144+ "abcdef",
3145+ },
3146+ // HTTP/1.1 response with unknown length and not setting connection: close, but
3147+ // setting chunked.
3148+ {
3149+ Response{
3150+ StatusCode: 200,
3151+ ProtoMajor: 1,
3152+ ProtoMinor: 1,
3153+ Request: dummyReq11("GET"),
3154+ Header: http.Header{},
3155+ Body: ioutil.NopCloser(strings.NewReader("abcdef")),
3156+ ContentLength: -1,
3157+ TransferEncoding: []string{"chunked"},
3158+ Close: false,
3159+ },
3160+ "HTTP/1.1 200 OK\r\n" +
3161+ "Transfer-Encoding: chunked\r\n\r\n" +
3162+ "6\r\nabcdef\r\n0\r\n\r\n",
3163+ },
3164+ // HTTP/1.1 response 0 content-length, and nil body
3165+ {
3166+ Response{
3167+ StatusCode: 200,
3168+ ProtoMajor: 1,
3169+ ProtoMinor: 1,
3170+ Request: dummyReq11("GET"),
3171+ Header: http.Header{},
3172+ Body: nil,
3173+ ContentLength: 0,
3174+ Close: false,
3175+ },
3176+ "HTTP/1.1 200 OK\r\n" +
3177+ "Content-Length: 0\r\n" +
3178+ "\r\n",
3179+ },
3180+ // HTTP/1.1 response 0 content-length, and non-nil empty body
3181+ {
3182+ Response{
3183+ StatusCode: 200,
3184+ ProtoMajor: 1,
3185+ ProtoMinor: 1,
3186+ Request: dummyReq11("GET"),
3187+ Header: http.Header{},
3188+ Body: ioutil.NopCloser(strings.NewReader("")),
3189+ ContentLength: 0,
3190+ Close: false,
3191+ },
3192+ "HTTP/1.1 200 OK\r\n" +
3193+ "Content-Length: 0\r\n" +
3194+ "\r\n",
3195+ },
3196+ // HTTP/1.1 response 0 content-length, and non-nil non-empty body
3197+ {
3198+ Response{
3199+ StatusCode: 200,
3200+ ProtoMajor: 1,
3201+ ProtoMinor: 1,
3202+ Request: dummyReq11("GET"),
3203+ Header: http.Header{},
3204+ Body: ioutil.NopCloser(strings.NewReader("foo")),
3205+ ContentLength: 0,
3206+ Close: false,
3207+ },
3208+ "HTTP/1.1 200 OK\r\n" +
3209+ "Connection: close\r\n" +
3210+ "\r\nfoo",
3211+ },
3212 // HTTP/1.1, chunked coding; empty trailer; close
3213 {
3214 Response{
3215@@ -92,6 +192,22 @@
3216 "Foo: Bar Baz\r\n" +
3217 "\r\n",
3218 },
3219+
3220+ // Want a single Content-Length header. Fixing issue 8180 where
3221+ // there were two.
3222+ {
3223+ Response{
3224+ StatusCode: http.StatusOK,
3225+ ProtoMajor: 1,
3226+ ProtoMinor: 1,
3227+ Request: &Request{Method: "POST"},
3228+ Header: http.Header{},
3229+ ContentLength: 0,
3230+ TransferEncoding: nil,
3231+ Body: nil,
3232+ },
3233+ "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
3234+ },
3235 }
3236
3237 for i := range respWriteTests {
3238
3239=== modified file 'http13client/server.go'
3240--- http13client/server.go 2014-03-19 23:13:58 +0000
3241+++ http13client/server.go 2014-06-20 13:24:39 +0000
3242@@ -10,21 +10,27 @@
3243 "io/ioutil"
3244 "log"
3245 "net"
3246- "strings"
3247 "sync"
3248 )
3249
3250+type eofReaderWithWriteTo struct{}
3251+
3252+func (eofReaderWithWriteTo) WriteTo(io.Writer) (int64, error) { return 0, nil }
3253+func (eofReaderWithWriteTo) Read([]byte) (int, error) { return 0, io.EOF }
3254+
3255 // eofReader is a non-nil io.ReadCloser that always returns EOF.
3256-// It embeds a *strings.Reader so it still has a WriteTo method
3257-// and io.Copy won't need a buffer.
3258+// It has a WriteTo method so io.Copy won't need a buffer.
3259 var eofReader = &struct {
3260- *strings.Reader
3261+ eofReaderWithWriteTo
3262 io.Closer
3263 }{
3264- strings.NewReader(""),
3265+ eofReaderWithWriteTo{},
3266 ioutil.NopCloser(nil),
3267 }
3268
3269+// Verify that an io.Copy from an eofReader won't require a buffer.
3270+var _ io.WriterTo = eofReader
3271+
3272 // loggingConn is used for debugging.
3273 type loggingConn struct {
3274 name string
3275
3276=== modified file 'http13client/transfer.go'
3277--- http13client/transfer.go 2014-03-19 23:13:58 +0000
3278+++ http13client/transfer.go 2014-06-20 13:24:39 +0000
3279@@ -13,6 +13,7 @@
3280 "io/ioutil"
3281 "net/http"
3282 "net/textproto"
3283+ "sort"
3284 "strconv"
3285 "strings"
3286 "sync"
3287@@ -144,11 +145,10 @@
3288 return false
3289 }
3290
3291-func (t *transferWriter) WriteHeader(w io.Writer) (err error) {
3292+func (t *transferWriter) WriteHeader(w io.Writer) error {
3293 if t.Close {
3294- _, err = io.WriteString(w, "Connection: close\r\n")
3295- if err != nil {
3296- return
3297+ if _, err := io.WriteString(w, "Connection: close\r\n"); err != nil {
3298+ return err
3299 }
3300 }
3301
3302@@ -156,43 +156,44 @@
3303 // function of the sanitized field triple (Body, ContentLength,
3304 // TransferEncoding)
3305 if t.shouldSendContentLength() {
3306- io.WriteString(w, "Content-Length: ")
3307- _, err = io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n")
3308- if err != nil {
3309- return
3310+ if _, err := io.WriteString(w, "Content-Length: "); err != nil {
3311+ return err
3312+ }
3313+ if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil {
3314+ return err
3315 }
3316 } else if chunked(t.TransferEncoding) {
3317- _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
3318- if err != nil {
3319- return
3320+ if _, err := io.WriteString(w, "Transfer-Encoding: chunked\r\n"); err != nil {
3321+ return err
3322 }
3323 }
3324
3325 // Write Trailer header
3326 if t.Trailer != nil {
3327- // TODO: At some point, there should be a generic mechanism for
3328- // writing long headers, using HTTP line splitting
3329- io.WriteString(w, "Trailer: ")
3330- needComma := false
3331+ keys := make([]string, 0, len(t.Trailer))
3332 for k := range t.Trailer {
3333 k = http.CanonicalHeaderKey(k)
3334 switch k {
3335 case "Transfer-Encoding", "Trailer", "Content-Length":
3336 return &badStringError{"invalid Trailer key", k}
3337 }
3338- if needComma {
3339- io.WriteString(w, ",")
3340+ keys = append(keys, k)
3341+ }
3342+ if len(keys) > 0 {
3343+ sort.Strings(keys)
3344+ // TODO: could do better allocation-wise here, but trailers are rare,
3345+ // so being lazy for now.
3346+ if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil {
3347+ return err
3348 }
3349- io.WriteString(w, k)
3350- needComma = true
3351 }
3352- _, err = io.WriteString(w, "\r\n")
3353 }
3354
3355- return
3356+ return nil
3357 }
3358
3359-func (t *transferWriter) WriteBody(w io.Writer) (err error) {
3360+func (t *transferWriter) WriteBody(w io.Writer) error {
3361+ var err error
3362 var ncopy int64
3363
3364 // Write body
3365@@ -229,11 +230,16 @@
3366
3367 // TODO(petar): Place trailer writer code here.
3368 if chunked(t.TransferEncoding) {
3369+ // Write Trailer header
3370+ if t.Trailer != nil {
3371+ if err := t.Trailer.Write(w); err != nil {
3372+ return err
3373+ }
3374+ }
3375 // Last chunk, empty trailer
3376 _, err = io.WriteString(w, "\r\n")
3377 }
3378-
3379- return
3380+ return err
3381 }
3382
3383 type transferReader struct {
3384@@ -265,6 +271,22 @@
3385 return true
3386 }
3387
3388+var (
3389+ suppressedHeaders304 = []string{"Content-Type", "Content-Length", "Transfer-Encoding"}
3390+ suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"}
3391+)
3392+
3393+func suppressedHeaders(status int) []string {
3394+ switch {
3395+ case status == 304:
3396+ // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
3397+ return suppressedHeaders304
3398+ case !bodyAllowedForStatus(status):
3399+ return suppressedHeadersNoBody
3400+ }
3401+ return nil
3402+}
3403+
3404 // msg is *Request or *Response.
3405 func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
3406 t := &transferReader{RequestMethod: "GET"}
3407@@ -511,7 +533,7 @@
3408 case "Transfer-Encoding", "Trailer", "Content-Length":
3409 return nil, &badStringError{"bad trailer key", key}
3410 }
3411- trailer.Del(key)
3412+ trailer[key] = nil
3413 }
3414 if len(trailer) == 0 {
3415 return nil, nil
3416@@ -643,13 +665,23 @@
3417 }
3418 switch rr := b.hdr.(type) {
3419 case *Request:
3420- rr.Trailer = http.Header(hdr)
3421+ mergeSetHeader(&rr.Trailer, http.Header(hdr))
3422 case *Response:
3423- rr.Trailer = http.Header(hdr)
3424+ mergeSetHeader(&rr.Trailer, http.Header(hdr))
3425 }
3426 return nil
3427 }
3428
3429+func mergeSetHeader(dst *http.Header, src http.Header) {
3430+ if *dst == nil {
3431+ *dst = src
3432+ return
3433+ }
3434+ for k, vv := range src {
3435+ (*dst)[k] = vv
3436+ }
3437+}
3438+
3439 func (b *body) Close() error {
3440 b.mu.Lock()
3441 defer b.mu.Unlock()
3442
3443=== modified file 'http13client/transport.go'
3444--- http13client/transport.go 2014-03-20 09:26:28 +0000
3445+++ http13client/transport.go 2014-06-20 13:24:39 +0000
3446@@ -110,6 +110,9 @@
3447 // An error is returned if the proxy environment is invalid.
3448 // A nil URL and nil error are returned if no proxy is defined in the
3449 // environment, or a proxy should not be used for the given request.
3450+//
3451+// As a special case, if req.URL.Host is "localhost" (with or without
3452+// a port number), then a nil URL and nil error will be returned.
3453 func ProxyFromEnvironment(req *Request) (*url.URL, error) {
3454 proxy := httpProxyEnv.Get()
3455 if proxy == "" {
3456@@ -161,9 +164,11 @@
3457 // and redirects), see Get, Post, and the Client type.
3458 func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
3459 if req.URL == nil {
3460+ req.closeBody()
3461 return nil, errors.New("http: nil Request.URL")
3462 }
3463 if req.Header == nil {
3464+ req.closeBody()
3465 return nil, errors.New("http: nil Request.Header")
3466 }
3467 if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
3468@@ -174,16 +179,19 @@
3469 }
3470 t.altMu.RUnlock()
3471 if rt == nil {
3472+ req.closeBody()
3473 return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
3474 }
3475 return rt.RoundTrip(req)
3476 }
3477 if req.URL.Host == "" {
3478+ req.closeBody()
3479 return nil, errors.New("http: no Host in request URL")
3480 }
3481 treq := &transportRequest{Request: req}
3482 cm, err := t.connectMethodForRequest(treq)
3483 if err != nil {
3484+ req.closeBody()
3485 return nil, err
3486 }
3487
3488@@ -194,6 +202,7 @@
3489 pconn, err := t.getConn(req, cm)
3490 if err != nil {
3491 t.setReqCanceler(req, nil)
3492+ req.closeBody()
3493 return nil, err
3494 }
3495
3496@@ -231,9 +240,6 @@
3497 t.idleConn = nil
3498 t.idleConnCh = nil
3499 t.idleMu.Unlock()
3500- if m == nil {
3501- return
3502- }
3503 for _, conns := range m {
3504 for _, pconn := range conns {
3505 pconn.close()
3506@@ -499,12 +505,13 @@
3507 pa := cm.proxyAuth()
3508
3509 pconn := &persistConn{
3510- t: t,
3511- cacheKey: cm.key(),
3512- conn: conn,
3513- reqch: make(chan requestAndChan, 50),
3514- writech: make(chan writeRequest, 50),
3515- closech: make(chan struct{}),
3516+ t: t,
3517+ cacheKey: cm.key(),
3518+ conn: conn,
3519+ reqch: make(chan requestAndChan, 1),
3520+ writech: make(chan writeRequest, 1),
3521+ closech: make(chan struct{}),
3522+ writeErrCh: make(chan error, 1),
3523 }
3524
3525 switch {
3526@@ -589,7 +596,7 @@
3527 pconn.conn = tlsConn
3528 }
3529
3530- pconn.br = bufio.NewReader(pconn.conn)
3531+ pconn.br = bufio.NewReader(noteEOFReader{pconn.conn, &pconn.sawEOF})
3532 pconn.bw = bufio.NewWriter(pconn.conn)
3533 go pconn.readLoop()
3534 go pconn.writeLoop()
3535@@ -722,16 +729,22 @@
3536 cacheKey connectMethodKey
3537 conn net.Conn
3538 tlsState *tls.ConnectionState
3539- closed bool // whether conn has been closed
3540 br *bufio.Reader // from conn
3541+ sawEOF bool // whether we've seen EOF from conn; owned by readLoop
3542 bw *bufio.Writer // to conn
3543 reqch chan requestAndChan // written by roundTrip; read by readLoop
3544 writech chan writeRequest // written by roundTrip; read by writeLoop
3545- closech chan struct{} // broadcast close when readLoop (TCP connection) closes
3546+ closech chan struct{} // closed when conn closed
3547 isProxy bool
3548+ // writeErrCh passes the request write error (usually nil)
3549+ // from the writeLoop goroutine to the readLoop which passes
3550+ // it off to the res.Body reader, which then uses it to decide
3551+ // whether or not a connection can be reused. Issue 7569.
3552+ writeErrCh chan error
3553
3554- lk sync.Mutex // guards following 3 fields
3555+ lk sync.Mutex // guards following fields
3556 numExpectedResponses int
3557+ closed bool // whether conn has been closed
3558 broken bool // an error has happened on this connection; marked broken so it's not reused.
3559 // mutateHeaderFunc is an optional func to modify extra
3560 // headers on each outbound request before it's written. (the
3561@@ -739,6 +752,7 @@
3562 mutateHeaderFunc func(http.Header)
3563 }
3564
3565+// isBroken reports whether this connection is in a known broken state.
3566 func (pc *persistConn) isBroken() bool {
3567 pc.lk.Lock()
3568 b := pc.broken
3569@@ -763,7 +777,6 @@
3570 }
3571
3572 func (pc *persistConn) readLoop() {
3573- defer close(pc.closech)
3574 alive := true
3575
3576 for alive {
3577@@ -771,12 +784,14 @@
3578
3579 pc.lk.Lock()
3580 if pc.numExpectedResponses == 0 {
3581- pc.closeLocked()
3582+ if !pc.closed {
3583+ pc.closeLocked()
3584+ if len(pb) > 0 {
3585+ log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
3586+ string(pb), err)
3587+ }
3588+ }
3589 pc.lk.Unlock()
3590- if len(pb) > 0 {
3591- log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
3592- string(pb), err)
3593- }
3594 return
3595 }
3596 pc.lk.Unlock()
3597@@ -809,13 +824,7 @@
3598 resp.Header.Del("Content-Encoding")
3599 resp.Header.Del("Content-Length")
3600 resp.ContentLength = -1
3601- gzReader, zerr := gzip.NewReader(resp.Body)
3602- if zerr != nil {
3603- pc.close()
3604- err = zerr
3605- } else {
3606- resp.Body = &readerAndCloser{gzReader, resp.Body}
3607- }
3608+ resp.Body = &gzipReader{body: resp.Body}
3609 }
3610 resp.Body = &bodyEOFSignal{body: resp.Body}
3611 }
3612@@ -838,24 +847,18 @@
3613 return nil
3614 }
3615 resp.Body.(*bodyEOFSignal).fn = func(err error) {
3616- alive1 := alive
3617- if err != nil {
3618- alive1 = false
3619- }
3620- if alive1 && !pc.t.putIdleConn(pc) {
3621- alive1 = false
3622- }
3623- if !alive1 || pc.isBroken() {
3624- pc.close()
3625- }
3626- waitForBodyRead <- alive1
3627+ waitForBodyRead <- alive &&
3628+ err == nil &&
3629+ !pc.sawEOF &&
3630+ pc.wroteRequest() &&
3631+ pc.t.putIdleConn(pc)
3632 }
3633 }
3634
3635 if alive && !hasBody {
3636- if !pc.t.putIdleConn(pc) {
3637- alive = false
3638- }
3639+ alive = !pc.sawEOF &&
3640+ pc.wroteRequest() &&
3641+ pc.t.putIdleConn(pc)
3642 }
3643
3644 rc.ch <- responseAndError{resp, err}
3645@@ -863,7 +866,11 @@
3646 // Wait for the just-returned response body to be fully consumed
3647 // before we race and peek on the underlying bufio reader.
3648 if waitForBodyRead != nil {
3649- alive = <-waitForBodyRead
3650+ select {
3651+ case alive = <-waitForBodyRead:
3652+ case <-pc.closech:
3653+ alive = false
3654+ }
3655 }
3656
3657 pc.t.setReqCanceler(rc.req, nil)
3658@@ -888,14 +895,44 @@
3659 }
3660 if err != nil {
3661 pc.markBroken()
3662+ wr.req.Request.closeBody()
3663 }
3664- wr.ch <- err
3665+ pc.writeErrCh <- err // to the body reader, which might recycle us
3666+ wr.ch <- err // to the roundTrip function
3667 case <-pc.closech:
3668 return
3669 }
3670 }
3671 }
3672
3673+// wroteRequest is a check before recycling a connection that the previous write
3674+// (from writeLoop above) happened and was successful.
3675+func (pc *persistConn) wroteRequest() bool {
3676+ select {
3677+ case err := <-pc.writeErrCh:
3678+ // Common case: the write happened well before the response, so
3679+ // avoid creating a timer.
3680+ return err == nil
3681+ default:
3682+ // Rare case: the request was written in writeLoop above but
3683+ // before it could send to pc.writeErrCh, the reader read it
3684+ // all, processed it, and called us here. In this case, give the
3685+ // write goroutine a bit of time to finish its send.
3686+ //
3687+ // Less rare case: We also get here in the legitimate case of
3688+ // Issue 7569, where the writer is still writing (or stalled),
3689+ // but the server has already replied. In this case, we don't
3690+ // want to wait too long, and we want to return false so this
3691+ // connection isn't re-used.
3692+ select {
3693+ case err := <-pc.writeErrCh:
3694+ return err == nil
3695+ case <-time.After(50 * time.Millisecond):
3696+ return false
3697+ }
3698+ }
3699+}
3700+
3701 type responseAndError struct {
3702 res *Response
3703 err error
3704@@ -1043,6 +1080,7 @@
3705 if !pc.closed {
3706 pc.conn.Close()
3707 pc.closed = true
3708+ close(pc.closech)
3709 }
3710 pc.mutateHeaderFunc = nil
3711 }
3712@@ -1125,6 +1163,27 @@
3713 es.fn = nil
3714 }
3715
3716+// gzipReader wraps a response body so it can lazily
3717+// call gzip.NewReader on the first call to Read
3718+type gzipReader struct {
3719+ body io.ReadCloser // underlying Response.Body
3720+ zr io.Reader // lazily-initialized gzip reader
3721+}
3722+
3723+func (gz *gzipReader) Read(p []byte) (n int, err error) {
3724+ if gz.zr == nil {
3725+ gz.zr, err = gzip.NewReader(gz.body)
3726+ if err != nil {
3727+ return 0, err
3728+ }
3729+ }
3730+ return gz.zr.Read(p)
3731+}
3732+
3733+func (gz *gzipReader) Close() error {
3734+ return gz.body.Close()
3735+}
3736+
3737 type readerAndCloser struct {
3738 io.Reader
3739 io.Closer
3740@@ -1135,3 +1194,16 @@
3741 func (tlsHandshakeTimeoutError) Timeout() bool { return true }
3742 func (tlsHandshakeTimeoutError) Temporary() bool { return true }
3743 func (tlsHandshakeTimeoutError) Error() string { return "net/http: TLS handshake timeout" }
3744+
3745+type noteEOFReader struct {
3746+ r io.Reader
3747+ sawEOF *bool
3748+}
3749+
3750+func (nr noteEOFReader) Read(p []byte) (n int, err error) {
3751+ n, err = nr.r.Read(p)
3752+ if err == io.EOF {
3753+ *nr.sawEOF = true
3754+ }
3755+ return
3756+}
3757
3758=== modified file 'http13client/transport_test.go'
3759--- http13client/transport_test.go 2014-03-20 09:26:28 +0000
3760+++ http13client/transport_test.go 2014-06-20 13:24:39 +0000
3761@@ -11,6 +11,7 @@
3762 "bytes"
3763 "compress/gzip"
3764 "crypto/rand"
3765+ "crypto/tls"
3766 "errors"
3767 "fmt"
3768 "io"
3769@@ -56,21 +57,21 @@
3770 // been closed.
3771 type testConnSet struct {
3772 t *testing.T
3773+ mu sync.Mutex // guards closed and list
3774 closed map[net.Conn]bool
3775 list []net.Conn // in order created
3776- mutex sync.Mutex
3777 }
3778
3779 func (tcs *testConnSet) insert(c net.Conn) {
3780- tcs.mutex.Lock()
3781- defer tcs.mutex.Unlock()
3782+ tcs.mu.Lock()
3783+ defer tcs.mu.Unlock()
3784 tcs.closed[c] = false
3785 tcs.list = append(tcs.list, c)
3786 }
3787
3788 func (tcs *testConnSet) remove(c net.Conn) {
3789- tcs.mutex.Lock()
3790- defer tcs.mutex.Unlock()
3791+ tcs.mu.Lock()
3792+ defer tcs.mu.Unlock()
3793 tcs.closed[c] = true
3794 }
3795
3796@@ -93,11 +94,19 @@
3797 }
3798
3799 func (tcs *testConnSet) check(t *testing.T) {
3800- tcs.mutex.Lock()
3801- defer tcs.mutex.Unlock()
3802-
3803- for i, c := range tcs.list {
3804- if !tcs.closed[c] {
3805+ tcs.mu.Lock()
3806+ defer tcs.mu.Unlock()
3807+ for i := 4; i >= 0; i-- {
3808+ for i, c := range tcs.list {
3809+ if tcs.closed[c] {
3810+ continue
3811+ }
3812+ if i != 0 {
3813+ tcs.mu.Unlock()
3814+ time.Sleep(50 * time.Millisecond)
3815+ tcs.mu.Lock()
3816+ continue
3817+ }
3818 t.Errorf("TCP connection #%d, %p (of %d total) was not closed", i+1, c, len(tcs.list))
3819 }
3820 }
3821@@ -794,6 +803,33 @@
3822 }
3823 }
3824
3825+// golang.org/issue/7750: request fails when server replies with
3826+// a short gzip body
3827+func TestTransportGzipShort(t *testing.T) {
3828+ defer afterTest(t)
3829+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3830+ w.Header().Set("Content-Encoding", "gzip")
3831+ w.Write([]byte{0x1f, 0x8b})
3832+ }))
3833+ defer ts.Close()
3834+
3835+ tr := &Transport{}
3836+ defer tr.CloseIdleConnections()
3837+ c := &Client{Transport: tr}
3838+ res, err := c.Get(ts.URL)
3839+ if err != nil {
3840+ t.Fatal(err)
3841+ }
3842+ defer res.Body.Close()
3843+ _, err = ioutil.ReadAll(res.Body)
3844+ if err == nil {
3845+ t.Fatal("Expect an error from reading a body.")
3846+ }
3847+ if err != io.ErrUnexpectedEOF {
3848+ t.Errorf("ReadAll error = %v; want io.ErrUnexpectedEOF", err)
3849+ }
3850+}
3851+
3852 // tests that persistent goroutine connections shut down when no longer desired.
3853 func TestTransportPersistConnLeak(t *testing.T) {
3854 if runtime.GOOS == "plan9" {
3855@@ -1214,9 +1250,13 @@
3856 if testing.Short() {
3857 t.Skip("skipping timeout test in -short mode")
3858 }
3859+ inHandler := make(chan bool, 1)
3860 mux := http.NewServeMux()
3861- mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {})
3862+ mux.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {
3863+ inHandler <- true
3864+ })
3865 mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
3866+ inHandler <- true
3867 time.Sleep(2 * time.Second)
3868 })
3869 ts := httptest.NewServer(mux)
3870@@ -1239,6 +1279,12 @@
3871 }
3872 for i, tt := range tests {
3873 res, err := c.Get(ts.URL + tt.path)
3874+ select {
3875+ case <-inHandler:
3876+ case <-time.After(5 * time.Second):
3877+ t.Errorf("never entered handler for test index %d, %s", i, tt.path)
3878+ continue
3879+ }
3880 if err != nil {
3881 uerr, ok := err.(*url.Error)
3882 if !ok {
3883@@ -1366,7 +1412,6 @@
3884 case <-gotres:
3885 case <-time.After(5 * time.Second):
3886 panic("hang. events are: " + logbuf.String())
3887- t.Fatal("timeout; cancel didn't work?")
3888 }
3889
3890 got := logbuf.String()
3891@@ -1508,8 +1553,10 @@
3892 dialGate := make(chan bool, 1)
3893 tr := &Transport{
3894 Dial: func(n, addr string) (net.Conn, error) {
3895- <-dialGate
3896- return net.Dial(n, addr)
3897+ if <-dialGate {
3898+ return net.Dial(n, addr)
3899+ }
3900+ return nil, errors.New("manually closed")
3901 },
3902 DisableKeepAlives: false,
3903 }
3904@@ -1544,7 +1591,7 @@
3905 t.Fatalf("/foo came from conn %q; /bar came from %q instead", fooAddr, barAddr)
3906 }
3907 barRes.Body.Close()
3908- dialGate <- true
3909+ dialGate <- false
3910 }
3911
3912 // Issue 2184
3913@@ -1823,10 +1870,10 @@
3914 return
3915 }
3916 if !ne.Timeout() {
3917- t.Error("expected timeout error; got %v", err)
3918+ t.Errorf("expected timeout error; got %v", err)
3919 }
3920 if !strings.Contains(err.Error(), "handshake timeout") {
3921- t.Error("expected 'handshake timeout' in error; got %v", err)
3922+ t.Errorf("expected 'handshake timeout' in error; got %v", err)
3923 }
3924 }()
3925 select {
3926@@ -1836,6 +1883,238 @@
3927 }
3928 }
3929
3930+// Trying to repro golang.org/issue/3514
3931+func TestTLSServerClosesConnection(t *testing.T) {
3932+ defer afterTest(t)
3933+ if runtime.GOOS == "windows" {
3934+ t.Skip("skipping flaky test on Windows; golang.org/issue/7634")
3935+ }
3936+ closedc := make(chan bool, 1)
3937+ ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3938+ if strings.Contains(r.URL.Path, "/keep-alive-then-die") {
3939+ conn, _, _ := w.(http.Hijacker).Hijack()
3940+ conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
3941+ conn.Close()
3942+ closedc <- true
3943+ return
3944+ }
3945+ fmt.Fprintf(w, "hello")
3946+ }))
3947+ defer ts.Close()
3948+ tr := &Transport{
3949+ TLSClientConfig: &tls.Config{
3950+ InsecureSkipVerify: true,
3951+ },
3952+ }
3953+ defer tr.CloseIdleConnections()
3954+ client := &Client{Transport: tr}
3955+
3956+ var nSuccess = 0
3957+ var errs []error
3958+ const trials = 20
3959+ for i := 0; i < trials; i++ {
3960+ tr.CloseIdleConnections()
3961+ res, err := client.Get(ts.URL + "/keep-alive-then-die")
3962+ if err != nil {
3963+ t.Fatal(err)
3964+ }
3965+ <-closedc
3966+ slurp, err := ioutil.ReadAll(res.Body)
3967+ if err != nil {
3968+ t.Fatal(err)
3969+ }
3970+ if string(slurp) != "foo" {
3971+ t.Errorf("Got %q, want foo", slurp)
3972+ }
3973+
3974+ // Now try again and see if we successfully
3975+ // pick a new connection.
3976+ res, err = client.Get(ts.URL + "/")
3977+ if err != nil {
3978+ errs = append(errs, err)
3979+ continue
3980+ }
3981+ slurp, err = ioutil.ReadAll(res.Body)
3982+ if err != nil {
3983+ errs = append(errs, err)
3984+ continue
3985+ }
3986+ nSuccess++
3987+ }
3988+ if nSuccess > 0 {
3989+ t.Logf("successes = %d of %d", nSuccess, trials)
3990+ } else {
3991+ t.Errorf("All runs failed:")
3992+ }
3993+ for _, err := range errs {
3994+ t.Logf(" err: %v", err)
3995+ }
3996+}
3997+
3998+// byteFromChanReader is an io.Reader that reads a single byte at a
3999+// time from the channel. When the channel is closed, the reader
4000+// returns io.EOF.
4001+type byteFromChanReader chan byte
4002+
4003+func (c byteFromChanReader) Read(p []byte) (n int, err error) {
4004+ if len(p) == 0 {
4005+ return
4006+ }
4007+ b, ok := <-c
4008+ if !ok {
4009+ return 0, io.EOF
4010+ }
4011+ p[0] = b
4012+ return 1, nil
4013+}
4014+
4015+// Verifies that the Transport doesn't reuse a connection in the case
4016+// where the server replies before the request has been fully
4017+// written. We still honor that reply (see TestIssue3595), but don't
4018+// send future requests on the connection because it's then in a
4019+// questionable state.
4020+// golang.org/issue/7569
4021+func TestTransportNoReuseAfterEarlyResponse(t *testing.T) {
4022+ defer afterTest(t)
4023+ var sconn struct {
4024+ sync.Mutex
4025+ c net.Conn
4026+ }
4027+ var getOkay bool
4028+ closeConn := func() {
4029+ sconn.Lock()
4030+ defer sconn.Unlock()
4031+ if sconn.c != nil {
4032+ sconn.c.Close()
4033+ sconn.c = nil
4034+ if !getOkay {
4035+ t.Logf("Closed server connection")
4036+ }
4037+ }
4038+ }
4039+ defer closeConn()
4040+
4041+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
4042+ if r.Method == "GET" {
4043+ io.WriteString(w, "bar")
4044+ return
4045+ }
4046+ conn, _, _ := w.(http.Hijacker).Hijack()
4047+ sconn.Lock()
4048+ sconn.c = conn
4049+ sconn.Unlock()
4050+ conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo")) // keep-alive
4051+ go io.Copy(ioutil.Discard, conn)
4052+ }))
4053+ defer ts.Close()
4054+ tr := &Transport{}
4055+ defer tr.CloseIdleConnections()
4056+ client := &Client{Transport: tr}
4057+
4058+ const bodySize = 256 << 10
4059+ finalBit := make(byteFromChanReader, 1)
4060+ req, _ := NewRequest("POST", ts.URL, io.MultiReader(io.LimitReader(neverEnding('x'), bodySize-1), finalBit))
4061+ req.ContentLength = bodySize
4062+ res, err := client.Do(req)
4063+ if err := wantBody(res, err, "foo"); err != nil {
4064+ t.Errorf("POST response: %v", err)
4065+ }
4066+ donec := make(chan bool)
4067+ go func() {
4068+ defer close(donec)
4069+ res, err = client.Get(ts.URL)
4070+ if err := wantBody(res, err, "bar"); err != nil {
4071+ t.Errorf("GET response: %v", err)
4072+ return
4073+ }
4074+ getOkay = true // suppress test noise
4075+ }()
4076+ time.AfterFunc(5*time.Second, closeConn)
4077+ select {
4078+ case <-donec:
4079+ finalBit <- 'x' // unblock the writeloop of the first Post
4080+ close(finalBit)
4081+ case <-time.After(7 * time.Second):
4082+ t.Fatal("timeout waiting for GET request to finish")
4083+ }
4084+}
4085+
4086+type errorReader struct {
4087+ err error
4088+}
4089+
4090+func (e errorReader) Read(p []byte) (int, error) { return 0, e.err }
4091+
4092+type closerFunc func() error
4093+
4094+func (f closerFunc) Close() error { return f() }
4095+
4096+// Issue 6981
4097+func TestTransportClosesBodyOnError(t *testing.T) {
4098+ if runtime.GOOS == "plan9" {
4099+ t.Skip("skipping test; see http://golang.org/issue/7782")
4100+ }
4101+ defer afterTest(t)
4102+ readBody := make(chan error, 1)
4103+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
4104+ _, err := ioutil.ReadAll(r.Body)
4105+ readBody <- err
4106+ }))
4107+ defer ts.Close()
4108+ fakeErr := errors.New("fake error")
4109+ didClose := make(chan bool, 1)
4110+ req, _ := NewRequest("POST", ts.URL, struct {
4111+ io.Reader
4112+ io.Closer
4113+ }{
4114+ io.MultiReader(io.LimitReader(neverEnding('x'), 1<<20), errorReader{fakeErr}),
4115+ closerFunc(func() error {
4116+ select {
4117+ case didClose <- true:
4118+ default:
4119+ }
4120+ return nil
4121+ }),
4122+ })
4123+ res, err := DefaultClient.Do(req)
4124+ if res != nil {
4125+ defer res.Body.Close()
4126+ }
4127+ if err == nil || !strings.Contains(err.Error(), fakeErr.Error()) {
4128+ t.Fatalf("Do error = %v; want something containing %q", err, fakeErr.Error())
4129+ }
4130+ select {
4131+ case err := <-readBody:
4132+ if err == nil {
4133+ t.Errorf("Unexpected success reading request body from handler; want 'unexpected EOF reading trailer'")
4134+ }
4135+ case <-time.After(5 * time.Second):
4136+ t.Error("timeout waiting for server handler to complete")
4137+ }
4138+ select {
4139+ case <-didClose:
4140+ default:
4141+ t.Errorf("didn't see Body.Close")
4142+ }
4143+}
4144+
4145+func wantBody(res *Response, err error, want string) error {
4146+ if err != nil {
4147+ return err
4148+ }
4149+ slurp, err := ioutil.ReadAll(res.Body)
4150+ if err != nil {
4151+ return fmt.Errorf("error reading body: %v", err)
4152+ }
4153+ if string(slurp) != want {
4154+ return fmt.Errorf("body = %q; want %q", slurp, want)
4155+ }
4156+ if err := res.Body.Close(); err != nil {
4157+ return fmt.Errorf("body Close = %v", err)
4158+ }
4159+ return nil
4160+}
4161+
4162 func newLocalListener(t *testing.T) net.Listener {
4163 ln, err := net.Listen("tcp", "127.0.0.1:0")
4164 if err != nil {

Subscribers

People subscribed via source and target branches