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