github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/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 v := unhex(s[i+1])<<4 | unhex(s[i+2]) 197 if s[i:i+3] != "%25" && shouldEscape(v, encodeHost) { 198 return "", EscapeError(s[i : i+3]) 199 } 200 } 201 i += 3 202 case '+': 203 hasPlus = mode == encodeQueryComponent 204 i++ 205 default: 206 if (mode == encodeHost || mode == encodeZone) && s[i] < 0x80 && shouldEscape(s[i], mode) { 207 return "", InvalidHostError(s[i : i+1]) 208 } 209 i++ 210 } 211 } 212 213 if n == 0 && !hasPlus { 214 return s, nil 215 } 216 217 t := make([]byte, len(s)-2*n) 218 j := 0 219 for i := 0; i < len(s); { 220 switch s[i] { 221 case '%': 222 t[j] = unhex(s[i+1])<<4 | unhex(s[i+2]) 223 j++ 224 i += 3 225 case '+': 226 if mode == encodeQueryComponent { 227 t[j] = ' ' 228 } else { 229 t[j] = '+' 230 } 231 j++ 232 i++ 233 default: 234 t[j] = s[i] 235 j++ 236 i++ 237 } 238 } 239 return string(t), nil 240 } 241 242 // QueryEscape escapes the string so it can be safely placed 243 // inside a URL query. 244 func QueryEscape(s string) string { 245 return escape(s, encodeQueryComponent) 246 } 247 248 func escape(s string, mode encoding) string { 249 spaceCount, hexCount := 0, 0 250 for i := 0; i < len(s); i++ { 251 c := s[i] 252 if shouldEscape(c, mode) { 253 if c == ' ' && mode == encodeQueryComponent { 254 spaceCount++ 255 } else { 256 hexCount++ 257 } 258 } 259 } 260 261 if spaceCount == 0 && hexCount == 0 { 262 return s 263 } 264 265 t := make([]byte, len(s)+2*hexCount) 266 j := 0 267 for i := 0; i < len(s); i++ { 268 switch c := s[i]; { 269 case c == ' ' && mode == encodeQueryComponent: 270 t[j] = '+' 271 j++ 272 case shouldEscape(c, mode): 273 t[j] = '%' 274 t[j+1] = "0123456789ABCDEF"[c>>4] 275 t[j+2] = "0123456789ABCDEF"[c&15] 276 j += 3 277 default: 278 t[j] = s[i] 279 j++ 280 } 281 } 282 return string(t) 283 } 284 285 // A URL represents a parsed URL (technically, a URI reference). 286 // The general form represented is: 287 // 288 // scheme://[userinfo@]host/path[?query][#fragment] 289 // 290 // URLs that do not start with a slash after the scheme are interpreted as: 291 // 292 // scheme:opaque[?query][#fragment] 293 // 294 // Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/. 295 // A consequence is that it is impossible to tell which slashes in the Path were 296 // slashes in the raw URL and which were %2f. This distinction is rarely important, 297 // but when it is, code must not use Path directly. 298 // 299 // Go 1.5 introduced the RawPath field to hold the encoded form of Path. 300 // The Parse function sets both Path and RawPath in the URL it returns, 301 // and URL's String method uses RawPath if it is a valid encoding of Path, 302 // by calling the EscapedPath method. 303 // 304 // In earlier versions of Go, the more indirect workarounds were that an 305 // HTTP server could consult req.RequestURI and an HTTP client could 306 // construct a URL struct directly and set the Opaque field instead of Path. 307 // These still work as well. 308 type URL struct { 309 Scheme string 310 Opaque string // encoded opaque data 311 User *Userinfo // username and password information 312 Host string // host or host:port 313 Path string 314 RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method) 315 RawQuery string // encoded query values, without '?' 316 Fragment string // fragment for references, without '#' 317 } 318 319 // User returns a Userinfo containing the provided username 320 // and no password set. 321 func User(username string) *Userinfo { 322 return &Userinfo{username, "", false} 323 } 324 325 // UserPassword returns a Userinfo containing the provided username 326 // and password. 327 // This functionality should only be used with legacy web sites. 328 // RFC 2396 warns that interpreting Userinfo this way 329 // ``is NOT RECOMMENDED, because the passing of authentication 330 // information in clear text (such as URI) has proven to be a 331 // security risk in almost every case where it has been used.'' 332 func UserPassword(username, password string) *Userinfo { 333 return &Userinfo{username, password, true} 334 } 335 336 // The Userinfo type is an immutable encapsulation of username and 337 // password details for a URL. An existing Userinfo value is guaranteed 338 // to have a username set (potentially empty, as allowed by RFC 2396), 339 // and optionally a password. 340 type Userinfo struct { 341 username string 342 password string 343 passwordSet bool 344 } 345 346 // Username returns the username. 347 func (u *Userinfo) Username() string { 348 return u.username 349 } 350 351 // Password returns the password in case it is set, and whether it is set. 352 func (u *Userinfo) Password() (string, bool) { 353 if u.passwordSet { 354 return u.password, true 355 } 356 return "", false 357 } 358 359 // String returns the encoded userinfo information in the standard form 360 // of "username[:password]". 361 func (u *Userinfo) String() string { 362 s := escape(u.username, encodeUserPassword) 363 if u.passwordSet { 364 s += ":" + escape(u.password, encodeUserPassword) 365 } 366 return s 367 } 368 369 // Maybe rawurl is of the form scheme:path. 370 // (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*) 371 // If so, return scheme, path; else return "", rawurl. 372 func getscheme(rawurl string) (scheme, path string, err error) { 373 for i := 0; i < len(rawurl); i++ { 374 c := rawurl[i] 375 switch { 376 case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': 377 // do nothing 378 case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.': 379 if i == 0 { 380 return "", rawurl, nil 381 } 382 case c == ':': 383 if i == 0 { 384 return "", "", errors.New("missing protocol scheme") 385 } 386 return rawurl[:i], rawurl[i+1:], nil 387 default: 388 // we have encountered an invalid character, 389 // so there is no valid scheme 390 return "", rawurl, nil 391 } 392 } 393 return "", rawurl, nil 394 } 395 396 // Maybe s is of the form t c u. 397 // If so, return t, c u (or t, u if cutc == true). 398 // If not, return s, "". 399 func split(s string, c string, cutc bool) (string, string) { 400 i := strings.Index(s, c) 401 if i < 0 { 402 return s, "" 403 } 404 if cutc { 405 return s[:i], s[i+len(c):] 406 } 407 return s[:i], s[i:] 408 } 409 410 // Parse parses rawurl into a URL structure. 411 // The rawurl may be relative or absolute. 412 func Parse(rawurl string) (url *URL, err error) { 413 // Cut off #frag 414 u, frag := split(rawurl, "#", true) 415 if url, err = parse(u, false); err != nil { 416 return nil, err 417 } 418 if frag == "" { 419 return url, nil 420 } 421 if url.Fragment, err = unescape(frag, encodeFragment); err != nil { 422 return nil, &Error{"parse", rawurl, err} 423 } 424 return url, nil 425 } 426 427 // ParseRequestURI parses rawurl into a URL structure. It assumes that 428 // rawurl was received in an HTTP request, so the rawurl is interpreted 429 // only as an absolute URI or an absolute path. 430 // The string rawurl is assumed not to have a #fragment suffix. 431 // (Web browsers strip #fragment before sending the URL to a web server.) 432 func ParseRequestURI(rawurl string) (url *URL, err error) { 433 return parse(rawurl, true) 434 } 435 436 // parse parses a URL from a string in one of two contexts. If 437 // viaRequest is true, the URL is assumed to have arrived via an HTTP request, 438 // in which case only absolute URLs or path-absolute relative URLs are allowed. 439 // If viaRequest is false, all forms of relative URLs are allowed. 440 func parse(rawurl string, viaRequest bool) (url *URL, err error) { 441 var rest string 442 443 if rawurl == "" && viaRequest { 444 err = errors.New("empty url") 445 goto Error 446 } 447 url = new(URL) 448 449 if rawurl == "*" { 450 url.Path = "*" 451 return 452 } 453 454 // Split off possible leading "http:", "mailto:", etc. 455 // Cannot contain escaped characters. 456 if url.Scheme, rest, err = getscheme(rawurl); err != nil { 457 goto Error 458 } 459 url.Scheme = strings.ToLower(url.Scheme) 460 461 rest, url.RawQuery = split(rest, "?", true) 462 463 if !strings.HasPrefix(rest, "/") { 464 if url.Scheme != "" { 465 // We consider rootless paths per RFC 3986 as opaque. 466 url.Opaque = rest 467 return url, nil 468 } 469 if viaRequest { 470 err = errors.New("invalid URI for request") 471 goto Error 472 } 473 } 474 475 if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") { 476 var authority string 477 authority, rest = split(rest[2:], "/", false) 478 url.User, url.Host, err = parseAuthority(authority) 479 if err != nil { 480 goto Error 481 } 482 } 483 if url.Path, err = unescape(rest, encodePath); err != nil { 484 goto Error 485 } 486 // RawPath is a hint as to the encoding of Path to use 487 // in url.EscapedPath. If that method already gets the 488 // right answer without RawPath, leave it empty. 489 // This will help make sure that people don't rely on it in general. 490 if url.EscapedPath() != rest && validEncodedPath(rest) { 491 url.RawPath = rest 492 } 493 return url, nil 494 495 Error: 496 return nil, &Error{"parse", rawurl, err} 497 } 498 499 func parseAuthority(authority string) (user *Userinfo, host string, err error) { 500 i := strings.LastIndex(authority, "@") 501 if i < 0 { 502 host, err = parseHost(authority) 503 } else { 504 host, err = parseHost(authority[i+1:]) 505 } 506 if err != nil { 507 return nil, "", err 508 } 509 if i < 0 { 510 return nil, host, nil 511 } 512 userinfo := authority[:i] 513 if strings.Index(userinfo, ":") < 0 { 514 if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil { 515 return nil, "", err 516 } 517 user = User(userinfo) 518 } else { 519 username, password := split(userinfo, ":", true) 520 if username, err = unescape(username, encodeUserPassword); err != nil { 521 return nil, "", err 522 } 523 if password, err = unescape(password, encodeUserPassword); err != nil { 524 return nil, "", err 525 } 526 user = UserPassword(username, password) 527 } 528 return user, host, nil 529 } 530 531 // parseHost parses host as an authority without user 532 // information. That is, as host[:port]. 533 func parseHost(host string) (string, error) { 534 if strings.HasPrefix(host, "[") { 535 // Parse an IP-Literal in RFC 3986 and RFC 6874. 536 // E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80". 537 i := strings.LastIndex(host, "]") 538 if i < 0 { 539 return "", errors.New("missing ']' in host") 540 } 541 colonPort := host[i+1:] 542 if !validOptionalPort(colonPort) { 543 return "", fmt.Errorf("invalid port %q after host", colonPort) 544 } 545 546 // RFC 6874 defines that %25 (%-encoded percent) introduces 547 // the zone identifier, and the zone identifier can use basically 548 // any %-encoding it likes. That's different from the host, which 549 // can only %-encode non-ASCII bytes. 550 // We do impose some restrictions on the zone, to avoid stupidity 551 // like newlines. 552 zone := strings.Index(host[:i], "%25") 553 if zone >= 0 { 554 host1, err := unescape(host[:zone], encodeHost) 555 if err != nil { 556 return "", err 557 } 558 host2, err := unescape(host[zone:i], encodeZone) 559 if err != nil { 560 return "", err 561 } 562 host3, err := unescape(host[i:], encodeHost) 563 if err != nil { 564 return "", err 565 } 566 return host1 + host2 + host3, nil 567 } 568 } 569 570 var err error 571 if host, err = unescape(host, encodeHost); err != nil { 572 return "", err 573 } 574 return host, nil 575 } 576 577 // EscapedPath returns the escaped form of u.Path. 578 // In general there are multiple possible escaped forms of any path. 579 // EscapedPath returns u.RawPath when it is a valid escaping of u.Path. 580 // Otherwise EscapedPath ignores u.RawPath and computes an escaped 581 // form on its own. 582 // The String and RequestURI methods use EscapedPath to construct 583 // their results. 584 // In general, code should call EscapedPath instead of 585 // reading u.RawPath directly. 586 func (u *URL) EscapedPath() string { 587 if u.RawPath != "" && validEncodedPath(u.RawPath) { 588 p, err := unescape(u.RawPath, encodePath) 589 if err == nil && p == u.Path { 590 return u.RawPath 591 } 592 } 593 if u.Path == "*" { 594 return "*" // don't escape (Issue 11202) 595 } 596 return escape(u.Path, encodePath) 597 } 598 599 // validEncodedPath reports whether s is a valid encoded path. 600 // It must not contain any bytes that require escaping during path encoding. 601 func validEncodedPath(s string) bool { 602 for i := 0; i < len(s); i++ { 603 // RFC 3986, Appendix A. 604 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@". 605 // shouldEscape is not quite compliant with the RFC, 606 // so we check the sub-delims ourselves and let 607 // shouldEscape handle the others. 608 switch s[i] { 609 case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '@': 610 // ok 611 case '[', ']': 612 // ok - not specified in RFC 3986 but left alone by modern browsers 613 case '%': 614 // ok - percent encoded, will decode 615 default: 616 if shouldEscape(s[i], encodePath) { 617 return false 618 } 619 } 620 } 621 return true 622 } 623 624 // validOptionalPort reports whether port is either an empty string 625 // or matches /^:\d*$/ 626 func validOptionalPort(port string) bool { 627 if port == "" { 628 return true 629 } 630 if port[0] != ':' { 631 return false 632 } 633 for _, b := range port[1:] { 634 if b < '0' || b > '9' { 635 return false 636 } 637 } 638 return true 639 } 640 641 // String reassembles the URL into a valid URL string. 642 // The general form of the result is one of: 643 // 644 // scheme:opaque?query#fragment 645 // scheme://userinfo@host/path?query#fragment 646 // 647 // If u.Opaque is non-empty, String uses the first form; 648 // otherwise it uses the second form. 649 // To obtain the path, String uses u.EscapedPath(). 650 // 651 // In the second form, the following rules apply: 652 // - if u.Scheme is empty, scheme: is omitted. 653 // - if u.User is nil, userinfo@ is omitted. 654 // - if u.Host is empty, host/ is omitted. 655 // - if u.Scheme and u.Host are empty and u.User is nil, 656 // the entire scheme://userinfo@host/ is omitted. 657 // - if u.Host is non-empty and u.Path begins with a /, 658 // the form host/path does not add its own /. 659 // - if u.RawQuery is empty, ?query is omitted. 660 // - if u.Fragment is empty, #fragment is omitted. 661 func (u *URL) String() string { 662 var buf bytes.Buffer 663 if u.Scheme != "" { 664 buf.WriteString(u.Scheme) 665 buf.WriteByte(':') 666 } 667 if u.Opaque != "" { 668 buf.WriteString(u.Opaque) 669 } else { 670 if u.Scheme != "" || u.Host != "" || u.User != nil { 671 buf.WriteString("//") 672 if ui := u.User; ui != nil { 673 buf.WriteString(ui.String()) 674 buf.WriteByte('@') 675 } 676 if h := u.Host; h != "" { 677 buf.WriteString(escape(h, encodeHost)) 678 } 679 } 680 path := u.EscapedPath() 681 if path != "" && path[0] != '/' && u.Host != "" { 682 buf.WriteByte('/') 683 } 684 buf.WriteString(path) 685 } 686 if u.RawQuery != "" { 687 buf.WriteByte('?') 688 buf.WriteString(u.RawQuery) 689 } 690 if u.Fragment != "" { 691 buf.WriteByte('#') 692 buf.WriteString(escape(u.Fragment, encodeFragment)) 693 } 694 return buf.String() 695 } 696 697 // Values maps a string key to a list of values. 698 // It is typically used for query parameters and form values. 699 // Unlike in the http.Header map, the keys in a Values map 700 // are case-sensitive. 701 type Values map[string][]string 702 703 // Get gets the first value associated with the given key. 704 // If there are no values associated with the key, Get returns 705 // the empty string. To access multiple values, use the map 706 // directly. 707 func (v Values) Get(key string) string { 708 if v == nil { 709 return "" 710 } 711 vs, ok := v[key] 712 if !ok || len(vs) == 0 { 713 return "" 714 } 715 return vs[0] 716 } 717 718 // Set sets the key to value. It replaces any existing 719 // values. 720 func (v Values) Set(key, value string) { 721 v[key] = []string{value} 722 } 723 724 // Add adds the value to key. It appends to any existing 725 // values associated with key. 726 func (v Values) Add(key, value string) { 727 v[key] = append(v[key], value) 728 } 729 730 // Del deletes the values associated with key. 731 func (v Values) Del(key string) { 732 delete(v, key) 733 } 734 735 // ParseQuery parses the URL-encoded query string and returns 736 // a map listing the values specified for each key. 737 // ParseQuery always returns a non-nil map containing all the 738 // valid query parameters found; err describes the first decoding error 739 // encountered, if any. 740 func ParseQuery(query string) (m Values, err error) { 741 m = make(Values) 742 err = parseQuery(m, query) 743 return 744 } 745 746 func parseQuery(m Values, query string) (err error) { 747 for query != "" { 748 key := query 749 if i := strings.IndexAny(key, "&;"); i >= 0 { 750 key, query = key[:i], key[i+1:] 751 } else { 752 query = "" 753 } 754 if key == "" { 755 continue 756 } 757 value := "" 758 if i := strings.Index(key, "="); i >= 0 { 759 key, value = key[:i], key[i+1:] 760 } 761 key, err1 := QueryUnescape(key) 762 if err1 != nil { 763 if err == nil { 764 err = err1 765 } 766 continue 767 } 768 value, err1 = QueryUnescape(value) 769 if err1 != nil { 770 if err == nil { 771 err = err1 772 } 773 continue 774 } 775 m[key] = append(m[key], value) 776 } 777 return err 778 } 779 780 // Encode encodes the values into ``URL encoded'' form 781 // ("bar=baz&foo=quux") sorted by key. 782 func (v Values) Encode() string { 783 if v == nil { 784 return "" 785 } 786 var buf bytes.Buffer 787 keys := make([]string, 0, len(v)) 788 for k := range v { 789 keys = append(keys, k) 790 } 791 sort.Strings(keys) 792 for _, k := range keys { 793 vs := v[k] 794 prefix := QueryEscape(k) + "=" 795 for _, v := range vs { 796 if buf.Len() > 0 { 797 buf.WriteByte('&') 798 } 799 buf.WriteString(prefix) 800 buf.WriteString(QueryEscape(v)) 801 } 802 } 803 return buf.String() 804 } 805 806 // resolvePath applies special path segments from refs and applies 807 // them to base, per RFC 3986. 808 func resolvePath(base, ref string) string { 809 var full string 810 if ref == "" { 811 full = base 812 } else if ref[0] != '/' { 813 i := strings.LastIndex(base, "/") 814 full = base[:i+1] + ref 815 } else { 816 full = ref 817 } 818 if full == "" { 819 return "" 820 } 821 var dst []string 822 src := strings.Split(full, "/") 823 for _, elem := range src { 824 switch elem { 825 case ".": 826 // drop 827 case "..": 828 if len(dst) > 0 { 829 dst = dst[:len(dst)-1] 830 } 831 default: 832 dst = append(dst, elem) 833 } 834 } 835 if last := src[len(src)-1]; last == "." || last == ".." { 836 // Add final slash to the joined path. 837 dst = append(dst, "") 838 } 839 return "/" + strings.TrimLeft(strings.Join(dst, "/"), "/") 840 } 841 842 // IsAbs reports whether the URL is absolute. 843 func (u *URL) IsAbs() bool { 844 return u.Scheme != "" 845 } 846 847 // Parse parses a URL in the context of the receiver. The provided URL 848 // may be relative or absolute. Parse returns nil, err on parse 849 // failure, otherwise its return value is the same as ResolveReference. 850 func (u *URL) Parse(ref string) (*URL, error) { 851 refurl, err := Parse(ref) 852 if err != nil { 853 return nil, err 854 } 855 return u.ResolveReference(refurl), nil 856 } 857 858 // ResolveReference resolves a URI reference to an absolute URI from 859 // an absolute base URI, per RFC 3986 Section 5.2. The URI reference 860 // may be relative or absolute. ResolveReference always returns a new 861 // URL instance, even if the returned URL is identical to either the 862 // base or reference. If ref is an absolute URL, then ResolveReference 863 // ignores base and returns a copy of ref. 864 func (u *URL) ResolveReference(ref *URL) *URL { 865 url := *ref 866 if ref.Scheme == "" { 867 url.Scheme = u.Scheme 868 } 869 if ref.Scheme != "" || ref.Host != "" || ref.User != nil { 870 // The "absoluteURI" or "net_path" cases. 871 url.Path = resolvePath(ref.Path, "") 872 return &url 873 } 874 if ref.Opaque != "" { 875 url.User = nil 876 url.Host = "" 877 url.Path = "" 878 return &url 879 } 880 if ref.Path == "" { 881 if ref.RawQuery == "" { 882 url.RawQuery = u.RawQuery 883 if ref.Fragment == "" { 884 url.Fragment = u.Fragment 885 } 886 } 887 } 888 // The "abs_path" or "rel_path" cases. 889 url.Host = u.Host 890 url.User = u.User 891 url.Path = resolvePath(u.Path, ref.Path) 892 return &url 893 } 894 895 // Query parses RawQuery and returns the corresponding values. 896 func (u *URL) Query() Values { 897 v, _ := ParseQuery(u.RawQuery) 898 return v 899 } 900 901 // RequestURI returns the encoded path?query or opaque?query 902 // string that would be used in an HTTP request for u. 903 func (u *URL) RequestURI() string { 904 result := u.Opaque 905 if result == "" { 906 result = u.EscapedPath() 907 if result == "" { 908 result = "/" 909 } 910 } else { 911 if strings.HasPrefix(result, "//") { 912 result = u.Scheme + ":" + result 913 } 914 } 915 if u.RawQuery != "" { 916 result += "?" + u.RawQuery 917 } 918 return result 919 }