url.go 30.6 KB
Newer Older
1 2 3 4
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

5
// Package url parses URLs and implements query escaping.
Rob Pike's avatar
Rob Pike committed
6
package url
7

8 9 10 11 12
// See RFC 3986. This package generally follows RFC 3986, except where
// it deviates for compatibility reasons. When sending changes, first
// search old issues for history on decisions. Unit tests should also
// contain references to issue numbers with details.

13
import (
14
	"errors"
15
	"fmt"
16
	"sort"
17 18
	"strconv"
	"strings"
19 20
)

Rob Pike's avatar
Rob Pike committed
21 22
// Error reports an error and the operation and URL that caused it.
type Error struct {
23 24 25
	Op  string
	URL string
	Err error
Russ Cox's avatar
Russ Cox committed
26 27
}

28
func (e *Error) Error() string { return e.Op + " " + e.URL + ": " + e.Err.Error() }
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
type timeout interface {
	Timeout() bool
}

func (e *Error) Timeout() bool {
	t, ok := e.Err.(timeout)
	return ok && t.Timeout()
}

type temporary interface {
	Temporary() bool
}

func (e *Error) Temporary() bool {
	t, ok := e.Err.(temporary)
	return ok && t.Temporary()
}

Russ Cox's avatar
Russ Cox committed
48
func ishex(c byte) bool {
49 50
	switch {
	case '0' <= c && c <= '9':
51
		return true
52
	case 'a' <= c && c <= 'f':
53
		return true
54
	case 'A' <= c && c <= 'F':
55
		return true
56
	}
57
	return false
58 59
}

Russ Cox's avatar
Russ Cox committed
60
func unhex(c byte) byte {
61 62
	switch {
	case '0' <= c && c <= '9':
63
		return c - '0'
64
	case 'a' <= c && c <= 'f':
65
		return c - 'a' + 10
66
	case 'A' <= c && c <= 'F':
67
		return c - 'A' + 10
68
	}
69
	return 0
70 71
}

72 73 74 75
type encoding int

const (
	encodePath encoding = 1 + iota
76
	encodePathSegment
77
	encodeHost
78
	encodeZone
79 80 81 82 83
	encodeUserPassword
	encodeQueryComponent
	encodeFragment
)

Rob Pike's avatar
Rob Pike committed
84
type EscapeError string
85

86
func (e EscapeError) Error() string {
87
	return "invalid URL escape " + strconv.Quote(string(e))
Russ Cox's avatar
Russ Cox committed
88 89
}

90 91 92 93 94 95
type InvalidHostError string

func (e InvalidHostError) Error() string {
	return "invalid character " + strconv.Quote(string(e)) + " in host name"
}

96
// Return true if the specified character should be escaped when
97
// appearing in a URL string, according to RFC 3986.
98 99 100
//
// Please be informed that for now shouldEscape does not check all
// reserved characters correctly. See golang.org/issue/5684.
101
func shouldEscape(c byte, mode encoding) bool {
102
	// §2.3 Unreserved characters (alphanum)
103 104
	if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
		return false
105
	}
106

107
	if mode == encodeHost || mode == encodeZone {
108 109 110 111
		// §3.2.2 Host allows
		//	sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
		// as part of reg-name.
		// We add : because we include :port as part of host.
112 113 114 115 116
		// We add [ ] because we include [ipv6]:port as part of host.
		// We add < > because they're the only characters left that
		// we could possibly allow, and Parse will reject them if we
		// escape them (because hosts can't use %-encoding for
		// ASCII bytes).
117
		switch c {
118
		case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':
119 120 121 122
			return false
		}
	}

Steve Newman's avatar
Steve Newman committed
123
	switch c {
124
	case '-', '_', '.', '~': // §2.3 Unreserved characters (mark)
125 126 127 128 129 130 131
		return false

	case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)
		// Different sections of the URL allow a few of
		// the reserved characters to appear unescaped.
		switch mode {
		case encodePath: // §3.3
132 133
			// The RFC allows : @ & = + $ but saves / ; , for assigning
			// meaning to individual path segments. This package
134
			// only manipulates the path as a whole, so we allow those
135
			// last three as well. That leaves only ? to escape.
136 137
			return c == '?'

138 139 140 141 142
		case encodePathSegment: // §3.3
			// The RFC allows : @ & = + $ but saves / ; , for assigning
			// meaning to individual path segments.
			return c == '/' || c == ';' || c == ',' || c == '?'

143 144 145 146 147 148
		case encodeUserPassword: // §3.2.1
			// The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
			// userinfo, so we must escape only '@', '/', and '?'.
			// The parsing of userinfo treats ':' as special so we must escape
			// that too.
			return c == '@' || c == '/' || c == '?' || c == ':'
149 150 151 152 153 154 155 156 157 158

		case encodeQueryComponent: // §3.4
			// The RFC reserves (so we must escape) everything.
			return true

		case encodeFragment: // §4.1
			// The RFC text is silent but the grammar allows
			// everything, so escape nothing.
			return false
		}
