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