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