Steve Newman's avatar
Steve Newman committed
159
	}
160

161 162 163 164 165 166 167 168 169 170 171 172 173
	if mode == encodeFragment {
		// RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are
		// included in reserved from RFC 2396 §2.2. The remaining sub-delims do not
		// need to be escaped. To minimize potential breakage, we apply two restrictions:
		// (1) we always escape sub-delims outside of the fragment, and (2) we always
		// escape single quote to avoid breaking callers that had previously assumed that
		// single quotes would be escaped. See issue #19917.
		switch c {
		case '!', '(', ')', '*':
			return false
		}
	}

174 175
	// Everything else must be escaped.
	return true
Steve Newman's avatar
Steve Newman committed
176 177
}

178
// QueryUnescape does the inverse transformation of QueryEscape,
179
// converting each 3-byte encoded substring of the form "%AB" into the
180
// hex-decoded byte 0xAB.
181 182
// It returns an error if any % is not followed by two hexadecimal
// digits.
183
func QueryUnescape(s string) (string, error) {
Rob Pike's avatar
Rob Pike committed
184
	return unescape(s, encodeQueryComponent)
185
}
186

187
// PathUnescape does the inverse transformation of PathEscape,
188
// converting each 3-byte encoded substring of the form "%AB" into the
189 190
// hex-decoded byte 0xAB. It returns an error if any % is not followed
// by two hexadecimal digits.
191
//
192 193
// PathUnescape is identical to QueryUnescape except that it does not
// unescape '+' to ' ' (space).
194 195 196 197
func PathUnescape(s string) (string, error) {
	return unescape(s, encodePathSegment)
}

Rob Pike's avatar
Rob Pike committed
198 199
// unescape unescapes a string; the mode specifies
// which section of the URL string is being unescaped.
200
func unescape(s string, mode encoding) (string, error) {
201
	// Count %, check that they're well-formed.
202 203
	n := 0
	hasPlus := false
204
	for i := 0; i < len(s); {
Steve Newman's avatar
Steve Newman committed
205 206
		switch s[i] {
		case '%':
207
			n++
Steve Newman's avatar
Steve Newman committed
208
			if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
209
				s = s[i:]
Russ Cox's avatar
Russ Cox committed
210
				if len(s) > 3 {
211
					s = s[:3]
Russ Cox's avatar
Russ Cox committed
212
				}
Rob Pike's avatar
Rob Pike committed
213
				return "", EscapeError(s)
214
			}
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
			// Per https://tools.ietf.org/html/rfc3986#page-21
			// in the host component %-encoding can only be used
			// for non-ASCII bytes.
			// But https://tools.ietf.org/html/rfc6874#section-2
			// introduces %25 being allowed to escape a percent sign
			// in IPv6 scoped-address literals. Yay.
			if mode == encodeHost && unhex(s[i+1]) < 8 && s[i:i+3] != "%25" {
				return "", EscapeError(s[i : i+3])
			}
			if mode == encodeZone {
				// RFC 6874 says basically "anything goes" for zone identifiers
				// and that even non-ASCII can be redundantly escaped,
				// but it seems prudent to restrict %-escaped bytes here to those
				// that are valid host name bytes in their unescaped form.
				// That is, you can use escaping in the zone identifier but not
				// to introduce bytes you couldn't just write directly.
231
				// But Windows puts spaces here! Yay.
232
				v := unhex(s[i+1])<<4 | unhex(s[i+2])
233
				if s[i:i+3] != "%25" && v != ' ' && shouldEscape(v, encodeHost) {
234 235 236
					return "", EscapeError(s[i : i+3])
				}
			}
237
			i += 3
Steve Newman's avatar
Steve Newman committed
238
		case '+':
239
			hasPlus = mode == encodeQueryComponent
240
			i++
Steve Newman's avatar
Steve Newman committed
241
		default:
242 243 244
			if (mode == encodeHost || mode == encodeZone) && s[i] < 0x80 && shouldEscape(s[i], mode) {
				return "", InvalidHostError(s[i : i+1])
			}
245
			i++
246 247 248
		}
	}

249
	if n == 0 && !hasPlus {
250
		return s, nil
251 252
	}

253 254
	t := make([]byte, len(s)-2*n)
	j := 0
255
	for i := 0; i < len(s); {
Steve Newman's avatar
Steve Newman committed
256 257
		switch s[i] {
		case '%':
258 259 260
			t[j] = unhex(s[i+1])<<4 | unhex(s[i+2])
			j++
			i += 3
Steve Newman's avatar
Steve Newman committed
261
		case '+':
262
			if mode == encodeQueryComponent {
263 264 265 266
				t[j] = ' '
			} else {
				t[j] = '+'
			}
267 268
			j++
			i++
Steve Newman's avatar
Steve Newman committed
269
		default:
270 271 272
			t[j] = s[i]
			j++
			i++
273 274
		}
	}
275
	return string(t), nil
276 277
}

