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.
22 type stringMultimap map[string][]string
24 type parseTest struct {
29 var parseTests = []parseTest{
32 out: stringMultimap{"a": []string{"1"}, "b": []string{"2"}},
35 query: "a=1&a=2&a=banana",
36 out: stringMultimap{"a": []string{"1", "2", "banana"}},
39 query: "ascii=%3Ckey%3A+0x90%3E",
40 out: stringMultimap{"ascii": []string{"<key: 0x90>"}},
44 func TestParseForm(t *testing.T) {
45 for i, test := range parseTests {
46 form, err := ParseQuery(test.query)
48 t.Errorf("test %d: Unexpected error: %v", i, err)
51 if len(form) != len(test.out) {
52 t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
54 for k, evs := range test.out {
57 t.Errorf("test %d: Missing key %q", i, k)
60 if len(vs) != len(evs) {
61 t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
64 for j, ev := range evs {
65 if v := vs[j]; v != ev {
66 t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)
73 func TestQuery(t *testing.T) {
74 req := &Request{Method: "GET"}
75 req.URL, _ = ParseURL("http://www.google.com/search?q=foo&q=bar")
76 if q := req.FormValue("q"); q != "foo" {
77 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q)
81 func TestPostQuery(t *testing.T) {
82 req := &Request{Method: "POST"}
83 req.URL, _ = ParseURL("http://www.google.com/search?q=foo&q=bar&both=x")
85 "Content-Type": {"application/x-www-form-urlencoded; boo!"},
87 req.Body = ioutil.NopCloser(strings.NewReader("z=post&both=y"))
88 if q := req.FormValue("q"); q != "foo" {
89 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q)
91 if z := req.FormValue("z"); z != "post" {
92 t.Errorf(`req.FormValue("z") = %q, want "post"`, z)
94 if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"x", "y"}) {
95 t.Errorf(`req.FormValue("both") = %q, want ["x", "y"]`, both)
99 type stringMap map[string][]string
100 type parseContentTypeTest struct {
101 contentType stringMap
105 var parseContentTypeTests = []parseContentTypeTest{
106 {contentType: stringMap{"Content-Type": {"text/plain"}}},
107 {contentType: stringMap{}}, // Non-existent keys are not placed. The value nil is illegal.
108 {contentType: stringMap{"Content-Type": {"text/plain; boundary="}}},
110 contentType: stringMap{"Content-Type": {"application/unknown"}},
115 func TestPostContentTypeParsing(t *testing.T) {
116 for i, test := range parseContentTypeTests {
119 Header: Header(test.contentType),
120 Body: ioutil.NopCloser(bytes.NewBufferString("body")),
122 err := req.ParseForm()
123 if !test.error && err != nil {
124 t.Errorf("test %d: Unexpected error: %v", i, err)
126 if test.error && err == nil {
127 t.Errorf("test %d should have returned error", i)
132 func TestMultipartReader(t *testing.T) {
135 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
136 Body: ioutil.NopCloser(new(bytes.Buffer)),
138 multipart, err := req.MultipartReader()
139 if multipart == nil {
140 t.Errorf("expected multipart; error: %v", err)
143 req.Header = Header{"Content-Type": {"text/plain"}}
144 multipart, err = req.MultipartReader()
145 if multipart != nil {
146 t.Errorf("unexpected multipart for text/plain")
150 func TestRedirect(t *testing.T) {
151 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
154 w.Header().Set("Location", "/foo/")
155 w.WriteHeader(StatusSeeOther)
157 fmt.Fprintf(w, "foo")
159 w.WriteHeader(StatusBadRequest)
164 var end = regexp.MustCompile("/foo/$")
165 r, url, err := Get(ts.URL)
170 if r.StatusCode != 200 || !end.MatchString(url) {
171 t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url)
175 func TestMultipartRequest(t *testing.T) {
176 // Test that we can read the values and files of a
177 // multipart request with FormValue and FormFile,
178 // and that ParseMultipartForm can be called multiple times.
179 req := newTestMultipartRequest(t)
180 if err := req.ParseMultipartForm(25); err != nil {
181 t.Fatal("ParseMultipartForm first call:", err)
183 defer req.MultipartForm.RemoveAll()
184 validateTestMultipartContents(t, req, false)
185 if err := req.ParseMultipartForm(25); err != nil {
186 t.Fatal("ParseMultipartForm second call:", err)
188 validateTestMultipartContents(t, req, false)
191 func TestMultipartRequestAuto(t *testing.T) {
192 // Test that FormValue and FormFile automatically invoke
193 // ParseMultipartForm and return the right values.
194 req := newTestMultipartRequest(t)
196 if req.MultipartForm != nil {
197 req.MultipartForm.RemoveAll()
200 validateTestMultipartContents(t, req, true)
203 func TestEmptyMultipartRequest(t *testing.T) {
204 // Test that FormValue and FormFile automatically invoke
205 // ParseMultipartForm and return the right values.
206 req, err := NewRequest("GET", "/", nil)
208 t.Errorf("NewRequest err = %q", err)
210 testMissingFile(t, req)
213 func testMissingFile(t *testing.T, req *Request) {
214 f, fh, err := req.FormFile("missing")
216 t.Errorf("FormFile file = %q, want nil", f, nil)
219 t.Errorf("FormFile file header = %q, want nil", fh, nil)
221 if err != ErrMissingFile {
222 t.Errorf("FormFile err = %q, want nil", err, ErrMissingFile)
226 func newTestMultipartRequest(t *testing.T) *Request {
227 b := bytes.NewBufferString(strings.Replace(message, "\n", "\r\n", -1))
228 req, err := NewRequest("POST", "/", b)
230 t.Fatalf("NewRequest:", err)
232 ctype := fmt.Sprintf(`multipart/form-data; boundary="%s"`, boundary)
233 req.Header.Set("Content-type", ctype)
237 func validateTestMultipartContents(t *testing.T, req *Request, allMem bool) {
238 if g, e := req.FormValue("texta"), textaValue; g != e {
239 t.Errorf("texta value = %q, want %q", g, e)
241 if g, e := req.FormValue("texta"), textaValue; g != e {
242 t.Errorf("texta value = %q, want %q", g, e)
244 if g := req.FormValue("missing"); g != "" {
245 t.Errorf("missing value = %q, want empty string", g)
248 assertMem := func(n string, fd multipart.File) {
249 if _, ok := fd.(*os.File); ok {
250 t.Error(n, " is *os.File, should not be")
253 fd := testMultipartFile(t, req, "filea", "filea.txt", fileaContents)
254 assertMem("filea", fd)
255 fd = testMultipartFile(t, req, "fileb", "fileb.txt", filebContents)
257 assertMem("fileb", fd)
259 if _, ok := fd.(*os.File); !ok {
260 t.Errorf("fileb has unexpected underlying type %T", fd)
264 testMissingFile(t, req)
267 func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectContent string) multipart.File {
268 f, fh, err := req.FormFile(key)
270 t.Fatalf("FormFile(%q):", key, err)
272 if fh.Filename != expectFilename {
273 t.Errorf("filename = %q, want %q", fh.Filename, expectFilename)
276 _, err = io.Copy(&b, f)
278 t.Fatal("copying contents:", err)
280 if g := b.String(); g != expectContent {
281 t.Errorf("contents = %q, want %q", g, expectContent)
287 fileaContents = "This is a test file."
288 filebContents = "Another test file."
291 boundary = `MyBoundary`
296 Content-Disposition: form-data; name="filea"; filename="filea.txt"
297 Content-Type: text/plain
299 ` + fileaContents + `
301 Content-Disposition: form-data; name="fileb"; filename="fileb.txt"
302 Content-Type: text/plain
304 ` + filebContents + `
306 Content-Disposition: form-data; name="texta"
310 Content-Disposition: form-data; name="textb"