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