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