github.com/fjballest/golang@v0.0.0-20151209143359-e4c5fe594ca8/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 6847
   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 6847
   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 6847
   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 6847
   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  }
   515  
   516  // more useful string for debugging than fmt's struct printer
   517  func ufmt(u *URL) string {
   518  	var user, pass interface{}
   519  	if u.User != nil {
   520  		user = u.User.Username()
   521  		if p, ok := u.User.Password(); ok {
   522  			pass = p
   523  		}
   524  	}
   525  	return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawpath=%q, rawq=%q, frag=%q",
   526  		u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment)
   527  }
   528  
   529  func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
   530  	for _, tt := range tests {
   531  		u, err := parse(tt.in)
   532  		if err != nil {
   533  			t.Errorf("%s(%q) returned error %s", name, tt.in, err)
   534  			continue
   535  		}
   536  		if !reflect.DeepEqual(u, tt.out) {
   537  			t.Errorf("%s(%q):\n\thave %v\n\twant %v\n",
   538  				name, tt.in, ufmt(u), ufmt(tt.out))
   539  		}
   540  	}
   541  }
   542  
   543  func BenchmarkString(b *testing.B) {
   544  	b.StopTimer()
   545  	b.ReportAllocs()
   546  	for _, tt := range urltests {
   547  		u, err := Parse(tt.in)
   548  		if err != nil {
   549  			b.Errorf("Parse(%q) returned error %s", tt.in, err)
   550  			continue
   551  		}
   552  		if tt.roundtrip == "" {
   553  			continue
   554  		}
   555  		b.StartTimer()
   556  		var g string
   557  		for i := 0; i < b.N; i++ {
   558  			g = u.String()
   559  		}
   560  		b.StopTimer()
   561  		if w := tt.roundtrip; g != w {
   562  			b.Errorf("Parse(%q).String() == %q, want %q", tt.in, g, w)
   563  		}
   564  	}
   565  }
   566  
   567  func TestParse(t *testing.T) {
   568  	DoTest(t, Parse, "Parse", urltests)
   569  }
   570  
   571  const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
   572  
   573  var parseRequestURLTests = []struct {
   574  	url           string
   575  	expectedValid bool
   576  }{
   577  	{"http://foo.com", true},
   578  	{"http://foo.com/", true},
   579  	{"http://foo.com/path", true},
   580  	{"/", true},
   581  	{pathThatLooksSchemeRelative, true},
   582  	{"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
   583  	{"*", true},
   584  	{"http://192.168.0.1/", true},
   585  	{"http://192.168.0.1:8080/", true},
   586  	{"http://[fe80::1]/", true},
   587  	{"http://[fe80::1]:8080/", true},
   588  
   589  	// Tests exercising RFC 6874 compliance:
   590  	{"http://[fe80::1%25en0]/", true},                 // with alphanum zone identifier
   591  	{"http://[fe80::1%25en0]:8080/", true},            // with alphanum zone identifier
   592  	{"http://[fe80::1%25%65%6e%301-._~]/", true},      // with percent-encoded+unreserved zone identifier
   593  	{"http://[fe80::1%25%65%6e%301-._~]:8080/", true}, // with percent-encoded+unreserved zone identifier
   594  
   595  	{"foo.html", false},
   596  	{"../dir/", false},
   597  	{"http://192.168.0.%31/", false},
   598  	{"http://192.168.0.%31:8080/", false},
   599  	{"http://[fe80::%31]/", false},
   600  	{"http://[fe80::%31]:8080/", false},
   601  	{"http://[fe80::%31%25en0]/", false},
   602  	{"http://[fe80::%31%25en0]:8080/", false},
   603  
   604  	// These two cases are valid as textual representations as
   605  	// described in RFC 4007, but are not valid as address
   606  	// literals with IPv6 zone identifiers in URIs as described in
   607  	// RFC 6874.
   608  	{"http://[fe80::1%en0]/", false},
   609  	{"http://[fe80::1%en0]:8080/", false},
   610  }
   611  
   612  func TestParseRequestURI(t *testing.T) {
   613  	for _, test := range parseRequestURLTests {
   614  		_, err := ParseRequestURI(test.url)
   615  		valid := err == nil
   616  		if valid != test.expectedValid {
   617  			t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid)
   618  		}
   619  	}
   620  
   621  	url, err := ParseRequestURI(pathThatLooksSchemeRelative)
   622  	if err != nil {
   623  		t.Fatalf("Unexpected error %v", err)
   624  	}
   625  	if url.Path != pathThatLooksSchemeRelative {
   626  		t.Errorf("Expected path %q; got %q", pathThatLooksSchemeRelative, url.Path)
   627  	}
   628  }
   629  
   630  func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
   631  	for _, tt := range tests {
   632  		u, err := parse(tt.in)
   633  		if err != nil {
   634  			t.Errorf("%s(%q) returned error %s", name, tt.in, err)
   635  			continue
   636  		}
   637  		expected := tt.in
   638  		if len(tt.roundtrip) > 0 {
   639  			expected = tt.roundtrip
   640  		}
   641  		s := u.String()
   642  		if s != expected {
   643  			t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected)
   644  		}
   645  	}
   646  }
   647  
   648  func TestURLString(t *testing.T) {
   649  	DoTestString(t, Parse, "Parse", urltests)
   650  
   651  	// no leading slash on path should prepend
   652  	// slash on String() call
   653  	noslash := URLTest{
   654  		"http://www.google.com/search",
   655  		&URL{
   656  			Scheme: "http",
   657  			Host:   "www.google.com",
   658  			Path:   "search",
   659  		},
   660  		"",
   661  	}
   662  	s := noslash.out.String()
   663  	if s != noslash.in {
   664  		t.Errorf("Expected %s; go %s", noslash.in, s)
   665  	}
   666  }
   667  
   668  type EscapeTest struct {
   669  	in  string
   670  	out string
   671  	err error
   672  }
   673  
   674  var unescapeTests = []EscapeTest{
   675  	{
   676  		"",
   677  		"",
   678  		nil,
   679  	},
   680  	{
   681  		"abc",
   682  		"abc",
   683  		nil,
   684  	},
   685  	{
   686  		"1%41",
   687  		"1A",
   688  		nil,
   689  	},
   690  	{
   691  		"1%41%42%43",
   692  		"1ABC",
   693  		nil,
   694  	},
   695  	{
   696  		"%4a",
   697  		"J",
   698  		nil,
   699  	},
   700  	{
   701  		"%6F",
   702  		"o",
   703  		nil,
   704  	},
   705  	{
   706  		"%", // not enough characters after %
   707  		"",
   708  		EscapeError("%"),
   709  	},
   710  	{
   711  		"%a", // not enough characters after %
   712  		"",
   713  		EscapeError("%a"),
   714  	},
   715  	{
   716  		"%1", // not enough characters after %
   717  		"",
   718  		EscapeError("%1"),
   719  	},
   720  	{
   721  		"123%45%6", // not enough characters after %
   722  		"",
   723  		EscapeError("%6"),
   724  	},
   725  	{
   726  		"%zzzzz", // invalid hex digits
   727  		"",
   728  		EscapeError("%zz"),
   729  	},
   730  }
   731  
   732  func TestUnescape(t *testing.T) {
   733  	for _, tt := range unescapeTests {
   734  		actual, err := QueryUnescape(tt.in)
   735  		if actual != tt.out || (err != nil) != (tt.err != nil) {
   736  			t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err)
   737  		}
   738  	}
   739  }
   740  
   741  var escapeTests = []EscapeTest{
   742  	{
   743  		"",
   744  		"",
   745  		nil,
   746  	},
   747  	{
   748  		"abc",
   749  		"abc",
   750  		nil,
   751  	},
   752  	{
   753  		"one two",
   754  		"one+two",
   755  		nil,
   756  	},
   757  	{
   758  		"10%",
   759  		"10%25",
   760  		nil,
   761  	},
   762  	{
   763  		" ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
   764  		"+%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",
   765  		nil,
   766  	},
   767  }
   768  
   769  func TestEscape(t *testing.T) {
   770  	for _, tt := range escapeTests {
   771  		actual := QueryEscape(tt.in)
   772  		if tt.out != actual {
   773  			t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out)
   774  		}
   775  
   776  		// for bonus points, verify that escape:unescape is an identity.
   777  		roundtrip, err := QueryUnescape(actual)
   778  		if roundtrip != tt.in || err != nil {
   779  			t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
   780  		}
   781  	}
   782  }
   783  
   784  //var userinfoTests = []UserinfoTest{
   785  //	{"user", "password", "user:password"},
   786  //	{"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./",
   787  //		"foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"},
   788  //}
   789  
   790  type EncodeQueryTest struct {
   791  	m        Values
   792  	expected string
   793  }
   794  
   795  var encodeQueryTests = []EncodeQueryTest{
   796  	{nil, ""},
   797  	{Values{"q": {"puppies"}, "oe": {"utf8"}}, "oe=utf8&q=puppies"},
   798  	{Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7"},
   799  	{Values{
   800  		"a": {"a1", "a2", "a3"},
   801  		"b": {"b1", "b2", "b3"},
   802  		"c": {"c1", "c2", "c3"},
   803  	}, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"},
   804  }
   805  
   806  func TestEncodeQuery(t *testing.T) {
   807  	for _, tt := range encodeQueryTests {
   808  		if q := tt.m.Encode(); q != tt.expected {
   809  			t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected)
   810  		}
   811  	}
   812  }
   813  
   814  var resolvePathTests = []struct {
   815  	base, ref, expected string
   816  }{
   817  	{"a/b", ".", "/a/"},
   818  	{"a/b", "c", "/a/c"},
   819  	{"a/b", "..", "/"},
   820  	{"a/", "..", "/"},
   821  	{"a/", "../..", "/"},
   822  	{"a/b/c", "..", "/a/"},
   823  	{"a/b/c", "../d", "/a/d"},
   824  	{"a/b/c", ".././d", "/a/d"},
   825  	{"a/b", "./..", "/"},
   826  	{"a/./b", ".", "/a/"},
   827  	{"a/../", ".", "/"},
   828  	{"a/.././b", "c", "/c"},
   829  }
   830  
   831  func TestResolvePath(t *testing.T) {
   832  	for _, test := range resolvePathTests {
   833  		got := resolvePath(test.base, test.ref)
   834  		if got != test.expected {
   835  			t.Errorf("For %q + %q got %q; expected %q", test.base, test.ref, got, test.expected)
   836  		}
   837  	}
   838  }
   839  
   840  var resolveReferenceTests = []struct {
   841  	base, rel, expected string
   842  }{
   843  	// Absolute URL references
   844  	{"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
   845  	{"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
   846  	{"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
   847  
   848  	// Path-absolute references
   849  	{"http://foo.com/bar", "/baz", "http://foo.com/baz"},
   850  	{"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
   851  	{"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
   852  
   853  	// Scheme-relative
   854  	{"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"},
   855  
   856  	// Path-relative references:
   857  
   858  	// ... current directory
   859  	{"http://foo.com", ".", "http://foo.com/"},
   860  	{"http://foo.com/bar", ".", "http://foo.com/"},
   861  	{"http://foo.com/bar/", ".", "http://foo.com/bar/"},
   862  
   863  	// ... going down
   864  	{"http://foo.com", "bar", "http://foo.com/bar"},
   865  	{"http://foo.com/", "bar", "http://foo.com/bar"},
   866  	{"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"},
   867  
   868  	// ... going up
   869  	{"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"},
   870  	{"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
   871  	{"http://foo.com/bar", "..", "http://foo.com/"},
   872  	{"http://foo.com/bar/baz", "./..", "http://foo.com/"},
   873  	// ".." in the middle (issue 3560)
   874  	{"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
   875  	{"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
   876  	{"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
   877  	{"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
   878  	{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
   879  	{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
   880  	{"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
   881  	{"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot/"},
   882  
   883  	// Remove any dot-segments prior to forming the target URI.
   884  	// http://tools.ietf.org/html/rfc3986#section-5.2.4
   885  	{"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/baz"},
   886  
   887  	// Triple dot isn't special
   888  	{"http://foo.com/bar", "...", "http://foo.com/..."},
   889  
   890  	// Fragment
   891  	{"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
   892  
   893  	// RFC 3986: Normal Examples
   894  	// http://tools.ietf.org/html/rfc3986#section-5.4.1
   895  	{"http://a/b/c/d;p?q", "g:h", "g:h"},
   896  	{"http://a/b/c/d;p?q", "g", "http://a/b/c/g"},
   897  	{"http://a/b/c/d;p?q", "./g", "http://a/b/c/g"},
   898  	{"http://a/b/c/d;p?q", "g/", "http://a/b/c/g/"},
   899  	{"http://a/b/c/d;p?q", "/g", "http://a/g"},
   900  	{"http://a/b/c/d;p?q", "//g", "http://g"},
   901  	{"http://a/b/c/d;p?q", "?y", "http://a/b/c/d;p?y"},
   902  	{"http://a/b/c/d;p?q", "g?y", "http://a/b/c/g?y"},
   903  	{"http://a/b/c/d;p?q", "#s", "http://a/b/c/d;p?q#s"},
   904  	{"http://a/b/c/d;p?q", "g#s", "http://a/b/c/g#s"},
   905  	{"http://a/b/c/d;p?q", "g?y#s", "http://a/b/c/g?y#s"},
   906  	{"http://a/b/c/d;p?q", ";x", "http://a/b/c/;x"},
   907  	{"http://a/b/c/d;p?q", "g;x", "http://a/b/c/g;x"},
   908  	{"http://a/b/c/d;p?q", "g;x?y#s", "http://a/b/c/g;x?y#s"},
   909  	{"http://a/b/c/d;p?q", "", "http://a/b/c/d;p?q"},
   910  	{"http://a/b/c/d;p?q", ".", "http://a/b/c/"},
   911  	{"http://a/b/c/d;p?q", "./", "http://a/b/c/"},
   912  	{"http://a/b/c/d;p?q", "..", "http://a/b/"},
   913  	{"http://a/b/c/d;p?q", "../", "http://a/b/"},
   914  	{"http://a/b/c/d;p?q", "../g", "http://a/b/g"},
   915  	{"http://a/b/c/d;p?q", "../..", "http://a/"},
   916  	{"http://a/b/c/d;p?q", "../../", "http://a/"},
   917  	{"http://a/b/c/d;p?q", "../../g", "http://a/g"},
   918  
   919  	// RFC 3986: Abnormal Examples
   920  	// http://tools.ietf.org/html/rfc3986#section-5.4.2
   921  	{"http://a/b/c/d;p?q", "../../../g", "http://a/g"},
   922  	{"http://a/b/c/d;p?q", "../../../../g", "http://a/g"},
   923  	{"http://a/b/c/d;p?q", "/./g", "http://a/g"},
   924  	{"http://a/b/c/d;p?q", "/../g", "http://a/g"},
   925  	{"http://a/b/c/d;p?q", "g.", "http://a/b/c/g."},
   926  	{"http://a/b/c/d;p?q", ".g", "http://a/b/c/.g"},
   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/g"},
   930  	{"http://a/b/c/d;p?q", "./g/.", "http://a/b/c/g/"},
   931  	{"http://a/b/c/d;p?q", "g/./h", "http://a/b/c/g/h"},
   932  	{"http://a/b/c/d;p?q", "g/../h", "http://a/b/c/h"},
   933  	{"http://a/b/c/d;p?q", "g;x=1/./y", "http://a/b/c/g;x=1/y"},
   934  	{"http://a/b/c/d;p?q", "g;x=1/../y", "http://a/b/c/y"},
   935  	{"http://a/b/c/d;p?q", "g?y/./x", "http://a/b/c/g?y/./x"},
   936  	{"http://a/b/c/d;p?q", "g?y/../x", "http://a/b/c/g?y/../x"},
   937  	{"http://a/b/c/d;p?q", "g#s/./x", "http://a/b/c/g#s/./x"},
   938  	{"http://a/b/c/d;p?q", "g#s/../x", "http://a/b/c/g#s/../x"},
   939  
   940  	// Extras.
   941  	{"https://a/b/c/d;p?q", "//g?q", "https://g?q"},
   942  	{"https://a/b/c/d;p?q", "//g#s", "https://g#s"},
   943  	{"https://a/b/c/d;p?q", "//g/d/e/f?y#s", "https://g/d/e/f?y#s"},
   944  	{"https://a/b/c/d;p#s", "?y", "https://a/b/c/d;p?y"},
   945  	{"https://a/b/c/d;p?q#s", "?y", "https://a/b/c/d;p?y"},
   946  }
   947  
   948  func TestResolveReference(t *testing.T) {
   949  	mustParse := func(url string) *URL {
   950  		u, err := Parse(url)
   951  		if err != nil {
   952  			t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
   953  		}
   954  		return u
   955  	}
   956  	opaque := &URL{Scheme: "scheme", Opaque: "opaque"}
   957  	for _, test := range resolveReferenceTests {
   958  		base := mustParse(test.base)
   959  		rel := mustParse(test.rel)
   960  		url := base.ResolveReference(rel)
   961  		if url.String() != test.expected {
   962  			t.Errorf("URL(%q).ResolveReference(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
   963  		}
   964  		// Ensure that new instances are returned.
   965  		if base == url {
   966  			t.Errorf("Expected URL.ResolveReference to return new URL instance.")
   967  		}
   968  		// Test the convenience wrapper too.
   969  		url, err := base.Parse(test.rel)
   970  		if err != nil {
   971  			t.Errorf("URL(%q).Parse(%q) failed: %v", test.base, test.rel, err)
   972  		} else if url.String() != test.expected {
   973  			t.Errorf("URL(%q).Parse(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
   974  		} else if base == url {
   975  			// Ensure that new instances are returned for the wrapper too.
   976  			t.Errorf("Expected URL.Parse to return new URL instance.")
   977  		}
   978  		// Ensure Opaque resets the URL.
   979  		url = base.ResolveReference(opaque)
   980  		if *url != *opaque {
   981  			t.Errorf("ResolveReference failed to resolve opaque URL: want %#v, got %#v", url, opaque)
   982  		}
   983  		// Test the convenience wrapper with an opaque URL too.
   984  		url, err = base.Parse("scheme:opaque")
   985  		if err != nil {
   986  			t.Errorf(`URL(%q).Parse("scheme:opaque") failed: %v`, test.base, err)
   987  		} else if *url != *opaque {
   988  			t.Errorf("Parse failed to resolve opaque URL: want %#v, got %#v", url, opaque)
   989  		} else if base == url {
   990  			// Ensure that new instances are returned, again.
   991  			t.Errorf("Expected URL.Parse to return new URL instance.")
   992  		}
   993  	}
   994  }
   995  
   996  func TestQueryValues(t *testing.T) {
   997  	u, _ := Parse("http://x.com?foo=bar&bar=1&bar=2")
   998  	v := u.Query()
   999  	if len(v) != 2 {
  1000  		t.Errorf("got %d keys in Query values, want 2", len(v))
  1001  	}
  1002  	if g, e := v.Get("foo"), "bar"; g != e {
  1003  		t.Errorf("Get(foo) = %q, want %q", g, e)
  1004  	}
  1005  	// Case sensitive:
  1006  	if g, e := v.Get("Foo"), ""; g != e {
  1007  		t.Errorf("Get(Foo) = %q, want %q", g, e)
  1008  	}
  1009  	if g, e := v.Get("bar"), "1"; g != e {
  1010  		t.Errorf("Get(bar) = %q, want %q", g, e)
  1011  	}
  1012  	if g, e := v.Get("baz"), ""; g != e {
  1013  		t.Errorf("Get(baz) = %q, want %q", g, e)
  1014  	}
  1015  	v.Del("bar")
  1016  	if g, e := v.Get("bar"), ""; g != e {
  1017  		t.Errorf("second Get(bar) = %q, want %q", g, e)
  1018  	}
  1019  }
  1020  
  1021  type parseTest struct {
  1022  	query string
  1023  	out   Values
  1024  }
  1025  
  1026  var parseTests = []parseTest{
  1027  	{
  1028  		query: "a=1&b=2",
  1029  		out:   Values{"a": []string{"1"}, "b": []string{"2"}},
  1030  	},
  1031  	{
  1032  		query: "a=1&a=2&a=banana",
  1033  		out:   Values{"a": []string{"1", "2", "banana"}},
  1034  	},
  1035  	{
  1036  		query: "ascii=%3Ckey%3A+0x90%3E",
  1037  		out:   Values{"ascii": []string{"<key: 0x90>"}},
  1038  	},
  1039  	{
  1040  		query: "a=1;b=2",
  1041  		out:   Values{"a": []string{"1"}, "b": []string{"2"}},
  1042  	},
  1043  	{
  1044  		query: "a=1&a=2;a=banana",
  1045  		out:   Values{"a": []string{"1", "2", "banana"}},
  1046  	},
  1047  }
  1048  
  1049  func TestParseQuery(t *testing.T) {
  1050  	for i, test := range parseTests {
  1051  		form, err := ParseQuery(test.query)
  1052  		if err != nil {
  1053  			t.Errorf("test %d: Unexpected error: %v", i, err)
  1054  			continue
  1055  		}
  1056  		if len(form) != len(test.out) {
  1057  			t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
  1058  		}
  1059  		for k, evs := range test.out {
  1060  			vs, ok := form[k]
  1061  			if !ok {
  1062  				t.Errorf("test %d: Missing key %q", i, k)
  1063  				continue
  1064  			}
  1065  			if len(vs) != len(evs) {
  1066  				t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
  1067  				continue
  1068  			}
  1069  			for j, ev := range evs {
  1070  				if v := vs[j]; v != ev {
  1071  					t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)
  1072  				}
  1073  			}
  1074  		}
  1075  	}
  1076  }
  1077  
  1078  type RequestURITest struct {
  1079  	url *URL
  1080  	out string
  1081  }
  1082  
  1083  var requritests = []RequestURITest{
  1084  	{
  1085  		&URL{
  1086  			Scheme: "http",
  1087  			Host:   "example.com",
  1088  			Path:   "",
  1089  		},
  1090  		"/",
  1091  	},
  1092  	{
  1093  		&URL{
  1094  			Scheme: "http",
  1095  			Host:   "example.com",
  1096  			Path:   "/a b",
  1097  		},
  1098  		"/a%20b",
  1099  	},
  1100  	// golang.org/issue/4860 variant 1
  1101  	{
  1102  		&URL{
  1103  			Scheme: "http",
  1104  			Host:   "example.com",
  1105  			Opaque: "/%2F/%2F/",
  1106  		},
  1107  		"/%2F/%2F/",
  1108  	},
  1109  	// golang.org/issue/4860 variant 2
  1110  	{
  1111  		&URL{
  1112  			Scheme: "http",
  1113  			Host:   "example.com",
  1114  			Opaque: "//other.example.com/%2F/%2F/",
  1115  		},
  1116  		"http://other.example.com/%2F/%2F/",
  1117  	},
  1118  	// better fix for issue 4860
  1119  	{
  1120  		&URL{
  1121  			Scheme:  "http",
  1122  			Host:    "example.com",
  1123  			Path:    "/////",
  1124  			RawPath: "/%2F/%2F/",
  1125  		},
  1126  		"/%2F/%2F/",
  1127  	},
  1128  	{
  1129  		&URL{
  1130  			Scheme:  "http",
  1131  			Host:    "example.com",
  1132  			Path:    "/////",
  1133  			RawPath: "/WRONG/", // ignored because doesn't match Path
  1134  		},
  1135  		"/////",
  1136  	},
  1137  	{
  1138  		&URL{
  1139  			Scheme:   "http",
  1140  			Host:     "example.com",
  1141  			Path:     "/a b",
  1142  			RawQuery: "q=go+language",
  1143  		},
  1144  		"/a%20b?q=go+language",
  1145  	},
  1146  	{
  1147  		&URL{
  1148  			Scheme:   "http",
  1149  			Host:     "example.com",
  1150  			Path:     "/a b",
  1151  			RawPath:  "/a b", // ignored because invalid
  1152  			RawQuery: "q=go+language",
  1153  		},
  1154  		"/a%20b?q=go+language",
  1155  	},
  1156  	{
  1157  		&URL{
  1158  			Scheme:   "http",
  1159  			Host:     "example.com",
  1160  			Path:     "/a?b",
  1161  			RawPath:  "/a?b", // ignored because invalid
  1162  			RawQuery: "q=go+language",
  1163  		},
  1164  		"/a%3Fb?q=go+language",
  1165  	},
  1166  	{
  1167  		&URL{
  1168  			Scheme: "myschema",
  1169  			Opaque: "opaque",
  1170  		},
  1171  		"opaque",
  1172  	},
  1173  	{
  1174  		&URL{
  1175  			Scheme:   "myschema",
  1176  			Opaque:   "opaque",
  1177  			RawQuery: "q=go+language",
  1178  		},
  1179  		"opaque?q=go+language",
  1180  	},
  1181  }
  1182  
  1183  func TestRequestURI(t *testing.T) {
  1184  	for _, tt := range requritests {
  1185  		s := tt.url.RequestURI()
  1186  		if s != tt.out {
  1187  			t.Errorf("%#v.RequestURI() == %q (expected %q)", tt.url, s, tt.out)
  1188  		}
  1189  	}
  1190  }
  1191  
  1192  func TestParseFailure(t *testing.T) {
  1193  	// Test that the first parse error is returned.
  1194  	const url = "%gh&%ij"
  1195  	_, err := ParseQuery(url)
  1196  	errStr := fmt.Sprint(err)
  1197  	if !strings.Contains(errStr, "%gh") {
  1198  		t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
  1199  	}
  1200  }
  1201  
  1202  func TestParseAuthority(t *testing.T) {
  1203  	tests := []struct {
  1204  		in      string
  1205  		wantErr bool
  1206  	}{
  1207  		{"http://[::1]", false},
  1208  		{"http://[::1]:80", false},
  1209  		{"http://[::1]:namedport", true}, // rfc3986 3.2.3
  1210  		{"http://[::1]/", false},
  1211  		{"http://[::1]a", true},
  1212  		{"http://[::1]%23", true},
  1213  		{"http://[::1%25en0]", false},     // valid zone id
  1214  		{"http://[::1]:", false},          // colon, but no port OK
  1215  		{"http://[::1]:%38%30", true},     // not allowed: % encoding only for non-ASCII
  1216  		{"http://[::1%25%41]", false},     // RFC 6874 allows over-escaping in zone
  1217  		{"http://[%10::1]", true},         // no %xx escapes in IP address
  1218  		{"http://[::1]/%48", false},       // %xx in path is fine
  1219  		{"http://%41:8080/", true},        // not allowed: % encoding only for non-ASCII
  1220  		{"mysql://x@y(z:123)/foo", false}, // golang.org/issue/12023
  1221  		{"mysql://x@y(1.2.3.4:123)/foo", false},
  1222  		{"mysql://x@y([2001:db8::1]:123)/foo", false},
  1223  		{"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
  1224  	}
  1225  	for _, tt := range tests {
  1226  		u, err := Parse(tt.in)
  1227  		if tt.wantErr {
  1228  			if err == nil {
  1229  				t.Errorf("Parse(%q) = %#v; want an error", tt.in, u)
  1230  			}
  1231  			continue
  1232  		}
  1233  		if err != nil {
  1234  			t.Logf("Parse(%q) = %v; want no error", tt.in, err)
  1235  		}
  1236  	}
  1237  }
  1238  
  1239  // Issue 11202
  1240  func TestStarRequest(t *testing.T) {
  1241  	u, err := Parse("*")
  1242  	if err != nil {
  1243  		t.Fatal(err)
  1244  	}
  1245  	if got, want := u.RequestURI(), "*"; got != want {
  1246  		t.Errorf("RequestURI = %q; want %q", got, want)
  1247  	}
  1248  }
  1249  
  1250  type shouldEscapeTest struct {
  1251  	in     byte
  1252  	mode   encoding
  1253  	escape bool
  1254  }
  1255  
  1256  var shouldEscapeTests = []shouldEscapeTest{
  1257  	// Unreserved characters (§2.3)
  1258  	{'a', encodePath, false},
  1259  	{'a', encodeUserPassword, false},
  1260  	{'a', encodeQueryComponent, false},
  1261  	{'a', encodeFragment, false},
  1262  	{'a', encodeHost, false},
  1263  	{'z', encodePath, false},
  1264  	{'A', encodePath, false},
  1265  	{'Z', encodePath, false},
  1266  	{'0', encodePath, false},
  1267  	{'9', encodePath, false},
  1268  	{'-', encodePath, false},
  1269  	{'-', encodeUserPassword, false},
  1270  	{'-', encodeQueryComponent, false},
  1271  	{'-', encodeFragment, false},
  1272  	{'.', encodePath, false},
  1273  	{'_', encodePath, false},
  1274  	{'~', encodePath, false},
  1275  
  1276  	// User information (§3.2.1)
  1277  	{':', encodeUserPassword, true},
  1278  	{'/', encodeUserPassword, true},
  1279  	{'?', encodeUserPassword, true},
  1280  	{'@', encodeUserPassword, true},
  1281  	{'$', encodeUserPassword, false},
  1282  	{'&', encodeUserPassword, false},
  1283  	{'+', encodeUserPassword, false},
  1284  	{',', encodeUserPassword, false},
  1285  	{';', encodeUserPassword, false},
  1286  	{'=', encodeUserPassword, false},
  1287  
  1288  	// Host (IP address, IPv6 address, registered name, port suffix; §3.2.2)
  1289  	{'!', encodeHost, false},
  1290  	{'$', encodeHost, false},
  1291  	{'&', encodeHost, false},
  1292  	{'\'', encodeHost, false},
  1293  	{'(', encodeHost, false},
  1294  	{')', encodeHost, false},
  1295  	{'*', encodeHost, false},
  1296  	{'+', encodeHost, false},
  1297  	{',', encodeHost, false},
  1298  	{';', encodeHost, false},
  1299  	{'=', encodeHost, false},
  1300  	{':', encodeHost, false},
  1301  	{'[', encodeHost, false},
  1302  	{']', encodeHost, false},
  1303  	{'0', encodeHost, false},
  1304  	{'9', encodeHost, false},
  1305  	{'A', encodeHost, false},
  1306  	{'z', encodeHost, false},
  1307  	{'_', encodeHost, false},
  1308  	{'-', encodeHost, false},
  1309  	{'.', encodeHost, false},
  1310  }
  1311  
  1312  func TestShouldEscape(t *testing.T) {
  1313  	for _, tt := range shouldEscapeTests {
  1314  		if shouldEscape(tt.in, tt.mode) != tt.escape {
  1315  			t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape)
  1316  		}
  1317  	}
  1318  }
  1319  
  1320  type timeoutError struct {
  1321  	timeout bool
  1322  }
  1323  
  1324  func (e *timeoutError) Error() string { return "timeout error" }
  1325  func (e *timeoutError) Timeout() bool { return e.timeout }
  1326  
  1327  type temporaryError struct {
  1328  	temporary bool
  1329  }
  1330  
  1331  func (e *temporaryError) Error() string   { return "temporary error" }
  1332  func (e *temporaryError) Temporary() bool { return e.temporary }
  1333  
  1334  type timeoutTemporaryError struct {
  1335  	timeoutError
  1336  	temporaryError
  1337  }
  1338  
  1339  func (e *timeoutTemporaryError) Error() string { return "timeout/temporary error" }
  1340  
  1341  var netErrorTests = []struct {
  1342  	err       error
  1343  	timeout   bool
  1344  	temporary bool
  1345  }{{
  1346  	err:       &Error{"Get", "http://google.com/", &timeoutError{timeout: true}},
  1347  	timeout:   true,
  1348  	temporary: false,
  1349  }, {
  1350  	err:       &Error{"Get", "http://google.com/", &timeoutError{timeout: false}},
  1351  	timeout:   false,
  1352  	temporary: false,
  1353  }, {
  1354  	err:       &Error{"Get", "http://google.com/", &temporaryError{temporary: true}},
  1355  	timeout:   false,
  1356  	temporary: true,
  1357  }, {
  1358  	err:       &Error{"Get", "http://google.com/", &temporaryError{temporary: false}},
  1359  	timeout:   false,
  1360  	temporary: false,
  1361  }, {
  1362  	err:       &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: true}, temporaryError{temporary: true}}},
  1363  	timeout:   true,
  1364  	temporary: true,
  1365  }, {
  1366  	err:       &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: false}, temporaryError{temporary: true}}},
  1367  	timeout:   false,
  1368  	temporary: true,
  1369  }, {
  1370  	err:       &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: true}, temporaryError{temporary: false}}},
  1371  	timeout:   true,
  1372  	temporary: false,
  1373  }, {
  1374  	err:       &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: false}, temporaryError{temporary: false}}},
  1375  	timeout:   false,
  1376  	temporary: false,
  1377  }, {
  1378  	err:       &Error{"Get", "http://google.com/", io.EOF},
  1379  	timeout:   false,
  1380  	temporary: false,
  1381  }}
  1382  
  1383  // Test that url.Error implements net.Error and that it forwards
  1384  func TestURLErrorImplementsNetError(t *testing.T) {
  1385  	for i, tt := range netErrorTests {
  1386  		err, ok := tt.err.(net.Error)
  1387  		if !ok {
  1388  			t.Errorf("%d: %T does not implement net.Error", i+1, tt.err)
  1389  			continue
  1390  		}
  1391  		if err.Timeout() != tt.timeout {
  1392  			t.Errorf("%d: err.Timeout(): want %v, have %v", i+1, tt.timeout, err.Timeout())
  1393  			continue
  1394  		}
  1395  		if err.Temporary() != tt.temporary {
  1396  			t.Errorf("%d: err.Temporary(): want %v, have %v", i+1, tt.temporary, err.Temporary())
  1397  		}
  1398  	}
  1399  }