Rob Pike's avatar
Rob Pike committed
278 279 280 281
// QueryEscape escapes the string so it can be safely placed
// inside a URL query.
func QueryEscape(s string) string {
	return escape(s, encodeQueryComponent)
282
}
283

284 285 286 287 288 289
// PathEscape escapes the string so it can be safely placed
// inside a URL path segment.
func PathEscape(s string) string {
	return escape(s, encodePathSegment)
}

Rob Pike's avatar
Rob Pike committed
290
func escape(s string, mode encoding) string {
291
	spaceCount, hexCount := 0, 0
Steve Newman's avatar
Steve Newman committed
292
	for i := 0; i < len(s); i++ {
293
		c := s[i]
294 295
		if shouldEscape(c, mode) {
			if c == ' ' && mode == encodeQueryComponent {
296
				spaceCount++
Steve Newman's avatar
Steve Newman committed
297
			} else {
298
				hexCount++
Steve Newman's avatar
Steve Newman committed
299 300 301 302 303
			}
		}
	}

	if spaceCount == 0 && hexCount == 0 {
304
		return s
Steve Newman's avatar
Steve Newman committed
305 306
	}

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	var buf [64]byte
	var t []byte

	required := len(s) + 2*hexCount
	if required <= len(buf) {
		t = buf[:required]
	} else {
		t = make([]byte, required)
	}

	if hexCount == 0 {
		copy(t, s)
		for i := 0; i < len(s); i++ {
			if s[i] == ' ' {
				t[i] = '+'
			}
		}
		return string(t)
	}

327
	j := 0
Steve Newman's avatar
Steve Newman committed
328
	for i := 0; i < len(s); i++ {
Russ Cox's avatar
Russ Cox committed
329
		switch c := s[i]; {
330
		case c == ' ' && mode == encodeQueryComponent:
331 332
			t[j] = '+'
			j++
333
		case shouldEscape(c, mode):
334
			t[j] = '%'
335 336
			t[j+1] = "0123456789ABCDEF"[c>>4]
			t[j+2] = "0123456789ABCDEF"[c&15]
337
			j += 3
Russ Cox's avatar
Russ Cox committed
338
		default:
339 340
			t[j] = s[i]
			j++
Steve Newman's avatar
Steve Newman committed
341 342
		}
	}
343
	return string(t)
Steve Newman's avatar
Steve Newman committed
344 345
}

346
// A URL represents a parsed URL (technically, a URI reference).
347
//
348
// The general form represented is:
349
//
350
//	[scheme:][//[userinfo@]host][/]path[?query][#fragment]
351 352 353 354 355
//
// URLs that do not start with a slash after the scheme are interpreted as:
//
//	scheme:opaque[?query][#fragment]
//
356 357 358
// Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.
// A consequence is that it is impossible to tell which slashes in the Path were
// slashes in the raw URL and which were %2f. This distinction is rarely important,
359 360 361
// but when it is, code must not use Path directly.
// The Parse function sets both Path and RawPath in the URL it returns,
// and URL's String method uses RawPath if it is a valid encoding of Path,
362
// by calling the EscapedPath method.
363
type URL struct {
364 365 366 367
	Scheme     string
	Opaque     string    // encoded opaque data
	User       *Userinfo // username and password information
	Host       string    // host or host:port
368 369 370 371 372
	Path       string    // path (relative paths may omit leading slash)
	RawPath    string    // encoded path hint (see EscapedPath method)
	ForceQuery bool      // append a query ('?') even if RawQuery is empty
	RawQuery   string    // encoded query values, without '?'
	Fragment   string    // fragment for references, without '#'
373 374
}

375 376 377 378 379 380 381 382
// User returns a Userinfo containing the provided username
// and no password set.
func User(username string) *Userinfo {
	return &Userinfo{username, "", false}
}

// UserPassword returns a Userinfo containing the provided username
// and password.
383
//
384 385 386 387 388
// This functionality should only be used with legacy web sites.
// RFC 2396 warns that interpreting Userinfo this way
// ``is NOT RECOMMENDED, because the passing of authentication
// information in clear text (such as URI) has proven to be a
// security risk in almost every case where it has been used.''
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
func UserPassword(username, password string) *Userinfo {
	return &Userinfo{username, password, true}
}

// The Userinfo type is an immutable encapsulation of username and
// password details for a URL. An existing Userinfo value is guaranteed
// to have a username set (potentially empty, as allowed by RFC 2396),
// and optionally a password.
type Userinfo struct {
	username    string
	password    string
	passwordSet bool
}

// Username returns the username.
func (u *Userinfo) Username() string {
405 406 407
	if u == nil {
		return ""
	}
408 409 410 411 412
	return u.username
}

// Password returns the password in case it is set, and whether it is set.
func (u *Userinfo) Password() (string, bool) {
413 414 415
	if u == nil {
		return "", false
	}
Antonio Murdaca's avatar
Antonio Murdaca committed
416
	return u.password, u.passwordSet
417 418
}

