github.com/zxy12/golang151_with_comment@v0.0.0-20190507085033-721809559d3c/net/url/url_test.go (about)

     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  package url
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  type URLTest struct {
    15  	in        string
    16  	out       *URL   // expected parse; RawPath="" means same as Path
    17  	roundtrip string // expected result of reserializing the URL; empty means same as "in".
    18  }
    19  
    20  var urltests = []URLTest{
    21  	// no path
    22  	{
    23  		"http://www.google.com",
    24  		&URL{
    25  			Scheme: "http",
    26  			Host:   "www.google.com",
    27  		},
    28  		"",
    29  	},
    30  	// path
    31  	{
    32  		"http://www.google.com/",
    33  		&URL{
    34  			Scheme: "http",
    35  			Host:   "www.google.com",
    36  			Path:   "/",
    37  		},
    38  		"",
    39  	},
    40  	// path with hex escaping
    41  	{
    42  		"http://www.google.com/file%20one%26two",
    43  		&URL{
    44  			Scheme:  "http",
    45  			Host:    "www.google.com",
    46  			Path:    "/file one&two",
    47  			RawPath: "/file%20one%26two",
    48  		},
    49  		"",
    50  	},
    51  	// user
    52  	{
    53  		"ftp://webmaster@www.google.com/",
    54  		&URL{
    55  			Scheme: "ftp",
    56  			User:   User("webmaster"),
    57  			Host:   "www.google.com",
    58  			Path:   "/",
    59  		},
    60  		"",
    61  	},
    62  	// escape sequence in username
    63  	{
    64  		"ftp://john%20doe@www.google.com/",
    65  		&URL{
    66  			Scheme: "ftp",
    67  			User:   User("john doe"),
    68  			Host:   "www.google.com",
    69  			Path:   "/",
    70  		},
    71  		"ftp://john%20doe@www.google.com/",
    72  	},
    73  	// query
    74  	{
    75  		"http://www.google.com/?q=go+language",
    76  		&URL{
    77  			Scheme:   "http",
    78  			Host:     "www.google.com",
    79  			Path:     "/",
    80  			RawQuery: "q=go+language",
    81  		},
    82  		"",
    83  	},
    84  	// query with hex escaping: NOT parsed
    85  	{
    86  		"http://www.google.com/?q=go%20language",
    87  		&URL{
    88  			Scheme:   "http",
    89  			Host:     "www.google.com",
    90  			Path:     "/",
    91  			RawQuery: "q=go%20language",
    92  		},
    93  		"",
    94  	},
    95  	// %20 outside query
    96  	{
    97  		"http://www.google.com/a%20b?q=c+d",
    98  		&URL{
    99  			Scheme:   "http",
   100  			Host:     "www.google.com",
   101  			Path:     "/a b",
   102  			RawQuery: "q=c+d",
   103  		},
   104  		"",
   105  	},
   106  	// path without leading /, so no parsing
   107  	{
   108  		"http:www.google.com/?q=go+language",
   109  		&URL{
   110  			Scheme:   "http",
   111  			Opaque:   "www.google.com/",
   112  			RawQuery: "q=go+language",
   113  		},
   114  		"http:www.google.com/?q=go+language",
   115  	},
   116  	// path without leading /, so no parsing
   117  	{
   118  		"http:%2f%2fwww.google.com/?q=go+language",
   119  		&URL{
   120  			Scheme:   "http",
   121  			Opaque:   "%2f%2fwww.google.com/",
   122  			RawQuery: "q=go+language",
   123  		},
   124  		"http:%2f%2fwww.google.com/?q=go+language",
   125  	},
   126  	// non-authority with path
   127  	{
   128  		"mailto:/webmaster@golang.org",
   129  		&URL{
   130  			Scheme: "mailto",
   131  			Path:   "/webmaster@golang.org",
   132  		},
   133  		"mailto:///webmaster@golang.org", // unfortunate compromise
   134  	},
   135  	// non-authority
   136  	{
   137  		"mailto:webmaster@golang.org",
   138  		&URL{
   139  			Scheme: "mailto",
   140  			Opaque: "webmaster@golang.org",
   141  		},
   142  		"",
   143  	},
   144  	// unescaped :// in query should not create a scheme
   145  	{
   146  		"/foo?query=http://bad",
   147  		&URL{
   148  			Path:     "/foo",
   149  			RawQuery: "query=http://bad",
   150  		},
   151  		"",
   152  	},
   153  	// leading // without scheme should create an authority
   154  	{
   155  		"//foo",
   156  		&URL{
   157  			Host: "foo",
   158  		},
   159  		"",
   160  	},
   161  	// leading // without scheme, with userinfo, path, and query
   162  	{
   163  		"//user@foo/path?a=b",
   164  		&URL{
   165  			User:     User("user"),
   166  			Host:     "foo",
   167  			Path:     "/path",
   168  			RawQuery: "a=b",
   169  		},
   170  		"",
   171  	},
   172  	// Three leading slashes isn't an authority, but doesn't return an error.
   173  	// (We can't return an error, as this code is also used via
   174  	// ServeHTTP -> ReadRequest -> Parse, which is arguably a
   175  	// different URL parsing context, but currently shares the
   176  	// same codepath)
   177  	{
   178  		"///threeslashes",
   179  		&URL{
   180  			Path: "///threeslashes",
   181  		},
   182  		"",
   183  	},
   184  	{
   185  		"http://user:password@google.com",
   186  		&URL{
   187  			Scheme: "http",
   188  			User:   UserPassword("user", "password"),
   189  			Host:   "google.com",
   190  		},
   191  		"http://user:password@google.com",
   192  	},
   193  	// unescaped @ in username should not confuse host
   194  	{
   195  		"http://j@ne:password@google.com",
   196  		&URL{
   197  			Scheme: "http",
   198  			User:   UserPassword("j@ne", "password"),
   199  			Host:   "google.com",
   200  		},
   201  		"http://j%40ne:password@google.com",
   202  	},
   203  	// unescaped @ in password should not confuse host
   204  	{
   205  		"http://jane:p@ssword@google.com",
   206  		&URL{
   207  			Scheme: "http",
   208  			User:   UserPassword("jane", "p@ssword"),
   209  			Host:   "google.com",
   210  		},
   211  		"http://jane:p%40ssword@google.com",
   212  	},
   213  	{
   214  		"http://j@ne:password@google.com/p@th?q=@go",
   215  		&URL{
   216  			Scheme:   "http",
   217  			User:     UserPassword("j@ne", "password"),
   218  			Host:     "google.com",
   219  			Path:     "/p@th",
   220  			RawQuery: "q=@go",
   221  		},
   222  		"http://j%40ne:password@google.com/p@th?q=@go",
   223  	},
   224  	{
   225  		"http://www.google.com/?q=go+language#foo",
   226  		&URL{
   227  			Scheme:   "http",
   228  			Host:     "www.google.com",
   229  			Path:     "/",
   230  			RawQuery: "q=go+language",
   231  			Fragment: "foo",
   232  		},
   233  		"",
   234  	},
   235  	{
   236  		"http://www.google.com/?q=go+language#foo%26bar",
   237  		&URL{
   238  			Scheme:   "http",
   239  			Host:     "www.google.com",
   240  			Path:     "/",
   241  			RawQuery: "q=go+language",
   242  			Fragment: "foo&bar",
   243  		},
   244  		"http://www.google.com/?q=go+language#foo&bar",
   245  	},
   246  	{
   247  		"file:///home/adg/rabbits",
   248  		&URL{
   249  			Scheme: "file",
   250  			Host:   "",
   251  			Path:   "/home/adg/rabbits",
   252  		},
   253  		"file:///home/adg/rabbits",
   254  	},
   255  	// "Windows" paths are no exception to the rule.
   256  	// See golang.org/issue/6027, especially comment #9.
   257  	{
   258  		"file:///C:/FooBar/Baz.txt",
   259  		&URL{
   260  			Scheme: "file",
   261  			Host:   "",
   262  			Path:   "/C:/FooBar/Baz.txt",
   263  		},
   264  		"file:///C:/FooBar/Baz.txt",
   265  	},
   266  	// case-insensitive scheme
   267  	{
   268  		"MaIlTo:webmaster@golang.org",
   269  		&URL{
   270  			Scheme: "mailto",
   271  			Opaque: "webmaster@golang.org",
   272  		},
   273  		"mailto:webmaster@golang.org",
   274  	},
   275  	// Relative path
   276  	{
   277  		"a/b/c",
   278  		&URL{
   279  			Path: "a/b/c",
   280  		},
   281  		"a/b/c",
   282  	},
   283  	// escaped '?' in username and password
   284  	{
   285  		"http://%3Fam:pa%3Fsword@google.com",
   286  		&URL{
   287  			Scheme: "http",
   288  			User:   UserPassword("?am", "pa?sword"),
   289  			Host:   "google.com",
   290  		},
   291  		"",
   292  	},
   293  	// host subcomponent; IPv4 address in RFC 3986
   294  	{
   295  		"http://192.168.0.1/",
   296  		&URL{
   297  			Scheme: "http",
   298  			Host:   "192.168.0.1",
   299  			Path:   "/",
   300  		},
   301  		"",
   302  	},
   303  	// host and port subcomponents; IPv4 address in RFC 3986
   304  	{
   305  		"http://192.168.0.1:8080/",
   306  		&URL{
   307  			Scheme: "http",
   308  			Host:   "192.168.0.1:8080",
   309  			Path:   "/",
   310  		},
   311  		"",
   312  	},
   313  	// host subcomponent; IPv6 address in RFC 3986
   314  	{
   315  		"http://[fe80::1]/",
   316  		&URL{
   317  			Scheme: "http",
   318  			Host:   "[fe80::1]",
   319  			Path:   "/",
   320  		},
   321  		"",
   322  	},
   323  	// host and port subcomponents; IPv6 address in RFC 3986
   324  	{
   325  		"http://[fe80::1]:8080/",
   326  		&URL{
   327  			Scheme: "http",
   328  			Host:   "[fe80::1]:8080",
   329  			Path:   "/",
   330  		},
   331  		"",
   332  	},
   333  	// host subcomponent; IPv6 address with zone identifier in RFC 6847
   334  	{
   335  		"http://[fe80::1%25en0]/", // alphanum zone identifier
   336  		&URL{
   337  			Scheme: "http",
   338  			Host:   "[fe80::1%en0]",
   339  			Path:   "/",
   340  		},
   341  		"",
   342  	},
   343  	// host and port subcomponents; IPv6 address with zone identifier in RFC 6847
   344  	{
   345  		"http://[fe80::1%25en0]:8080/", // alphanum zone identifier
   346  		&URL{
   347  			Scheme: "http",
   348  			Host:   "[fe80::1%en0]:8080",
   349  			Path:   "/",
   350  		},
   351  		"",
   352  	},
   353  	// host subcomponent; IPv6 address with zone identifier in RFC 6847
   354  	{
   355  		"http://[fe80::1%25%65%6e%301-._~]/", // percent-encoded+unreserved zone identifier
   356  		&URL{
   357  			Scheme: "http",
   358  			Host:   "[fe80::1%en01-._~]",
   359  			Path:   "/",
   360  		},
   361  		"http://[fe80::1%25en01-._~]/",
   362  	},
   363  	// host and port subcomponents; IPv6 address with zone identifier in RFC 6847
   364  	{
   365  		"http://[fe80::1%25%65%6e%301-._~]:8080/", // percent-encoded+unreserved zone identifier
   366  		&URL{
   367  			Scheme: "http",
   368  			Host:   "[fe80::1%en01-._~]:8080",
   369  			Path:   "/",
   370  		},
   371  		"http://[fe80::1%25en01-._~]:8080/",
   372  	},
   373  	// alternate escapings of path survive round trip
   374  	{
   375  		"http://rest.rsc.io/foo%2fbar/baz%2Fquux?alt=media",
   376  		&URL{
   377  			Scheme:   "http",
   378  			Host:     "rest.rsc.io",
   379  			Path:     "/foo/bar/baz/quux",
   380  			RawPath:  "/foo%2fbar/baz%2Fquux",
   381  			RawQuery: "alt=media",
   382  		},
   383  		"",
   384  	},
   385  	// issue 12036
   386  	{
   387  		"mysql://a,b,c/bar",
   388  		&URL{
   389  			Scheme: "mysql",
   390  			Host:   "a,b,c",
   391  			Path:   "/bar",
   392  		},
   393  		"",
   394  	},
   395  	// worst case host, still round trips
   396  	{
   397  		"scheme://!$&'()*+,;=hello!:port/path",
   398  		&URL{
   399  			Scheme: "scheme",
   400  			Host:   "!$&'()*+,;=hello!:port",
   401  			Path:   "/path",
   402  		},
   403  		"",
   404  	},
   405  	// worst case path, still round trips
   406  	{
   407  		"http://host/!$&'()*+,;=:@[hello]",
   408  		&URL{
   409  			Scheme:  "http",
   410  			Host:    "host",
   411  			Path:    "/!$&'()*+,;=:@[hello]",
   412  			RawPath: "/!$&'()*+,;=:@[hello]",
   413  		},
   414  		"",
   415  	},
   416  	// golang.org/issue/5684
   417  	{
   418  		"http://example.com/oid/[order_id]",
   419  		&URL{
   420  			Scheme:  "http",
   421  			Host:    "example.com",
   422  			Path:    "/oid/[order_id]",
   423  			RawPath: "/oid/[order_id]",
   424  		},
   425  		"",
   426  	},
   427  }
   428  
   429  // more useful string for debugging than fmt's struct printer
   430  func ufmt(u *URL) string {
   431  	var user, pass interface{}
   432  	if u.User != nil {
   433  		user = u.User.Username()
   434  		if p, ok := u.User.Password(); ok {
   435  			pass = p
   436  		}
   437  	}
   438  	return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawpath=%q, rawq=%q, frag=%q",
   439  		u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment)
   440  }
   441  
   442  func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
   443  	for _, tt := range tests {
   444  		u, err := parse(tt.in)
   445  		if err != nil {
   446  			t.Errorf("%s(%q) returned error %s", name, tt.in, err)
   447  			continue
   448  		}
   449  		if !reflect.DeepEqual(u, tt.out) {
   450  			t.Errorf("%s(%q):\n\thave %v\n\twant %v\n",
   451  				name, tt.in, ufmt(u), ufmt(tt.out))
   452  		}
   453  	}
   454  }
   455  
   456  func BenchmarkString(b *testing.B) {
   457  	b.StopTimer()
   458  	b.ReportAllocs()
   459  	for _, tt := range urltests {
   460  		u, err := Parse(tt.in)
   461  		if err != nil {
   462  			b.Errorf("Parse(%q) returned error %s", tt.in, err)
   463  			continue
   464  		}
   465  		if tt.roundtrip == "" {
   466  			continue
   467  		}
   468  		b.StartTimer()
   469  		var g string
   470  		for i := 0; i < b.N; i++ {
   471  			g = u.String()
   472  		}
   473  		b.StopTimer()
   474  		if w := tt.roundtrip; g != w {
   475  			b.Errorf("Parse(%q).String() == %q, want %q", tt.in, g, w)
   476  		}
   477  	}
   478  }
   479  
   480  func TestParse(t *testing.T) {
   481  	DoTest(t, Parse, "Parse", urltests)
   482  }
   483  
   484  const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
   485  
   486  var parseRequestURLTests = []struct {
   487  	url           string
   488  	expectedValid bool
   489  }{
   490  	{"http://foo.com", true},
   491  	{"http://foo.com/", true},
   492  	{"http://foo.com/path", true},
   493  	{"/", true},
   494  	{pathThatLooksSchemeRelative, true},
   495  	{"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
   496  	{"*", true},
   497  	{"http://192.168.0.1/", true},
   498  	{"http://192.168.0.1:8080/", true},
   499  	{"http://[fe80::1]/", true},
   500  	{"http://[fe80::1]:8080/", true},
   501  
   502  	// Tests exercising RFC 6874 compliance:
   503  	{"http://[fe80::1%25en0]/", true},                 // with alphanum zone identifier
   504  	{"http://[fe80::1%25en0]:8080/", true},            // with alphanum zone identifier
   505  	{"http://[fe80::1%25%65%6e%301-._~]/", true},      // with percent-encoded+unreserved zone identifier
   506  	{"http://[fe80::1%25%65%6e%301-._~]:8080/", true}, // with percent-encoded+unreserved zone identifier
   507  
   508  	{"foo.html", false},
   509  	{"../dir/", false},
   510  	{"http://192.168.0.%31/", false},
   511  	{"http://192.168.0.%31:8080/", false},
   512  	{"http://[fe80::%31]/", false},
   513  	{"http://[fe80::%31]:8080/", false},
   514  	{"http://[fe80::%31%25en0]/", false},
   515  	{"http://[fe80::%31%25en0]:8080/", false},
   516  
   517  	// These two cases are valid as textual representations as
   518  	// described in RFC 4007, but are not valid as address
   519  	// literals with IPv6 zone identifiers in URIs as described in
   520  	// RFC 6874.
   521  	{"http://[fe80::1%en0]/", false},
   522  	{"http://[fe80::1%en0]:8080/", false},
   523  }
   524  
   525  func TestParseRequestURI(t *testing.T) {
   526  	for _, test := range parseRequestURLTests {
   527  		_, err := ParseRequestURI(test.url)
   528  		valid := err == nil
   529  		if valid != test.expectedValid {
   530  			t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid)
   531  		}
   532  	}
   533  
   534  	url, err := ParseRequestURI(pathThatLooksSchemeRelative)
   535  	if err != nil {
   536  		t.Fatalf("Unexpected error %v", err)
   537  	}
   538  	if url.Path != pathThatLooksSchemeRelative {
   539  		t.Errorf("Expected path %q; got %q", pathThatLooksSchemeRelative, url.Path)
   540  	}
   541  }
   542  
   543  func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
   544  	for _, tt := range tests {
   545  		u, err := parse(tt.in)
   546  		if err != nil {
   547  			t.Errorf("%s(%q) returned error %s", name, tt.in, err)
   548  			continue
   549  		}
   550  		expected := tt.in
   551  		if len(tt.roundtrip) > 0 {
   552  			expected = tt.roundtrip
   553  		}
   554  		s := u.String()
   555  		if s != expected {
   556  			t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected)
   557  		}
   558  	}
   559  }
   560  
   561  func TestURLString(t *testing.T) {
   562  	DoTestString(t, Parse, "Parse", urltests)
   563  
   564  	// no leading slash on path should prepend
   565  	// slash on String() call
   566  	noslash := URLTest{
   567  		"http://www.google.com/search",
   568  		&URL{
   569  			Scheme: "http",
   570  			Host:   "www.google.com",
   571  			Path:   "search",
   572  		},
   573  		"",
   574  	}
   575  	s := noslash.out.String()
   576  	if s != noslash.in {
   577  		t.Errorf("Expected %s; go %s", noslash.in, s)
   578  	}
   579  }
   580  
   581  type EscapeTest struct {
   582  	in  string
   583  	out string
   584  	err error
   585  }
   586  
   587  var unescapeTests = []EscapeTest{
   588  	{
   589  		"",
   590  		"",
   591  		nil,
   592  	},
   593  	{
   594  		"abc",
   595  		"abc",
   596  		nil,
   597  	},
   598  	{
   599  		"1%41",
   600  		"1A",
   601  		nil,
   602  	},
   603  	{
   604  		"1%41%42%43",
   605  		"1ABC",
   606  		nil,
   607  	},
   608  	{
   609  		"%4a",
   610  		"J",
   611  		nil,
   612  	},
   613  	{
   614  		"%6F",
   615  		"o",
   616  		nil,
   617  	},
   618  	{
   619  		"%", // not enough characters after %
   620  		"",
   621  		EscapeError("%"),
   622  	},
   623  	{
   624  		"%a", // not enough characters after %
   625  		"",
   626  		EscapeError("%a"),
   627  	},
   628  	{
   629  		"%1", // not enough characters after %
   630  		"",
   631  		EscapeError("%1"),
   632  	},
   633  	{
   634  		"123%45%6", // not enough characters after %
   635  		"",
   636  		EscapeError("%6"),
   637  	},
   638  	{
   639  		"%zzzzz", // invalid hex digits
   640  		"",
   641  		EscapeError("%zz"),
   642  	},
   643  }
   644  
   645  func TestUnescape(t *testing.T) {
   646  	for _, tt := range unescapeTests {
   647  		actual, err := QueryUnescape(tt.in)
   648  		if actual != tt.out || (err != nil) != (tt.err != nil) {
   649  			t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err)
   650  		}
   651  	}
   652  }
   653  
   654  var escapeTests = []EscapeTest{
   655  	{
   656  		"",
   657  		"",
   658  		nil,
   659  	},
   660  	{
   661  		"abc",
   662  		"abc",
   663  		nil,
   664  	},
   665  	{
   666  		"one two",
   667  		"one+two",
   668  		nil,
   669  	},
   670  	{
   671  		"10%",
   672  		"10%25",
   673  		nil,
   674  	},
   675  	{
   676  		" ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
   677  		"+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B",
   678  		nil,
   679  	},
   680  }
   681  
   682  func TestEscape(t *testing.T) {
   683  	for _, tt := range escapeTests {
   684  		actual := QueryEscape(tt.in)
   685  		if tt.out != actual {
   686  			t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out)
   687  		}
   688  
   689  		// for bonus points, verify that escape:unescape is an identity.
   690  		roundtrip, err := QueryUnescape(actual)
   691  		if roundtrip != tt.in || err != nil {
   692  			t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
   693  		}
   694  	}
   695  }
   696  
   697  //var userinfoTests = []UserinfoTest{
   698  //	{"user", "password", "user:password"},
   699  //	{"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./",
   700  //		"foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"},
   701  //}
   702  
   703  type EncodeQueryTest struct {
   704  	m        Values
   705  	expected string
   706  }
   707  
   708  var encodeQueryTests = []EncodeQueryTest{
   709  	{nil, ""},
   710  	{Values{"q": {"puppies"}, "oe": {"utf8"}}, "oe=utf8&q=puppies"},
   711  	{Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7"},
   712  	{Values{
   713  		"a": {"a1", "a2", "a3"},
   714  		"b": {"b1", "b2", "b3"},
   715  		"c": {"c1", "c2", "c3"},
   716  	}, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"},
   717  }
   718  
   719  func TestEncodeQuery(t *testing.T) {
   720  	for _, tt := range encodeQueryTests {
   721  		if q := tt.m.Encode(); q != tt.expected {
   722  			t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected)
   723  		}
   724  	}
   725  }
   726  
   727  var resolvePathTests = []struct {
   728  	base, ref, expected string
   729  }{
   730  	{"a/b", ".", "/a/"},
   731  	{"a/b", "c", "/a/c"},
   732  	{"a/b", "..", "/"},
   733  	{"a/", "..", "/"},
   734  	{"a/", "../..", "/"},
   735  	{"a/b/c", "..", "/a/"},
   736  	{"a/b/c", "../d", "/a/d"},
   737  	{"a/b/c", ".././d", "/a/d"},
   738  	{"a/b", "./..", "/"},
   739  	{"a/./b", ".", "/a/"},
   740  	{"a/../", ".", "/"},
   741  	{"a/.././b", "c", "/c"},
   742  }
   743  
   744  func TestResolvePath(t *testing.T) {
   745  	for _, test := range resolvePathTests {
   746  		got := resolvePath(test.base, test.ref)
   747  		if got != test.expected {
   748  			t.Errorf("For %q + %q got %q; expected %q", test.base, test.ref, got, test.expected)
   749  		}
   750  	}
   751  }
   752  
   753  var resolveReferenceTests = []struct {
   754  	base, rel, expected string
   755  }{
   756  	// Absolute URL references
   757  	{"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
   758  	{"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
   759  	{"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
   760  
   761  	// Path-absolute references
   762  	{"http://foo.com/bar", "/baz", "http://foo.com/baz"},
   763  	{"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
   764  	{"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
   765  
   766  	// Scheme-relative
   767  	{"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"},
   768  
   769  	// Path-relative references:
   770  
   771  	// ... current directory
   772  	{"http://foo.com", ".", "http://foo.com/"},
   773  	{"http://foo.com/bar", ".", "http://foo.com/"},
   774  	{"http://foo.com/bar/", ".", "http://foo.com/bar/"},
   775  
   776  	// ... going down
   777  	{"http://foo.com", "bar", "http://foo.com/bar"},
   778  	{"http://foo.com/", "bar", "http://foo.com/bar"},
   779  	{"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"},
   780  
   781  	// ... going up
   782  	{"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"},
   783  	{"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
   784  	{"http://foo.com/bar", "..", "http://foo.com/"},
   785  	{"http://foo.com/bar/baz", "./..", "http://foo.com/"},
   786  	// ".." in the middle (issue 3560)
   787  	{"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
   788  	{"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
   789  	{"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
   790  	{"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
   791  	{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
   792  	{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
   793  	{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
   794  	{"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot/"},
   795  
   796  	// Remove any dot-segments prior to forming the target URI.
   797  	// http://tools.ietf.org/html/rfc3986#section-5.2.4
   798  	{"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/baz"},
   799  
   800  	// Triple dot isn't special
   801  	{"http://foo.com/bar", "...", "http://foo.com/..."},
   802  
   803  	// Fragment
   804  	{"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
   805  
   806  	// RFC 3986: Normal Examples
   807  	// http://tools.ietf.org/html/rfc3986#section-5.4.1
   808  	{"http://a/b/c/d;p?q", "g:h", "g:h"},
   809  	{"http://a/b/c/d;p?q", "g", "http://a/b/c/g"},
   810  	{"http://a/b/c/d;p?q", "./g", "http://a/b/c/g"},
   811  	{"http://a/b/c/d;p?q", "g/", "http://a/b/c/g/"},
   812  	{"http://a/b/c/d;p?q", "/g", "http://a/g"},
   813  	{"http://a/b/c/d;p?q", "//g", "http://g"},
   814  	{"http://a/b/c/d;p?q", "?y", "http://a/b/c/d;p?y"},
   815  	{"http://a/b/c/d;p?q", "g?y", "http://a/b/c/g?y"},
   816  	{"http://a/b/c/d;p?q", "#s", "http://a/b/c/d;p?q#s"},
   817  	{"http://a/b/c/d;p?q", "g#s", "http://a/b/c/g#s"},
   818  	{"http://a/b/c/d;p?q", "g?y#s", "http://a/b/c/g?y#s"},
   819  	{"http://a/b/c/d;p?q", ";x", "http://a/b/c/;x"},
   820  	{"http://a/b/c/d;p?q", "g;x", "http://a/b/c/g;x"},
   821  	{"http://a/b/c/d;p?q", "g;x?y#s", "http://a/b/c/g;x?y#s"},
   822  	{"http://a/b/c/d;p?q", "", "http://a/b/c/d;p?q"},
   823  	{"http://a/b/c/d;p?q", ".", "http://a/b/c/"},
   824  	{"http://a/b/c/d;p?q", "./", "http://a/b/c/"},
   825  	{"http://a/b/c/d;p?q", "..", "http://a/b/"},
   826  	{"http://a/b/c/d;p?q", "../", "http://a/b/"},
   827  	{"http://a/b/c/d;p?q", "../g", "http://a/b/g"},
   828  	{"http://a/b/c/d;p?q", "../..", "http://a/"},
   829  	{"http://a/b/c/d;p?q", "../../", "http://a/"},
   830  	{"http://a/b/c/d;p?q", "../../g", "http://a/g"},
   831  
   832  	// RFC 3986: Abnormal Examples
   833  	// http://tools.ietf.org/html/rfc3986#section-5.4.2
   834  	{"http://a/b/c/d;p?q", "../../../g", "http://a/g"},
   835  	{"http://a/b/c/d;p?q", "../../../../g", "http://a/g"},
   836  	{"http://a/b/c/d;p?q", "/./g", "http://a/g"},
   837  	{"http://a/b/c/d;p?q", "/../g", "http://a/g"},
   838  	{"http://a/b/c/d;p?q", "g.", "http://a/b/c/g."},
   839  	{"http://a/b/c/d;p?q", ".g", "http://a/b/c/.g"},
   840  	{"http://a/b/c/d;p?q", "g..", "http://a/b/c/g.."},
   841  	{"http://a/b/c/d;p?q", "..g", "http://a/b/c/..g"},
   842  	{"http://a/b/c/d;p?q", "./../g", "http://a/b/g"},
   843  	{"http://a/b/c/d;p?q", "./g/.", "http://a/b/c/g/"},
   844  	{"http://a/b/c/d;p?q", "g/./h", "http://a/b/c/g/h"},
   845  	{"http://a/b/c/d;p?q", "g/../h", "http://a/b/c/h"},
   846  	{"http://a/b/c/d;p?q", "g;x=1/./y", "http://a/b/c/g;x=1/y"},
   847  	{"http://a/b/c/d;p?q", "g;x=1/../y", "http://a/b/c/y"},
   848  	{"http://a/b/c/d;p?q", "g?y/./x", "http://a/b/c/g?y/./x"},
   849  	{"http://a/b/c/d;p?q", "g?y/../x", "http://a/b/c/g?y/../x"},
   850  	{"http://a/b/c/d;p?q", "g#s/./x", "http://a/b/c/g#s/./x"},
   851  	{"http://a/b/c/d;p?q", "g#s/../x", "http://a/b/c/g#s/../x"},
   852  
   853  	// Extras.
   854  	{"https://a/b/c/d;p?q", "//g?q", "https://g?q"},
   855  	{"https://a/b/c/d;p?q", "//g#s", "https://g#s"},
   856  	{"https://a/b/c/d;p?q", "//g/d/e/f?y#s", "https://g/d/e/f?y#s"},
   857  	{"https://a/b/c/d;p#s", "?y", "https://a/b/c/d;p?y"},
   858  	{"https://a/b/c/d;p?q#s", "?y", "https://a/b/c/d;p?y"},
   859  }
   860  
   861  func TestResolveReference(t *testing.T) {
   862  	mustParse := func(url string) *URL {
   863  		u, err := Parse(url)
   864  		if err != nil {
   865  			t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
   866  		}
   867  		return u
   868  	}
   869  	opaque := &URL{Scheme: "scheme", Opaque: "opaque"}
   870  	for _, test := range resolveReferenceTests {
   871  		base := mustParse(test.base)
   872  		rel := mustParse(test.rel)
   873  		url := base.ResolveReference(rel)
   874  		if url.String() != test.expected {
   875  			t.Errorf("URL(%q).ResolveReference(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
   876  		}
   877  		// Ensure that new instances are returned.
   878  		if base == url {
   879  			t.Errorf("Expected URL.ResolveReference to return new URL instance.")
   880  		}
   881  		// Test the convenience wrapper too.
   882  		url, err := base.Parse(test.rel)
   883  		if err != nil {
   884  			t.Errorf("URL(%q).Parse(%q) failed: %v", test.base, test.rel, err)
   885  		} else if url.String() != test.expected {
   886  			t.Errorf("URL(%q).Parse(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
   887  		} else if base == url {
   888  			// Ensure that new instances are returned for the wrapper too.
   889  			t.Errorf("Expected URL.Parse to return new URL instance.")
   890  		}
   891  		// Ensure Opaque resets the URL.
   892  		url = base.ResolveReference(opaque)
   893  		if *url != *opaque {
   894  			t.Errorf("ResolveReference failed to resolve opaque URL: want %#v, got %#v", url, opaque)
   895  		}
   896  		// Test the convenience wrapper with an opaque URL too.
   897  		url, err = base.Parse("scheme:opaque")
   898  		if err != nil {
   899  			t.Errorf(`URL(%q).Parse("scheme:opaque") failed: %v`, test.base, err)
   900  		} else if *url != *opaque {
   901  			t.Errorf("Parse failed to resolve opaque URL: want %#v, got %#v", url, opaque)
   902  		} else if base == url {
   903  			// Ensure that new instances are returned, again.
   904  			t.Errorf("Expected URL.Parse to return new URL instance.")
   905  		}
   906  	}
   907  }
   908  
   909  func TestQueryValues(t *testing.T) {
   910  	u, _ := Parse("http://x.com?foo=bar&bar=1&bar=2")
   911  	v := u.Query()
   912  	if len(v) != 2 {
   913  		t.Errorf("got %d keys in Query values, want 2", len(v))
   914  	}
   915  	if g, e := v.Get("foo"), "bar"; g != e {
   916  		t.Errorf("Get(foo) = %q, want %q", g, e)
   917  	}
   918  	// Case sensitive:
   919  	if g, e := v.Get("Foo"), ""; g != e {
   920  		t.Errorf("Get(Foo) = %q, want %q", g, e)
   921  	}
   922  	if g, e := v.Get("bar"), "1"; g != e {
   923  		t.Errorf("Get(bar) = %q, want %q", g, e)
   924  	}
   925  	if g, e := v.Get("baz"), ""; g != e {
   926  		t.Errorf("Get(baz) = %q, want %q", g, e)
   927  	}
   928  	v.Del("bar")
   929  	if g, e := v.Get("bar"), ""; g != e {
   930  		t.Errorf("second Get(bar) = %q, want %q", g, e)
   931  	}
   932  }
   933  
   934  type parseTest struct {
   935  	query string
   936  	out   Values
   937  }
   938  
   939  var parseTests = []parseTest{
   940  	{
   941  		query: "a=1&b=2",
   942  		out:   Values{"a": []string{"1"}, "b": []string{"2"}},
   943  	},
   944  	{
   945  		query: "a=1&a=2&a=banana",
   946  		out:   Values{"a": []string{"1", "2", "banana"}},
   947  	},
   948  	{
   949  		query: "ascii=%3Ckey%3A+0x90%3E",
   950  		out:   Values{"ascii": []string{"<key: 0x90>"}},
   951  	},
   952  	{
   953  		query: "a=1;b=2",
   954  		out:   Values{"a": []string{"1"}, "b": []string{"2"}},
   955  	},
   956  	{
   957  		query: "a=1&a=2;a=banana",
   958  		out:   Values{"a": []string{"1", "2", "banana"}},
   959  	},
   960  }
   961  
   962  func TestParseQuery(t *testing.T) {
   963  	for i, test := range parseTests {
   964  		form, err := ParseQuery(test.query)
   965  		if err != nil {
   966  			t.Errorf("test %d: Unexpected error: %v", i, err)
   967  			continue
   968  		}
   969  		if len(form) != len(test.out) {
   970  			t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
   971  		}
   972  		for k, evs := range test.out {
   973  			vs, ok := form[k]
   974  			if !ok {
   975  				t.Errorf("test %d: Missing key %q", i, k)
   976  				continue
   977  			}
   978  			if len(vs) != len(evs) {
   979  				t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
   980  				continue
   981  			}
   982  			for j, ev := range evs {
   983  				if v := vs[j]; v != ev {
   984  					t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)
   985  				}
   986  			}
   987  		}
   988  	}
   989  }
   990  
   991  type RequestURITest struct {
   992  	url *URL
   993  	out string
   994  }
   995  
   996  var requritests = []RequestURITest{
   997  	{
   998  		&URL{
   999  			Scheme: "http",
  1000  			Host:   "example.com",
  1001  			Path:   "",
  1002  		},
  1003  		"/",
  1004  	},
  1005  	{
  1006  		&URL{
  1007  			Scheme: "http",
  1008  			Host:   "example.com",
  1009  			Path:   "/a b",
  1010  		},
  1011  		"/a%20b",
  1012  	},
  1013  	// golang.org/issue/4860 variant 1
  1014  	{
  1015  		&URL{
  1016  			Scheme: "http",
  1017  			Host:   "example.com",
  1018  			Opaque: "/%2F/%2F/",
  1019  		},
  1020  		"/%2F/%2F/",
  1021  	},
  1022  	// golang.org/issue/4860 variant 2
  1023  	{
  1024  		&URL{
  1025  			Scheme: "http",
  1026  			Host:   "example.com",
  1027  			Opaque: "//other.example.com/%2F/%2F/",
  1028  		},
  1029  		"http://other.example.com/%2F/%2F/",
  1030  	},
  1031  	// better fix for issue 4860
  1032  	{
  1033  		&URL{
  1034  			Scheme:  "http",
  1035  			Host:    "example.com",
  1036  			Path:    "/////",
  1037  			RawPath: "/%2F/%2F/",
  1038  		},
  1039  		"/%2F/%2F/",
  1040  	},
  1041  	{
  1042  		&URL{
  1043  			Scheme:  "http",
  1044  			Host:    "example.com",
  1045  			Path:    "/////",
  1046  			RawPath: "/WRONG/", // ignored because doesn't match Path
  1047  		},
  1048  		"/////",
  1049  	},
  1050  	{
  1051  		&URL{
  1052  			Scheme:   "http",
  1053  			Host:     "example.com",
  1054  			Path:     "/a b",
  1055  			RawQuery: "q=go+language",
  1056  		},
  1057  		"/a%20b?q=go+language",
  1058  	},
  1059  	{
  1060  		&URL{
  1061  			Scheme:   "http",
  1062  			Host:     "example.com",
  1063  			Path:     "/a b",
  1064  			RawPath:  "/a b", // ignored because invalid
  1065  			RawQuery: "q=go+language",
  1066  		},
  1067  		"/a%20b?q=go+language",
  1068  	},
  1069  	{
  1070  		&URL{
  1071  			Scheme:   "http",
  1072  			Host:     "example.com",
  1073  			Path:     "/a?b",
  1074  			RawPath:  "/a?b", // ignored because invalid
  1075  			RawQuery: "q=go+language",
  1076  		},
  1077  		"/a%3Fb?q=go+language",
  1078  	},
  1079  	{
  1080  		&URL{
  1081  			Scheme: "myschema",
  1082  			Opaque: "opaque",
  1083  		},
  1084  		"opaque",
  1085  	},
  1086  	{
  1087  		&URL{
  1088  			Scheme:   "myschema",
  1089  			Opaque:   "opaque",
  1090  			RawQuery: "q=go+language",
  1091  		},
  1092  		"opaque?q=go+language",
  1093  	},
  1094  }
  1095  
  1096  func TestRequestURI(t *testing.T) {
  1097  	for _, tt := range requritests {
  1098  		s := tt.url.RequestURI()
  1099  		if s != tt.out {
  1100  			t.Errorf("%#v.RequestURI() == %q (expected %q)", tt.url, s, tt.out)
  1101  		}
  1102  	}
  1103  }
  1104  
  1105  func TestParseFailure(t *testing.T) {
  1106  	// Test that the first parse error is returned.
  1107  	const url = "%gh&%ij"
  1108  	_, err := ParseQuery(url)
  1109  	errStr := fmt.Sprint(err)
  1110  	if !strings.Contains(errStr, "%gh") {
  1111  		t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
  1112  	}
  1113  }
  1114  
  1115  func TestParseAuthority(t *testing.T) {
  1116  	tests := []struct {
  1117  		in      string
  1118  		wantErr bool
  1119  	}{
  1120  		{"http://[::1]", false},
  1121  		{"http://[::1]:80", false},
  1122  		{"http://[::1]:namedport", true}, // rfc3986 3.2.3
  1123  		{"http://[::1]/", false},
  1124  		{"http://[::1]a", true},
  1125  		{"http://[::1]%23", true},
  1126  		{"http://[::1%25en0]", false},     // valid zone id
  1127  		{"http://[::1]:", true},           // colon, but no port
  1128  		{"http://[::1]:%38%30", true},     // no hex in port
  1129  		{"http://[::1%25%10]", false},     // TODO: reject the %10 after the valid zone %25 separator?
  1130  		{"http://[%10::1]", true},         // no %xx escapes in IP address
  1131  		{"http://[::1]/%48", false},       // %xx in path is fine
  1132  		{"http://%41:8080/", true},        // TODO: arguably we should accept reg-name with %xx
  1133  		{"mysql://x@y(z:123)/foo", false}, // golang.org/issue/12023
  1134  		{"mysql://x@y(1.2.3.4:123)/foo", false},
  1135  		{"mysql://x@y([2001:db8::1]:123)/foo", false},
  1136  		{"http://[]%20%48%54%54%50%2f%31%2e%31%0a%4d%79%48%65%61%64%65%72%3a%20%31%32%33%0a%0a/", true}, // golang.org/issue/11208
  1137  	}
  1138  	for _, tt := range tests {
  1139  		u, err := Parse(tt.in)
  1140  		if tt.wantErr {
  1141  			if err == nil {
  1142  				t.Errorf("Parse(%q) = %#v; want an error", tt.in, u)
  1143  			}
  1144  			continue
  1145  		}
  1146  		if err != nil {
  1147  			t.Logf("Parse(%q) = %v; want no error", tt.in, err)
  1148  		}
  1149  	}
  1150  }
  1151  
  1152  // Issue 11202
  1153  func TestStarRequest(t *testing.T) {
  1154  	u, err := Parse("*")
  1155  	if err != nil {
  1156  		t.Fatal(err)
  1157  	}
  1158  	if got, want := u.RequestURI(), "*"; got != want {
  1159  		t.Errorf("RequestURI = %q; want %q", got, want)
  1160  	}
  1161  }
  1162  
  1163  type shouldEscapeTest struct {
  1164  	in     byte
  1165  	mode   encoding
  1166  	escape bool
  1167  }
  1168  
  1169  var shouldEscapeTests = []shouldEscapeTest{
  1170  	// Unreserved characters (§2.3)
  1171  	{'a', encodePath, false},
  1172  	{'a', encodeUserPassword, false},
  1173  	{'a', encodeQueryComponent, false},
  1174  	{'a', encodeFragment, false},
  1175  	{'a', encodeHost, false},
  1176  	{'z', encodePath, false},
  1177  	{'A', encodePath, false},
  1178  	{'Z', encodePath, false},
  1179  	{'0', encodePath, false},
  1180  	{'9', encodePath, false},
  1181  	{'-', encodePath, false},
  1182  	{'-', encodeUserPassword, false},
  1183  	{'-', encodeQueryComponent, false},
  1184  	{'-', encodeFragment, false},
  1185  	{'.', encodePath, false},
  1186  	{'_', encodePath, false},
  1187  	{'~', encodePath, false},
  1188  
  1189  	// User information (§3.2.1)
  1190  	{':', encodeUserPassword, true},
  1191  	{'/', encodeUserPassword, true},
  1192  	{'?', encodeUserPassword, true},
  1193  	{'@', encodeUserPassword, true},
  1194  	{'$', encodeUserPassword, false},
  1195  	{'&', encodeUserPassword, false},
  1196  	{'+', encodeUserPassword, false},
  1197  	{',', encodeUserPassword, false},
  1198  	{';', encodeUserPassword, false},
  1199  	{'=', encodeUserPassword, false},
  1200  
  1201  	// Host (IP address, IPv6 address, registered name, port suffix; §3.2.2)
  1202  	{'!', encodeHost, false},
  1203  	{'$', encodeHost, false},
  1204  	{'&', encodeHost, false},
  1205  	{'\'', encodeHost, false},
  1206  	{'(', encodeHost, false},
  1207  	{')', encodeHost, false},
  1208  	{'*', encodeHost, false},
  1209  	{'+', encodeHost, false},
  1210  	{',', encodeHost, false},
  1211  	{';', encodeHost, false},
  1212  	{'=', encodeHost, false},
  1213  	{':', encodeHost, false},
  1214  	{'[', encodeHost, false},
  1215  	{']', encodeHost, false},
  1216  	{'0', encodeHost, false},
  1217  	{'9', encodeHost, false},
  1218  	{'A', encodeHost, false},
  1219  	{'z', encodeHost, false},
  1220  	{'_', encodeHost, false},
  1221  	{'-', encodeHost, false},
  1222  	{'.', encodeHost, false},
  1223  }
  1224  
  1225  func TestShouldEscape(t *testing.T) {
  1226  	for _, tt := range shouldEscapeTests {
  1227  		if shouldEscape(tt.in, tt.mode) != tt.escape {
  1228  			t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape)
  1229  		}
  1230  	}
  1231  }