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