419 420 421
// String returns the encoded userinfo information in the standard form
// of "username[:password]".
func (u *Userinfo) String() string {
422 423 424
	if u == nil {
		return ""
	}
425 426 427 428 429
	s := escape(u.username, encodeUserPassword)
	if u.passwordSet {
		s += ":" + escape(u.password, encodeUserPassword)
	}
	return s
430 431 432 433 434
}

// Maybe rawurl is of the form scheme:path.
// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*)
// If so, return scheme, path; else return "", rawurl.
435
func getscheme(rawurl string) (scheme, path string, err error) {
436
	for i := 0; i < len(rawurl); i++ {
437
		c := rawurl[i]
438
		switch {
439 440
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
		// do nothing
441 442
		case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
			if i == 0 {
443
				return "", rawurl, nil
444 445 446
			}
		case c == ':':
			if i == 0 {
447
				return "", "", errors.New("missing protocol scheme")
448
			}
449
			return rawurl[:i], rawurl[i+1:], nil
450 451 452
		default:
			// we have encountered an invalid character,
			// so there is no valid scheme
453
			return "", rawurl, nil
454 455
		}
	}
456
	return "", rawurl, nil
457 458 459 460 461
}

// Maybe s is of the form t c u.
// If so, return t, c u (or t, u if cutc == true).
// If not, return s, "".
462 463 464 465 466 467
func split(s string, c string, cutc bool) (string, string) {
	i := strings.Index(s, c)
	if i < 0 {
		return s, ""
	}
	if cutc {
468
		return s[:i], s[i+len(c):]
469
	}
470
	return s[:i], s[i:]
471 472
}

Rob Pike's avatar
Rob Pike committed
473
// Parse parses rawurl into a URL structure.
474 475 476 477 478
//
// The rawurl may be relative (a path, without a host) or absolute
// (starting with a scheme). Trying to parse a hostname and path
// without a scheme is invalid but may not necessarily return an
// error, due to parsing ambiguities.
479
func Parse(rawurl string) (*URL, error) {
Russ Cox's avatar
Russ Cox committed
480
	// Cut off #frag
481
	u, frag := split(rawurl, "#", true)
482 483
	url, err := parse(u, false)
	if err != nil {
484
		return nil, &Error{"parse", u, err}
Russ Cox's avatar
Russ Cox committed
485 486 487 488 489 490 491 492
	}
	if frag == "" {
		return url, nil
	}
	if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
		return nil, &Error{"parse", rawurl, err}
	}
	return url, nil
493 494
}

495
// ParseRequestURI parses rawurl into a URL structure. It assumes that
Russ Cox's avatar
Russ Cox committed
496
// rawurl was received in an HTTP request, so the rawurl is interpreted
497 498 499
// only as an absolute URI or an absolute path.
// The string rawurl is assumed not to have a #fragment suffix.
// (Web browsers strip #fragment before sending the URL to a web server.)
500
func ParseRequestURI(rawurl string) (*URL, error) {
501 502 503 504 505
	url, err := parse(rawurl, true)
	if err != nil {
		return nil, &Error{"parse", rawurl, err}
	}
	return url, nil
506 507
}

508
// parse parses a URL from a string in one of two contexts. If
509 510 511
// viaRequest is true, the URL is assumed to have arrived via an HTTP request,
// in which case only absolute URLs or path-absolute relative URLs are allowed.
// If viaRequest is false, all forms of relative URLs are allowed.
512
func parse(rawurl string, viaRequest bool) (*URL, error) {
513
	var rest string
514
	var err error
Russ Cox's avatar
Russ Cox committed
515

516
	if rawurl == "" && viaRequest {
517
		return nil, errors.New("empty url")
518
	}
519
	url := new(URL)
520

521 522
	if rawurl == "*" {
		url.Path = "*"
523
		return url, nil
524 525
	}

526 527
	// Split off possible leading "http:", "mailto:", etc.
	// Cannot contain escaped characters.
528
	if url.Scheme, rest, err = getscheme(rawurl); err != nil {
529
		return nil, err
530
	}
531
	url.Scheme = strings.ToLower(url.Scheme)
532

533
	if strings.HasSuffix(rest, "?") && strings.Count(rest, "?") == 1 {
534 535 536
		url.ForceQuery = true
		rest = rest[:len(rest)-1]
	} else {
537
		rest, url.RawQuery = split(rest, "?", true)
538
	}
539

540 541 542 543 544
	if !strings.HasPrefix(rest, "/") {
		if url.Scheme != "" {
			// We consider rootless paths per RFC 3986 as opaque.
			url.Opaque = rest
			return url, nil
545
		}
546
		if viaRequest {
547
			return nil, errors.New("invalid URI for request")
548
		}
549 550 551 552 553 554 555

		// Avoid confusion with malformed schemes, like cache_object:foo/bar.
		// See golang.org/issue/16822.
		//
		// RFC 3986, §3.3:
		// In addition, a URI reference (Section 4.1) may be a relative-path reference,
		// in which case the first path segment cannot contain a colon (":") character.
556 557
		colon := strings.Index(rest, ":")
		slash := strings.Index(rest, "/")
558 559 560 561
		if colon >= 0 && (slash < 0 || colon < slash) {
			// First path segment has colon. Not allowed in relative URL.
			return nil, errors.New("first path segment in URL cannot contain colon")
		}
562
	}
563

564
	if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") {
565
		var authority string
566
		authority, rest = split(rest[2:], "/", false)
567 568
		url.User, url.Host, err = parseAuthority(authority)
		if err != nil {
569
			return nil, err
570 571
		}
	}
