...
Run Format

Source file src/net/url/url.go

     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 parses URLs and implements query escaping.
     6	package url
     7	
     8	// See RFC 3986. This package generally follows RFC 3986, except where
     9	// it deviates for compatibility reasons. When sending changes, first
    10	// search old issues for history on decisions. Unit tests should also
    11	// contain references to issue numbers with details.
    12	
    13	import (
    14		"bytes"
    15		"errors"
    16		"fmt"
    17		"sort"
    18		"strconv"
    19		"strings"
    20	)
    21	
    22	// Error reports an error and the operation and URL that caused it.
    23	type Error struct {
    24		Op  string
    25		URL string
    26		Err error
    27	}
    28	
    29	func (e *Error) Error() string { return e.Op + " " + e.URL + ": " + e.Err.Error() }
    30	
    31	type timeout interface {
    32		Timeout() bool
    33	}
    34	
    35	func (e *Error) Timeout() bool {
    36		t, ok := e.Err.(timeout)
    37		return ok && t.Timeout()
    38	}
    39	
    40	type temporary interface {
    41		Temporary() bool
    42	}
    43	
    44	func (e *Error) Temporary() bool {
    45		t, ok := e.Err.(temporary)
    46		return ok && t.Temporary()
    47	}
    48	
    49	func ishex(c byte) bool {
    50		switch {
    51		case '0' <= c && c <= '9':
    52			return true
    53		case 'a' <= c && c <= 'f':
    54			return true
    55		case 'A' <= c && c <= 'F':
    56			return true
    57		}
    58		return false
    59	}
    60	
    61	func unhex(c byte) byte {
    62		switch {
    63		case '0' <= c && c <= '9':
    64			return c - '0'
    65		case 'a' <= c && c <= 'f':
    66			return c - 'a' + 10
    67		case 'A' <= c && c <= 'F':
    68			return c - 'A' + 10
    69		}
    70		return 0
    71	}
    72	
    73	type encoding int
    74	
    75	const (
    76		encodePath encoding = 1 + iota
    77		encodeHost
    78		encodeZone
    79		encodeUserPassword
    80		encodeQueryComponent
    81		encodeFragment
    82	)
    83	
    84	type EscapeError string
    85	
    86	func (e EscapeError) Error() string {
    87		return "invalid URL escape " + strconv.Quote(string(e))
    88	}
    89	
    90	type InvalidHostError string
    91	
    92	func (e InvalidHostError) Error() string {
    93		return "invalid character " + strconv.Quote(string(e)) + " in host name"
    94	}
    95	
    96	// Return true if the specified character should be escaped when
    97	// appearing in a URL string, according to RFC 3986.
    98	//
    99	// Please be informed that for now shouldEscape does not check all
   100	// reserved characters correctly. See golang.org/issue/5684.
   101	func shouldEscape(c byte, mode encoding) bool {
   102		// §2.3 Unreserved characters (alphanum)
   103		if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
   104			return false
   105		}
   106	
   107		if mode == encodeHost || mode == encodeZone {
   108			// §3.2.2 Host allows
   109			//	sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
   110			// as part of reg-name.
   111			// We add : because we include :port as part of host.
   112			// We add [ ] because we include [ipv6]:port as part of host.
   113			// We add < > because they're the only characters left that
   114			// we could possibly allow, and Parse will reject them if we
   115			// escape them (because hosts can't use %-encoding for
   116			// ASCII bytes).
   117			switch c {
   118			case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':
   119				return false
   120			}
   121		}
   122	
   123		switch c {
   124		case '-', '_', '.', '~': // §2.3 Unreserved characters (mark)
   125			return false
   126	
   127		case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)
   128			// Different sections of the URL allow a few of
   129			// the reserved characters to appear unescaped.
   130			switch mode {
   131			case encodePath: // §3.3
   132				// The RFC allows : @ & = + $ but saves / ; , for assigning
   133				// meaning to individual path segments. This package
   134				// only manipulates the path as a whole, so we allow those
   135				// last two as well. That leaves only ? to escape.
   136				return c == '?'
   137	
   138			case encodeUserPassword: // §3.2.1
   139				// The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
   140				// userinfo, so we must escape only '@', '/', and '?'.
   141				// The parsing of userinfo treats ':' as special so we must escape
   142				// that too.
   143				return c == '@' || c == '/' || c == '?' || c == ':'
   144	
   145			case encodeQueryComponent: // §3.4
   146				// The RFC reserves (so we must escape) everything.
   147				return true
   148	
   149			case encodeFragment: // §4.1
   150				// The RFC text is silent but the grammar allows
   151				// everything, so escape nothing.
   152				return false
   153			}
   154		}
   155	
   156		// Everything else must be escaped.
   157		return true
   158	}
   159	
   160	// QueryUnescape does the inverse transformation of QueryEscape, converting
   161	// %AB into the byte 0xAB and '+' into ' ' (space). It returns an error if
   162	// any % is not followed by two hexadecimal digits.
   163	func QueryUnescape(s string) (string, error) {
   164		return unescape(s, encodeQueryComponent)
   165	}
   166	
   167	// unescape unescapes a string; the mode specifies
   168	// which section of the URL string is being unescaped.
   169	func unescape(s string, mode encoding) (string, error) {
   170		// Count %, check that they're well-formed.
   171		n := 0
   172		hasPlus := false
   173		for i := 0; i < len(s); {
   174			switch s[i] {
   175			case '%':
   176				n++
   177				if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
   178					s = s[i:]
   179					if len(s) > 3 {
   180						s = s[:3]
   181					}
   182					return "", EscapeError(s)
   183				}
   184				// Per https://tools.ietf.org/html/rfc3986#page-21
   185				// in the host component %-encoding can only be used
   186				// for non-ASCII bytes.
   187				// But https://tools.ietf.org/html/rfc6874#section-2
   188				// introduces %25 being allowed to escape a percent sign
   189				// in IPv6 scoped-address literals. Yay.
   190				if mode == encodeHost && unhex(s[i+1]) < 8 && s[i:i+3] != "%25" {
   191					return "", EscapeError(s[i : i+3])
   192				}
   193				if mode == encodeZone {
   194					// RFC 6874 says basically "anything goes" for zone identifiers
   195					// and that even non-ASCII can be redundantly escaped,
   196					// but it seems prudent to restrict %-escaped bytes here to those
   197					// that are valid host name bytes in their unescaped form.
   198					// That is, you can use escaping in the zone identifier but not
   199					// to introduce bytes you couldn't just write directly.
   200					// But Windows puts spaces here! Yay.
   201					v := unhex(s[i+1])<<4 | unhex(s[i+2])
   202					if s[i:i+3] != "%25" && v != ' ' && shouldEscape(v, encodeHost) {
   203						return "", EscapeError(s[i : i+3])
   204					}
   205				}
   206				i += 3
   207			case '+':
   208				hasPlus = mode == encodeQueryComponent
   209				i++
   210			default:
   211				if (mode == encodeHost || mode == encodeZone) && s[i] < 0x80 && shouldEscape(s[i], mode) {
   212					return "", InvalidHostError(s[i : i+1])
   213				}
   214				i++
   215			}
   216		}
   217	
   218		if n == 0 && !hasPlus {
   219			return s, nil
   220		}
   221	
   222		t := make([]byte, len(s)-2*n)
   223		j := 0
   224		for i := 0; i < len(s); {
   225			switch s[i] {
   226			case '%':
   227				t[j] = unhex(s[i+1])<<4 | unhex(s[i+2])
   228				j++
   229				i += 3
   230			case '+':
   231				if mode == encodeQueryComponent {
   232					t[j] = ' '
   233				} else {
   234					t[j] = '+'
   235				}
   236				j++
   237				i++
   238			default:
   239				t[j] = s[i]
   240				j++
   241				i++
   242			}
   243		}
   244		return string(t), nil
   245	}
   246	
   247	// QueryEscape escapes the string so it can be safely placed
   248	// inside a URL query.
   249	func QueryEscape(s string) string {
   250		return escape(s, encodeQueryComponent)
   251	}
   252	
   253	func escape(s string, mode encoding) string {
   254		spaceCount, hexCount := 0, 0
   255		for i := 0; i < len(s); i++ {
   256			c := s[i]
   257			if shouldEscape(c, mode) {
   258				if c == ' ' && mode == encodeQueryComponent {
   259					spaceCount++
   260				} else {
   261					hexCount++
   262				}
   263			}
   264		}
   265	
   266		if spaceCount == 0 && hexCount == 0 {
   267			return s
   268		}
   269	
   270		t := make([]byte, len(s)+2*hexCount)
   271		j := 0
   272		for i := 0; i < len(s); i++ {
   273			switch c := s[i]; {
   274			case c == ' ' && mode == encodeQueryComponent:
   275				t[j] = '+'
   276				j++
   277			case shouldEscape(c, mode):
   278				t[j] = '%'
   279				t[j+1] = "0123456789ABCDEF"[c>>4]
   280				t[j+2] = "0123456789ABCDEF"[c&15]
   281				j += 3
   282			default:
   283				t[j] = s[i]
   284				j++
   285			}
   286		}
   287		return string(t)
   288	}
   289	
   290	// A URL represents a parsed URL (technically, a URI reference).
   291	// The general form represented is:
   292	//
   293	//	scheme://[userinfo@]host/path[?query][#fragment]
   294	//
   295	// URLs that do not start with a slash after the scheme are interpreted as:
   296	//
   297	//	scheme:opaque[?query][#fragment]
   298	//
   299	// Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.
   300	// A consequence is that it is impossible to tell which slashes in the Path were
   301	// slashes in the raw URL and which were %2f. This distinction is rarely important,
   302	// but when it is, code must not use Path directly.
   303	//
   304	// Go 1.5 introduced the RawPath field to hold the encoded form of Path.
   305	// The Parse function sets both Path and RawPath in the URL it returns,
   306	// and URL's String method uses RawPath if it is a valid encoding of Path,
   307	// by calling the EscapedPath method.
   308	//
   309	// In earlier versions of Go, the more indirect workarounds were that an
   310	// HTTP server could consult req.RequestURI and an HTTP client could
   311	// construct a URL struct directly and set the Opaque field instead of Path.
   312	// These still work as well.
   313	type URL struct {
   314		Scheme     string
   315		Opaque     string    // encoded opaque data
   316		User       *Userinfo // username and password information
   317		Host       string    // host or host:port
   318		Path       string
   319		RawPath    string // encoded path hint (Go 1.5 and later only; see EscapedPath method)
   320		ForceQuery bool   // append a query ('?') even if RawQuery is empty
   321		RawQuery   string // encoded query values, without '?'
   322		Fragment   string // fragment for references, without '#'
   323	}
   324	
   325	// User returns a Userinfo containing the provided username
   326	// and no password set.
   327	func User(username string) *Userinfo {
   328		return &Userinfo{username, "", false}
   329	}
   330	
   331	// UserPassword returns a Userinfo containing the provided username
   332	// and password.
   333	// This functionality should only be used with legacy web sites.
   334	// RFC 2396 warns that interpreting Userinfo this way
   335	// ``is NOT RECOMMENDED, because the passing of authentication
   336	// information in clear text (such as URI) has proven to be a
   337	// security risk in almost every case where it has been used.''
   338	func UserPassword(username, password string) *Userinfo {
   339		return &Userinfo{username, password, true}
   340	}
   341	
   342	// The Userinfo type is an immutable encapsulation of username and
   343	// password details for a URL. An existing Userinfo value is guaranteed
   344	// to have a username set (potentially empty, as allowed by RFC 2396),
   345	// and optionally a password.
   346	type Userinfo struct {
   347		username    string
   348		password    string
   349		passwordSet bool
   350	}
   351	
   352	// Username returns the username.
   353	func (u *Userinfo) Username() string {
   354		return u.username
   355	}
   356	
   357	// Password returns the password in case it is set, and whether it is set.
   358	func (u *Userinfo) Password() (string, bool) {
   359		if u.passwordSet {
   360			return u.password, true
   361		}
   362		return "", false
   363	}
   364	
   365	// String returns the encoded userinfo information in the standard form
   366	// of "username[:password]".
   367	func (u *Userinfo) String() string {
   368		s := escape(u.username, encodeUserPassword)
   369		if u.passwordSet {
   370			s += ":" + escape(u.password, encodeUserPassword)
   371		}
   372		return s
   373	}
   374	
   375	// Maybe rawurl is of the form scheme:path.
   376	// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*)
   377	// If so, return scheme, path; else return "", rawurl.
   378	func getscheme(rawurl string) (scheme, path string, err error) {
   379		for i := 0; i < len(rawurl); i++ {
   380			c := rawurl[i]
   381			switch {
   382			case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
   383			// do nothing
   384			case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
   385				if i == 0 {
   386					return "", rawurl, nil
   387				}
   388			case c == ':':
   389				if i == 0 {
   390					return "", "", errors.New("missing protocol scheme")
   391				}
   392				return rawurl[:i], rawurl[i+1:], nil
   393			default:
   394				// we have encountered an invalid character,
   395				// so there is no valid scheme
   396				return "", rawurl, nil
   397			}
   398		}
   399		return "", rawurl, nil
   400	}
   401	
   402	// Maybe s is of the form t c u.
   403	// If so, return t, c u (or t, u if cutc == true).
   404	// If not, return s, "".
   405	func split(s string, c string, cutc bool) (string, string) {
   406		i := strings.Index(s, c)
   407		if i < 0 {
   408			return s, ""
   409		}
   410		if cutc {
   411			return s[:i], s[i+len(c):]
   412		}
   413		return s[:i], s[i:]
   414	}
   415	
   416	// Parse parses rawurl into a URL structure.
   417	// The rawurl may be relative or absolute.
   418	func Parse(rawurl string) (*URL, error) {
   419		// Cut off #frag
   420		u, frag := split(rawurl, "#", true)
   421		url, err := parse(u, false)
   422		if err != nil {
   423			return nil, err
   424		}
   425		if frag == "" {
   426			return url, nil
   427		}
   428		if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
   429			return nil, &Error{"parse", rawurl, err}
   430		}
   431		return url, nil
   432	}
   433	
   434	// ParseRequestURI parses rawurl into a URL structure. It assumes that
   435	// rawurl was received in an HTTP request, so the rawurl is interpreted
   436	// only as an absolute URI or an absolute path.
   437	// The string rawurl is assumed not to have a #fragment suffix.
   438	// (Web browsers strip #fragment before sending the URL to a web server.)
   439	func ParseRequestURI(rawurl string) (*URL, error) {
   440		return parse(rawurl, true)
   441	}
   442	
   443	// parse parses a URL from a string in one of two contexts. If
   444	// viaRequest is true, the URL is assumed to have arrived via an HTTP request,
   445	// in which case only absolute URLs or path-absolute relative URLs are allowed.
   446	// If viaRequest is false, all forms of relative URLs are allowed.
   447	func parse(rawurl string, viaRequest bool) (url *URL, err error) {
   448		var rest string
   449	
   450		if rawurl == "" && viaRequest {
   451			err = errors.New("empty url")
   452			goto Error
   453		}
   454		url = new(URL)
   455	
   456		if rawurl == "*" {
   457			url.Path = "*"
   458			return
   459		}
   460	
   461		// Split off possible leading "http:", "mailto:", etc.
   462		// Cannot contain escaped characters.
   463		if url.Scheme, rest, err = getscheme(rawurl); err != nil {
   464			goto Error
   465		}
   466		url.Scheme = strings.ToLower(url.Scheme)
   467	
   468		if strings.HasSuffix(rest, "?") && strings.Count(rest, "?") == 1 {
   469			url.ForceQuery = true
   470			rest = rest[:len(rest)-1]
   471		} else {
   472			rest, url.RawQuery = split(rest, "?", true)
   473		}
   474	
   475		if !strings.HasPrefix(rest, "/") {
   476			if url.Scheme != "" {
   477				// We consider rootless paths per RFC 3986 as opaque.
   478				url.Opaque = rest
   479				return url, nil
   480			}
   481			if viaRequest {
   482				err = errors.New("invalid URI for request")
   483				goto Error
   484			}
   485		}
   486	
   487		if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") {
   488			var authority string
   489			authority, rest = split(rest[2:], "/", false)
   490			url.User, url.Host, err = parseAuthority(authority)
   491			if err != nil {
   492				goto Error
   493			}
   494		}
   495		if url.Path, err = unescape(rest, encodePath); err != nil {
   496			goto Error
   497		}
   498		// RawPath is a hint as to the encoding of Path to use
   499		// in url.EscapedPath. If that method already gets the
   500		// right answer without RawPath, leave it empty.
   501		// This will help make sure that people don't rely on it in general.
   502		if url.EscapedPath() != rest && validEncodedPath(rest) {
   503			url.RawPath = rest
   504		}
   505		return url, nil
   506	
   507	Error:
   508		return nil, &Error{"parse", rawurl, err}
   509	}
   510	
   511	func parseAuthority(authority string) (user *Userinfo, host string, err error) {
   512		i := strings.LastIndex(authority, "@")
   513		if i < 0 {
   514			host, err = parseHost(authority)
   515		} else {
   516			host, err = parseHost(authority[i+1:])
   517		}
   518		if err != nil {
   519			return nil, "", err
   520		}
   521		if i < 0 {
   522			return nil, host, nil
   523		}
   524		userinfo := authority[:i]
   525		if !strings.Contains(userinfo, ":") {
   526			if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
   527				return nil, "", err
   528			}
   529			user = User(userinfo)
   530		} else {
   531			username, password := split(userinfo, ":", true)
   532			if username, err = unescape(username, encodeUserPassword); err != nil {
   533				return nil, "", err
   534			}
   535			if password, err = unescape(password, encodeUserPassword); err != nil {
   536				return nil, "", err
   537			}
   538			user = UserPassword(username, password)
   539		}
   540		return user, host, nil
   541	}
   542	
   543	// parseHost parses host as an authority without user
   544	// information. That is, as host[:port].
   545	func parseHost(host string) (string, error) {
   546		if strings.HasPrefix(host, "[") {
   547			// Parse an IP-Literal in RFC 3986 and RFC 6874.
   548			// E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80".
   549			i := strings.LastIndex(host, "]")
   550			if i < 0 {
   551				return "", errors.New("missing ']' in host")
   552			}
   553			colonPort := host[i+1:]
   554			if !validOptionalPort(colonPort) {
   555				return "", fmt.Errorf("invalid port %q after host", colonPort)
   556			}
   557	
   558			// RFC 6874 defines that %25 (%-encoded percent) introduces
   559			// the zone identifier, and the zone identifier can use basically
   560			// any %-encoding it likes. That's different from the host, which
   561			// can only %-encode non-ASCII bytes.
   562			// We do impose some restrictions on the zone, to avoid stupidity
   563			// like newlines.
   564			zone := strings.Index(host[:i], "%25")
   565			if zone >= 0 {
   566				host1, err := unescape(host[:zone], encodeHost)
   567				if err != nil {
   568					return "", err
   569				}
   570				host2, err := unescape(host[zone:i], encodeZone)
   571				if err != nil {
   572					return "", err
   573				}
   574				host3, err := unescape(host[i:], encodeHost)
   575				if err != nil {
   576					return "", err
   577				}
   578				return host1 + host2 + host3, nil
   579			}
   580		}
   581	
   582		var err error
   583		if host, err = unescape(host, encodeHost); err != nil {
   584			return "", err
   585		}
   586		return host, nil
   587	}
   588	
   589	// EscapedPath returns the escaped form of u.Path.
   590	// In general there are multiple possible escaped forms of any path.
   591	// EscapedPath returns u.RawPath when it is a valid escaping of u.Path.
   592	// Otherwise EscapedPath ignores u.RawPath and computes an escaped
   593	// form on its own.
   594	// The String and RequestURI methods use EscapedPath to construct
   595	// their results.
   596	// In general, code should call EscapedPath instead of
   597	// reading u.RawPath directly.
   598	func (u *URL) EscapedPath() string {
   599		if u.RawPath != "" && validEncodedPath(u.RawPath) {
   600			p, err := unescape(u.RawPath, encodePath)
   601			if err == nil && p == u.Path {
   602				return u.RawPath
   603			}
   604		}
   605		if u.Path == "*" {
   606			return "*" // don't escape (Issue 11202)
   607		}
   608		return escape(u.Path, encodePath)
   609	}
   610	
   611	// validEncodedPath reports whether s is a valid encoded path.
   612	// It must not contain any bytes that require escaping during path encoding.
   613	func validEncodedPath(s string) bool {
   614		for i := 0; i < len(s); i++ {
   615			// RFC 3986, Appendix A.
   616			// pchar = unreserved / pct-encoded / sub-delims / ":" / "@".
   617			// shouldEscape is not quite compliant with the RFC,
   618			// so we check the sub-delims ourselves and let
   619			// shouldEscape handle the others.
   620			switch s[i] {
   621			case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '@':
   622				// ok
   623			case '[', ']':
   624				// ok - not specified in RFC 3986 but left alone by modern browsers
   625			case '%':
   626				// ok - percent encoded, will decode
   627			default:
   628				if shouldEscape(s[i], encodePath) {
   629					return false
   630				}
   631			}
   632		}
   633		return true
   634	}
   635	
   636	// validOptionalPort reports whether port is either an empty string
   637	// or matches /^:\d*$/
   638	func validOptionalPort(port string) bool {
   639		if port == "" {
   640			return true
   641		}
   642		if port[0] != ':' {
   643			return false
   644		}
   645		for _, b := range port[1:] {
   646			if b < '0' || b > '9' {
   647				return false
   648			}
   649		}
   650		return true
   651	}
   652	
   653	// String reassembles the URL into a valid URL string.
   654	// The general form of the result is one of:
   655	//
   656	//	scheme:opaque?query#fragment
   657	//	scheme://userinfo@host/path?query#fragment
   658	//
   659	// If u.Opaque is non-empty, String uses the first form;
   660	// otherwise it uses the second form.
   661	// To obtain the path, String uses u.EscapedPath().
   662	//
   663	// In the second form, the following rules apply:
   664	//	- if u.Scheme is empty, scheme: is omitted.
   665	//	- if u.User is nil, userinfo@ is omitted.
   666	//	- if u.Host is empty, host/ is omitted.
   667	//	- if u.Scheme and u.Host are empty and u.User is nil,
   668	//	   the entire scheme://userinfo@host/ is omitted.
   669	//	- if u.Host is non-empty and u.Path begins with a /,
   670	//	   the form host/path does not add its own /.
   671	//	- if u.RawQuery is empty, ?query is omitted.
   672	//	- if u.Fragment is empty, #fragment is omitted.
   673	func (u *URL) String() string {
   674		var buf bytes.Buffer
   675		if u.Scheme != "" {
   676			buf.WriteString(u.Scheme)
   677			buf.WriteByte(':')
   678		}
   679		if u.Opaque != "" {
   680			buf.WriteString(u.Opaque)
   681		} else {
   682			if u.Scheme != "" || u.Host != "" || u.User != nil {
   683				buf.WriteString("//")
   684				if ui := u.User; ui != nil {
   685					buf.WriteString(ui.String())
   686					buf.WriteByte('@')
   687				}
   688				if h := u.Host; h != "" {
   689					buf.WriteString(escape(h, encodeHost))
   690				}
   691			}
   692			path := u.EscapedPath()
   693			if path != "" && path[0] != '/' && u.Host != "" {
   694				buf.WriteByte('/')
   695			}
   696			buf.WriteString(path)
   697		}
   698		if u.ForceQuery || u.RawQuery != "" {
   699			buf.WriteByte('?')
   700			buf.WriteString(u.RawQuery)
   701		}
   702		if u.Fragment != "" {
   703			buf.WriteByte('#')
   704			buf.WriteString(escape(u.Fragment, encodeFragment))
   705		}
   706		return buf.String()
   707	}
   708	
   709	// Values maps a string key to a list of values.
   710	// It is typically used for query parameters and form values.
   711	// Unlike in the http.Header map, the keys in a Values map
   712	// are case-sensitive.
   713	type Values map[string][]string
   714	
   715	// Get gets the first value associated with the given key.
   716	// If there are no values associated with the key, Get returns
   717	// the empty string. To access multiple values, use the map
   718	// directly.
   719	func (v Values) Get(key string) string {
   720		if v == nil {
   721			return ""
   722		}
   723		vs := v[key]
   724		if len(vs) == 0 {
   725			return ""
   726		}
   727		return vs[0]
   728	}
   729	
   730	// Set sets the key to value. It replaces any existing
   731	// values.
   732	func (v Values) Set(key, value string) {
   733		v[key] = []string{value}
   734	}
   735	
   736	// Add adds the value to key. It appends to any existing
   737	// values associated with key.
   738	func (v Values) Add(key, value string) {
   739		v[key] = append(v[key], value)
   740	}
   741	
   742	// Del deletes the values associated with key.
   743	func (v Values) Del(key string) {
   744		delete(v, key)
   745	}
   746	
   747	// ParseQuery parses the URL-encoded query string and returns
   748	// a map listing the values specified for each key.
   749	// ParseQuery always returns a non-nil map containing all the
   750	// valid query parameters found; err describes the first decoding error
   751	// encountered, if any.
   752	func ParseQuery(query string) (Values, error) {
   753		m := make(Values)
   754		err := parseQuery(m, query)
   755		return m, err
   756	}
   757	
   758	func parseQuery(m Values, query string) (err error) {
   759		for query != "" {
   760			key := query
   761			if i := strings.IndexAny(key, "&;"); i >= 0 {
   762				key, query = key[:i], key[i+1:]
   763			} else {
   764				query = ""
   765			}
   766			if key == "" {
   767				continue
   768			}
   769			value := ""
   770			if i := strings.Index(key, "="); i >= 0 {
   771				key, value = key[:i], key[i+1:]
   772			}
   773			key, err1 := QueryUnescape(key)
   774			if err1 != nil {
   775				if err == nil {
   776					err = err1
   777				}
   778				continue
   779			}
   780			value, err1 = QueryUnescape(value)
   781			if err1 != nil {
   782				if err == nil {
   783					err = err1
   784				}
   785				continue
   786			}
   787			m[key] = append(m[key], value)
   788		}
   789		return err
   790	}
   791	
   792	// Encode encodes the values into ``URL encoded'' form
   793	// ("bar=baz&foo=quux") sorted by key.
   794	func (v Values) Encode() string {
   795		if v == nil {
   796			return ""
   797		}
   798		var buf bytes.Buffer
   799		keys := make([]string, 0, len(v))
   800		for k := range v {
   801			keys = append(keys, k)
   802		}
   803		sort.Strings(keys)
   804		for _, k := range keys {
   805			vs := v[k]
   806			prefix := QueryEscape(k) + "="
   807			for _, v := range vs {
   808				if buf.Len() > 0 {
   809					buf.WriteByte('&')
   810				}
   811				buf.WriteString(prefix)
   812				buf.WriteString(QueryEscape(v))
   813			}
   814		}
   815		return buf.String()
   816	}
   817	
   818	// resolvePath applies special path segments from refs and applies
   819	// them to base, per RFC 3986.
   820	func resolvePath(base, ref string) string {
   821		var full string
   822		if ref == "" {
   823			full = base
   824		} else if ref[0] != '/' {
   825			i := strings.LastIndex(base, "/")
   826			full = base[:i+1] + ref
   827		} else {
   828			full = ref
   829		}
   830		if full == "" {
   831			return ""
   832		}
   833		var dst []string
   834		src := strings.Split(full, "/")
   835		for _, elem := range src {
   836			switch elem {
   837			case ".":
   838				// drop
   839			case "..":
   840				if len(dst) > 0 {
   841					dst = dst[:len(dst)-1]
   842				}
   843			default:
   844				dst = append(dst, elem)
   845			}
   846		}
   847		if last := src[len(src)-1]; last == "." || last == ".." {
   848			// Add final slash to the joined path.
   849			dst = append(dst, "")
   850		}
   851		return "/" + strings.TrimLeft(strings.Join(dst, "/"), "/")
   852	}
   853	
   854	// IsAbs reports whether the URL is absolute.
   855	func (u *URL) IsAbs() bool {
   856		return u.Scheme != ""
   857	}
   858	
   859	// Parse parses a URL in the context of the receiver. The provided URL
   860	// may be relative or absolute. Parse returns nil, err on parse
   861	// failure, otherwise its return value is the same as ResolveReference.
   862	func (u *URL) Parse(ref string) (*URL, error) {
   863		refurl, err := Parse(ref)
   864		if err != nil {
   865			return nil, err
   866		}
   867		return u.ResolveReference(refurl), nil
   868	}
   869	
   870	// ResolveReference resolves a URI reference to an absolute URI from
   871	// an absolute base URI, per RFC 3986 Section 5.2.  The URI reference
   872	// may be relative or absolute. ResolveReference always returns a new
   873	// URL instance, even if the returned URL is identical to either the
   874	// base or reference. If ref is an absolute URL, then ResolveReference
   875	// ignores base and returns a copy of ref.
   876	func (u *URL) ResolveReference(ref *URL) *URL {
   877		url := *ref
   878		if ref.Scheme == "" {
   879			url.Scheme = u.Scheme
   880		}
   881		if ref.Scheme != "" || ref.Host != "" || ref.User != nil {
   882			// The "absoluteURI" or "net_path" cases.
   883			url.Path = resolvePath(ref.Path, "")
   884			return &url
   885		}
   886		if ref.Opaque != "" {
   887			url.User = nil
   888			url.Host = ""
   889			url.Path = ""
   890			return &url
   891		}
   892		if ref.Path == "" {
   893			if ref.RawQuery == "" {
   894				url.RawQuery = u.RawQuery
   895				if ref.Fragment == "" {
   896					url.Fragment = u.Fragment
   897				}
   898			}
   899		}
   900		// The "abs_path" or "rel_path" cases.
   901		url.Host = u.Host
   902		url.User = u.User
   903		url.Path = resolvePath(u.Path, ref.Path)
   904		return &url
   905	}
   906	
   907	// Query parses RawQuery and returns the corresponding values.
   908	func (u *URL) Query() Values {
   909		v, _ := ParseQuery(u.RawQuery)
   910		return v
   911	}
   912	
   913	// RequestURI returns the encoded path?query or opaque?query
   914	// string that would be used in an HTTP request for u.
   915	func (u *URL) RequestURI() string {
   916		result := u.Opaque
   917		if result == "" {
   918			result = u.EscapedPath()
   919			if result == "" {
   920				result = "/"
   921			}
   922		} else {
   923			if strings.HasPrefix(result, "//") {
   924				result = u.Scheme + ":" + result
   925			}
   926		}
   927		if u.ForceQuery || u.RawQuery != "" {
   928			result += "?" + u.RawQuery
   929		}
   930		return result
   931	}
   932	

View as plain text