github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/net/url/url.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 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 }