572 573 574 575 576
	// Set Path and, optionally, RawPath.
	// RawPath is a hint of the encoding of Path. We don't want to set it if
	// the default escaping of Path is equivalent, to help make sure that people
	// don't rely on it in general.
	if err := url.setPath(rest); err != nil {
577
		return nil, err
578
	}
579
	return url, nil
580
}
Russ Cox's avatar
Russ Cox committed
581

582
func parseAuthority(authority string) (user *Userinfo, host string, err error) {
583
	i := strings.LastIndex(authority, "@")
584
	if i < 0 {
585 586 587
		host, err = parseHost(authority)
	} else {
		host, err = parseHost(authority[i+1:])
588
	}
589 590 591 592 593 594 595
	if err != nil {
		return nil, "", err
	}
	if i < 0 {
		return nil, host, nil
	}
	userinfo := authority[:i]
596 597 598
	if !validUserinfo(userinfo) {
		return nil, "", errors.New("net/url: invalid userinfo")
	}
599
	if !strings.Contains(userinfo, ":") {
600
		if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
601
			return nil, "", err
602 603 604
		}
		user = User(userinfo)
	} else {
605
		username, password := split(userinfo, ":", true)
606
		if username, err = unescape(username, encodeUserPassword); err != nil {
607
			return nil, "", err
608 609
		}
		if password, err = unescape(password, encodeUserPassword); err != nil {
610
			return nil, "", err
611 612 613
		}
		user = UserPassword(username, password)
	}
614 615 616
	return user, host, nil
}

617 618
// parseHost parses host as an authority without user
// information. That is, as host[:port].
619 620 621
func parseHost(host string) (string, error) {
	if strings.HasPrefix(host, "[") {
		// Parse an IP-Literal in RFC 3986 and RFC 6874.
622
		// E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80".
623
		i := strings.LastIndex(host, "]")
624 625 626
		if i < 0 {
			return "", errors.New("missing ']' in host")
		}
627 628 629 630
		colonPort := host[i+1:]
		if !validOptionalPort(colonPort) {
			return "", fmt.Errorf("invalid port %q after host", colonPort)
		}
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652

		// RFC 6874 defines that %25 (%-encoded percent) introduces
		// the zone identifier, and the zone identifier can use basically
		// any %-encoding it likes. That's different from the host, which
		// can only %-encode non-ASCII bytes.
		// We do impose some restrictions on the zone, to avoid stupidity
		// like newlines.
		zone := strings.Index(host[:i], "%25")
		if zone >= 0 {
			host1, err := unescape(host[:zone], encodeHost)
			if err != nil {
				return "", err
			}
			host2, err := unescape(host[zone:i], encodeZone)
			if err != nil {
				return "", err
			}
			host3, err := unescape(host[i:], encodeHost)
			if err != nil {
				return "", err
			}
			return host1 + host2 + host3, nil
653
		}
654
	}
655

656 657 658 659 660
	var err error
	if host, err = unescape(host, encodeHost); err != nil {
		return "", err
	}
	return host, nil
661 662
}

663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
// setPath sets the Path and RawPath fields of the URL based on the provided
// escaped path p. It maintains the invariant that RawPath is only specified
// when it differs from the default encoding of the path.
// For example:
// - setPath("/foo/bar")   will set Path="/foo/bar" and RawPath=""
// - setPath("/foo%2fbar") will set Path="/foo/bar" and RawPath="/foo%2fbar"
// setPath will return an error only if the provided path contains an invalid
// escaping.
func (u *URL) setPath(p string) error {
	path, err := unescape(p, encodePath)
	if err != nil {
		return err
	}
	u.Path = path
	if escp := escape(path, encodePath); p == escp {
		// Default encoding is fine.
		u.RawPath = ""
	} else {
		u.RawPath = p
	}
	return nil
}

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
// EscapedPath returns the escaped form of u.Path.
// In general there are multiple possible escaped forms of any path.
// EscapedPath returns u.RawPath when it is a valid escaping of u.Path.
// Otherwise EscapedPath ignores u.RawPath and computes an escaped
// form on its own.
// The String and RequestURI methods use EscapedPath to construct
// their results.
// In general, code should call EscapedPath instead of
// reading u.RawPath directly.
func (u *URL) EscapedPath() string {
	if u.RawPath != "" && validEncodedPath(u.RawPath) {
		p, err := unescape(u.RawPath, encodePath)
		if err == nil && p == u.Path {
			return u.RawPath
		}
	}
702 703 704
	if u.Path == "*" {
		return "*" // don't escape (Issue 11202)
	}
705 706 707 708
	return escape(u.Path, encodePath)
}

