mirror of
https://github.com/golang/go
synced 2024-10-07 11:21:22 -06:00
6e9b1a78ff
Previously Request and Response had redundant fields for Referer, UserAgent, and cookies which caused confusion and bugs. It also didn't allow us to expand the package over time, since the way to access fields would be in the Headers one day and promoted to a field the next day. That would be hard to gofix, especially with code ranging over Headers. After a discussion on the mail package's design with a similar problem, we've designed to make the Headers be the source of truth and add accessors instead. Request: change: Referer -> Referer() change: UserAgent -> UserAgent() change: Cookie -> Cookies() new: Cookie(name) *Cookie new: AddCookie(*Cookie) Response: change: Cookie -> Cookies() Cookie: new: String() string R=rsc CC=golang-dev https://golang.org/cl/4620049
246 lines
6.4 KiB
Go
246 lines
6.4 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Tests for client.go
|
|
|
|
package http_test
|
|
|
|
import (
|
|
"fmt"
|
|
. "http"
|
|
"http/httptest"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
w.Header().Set("Last-Modified", "sometime")
|
|
fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
|
|
})
|
|
|
|
func TestClient(t *testing.T) {
|
|
ts := httptest.NewServer(robotsTxtHandler)
|
|
defer ts.Close()
|
|
|
|
r, err := Get(ts.URL)
|
|
var b []byte
|
|
if err == nil {
|
|
b, err = ioutil.ReadAll(r.Body)
|
|
r.Body.Close()
|
|
}
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
|
|
t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
|
|
}
|
|
}
|
|
|
|
func TestClientHead(t *testing.T) {
|
|
ts := httptest.NewServer(robotsTxtHandler)
|
|
defer ts.Close()
|
|
|
|
r, err := Head(ts.URL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, ok := r.Header["Last-Modified"]; !ok {
|
|
t.Error("Last-Modified header not found.")
|
|
}
|
|
}
|
|
|
|
type recordingTransport struct {
|
|
req *Request
|
|
}
|
|
|
|
func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err os.Error) {
|
|
t.req = req
|
|
return nil, os.NewError("dummy impl")
|
|
}
|
|
|
|
func TestGetRequestFormat(t *testing.T) {
|
|
tr := &recordingTransport{}
|
|
client := &Client{Transport: tr}
|
|
url := "http://dummy.faketld/"
|
|
client.Get(url) // Note: doesn't hit network
|
|
if tr.req.Method != "GET" {
|
|
t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
|
|
}
|
|
if tr.req.URL.String() != url {
|
|
t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
|
|
}
|
|
if tr.req.Header == nil {
|
|
t.Errorf("expected non-nil request Header")
|
|
}
|
|
}
|
|
|
|
func TestPostRequestFormat(t *testing.T) {
|
|
tr := &recordingTransport{}
|
|
client := &Client{Transport: tr}
|
|
|
|
url := "http://dummy.faketld/"
|
|
json := `{"key":"value"}`
|
|
b := strings.NewReader(json)
|
|
client.Post(url, "application/json", b) // Note: doesn't hit network
|
|
|
|
if tr.req.Method != "POST" {
|
|
t.Errorf("got method %q, want %q", tr.req.Method, "POST")
|
|
}
|
|
if tr.req.URL.String() != url {
|
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
|
|
}
|
|
if tr.req.Header == nil {
|
|
t.Fatalf("expected non-nil request Header")
|
|
}
|
|
if tr.req.Close {
|
|
t.Error("got Close true, want false")
|
|
}
|
|
if g, e := tr.req.ContentLength, int64(len(json)); g != e {
|
|
t.Errorf("got ContentLength %d, want %d", g, e)
|
|
}
|
|
}
|
|
|
|
func TestPostFormRequestFormat(t *testing.T) {
|
|
tr := &recordingTransport{}
|
|
client := &Client{Transport: tr}
|
|
|
|
url := "http://dummy.faketld/"
|
|
form := make(Values)
|
|
form.Set("foo", "bar")
|
|
form.Add("foo", "bar2")
|
|
form.Set("bar", "baz")
|
|
client.PostForm(url, form) // Note: doesn't hit network
|
|
|
|
if tr.req.Method != "POST" {
|
|
t.Errorf("got method %q, want %q", tr.req.Method, "POST")
|
|
}
|
|
if tr.req.URL.String() != url {
|
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
|
|
}
|
|
if tr.req.Header == nil {
|
|
t.Fatalf("expected non-nil request Header")
|
|
}
|
|
if g, e := tr.req.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; g != e {
|
|
t.Errorf("got Content-Type %q, want %q", g, e)
|
|
}
|
|
if tr.req.Close {
|
|
t.Error("got Close true, want false")
|
|
}
|
|
expectedBody := "foo=bar&foo=bar2&bar=baz"
|
|
if g, e := tr.req.ContentLength, int64(len(expectedBody)); g != e {
|
|
t.Errorf("got ContentLength %d, want %d", g, e)
|
|
}
|
|
bodyb, err := ioutil.ReadAll(tr.req.Body)
|
|
if err != nil {
|
|
t.Fatalf("ReadAll on req.Body: %v", err)
|
|
}
|
|
if g := string(bodyb); g != expectedBody {
|
|
t.Errorf("got body %q, want %q", g, expectedBody)
|
|
}
|
|
}
|
|
|
|
func TestRedirects(t *testing.T) {
|
|
var ts *httptest.Server
|
|
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
n, _ := strconv.Atoi(r.FormValue("n"))
|
|
// Test Referer header. (7 is arbitrary position to test at)
|
|
if n == 7 {
|
|
if g, e := r.Referer(), ts.URL+"/?n=6"; e != g {
|
|
t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g)
|
|
}
|
|
}
|
|
if n < 15 {
|
|
Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusFound)
|
|
return
|
|
}
|
|
fmt.Fprintf(w, "n=%d", n)
|
|
}))
|
|
defer ts.Close()
|
|
|
|
c := &Client{}
|
|
_, err := c.Get(ts.URL)
|
|
if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
|
|
t.Errorf("with default client Get, expected error %q, got %q", e, g)
|
|
}
|
|
|
|
// HEAD request should also have the ability to follow redirects.
|
|
_, err = c.Head(ts.URL)
|
|
if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
|
|
t.Errorf("with default client Head, expected error %q, got %q", e, g)
|
|
}
|
|
|
|
// Do should also follow redirects.
|
|
greq, _ := NewRequest("GET", ts.URL, nil)
|
|
_, err = c.Do(greq)
|
|
if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
|
|
t.Errorf("with default client Do, expected error %q, got %q", e, g)
|
|
}
|
|
|
|
var checkErr os.Error
|
|
var lastVia []*Request
|
|
c = &Client{CheckRedirect: func(_ *Request, via []*Request) os.Error {
|
|
lastVia = via
|
|
return checkErr
|
|
}}
|
|
res, err := c.Get(ts.URL)
|
|
finalUrl := res.Request.URL.String()
|
|
if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
|
|
t.Errorf("with custom client, expected error %q, got %q", e, g)
|
|
}
|
|
if !strings.HasSuffix(finalUrl, "/?n=15") {
|
|
t.Errorf("expected final url to end in /?n=15; got url %q", finalUrl)
|
|
}
|
|
if e, g := 15, len(lastVia); e != g {
|
|
t.Errorf("expected lastVia to have contained %d elements; got %d", e, g)
|
|
}
|
|
|
|
checkErr = os.NewError("no redirects allowed")
|
|
res, err = c.Get(ts.URL)
|
|
finalUrl = res.Request.URL.String()
|
|
if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v", err); e != g {
|
|
t.Errorf("with redirects forbidden, expected error %q, got %q", e, g)
|
|
}
|
|
}
|
|
|
|
func TestStreamingGet(t *testing.T) {
|
|
say := make(chan string)
|
|
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
w.(Flusher).Flush()
|
|
for str := range say {
|
|
w.Write([]byte(str))
|
|
w.(Flusher).Flush()
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
|
|
c := &Client{}
|
|
res, err := c.Get(ts.URL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var buf [10]byte
|
|
for _, str := range []string{"i", "am", "also", "known", "as", "comet"} {
|
|
say <- str
|
|
n, err := io.ReadFull(res.Body, buf[0:len(str)])
|
|
if err != nil {
|
|
t.Fatalf("ReadFull on %q: %v", str, err)
|
|
}
|
|
if n != len(str) {
|
|
t.Fatalf("Receiving %q, only read %d bytes", str, n)
|
|
}
|
|
got := string(buf[0:n])
|
|
if got != str {
|
|
t.Fatalf("Expected %q, got %q", str, got)
|
|
}
|
|
}
|
|
close(say)
|
|
_, err = io.ReadFull(res.Body, buf[0:1])
|
|
if err != os.EOF {
|
|
t.Fatalf("at end expected EOF, got %v", err)
|
|
}
|
|
}
|