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.
16 roundtrip string // expected result of reserializing the URL; empty means same as "in".
19 var urltests = []URLTest{
22 "http://www.google.com",
24 Raw: "http://www.google.com",
26 RawAuthority: "www.google.com",
27 Host: "www.google.com",
33 "http://www.google.com/",
35 Raw: "http://www.google.com/",
37 RawAuthority: "www.google.com",
38 Host: "www.google.com",
44 // path with hex escaping
46 "http://www.google.com/file%20one%26two",
48 Raw: "http://www.google.com/file%20one%26two",
50 RawAuthority: "www.google.com",
51 Host: "www.google.com",
52 RawPath: "/file%20one%26two",
53 Path: "/file one&two",
55 "http://www.google.com/file%20one&two",
59 "ftp://webmaster@www.google.com/",
61 Raw: "ftp://webmaster@www.google.com/",
63 RawAuthority: "webmaster@www.google.com",
64 RawUserinfo: "webmaster",
65 Host: "www.google.com",
71 // escape sequence in username
73 "ftp://john%20doe@www.google.com/",
75 Raw: "ftp://john%20doe@www.google.com/",
77 RawAuthority: "john%20doe@www.google.com",
78 RawUserinfo: "john%20doe",
79 Host: "www.google.com",
83 "ftp://john%20doe@www.google.com/",
87 "http://www.google.com/?q=go+language",
89 Raw: "http://www.google.com/?q=go+language",
91 RawAuthority: "www.google.com",
92 Host: "www.google.com",
93 RawPath: "/?q=go+language",
95 RawQuery: "q=go+language",
99 // query with hex escaping: NOT parsed
101 "http://www.google.com/?q=go%20language",
103 Raw: "http://www.google.com/?q=go%20language",
105 RawAuthority: "www.google.com",
106 Host: "www.google.com",
107 RawPath: "/?q=go%20language",
109 RawQuery: "q=go%20language",
115 "http://www.google.com/a%20b?q=c+d",
117 Raw: "http://www.google.com/a%20b?q=c+d",
119 RawAuthority: "www.google.com",
120 Host: "www.google.com",
121 RawPath: "/a%20b?q=c+d",
127 // path without leading /, so no query parsing
129 "http:www.google.com/?q=go+language",
131 Raw: "http:www.google.com/?q=go+language",
133 RawPath: "www.google.com/?q=go+language",
134 Path: "www.google.com/?q=go+language",
137 "http:www.google.com/?q=go+language",
139 // path without leading /, so no query parsing
141 "http:%2f%2fwww.google.com/?q=go+language",
143 Raw: "http:%2f%2fwww.google.com/?q=go+language",
145 RawPath: "%2f%2fwww.google.com/?q=go+language",
146 Path: "//www.google.com/?q=go+language",
149 "http:%2f/www.google.com/?q=go+language",
153 "mailto:/webmaster@golang.org",
155 Raw: "mailto:/webmaster@golang.org",
157 RawPath: "/webmaster@golang.org",
158 Path: "/webmaster@golang.org",
164 "mailto:webmaster@golang.org",
166 Raw: "mailto:webmaster@golang.org",
168 RawPath: "webmaster@golang.org",
169 Path: "webmaster@golang.org",
174 // unescaped :// in query should not create a scheme
176 "/foo?query=http://bad",
178 Raw: "/foo?query=http://bad",
179 RawPath: "/foo?query=http://bad",
181 RawQuery: "query=http://bad",
185 // leading // without scheme should create an authority
198 // leading // without scheme, with userinfo, path, and query
200 "//user@foo/path?a=b",
202 Raw: "//user@foo/path?a=b",
203 RawAuthority: "user@foo",
206 RawPath: "/path?a=b",
213 // Three leading slashes isn't an authority, but doesn't return an error.
214 // (We can't return an error, as this code is also used via
215 // ServeHTTP -> ReadRequest -> Parse, which is arguably a
216 // different URL parsing context, but currently shares the
222 Raw: "///threeslashes",
225 RawPath: "///threeslashes",
226 Path: "///threeslashes",
231 "http://user:password@google.com",
233 Raw: "http://user:password@google.com",
235 RawAuthority: "user:password@google.com",
236 RawUserinfo: "user:password",
239 "http://user:******@google.com",
242 "http://user:longerpass@google.com",
244 Raw: "http://user:longerpass@google.com",
246 RawAuthority: "user:longerpass@google.com",
247 RawUserinfo: "user:longerpass",
250 "http://user:******@google.com",
254 var urlnofragtests = []URLTest{
256 "http://www.google.com/?q=go+language#foo",
258 Raw: "http://www.google.com/?q=go+language#foo",
260 RawAuthority: "www.google.com",
261 Host: "www.google.com",
262 RawPath: "/?q=go+language#foo",
264 RawQuery: "q=go+language#foo",
270 var urlfragtests = []URLTest{
272 "http://www.google.com/?q=go+language#foo",
274 Raw: "http://www.google.com/?q=go+language#foo",
276 RawAuthority: "www.google.com",
277 Host: "www.google.com",
278 RawPath: "/?q=go+language#foo",
280 RawQuery: "q=go+language",
286 "http://www.google.com/?q=go+language#foo%26bar",
288 Raw: "http://www.google.com/?q=go+language#foo%26bar",
290 RawAuthority: "www.google.com",
291 Host: "www.google.com",
292 RawPath: "/?q=go+language#foo%26bar",
294 RawQuery: "q=go+language",
297 "http://www.google.com/?q=go+language#foo&bar",
301 // more useful string for debugging than fmt's struct printer
302 func ufmt(u *URL) string {
303 return fmt.Sprintf("raw=%q, scheme=%q, rawpath=%q, auth=%q, userinfo=%q, host=%q, path=%q, rawq=%q, frag=%q",
304 u.Raw, u.Scheme, u.RawPath, u.RawAuthority, u.RawUserinfo,
305 u.Host, u.Path, u.RawQuery, u.Fragment)
308 func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
309 for _, tt := range tests {
310 u, err := parse(tt.in)
312 t.Errorf("%s(%q) returned error %s", name, tt.in, err)
315 if !reflect.DeepEqual(u, tt.out) {
316 t.Errorf("%s(%q):\n\thave %v\n\twant %v\n",
317 name, tt.in, ufmt(u), ufmt(tt.out))
322 func TestParse(t *testing.T) {
323 DoTest(t, Parse, "Parse", urltests)
324 DoTest(t, Parse, "Parse", urlnofragtests)
327 func TestParseWithReference(t *testing.T) {
328 DoTest(t, ParseWithReference, "ParseWithReference", urltests)
329 DoTest(t, ParseWithReference, "ParseWithReference", urlfragtests)
332 const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
334 var parseRequestUrlTests = []struct {
338 {"http://foo.com", true},
339 {"http://foo.com/", true},
340 {"http://foo.com/path", true},
342 {pathThatLooksSchemeRelative, true},
343 {"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
348 func TestParseRequest(t *testing.T) {
349 for _, test := range parseRequestUrlTests {
350 _, err := ParseRequest(test.url)
352 if valid != test.expectedValid {
353 t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid)
357 url, err := ParseRequest(pathThatLooksSchemeRelative)
359 t.Fatalf("Unexpected error %v", err)
361 if url.Path != pathThatLooksSchemeRelative {
362 t.Errorf("Expected path %q; got %q", pathThatLooksSchemeRelative, url.Path)
366 func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
367 for _, tt := range tests {
368 u, err := parse(tt.in)
370 t.Errorf("%s(%q) returned error %s", name, tt.in, err)
375 if len(tt.roundtrip) > 0 {
376 expected = tt.roundtrip
379 t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected)
384 func TestURLString(t *testing.T) {
385 DoTestString(t, Parse, "Parse", urltests)
386 DoTestString(t, Parse, "Parse", urlnofragtests)
387 DoTestString(t, ParseWithReference, "ParseWithReference", urltests)
388 DoTestString(t, ParseWithReference, "ParseWithReference", urlfragtests)
391 type EscapeTest struct {
397 var unescapeTests = []EscapeTest{
429 "%", // not enough characters after %
434 "%a", // not enough characters after %
439 "%1", // not enough characters after %
444 "123%45%6", // not enough characters after %
449 "%zzzzz", // invalid hex digits
455 func TestUnescape(t *testing.T) {
456 for _, tt := range unescapeTests {
457 actual, err := QueryUnescape(tt.in)
458 if actual != tt.out || (err != nil) != (tt.err != nil) {
459 t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err)
464 var escapeTests = []EscapeTest{
486 " ?&=#+%!<>#\"{}|\\^[]`☺\t",
487 "+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09",
492 func TestEscape(t *testing.T) {
493 for _, tt := range escapeTests {
494 actual := QueryEscape(tt.in)
495 if tt.out != actual {
496 t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out)
499 // for bonus points, verify that escape:unescape is an identity.
500 roundtrip, err := QueryUnescape(actual)
501 if roundtrip != tt.in || err != nil {
502 t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
507 type UserinfoTest struct {
513 var userinfoTests = []UserinfoTest{
514 {"user", "password", "user:password"},
515 {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./",
516 "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"},
519 func TestEscapeUserinfo(t *testing.T) {
520 for _, tt := range userinfoTests {
521 if raw := EscapeUserinfo(tt.User, tt.Password); raw != tt.Raw {
522 t.Errorf("EscapeUserinfo(%q, %q) = %q, want %q", tt.User, tt.Password, raw, tt.Raw)
527 func TestUnescapeUserinfo(t *testing.T) {
528 for _, tt := range userinfoTests {
529 if user, pass, err := UnescapeUserinfo(tt.Raw); user != tt.User || pass != tt.Password || err != nil {
530 t.Errorf("UnescapeUserinfo(%q) = %q, %q, %v, want %q, %q, nil", tt.Raw, user, pass, err, tt.User, tt.Password)
535 type EncodeQueryTest struct {
541 var encodeQueryTests = []EncodeQueryTest{
543 {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"},
544 {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"},
547 func TestEncodeQuery(t *testing.T) {
548 for _, tt := range encodeQueryTests {
549 if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 {
550 t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected)
555 var resolvePathTests = []struct {
556 base, ref, expected string
563 {"a/b/c", "..", "a/"},
564 {"a/b/c", "../d", "a/d"},
565 {"a/b/c", ".././d", "a/d"},
567 {"a/./b", ".", "a/./"},
568 {"a/../", ".", "a/../"},
569 {"a/.././b", "c", "a/.././c"},
572 func TestResolvePath(t *testing.T) {
573 for _, test := range resolvePathTests {
574 got := resolvePath(test.base, test.ref)
575 if got != test.expected {
576 t.Errorf("For %q + %q got %q; expected %q", test.base, test.ref, got, test.expected)
581 var resolveReferenceTests = []struct {
582 base, rel, expected string
584 // Absolute URL references
585 {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
586 {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
587 {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
589 // Path-absolute references
590 {"http://foo.com/bar", "/baz", "http://foo.com/baz"},
591 {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
592 {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
595 {"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"},
597 // Path-relative references:
599 // ... current directory
600 {"http://foo.com", ".", "http://foo.com/"},
601 {"http://foo.com/bar", ".", "http://foo.com/"},
602 {"http://foo.com/bar/", ".", "http://foo.com/bar/"},
605 {"http://foo.com", "bar", "http://foo.com/bar"},
606 {"http://foo.com/", "bar", "http://foo.com/bar"},
607 {"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"},
610 {"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"},
611 {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
612 {"http://foo.com/bar", "..", "http://foo.com/"},
613 {"http://foo.com/bar/baz", "./..", "http://foo.com/"},
615 // "." and ".." in the base aren't special
616 {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"},
618 // Triple dot isn't special
619 {"http://foo.com/bar", "...", "http://foo.com/..."},
622 {"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
625 func TestResolveReference(t *testing.T) {
626 mustParse := func(url string) *URL {
627 u, err := ParseWithReference(url)
629 t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
633 for _, test := range resolveReferenceTests {
634 base := mustParse(test.base)
635 rel := mustParse(test.rel)
636 url := base.ResolveReference(rel)
637 urlStr := url.String()
638 if urlStr != test.expected {
639 t.Errorf("Resolving %q + %q != %q; got %q", test.base, test.rel, test.expected, urlStr)
643 // Test that new instances are returned.
644 base := mustParse("http://foo.com/")
645 abs := base.ResolveReference(mustParse("."))
647 t.Errorf("Expected no-op reference to return new URL instance.")
649 barRef := mustParse("http://bar.com/")
650 abs = base.ResolveReference(barRef)
652 t.Errorf("Expected resolution of absolute reference to return new URL instance.")
655 // Test the convenience wrapper too
656 base = mustParse("http://foo.com/path/one/")
657 abs, _ = base.Parse("../two")
658 expected := "http://foo.com/path/two"
659 if abs.String() != expected {
660 t.Errorf("Parse wrapper got %q; expected %q", abs.String(), expected)
662 _, err := base.Parse("")
664 t.Errorf("Expected an error from Parse wrapper parsing an empty string.")
669 func TestQueryValues(t *testing.T) {
670 u, _ := Parse("http://x.com?foo=bar&bar=1&bar=2")
673 t.Errorf("got %d keys in Query values, want 2", len(v))
675 if g, e := v.Get("foo"), "bar"; g != e {
676 t.Errorf("Get(foo) = %q, want %q", g, e)
679 if g, e := v.Get("Foo"), ""; g != e {
680 t.Errorf("Get(Foo) = %q, want %q", g, e)
682 if g, e := v.Get("bar"), "1"; g != e {
683 t.Errorf("Get(bar) = %q, want %q", g, e)
685 if g, e := v.Get("baz"), ""; g != e {
686 t.Errorf("Get(baz) = %q, want %q", g, e)
689 if g, e := v.Get("bar"), ""; g != e {
690 t.Errorf("second Get(bar) = %q, want %q", g, e)
694 type parseTest struct {
699 var parseTests = []parseTest{
702 out: Values{"a": []string{"1"}, "b": []string{"2"}},
705 query: "a=1&a=2&a=banana",
706 out: Values{"a": []string{"1", "2", "banana"}},
709 query: "ascii=%3Ckey%3A+0x90%3E",
710 out: Values{"ascii": []string{"<key: 0x90>"}},
714 out: Values{"a": []string{"1"}, "b": []string{"2"}},
717 query: "a=1&a=2;a=banana",
718 out: Values{"a": []string{"1", "2", "banana"}},
722 func TestParseQuery(t *testing.T) {
723 for i, test := range parseTests {
724 form, err := ParseQuery(test.query)
726 t.Errorf("test %d: Unexpected error: %v", i, err)
729 if len(form) != len(test.out) {
730 t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
732 for k, evs := range test.out {
735 t.Errorf("test %d: Missing key %q", i, k)
738 if len(vs) != len(evs) {
739 t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
742 for j, ev := range evs {
743 if v := vs[j]; v != ev {
744 t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)