// validEncodedPath reports whether s is a valid encoded path.
709
// It must not contain any bytes that require escaping during path encoding.
710 711
func validEncodedPath(s string) bool {
	for i := 0; i < len(s); i++ {
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
		// RFC 3986, Appendix A.
		// pchar = unreserved / pct-encoded / sub-delims / ":" / "@".
		// shouldEscape is not quite compliant with the RFC,
		// so we check the sub-delims ourselves and let
		// shouldEscape handle the others.
		switch s[i] {
		case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '@':
			// ok
		case '[', ']':
			// ok - not specified in RFC 3986 but left alone by modern browsers
		case '%':
			// ok - percent encoded, will decode
		default:
			if shouldEscape(s[i], encodePath) {
				return false
			}
728 729 730 731 732
		}
	}
	return true
}

733
// validOptionalPort reports whether port is either an empty string
734
// or matches /^:\d*$/
735 736 737 738
func validOptionalPort(port string) bool {
	if port == "" {
		return true
	}
739
	if port[0] != ':' {
740 741 742 743 744 745 746 747 748 749
		return false
	}
	for _, b := range port[1:] {
		if b < '0' || b > '9' {
			return false
		}
	}
	return true
}

750
// String reassembles the URL into a valid URL string.
Russ Cox's avatar
Russ Cox committed
751 752
// The general form of the result is one of:
//
Shenghou Ma's avatar
Shenghou Ma committed
753
//	scheme:opaque?query#fragment
Russ Cox's avatar
Russ Cox committed
754 755 756 757
//	scheme://userinfo@host/path?query#fragment
//
// If u.Opaque is non-empty, String uses the first form;
// otherwise it uses the second form.
758
// Any non-ASCII characters in host are escaped.
759
// To obtain the path, String uses u.EscapedPath().
Russ Cox's avatar
Russ Cox committed
760 761 762 763 764 765 766 767 768 769 770
//
// In the second form, the following rules apply:
//	- if u.Scheme is empty, scheme: is omitted.
//	- if u.User is nil, userinfo@ is omitted.
//	- if u.Host is empty, host/ is omitted.
//	- if u.Scheme and u.Host are empty and u.User is nil,
//	   the entire scheme://userinfo@host/ is omitted.
//	- if u.Host is non-empty and u.Path begins with a /,
//	   the form host/path does not add its own /.
//	- if u.RawQuery is empty, ?query is omitted.
//	- if u.Fragment is empty, #fragment is omitted.
771
func (u *URL) String() string {
772
	var buf strings.Builder
773
	if u.Scheme != "" {
774 775
		buf.WriteString(u.Scheme)
		buf.WriteByte(':')
776
	}
777
	if u.Opaque != "" {
778
		buf.WriteString(u.Opaque)
779
	} else {
780
		if u.Scheme != "" || u.Host != "" || u.User != nil {
781 782 783
			if u.Host != "" || u.Path != "" || u.User != nil {
				buf.WriteString("//")
			}
784 785
			if ui := u.User; ui != nil {
				buf.WriteString(ui.String())
786
				buf.WriteByte('@')
787
			}
788
			if h := u.Host; h != "" {
789
				buf.WriteString(escape(h, encodeHost))
790
			}
791
		}
792 793
		path := u.EscapedPath()
		if path != "" && path[0] != '/' && u.Host != "" {
794 795
			buf.WriteByte('/')
		}
796 797 798 799 800 801 802 803 804 805 806
		if buf.Len() == 0 {
			// RFC 3986 §4.2
			// A path segment that contains a colon character (e.g., "this:that")
			// cannot be used as the first segment of a relative-path reference, as
			// it would be mistaken for a scheme name. Such a segment must be
			// preceded by a dot-segment (e.g., "./this:that") to make a relative-
			// path reference.
			if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 {
				buf.WriteString("./")
			}
		}
807
		buf.WriteString(path)
808
	}
809
	if u.ForceQuery || u.RawQuery != "" {
810 811
		buf.WriteByte('?')
		buf.WriteString(u.RawQuery)
812
	}
813
	if u.Fragment != "" {
814 815
		buf.WriteByte('#')
		buf.WriteString(escape(u.Fragment, encodeFragment))
816
	}
817
	return buf.String()
818
}
819

Rob Pike's avatar
Rob Pike committed
820 821 822 823 824 825 826 827 828 829 830 831 832 833
// Values maps a string key to a list of values.
// It is typically used for query parameters and form values.
// Unlike in the http.Header map, the keys in a Values map
// are case-sensitive.
type Values map[string][]string

// Get gets the first value associated with the given key.
// If there are no values associated with the key, Get returns
// the empty string. To access multiple values, use the map
// directly.
func (v Values) Get(key string) string {
	if v == nil {
		return ""
	}
834 835
	vs := v[key]
	if len(vs) == 0 {
Rob Pike's avatar
Rob Pike committed
836 837 838 839 840 841 842 843 844 845 846
		return ""
	}
	return vs[0]
}

