OSDN Git Service

9b4261b9f61e497b7ac41a827aba861d0fcabea3
[pf3gnuchains/gcc-fork.git] / libgo / go / net / http / client_test.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Tests for client.go
6
7 package http_test
8
9 import (
10         "crypto/tls"
11         "errors"
12         "fmt"
13         "io"
14         "io/ioutil"
15         "net"
16         . "net/http"
17         "net/http/httptest"
18         "net/url"
19         "strconv"
20         "strings"
21         "sync"
22         "testing"
23 )
24
25 var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
26         w.Header().Set("Last-Modified", "sometime")
27         fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
28 })
29
30 // pedanticReadAll works like ioutil.ReadAll but additionally
31 // verifies that r obeys the documented io.Reader contract.
32 func pedanticReadAll(r io.Reader) (b []byte, err error) {
33         var bufa [64]byte
34         buf := bufa[:]
35         for {
36                 n, err := r.Read(buf)
37                 if n == 0 && err == nil {
38                         return nil, fmt.Errorf("Read: n=0 with err=nil")
39                 }
40                 b = append(b, buf[:n]...)
41                 if err == io.EOF {
42                         n, err := r.Read(buf)
43                         if n != 0 || err != io.EOF {
44                                 return nil, fmt.Errorf("Read: n=%d err=%#v after EOF", n, err)
45                         }
46                         return b, nil
47                 }
48                 if err != nil {
49                         return b, err
50                 }
51         }
52         panic("unreachable")
53 }
54
55 func TestClient(t *testing.T) {
56         ts := httptest.NewServer(robotsTxtHandler)
57         defer ts.Close()
58
59         r, err := Get(ts.URL)
60         var b []byte
61         if err == nil {
62                 b, err = pedanticReadAll(r.Body)
63                 r.Body.Close()
64         }
65         if err != nil {
66                 t.Error(err)
67         } else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
68                 t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
69         }
70 }
71
72 func TestClientHead(t *testing.T) {
73         ts := httptest.NewServer(robotsTxtHandler)
74         defer ts.Close()
75
76         r, err := Head(ts.URL)
77         if err != nil {
78                 t.Fatal(err)
79         }
80         if _, ok := r.Header["Last-Modified"]; !ok {
81                 t.Error("Last-Modified header not found.")
82         }
83 }
84
85 type recordingTransport struct {
86         req *Request
87 }
88
89 func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err error) {
90         t.req = req
91         return nil, errors.New("dummy impl")
92 }
93
94 func TestGetRequestFormat(t *testing.T) {
95         tr := &recordingTransport{}
96         client := &Client{Transport: tr}
97         url := "http://dummy.faketld/"
98         client.Get(url) // Note: doesn't hit network
99         if tr.req.Method != "GET" {
100                 t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
101         }
102         if tr.req.URL.String() != url {
103                 t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
104         }
105         if tr.req.Header == nil {
106                 t.Errorf("expected non-nil request Header")
107         }
108 }
109
110 func TestPostRequestFormat(t *testing.T) {
111         tr := &recordingTransport{}
112         client := &Client{Transport: tr}
113
114         url := "http://dummy.faketld/"
115         json := `{"key":"value"}`
116         b := strings.NewReader(json)
117         client.Post(url, "application/json", b) // Note: doesn't hit network
118
119         if tr.req.Method != "POST" {
120                 t.Errorf("got method %q, want %q", tr.req.Method, "POST")
121         }
122         if tr.req.URL.String() != url {
123                 t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
124         }
125         if tr.req.Header == nil {
126                 t.Fatalf("expected non-nil request Header")
127         }
128         if tr.req.Close {
129                 t.Error("got Close true, want false")
130         }
131         if g, e := tr.req.ContentLength, int64(len(json)); g != e {
132                 t.Errorf("got ContentLength %d, want %d", g, e)
133         }
134 }
135
136 func TestPostFormRequestFormat(t *testing.T) {
137         tr := &recordingTransport{}
138         client := &Client{Transport: tr}
139
140         urlStr := "http://dummy.faketld/"
141         form := make(url.Values)
142         form.Set("foo", "bar")
143         form.Add("foo", "bar2")
144         form.Set("bar", "baz")
145         client.PostForm(urlStr, form) // Note: doesn't hit network
146
147         if tr.req.Method != "POST" {
148                 t.Errorf("got method %q, want %q", tr.req.Method, "POST")
149         }
150         if tr.req.URL.String() != urlStr {
151                 t.Errorf("got URL %q, want %q", tr.req.URL.String(), urlStr)
152         }
153         if tr.req.Header == nil {
154                 t.Fatalf("expected non-nil request Header")
155         }
156         if g, e := tr.req.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; g != e {
157                 t.Errorf("got Content-Type %q, want %q", g, e)
158         }
159         if tr.req.Close {
160                 t.Error("got Close true, want false")
161         }
162         // Depending on map iteration, body can be either of these.
163         expectedBody := "foo=bar&foo=bar2&bar=baz"
164         expectedBody1 := "bar=baz&foo=bar&foo=bar2"
165         if g, e := tr.req.ContentLength, int64(len(expectedBody)); g != e {
166                 t.Errorf("got ContentLength %d, want %d", g, e)
167         }
168         bodyb, err := ioutil.ReadAll(tr.req.Body)
169         if err != nil {
170                 t.Fatalf("ReadAll on req.Body: %v", err)
171         }
172         if g := string(bodyb); g != expectedBody && g != expectedBody1 {
173                 t.Errorf("got body %q, want %q or %q", g, expectedBody, expectedBody1)
174         }
175 }
176
177 func TestRedirects(t *testing.T) {
178         var ts *httptest.Server
179         ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
180                 n, _ := strconv.Atoi(r.FormValue("n"))
181                 // Test Referer header. (7 is arbitrary position to test at)
182                 if n == 7 {
183                         if g, e := r.Referer(), ts.URL+"/?n=6"; e != g {
184                                 t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g)
185                         }
186                 }
187                 if n < 15 {
188                         Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusFound)
189                         return
190                 }
191                 fmt.Fprintf(w, "n=%d", n)
192         }))
193         defer ts.Close()
194
195         c := &Client{}
196         _, err := c.Get(ts.URL)
197         if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
198                 t.Errorf("with default client Get, expected error %q, got %q", e, g)
199         }
200
201         // HEAD request should also have the ability to follow redirects.
202         _, err = c.Head(ts.URL)
203         if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
204                 t.Errorf("with default client Head, expected error %q, got %q", e, g)
205         }
206
207         // Do should also follow redirects.
208         greq, _ := NewRequest("GET", ts.URL, nil)
209         _, err = c.Do(greq)
210         if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
211                 t.Errorf("with default client Do, expected error %q, got %q", e, g)
212         }
213
214         var checkErr error
215         var lastVia []*Request
216         c = &Client{CheckRedirect: func(_ *Request, via []*Request) error {
217                 lastVia = via
218                 return checkErr
219         }}
220         res, err := c.Get(ts.URL)
221         finalUrl := res.Request.URL.String()
222         if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
223                 t.Errorf("with custom client, expected error %q, got %q", e, g)
224         }
225         if !strings.HasSuffix(finalUrl, "/?n=15") {
226                 t.Errorf("expected final url to end in /?n=15; got url %q", finalUrl)
227         }
228         if e, g := 15, len(lastVia); e != g {
229                 t.Errorf("expected lastVia to have contained %d elements; got %d", e, g)
230         }
231
232         checkErr = errors.New("no redirects allowed")
233         res, err = c.Get(ts.URL)
234         finalUrl = res.Request.URL.String()
235         if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v", err); e != g {
236                 t.Errorf("with redirects forbidden, expected error %q, got %q", e, g)
237         }
238 }
239
240 var expectedCookies = []*Cookie{
241         {Name: "ChocolateChip", Value: "tasty"},
242         {Name: "First", Value: "Hit"},
243         {Name: "Second", Value: "Hit"},
244 }
245
246 var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
247         for _, cookie := range r.Cookies() {
248                 SetCookie(w, cookie)
249         }
250         if r.URL.Path == "/" {
251                 SetCookie(w, expectedCookies[1])
252                 Redirect(w, r, "/second", StatusMovedPermanently)
253         } else {
254                 SetCookie(w, expectedCookies[2])
255                 w.Write([]byte("hello"))
256         }
257 })
258
259 func TestClientSendsCookieFromJar(t *testing.T) {
260         tr := &recordingTransport{}
261         client := &Client{Transport: tr}
262         client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
263         us := "http://dummy.faketld/"
264         u, _ := url.Parse(us)
265         client.Jar.SetCookies(u, expectedCookies)
266
267         client.Get(us) // Note: doesn't hit network
268         matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
269
270         client.Head(us) // Note: doesn't hit network
271         matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
272
273         client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
274         matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
275
276         client.PostForm(us, url.Values{}) // Note: doesn't hit network
277         matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
278
279         req, _ := NewRequest("GET", us, nil)
280         client.Do(req) // Note: doesn't hit network
281         matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
282 }
283
284 // Just enough correctness for our redirect tests. Uses the URL.Host as the
285 // scope of all cookies.
286 type TestJar struct {
287         m      sync.Mutex
288         perURL map[string][]*Cookie
289 }
290
291 func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) {
292         j.m.Lock()
293         defer j.m.Unlock()
294         j.perURL[u.Host] = cookies
295 }
296
297 func (j *TestJar) Cookies(u *url.URL) []*Cookie {
298         j.m.Lock()
299         defer j.m.Unlock()
300         return j.perURL[u.Host]
301 }
302
303 func TestRedirectCookiesOnRequest(t *testing.T) {
304         var ts *httptest.Server
305         ts = httptest.NewServer(echoCookiesRedirectHandler)
306         defer ts.Close()
307         c := &Client{}
308         req, _ := NewRequest("GET", ts.URL, nil)
309         req.AddCookie(expectedCookies[0])
310         // TODO: Uncomment when an implementation of a RFC6265 cookie jar lands.
311         _ = c
312         // resp, _ := c.Do(req)
313         // matchReturnedCookies(t, expectedCookies, resp.Cookies())
314
315         req, _ = NewRequest("GET", ts.URL, nil)
316         // resp, _ = c.Do(req)
317         // matchReturnedCookies(t, expectedCookies[1:], resp.Cookies())
318 }
319
320 func TestRedirectCookiesJar(t *testing.T) {
321         var ts *httptest.Server
322         ts = httptest.NewServer(echoCookiesRedirectHandler)
323         defer ts.Close()
324         c := &Client{}
325         c.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
326         u, _ := url.Parse(ts.URL)
327         c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
328         resp, _ := c.Get(ts.URL)
329         matchReturnedCookies(t, expectedCookies, resp.Cookies())
330 }
331
332 func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
333         t.Logf("Received cookies: %v", given)
334         if len(given) != len(expected) {
335                 t.Errorf("Expected %d cookies, got %d", len(expected), len(given))
336         }
337         for _, ec := range expected {
338                 foundC := false
339                 for _, c := range given {
340                         if ec.Name == c.Name && ec.Value == c.Value {
341                                 foundC = true
342                                 break
343                         }
344                 }
345                 if !foundC {
346                         t.Errorf("Missing cookie %v", ec)
347                 }
348         }
349 }
350
351 func TestStreamingGet(t *testing.T) {
352         say := make(chan string)
353         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
354                 w.(Flusher).Flush()
355                 for str := range say {
356                         w.Write([]byte(str))
357                         w.(Flusher).Flush()
358                 }
359         }))
360         defer ts.Close()
361
362         c := &Client{}
363         res, err := c.Get(ts.URL)
364         if err != nil {
365                 t.Fatal(err)
366         }
367         var buf [10]byte
368         for _, str := range []string{"i", "am", "also", "known", "as", "comet"} {
369                 say <- str
370                 n, err := io.ReadFull(res.Body, buf[0:len(str)])
371                 if err != nil {
372                         t.Fatalf("ReadFull on %q: %v", str, err)
373                 }
374                 if n != len(str) {
375                         t.Fatalf("Receiving %q, only read %d bytes", str, n)
376                 }
377                 got := string(buf[0:n])
378                 if got != str {
379                         t.Fatalf("Expected %q, got %q", str, got)
380                 }
381         }
382         close(say)
383         _, err = io.ReadFull(res.Body, buf[0:1])
384         if err != io.EOF {
385                 t.Fatalf("at end expected EOF, got %v", err)
386         }
387 }
388
389 type writeCountingConn struct {
390         net.Conn
391         count *int
392 }
393
394 func (c *writeCountingConn) Write(p []byte) (int, error) {
395         *c.count++
396         return c.Conn.Write(p)
397 }
398
399 // TestClientWrites verifies that client requests are buffered and we
400 // don't send a TCP packet per line of the http request + body.
401 func TestClientWrites(t *testing.T) {
402         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
403         }))
404         defer ts.Close()
405
406         writes := 0
407         dialer := func(netz string, addr string) (net.Conn, error) {
408                 c, err := net.Dial(netz, addr)
409                 if err == nil {
410                         c = &writeCountingConn{c, &writes}
411                 }
412                 return c, err
413         }
414         c := &Client{Transport: &Transport{Dial: dialer}}
415
416         _, err := c.Get(ts.URL)
417         if err != nil {
418                 t.Fatal(err)
419         }
420         if writes != 1 {
421                 t.Errorf("Get request did %d Write calls, want 1", writes)
422         }
423
424         writes = 0
425         _, err = c.PostForm(ts.URL, url.Values{"foo": {"bar"}})
426         if err != nil {
427                 t.Fatal(err)
428         }
429         if writes != 1 {
430                 t.Errorf("Post request did %d Write calls, want 1", writes)
431         }
432 }
433
434 func TestClientInsecureTransport(t *testing.T) {
435         ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
436                 w.Write([]byte("Hello"))
437         }))
438         defer ts.Close()
439
440         // TODO(bradfitz): add tests for skipping hostname checks too?
441         // would require a new cert for testing, and probably
442         // redundant with these tests.
443         for _, insecure := range []bool{true, false} {
444                 tr := &Transport{
445                         TLSClientConfig: &tls.Config{
446                                 InsecureSkipVerify: insecure,
447                         },
448                 }
449                 c := &Client{Transport: tr}
450                 _, err := c.Get(ts.URL)
451                 if (err == nil) != insecure {
452                         t.Errorf("insecure=%v: got unexpected err=%v", insecure, err)
453                 }
454         }
455 }
456
457 func TestClientErrorWithRequestURI(t *testing.T) {
458         req, _ := NewRequest("GET", "http://localhost:1234/", nil)
459         req.RequestURI = "/this/field/is/illegal/and/should/error/"
460         _, err := DefaultClient.Do(req)
461         if err == nil {
462                 t.Fatalf("expected an error")
463         }
464         if !strings.Contains(err.Error(), "RequestURI") {
465                 t.Errorf("wanted error mentioning RequestURI; got error: %v", err)
466         }
467 }