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