// Set sets the key to value. It replaces any existing
// values.
func (v Values) Set(key, value string) {
	v[key] = []string{value}
}

847
// Add adds the value to key. It appends to any existing
Rob Pike's avatar
Rob Pike committed
848 849 850 851 852 853 854
// values associated with key.
func (v Values) Add(key, value string) {
	v[key] = append(v[key], value)
}

// Del deletes the values associated with key.
func (v Values) Del(key string) {
Russ Cox's avatar
Russ Cox committed
855
	delete(v, key)
Rob Pike's avatar
Rob Pike committed
856 857 858 859 860 861 862
}

// ParseQuery parses the URL-encoded query string and returns
// a map listing the values specified for each key.
// ParseQuery always returns a non-nil map containing all the
// valid query parameters found; err describes the first decoding error
// encountered, if any.
863 864 865 866
//
// Query is expected to be a list of key=value settings separated by
// ampersands or semicolons. A setting without an equals sign is
// interpreted as a key set to an empty value.
867 868 869 870
func ParseQuery(query string) (Values, error) {
	m := make(Values)
	err := parseQuery(m, query)
	return m, err
Rob Pike's avatar
Rob Pike committed
871 872
}

873
func parseQuery(m Values, query string) (err error) {
Russ Cox's avatar
Russ Cox committed
874 875 876 877 878 879 880 881
	for query != "" {
		key := query
		if i := strings.IndexAny(key, "&;"); i >= 0 {
			key, query = key[:i], key[i+1:]
		} else {
			query = ""
		}
		if key == "" {
Rob Pike's avatar
Rob Pike committed
882 883
			continue
		}
Russ Cox's avatar
Russ Cox committed
884
		value := ""
885
		if i := strings.Index(key, "="); i >= 0 {
Russ Cox's avatar
Russ Cox committed
886 887 888 889
			key, value = key[:i], key[i+1:]
		}
		key, err1 := QueryUnescape(key)
		if err1 != nil {
890 891 892
			if err == nil {
				err = err1
			}
Russ Cox's avatar
Russ Cox committed
893
			continue
Rob Pike's avatar
Rob Pike committed
894
		}
Russ Cox's avatar
Russ Cox committed
895 896
		value, err1 = QueryUnescape(value)
		if err1 != nil {
897 898 899
			if err == nil {
				err = err1
			}
Rob Pike's avatar
Rob Pike committed
900 901 902 903 904 905 906
			continue
		}
		m[key] = append(m[key], value)
	}
	return err
}

907 908
// Encode encodes the values into ``URL encoded'' form
// ("bar=baz&foo=quux") sorted by key.
909 910 911 912
func (v Values) Encode() string {
	if v == nil {
		return ""
	}
913
	var buf strings.Builder
914 915 916 917 918 919 920
	keys := make([]string, 0, len(v))
	for k := range v {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		vs := v[k]
921
		keyEscaped := QueryEscape(k)
922
		for _, v := range vs {
923 924 925
			if buf.Len() > 0 {
				buf.WriteByte('&')
			}
926 927
			buf.WriteString(keyEscaped)
			buf.WriteByte('=')
928
			buf.WriteString(QueryEscape(v))
929 930
		}
	}
931
	return buf.String()
932
}
933 934

// resolvePath applies special path segments from refs and applies
935 936 937 938 939 940
// them to base, per RFC 3986.
func resolvePath(base, ref string) string {
	var full string
	if ref == "" {
		full = base
	} else if ref[0] != '/' {
941
		i := strings.LastIndex(base, "/")
942 943 944
		full = base[:i+1] + ref
	} else {
		full = ref
945
	}
946 947 948 949 950 951 952 953 954 955 956 957
	if full == "" {
		return ""
	}
	var dst []string
	src := strings.Split(full, "/")
	for _, elem := range src {
		switch elem {
		case ".":
			// drop
		case "..":
			if len(dst) > 0 {
				dst = dst[:len(dst)-1]
958
			}
959
		default:
960
			dst = append(dst, elem)
961 962
		}
	}
963 964 965 966
	if last := src[len(src)-1]; last == "." || last == ".." {
		// Add final slash to the joined path.
		dst = append(dst, "")
	}
967
	return "/" + strings.TrimPrefix(strings.Join(dst, "/"), "/")
968 969
}

970
// IsAbs reports whether the URL is absolute.
971
// Absolute means that it has a non-empty scheme.
972 973
func (u *URL) IsAbs() bool {
	return u.Scheme != ""
974 975
}

976 977
// Parse parses a URL in the context of the receiver. The provided URL
// may be relative or absolute. Parse returns nil, err on parse
978
// failure, otherwise its return value is the same as ResolveReference.
979
func (u *URL) Parse(ref string) (*URL, error) {
Rob Pike's avatar
Rob Pike committed
980
	refurl, err := Parse(ref)
981 982 983
	if err != nil {
		return nil, err
	}
984
	return u.ResolveReference(refurl), nil
985 986 987
}

