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