// ResolveReference resolves a URI reference to an absolute URI from
988
// an absolute base URI u, per RFC 3986 Section 5.2. The URI reference
989
// may be relative or absolute. ResolveReference always returns a new
990 991 992
// URL instance, even if the returned URL is identical to either the
// base or reference. If ref is an absolute URL, then ResolveReference
// ignores base and returns a copy of ref.
993
func (u *URL) ResolveReference(ref *URL) *URL {
994 995 996 997 998 999
	url := *ref
	if ref.Scheme == "" {
		url.Scheme = u.Scheme
	}
	if ref.Scheme != "" || ref.Host != "" || ref.User != nil {
		// The "absoluteURI" or "net_path" cases.
1000 1001 1002
		// We can ignore the error from setPath since we know we provided a
		// validly-escaped path.
		url.setPath(resolvePath(ref.EscapedPath(), ""))
1003 1004 1005 1006 1007 1008 1009 1010
		return &url
	}
	if ref.Opaque != "" {
		url.User = nil
		url.Host = ""
		url.Path = ""
		return &url
	}
1011 1012 1013 1014
	if ref.Path == "" && ref.RawQuery == "" {
		url.RawQuery = u.RawQuery
		if ref.Fragment == "" {
			url.Fragment = u.Fragment
1015 1016
		}
	}
1017 1018 1019
	// The "abs_path" or "rel_path" cases.
	url.Host = u.Host
	url.User = u.User
1020
	url.setPath(resolvePath(u.EscapedPath(), ref.EscapedPath()))
1021
	return &url
1022
}
1023 1024

// Query parses RawQuery and returns the corresponding values.
1025 1026
// It silently discards malformed value pairs.
// To check errors use ParseQuery.
1027 1028 1029 1030
func (u *URL) Query() Values {
	v, _ := ParseQuery(u.RawQuery)
	return v
}
Rob Pike's avatar
Rob Pike committed
1031

1032 1033 1034 1035 1036
// RequestURI returns the encoded path?query or opaque?query
// string that would be used in an HTTP request for u.
func (u *URL) RequestURI() string {
	result := u.Opaque
	if result == "" {
1037
		result = u.EscapedPath()
1038 1039 1040
		if result == "" {
			result = "/"
		}
1041 1042 1043 1044
	} else {
		if strings.HasPrefix(result, "//") {
			result = u.Scheme + ":" + result
		}
1045
	}
1046
	if u.ForceQuery || u.RawQuery != "" {
1047 1048 1049
		result += "?" + u.RawQuery
	}
	return result
Rob Pike's avatar
Rob Pike committed
1050
}
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090

// Hostname returns u.Host, without any port number.
//
// If Host is an IPv6 literal with a port number, Hostname returns the
// IPv6 literal without the square brackets. IPv6 literals may include
// a zone identifier.
func (u *URL) Hostname() string {
	return stripPort(u.Host)
}

// Port returns the port part of u.Host, without the leading colon.
// If u.Host doesn't contain a port, Port returns an empty string.
func (u *URL) Port() string {
	return portOnly(u.Host)
}

func stripPort(hostport string) string {
	colon := strings.IndexByte(hostport, ':')
	if colon == -1 {
		return hostport
	}
	if i := strings.IndexByte(hostport, ']'); i != -1 {
		return strings.TrimPrefix(hostport[:i], "[")
	}
	return hostport[:colon]
}

func portOnly(hostport string) string {
	colon := strings.IndexByte(hostport, ':')
	if colon == -1 {
		return ""
	}
	if i := strings.Index(hostport, "]:"); i != -1 {
		return hostport[i+len("]:"):]
	}
	if strings.Contains(hostport, "]") {
		return ""
	}
	return hostport[colon+len(":"):]
}
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106

// Marshaling interface implementations.
// Would like to implement MarshalText/UnmarshalText but that will change the JSON representation of URLs.

func (u *URL) MarshalBinary() (text []byte, err error) {
	return []byte(u.String()), nil
}

func (u *URL) UnmarshalBinary(text []byte) error {
	u1, err := Parse(string(text))
	if err != nil {
		return err
	}
	*u = *u1
	return nil
}
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136

// validUserinfo reports whether s is a valid userinfo string per RFC 3986
// Section 3.2.1:
//     userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )
//     unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
//     sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
//                   / "*" / "+" / "," / ";" / "="
//
// It doesn't validate pct-encoded. The caller does that via func unescape.
func validUserinfo(s string) bool {
	for _, r := range s {
		if 'A' <= r && r <= 'Z' {
			continue
		}
		if 'a' <= r && r <= 'z' {
			continue
		}
		if '0' <= r && r <= '9' {
			continue
		}
		switch r {
		case '-', '.', '_', ':', '~', '!', '$', '&', '\'',
			'(', ')', '*', '+', ',', ';', '=', '%', '@':
			continue
		default:
			return false
		}
	}
	return true
}