diff options
Diffstat (limited to 'llgo/third_party/gofrontend/libgo/go/net/url/url.go')
| -rw-r--r-- | llgo/third_party/gofrontend/libgo/go/net/url/url.go | 200 |
1 files changed, 175 insertions, 25 deletions
diff --git a/llgo/third_party/gofrontend/libgo/go/net/url/url.go b/llgo/third_party/gofrontend/libgo/go/net/url/url.go index f167408faba..8ffad663d5c 100644 --- a/llgo/third_party/gofrontend/libgo/go/net/url/url.go +++ b/llgo/third_party/gofrontend/libgo/go/net/url/url.go @@ -9,6 +9,7 @@ package url import ( "bytes" "errors" + "fmt" "sort" "strconv" "strings" @@ -51,6 +52,7 @@ type encoding int const ( encodePath encoding = 1 + iota + encodeHost encodeUserPassword encodeQueryComponent encodeFragment @@ -64,12 +66,27 @@ func (e EscapeError) Error() string { // Return true if the specified character should be escaped when // appearing in a URL string, according to RFC 3986. +// +// Please be informed that for now shouldEscape does not check all +// reserved characters correctly. See golang.org/issue/5684. func shouldEscape(c byte, mode encoding) bool { // §2.3 Unreserved characters (alphanum) if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { return false } + if mode == encodeHost { + // §3.2.2 Host allows + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + // as part of reg-name. + // We add : because we include :port as part of host. + // We add [ ] because we include [ipv6]:port as part of host + switch c { + case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']': + return false + } + } + switch c { case '-', '_', '.', '~': // §2.3 Unreserved characters (mark) return false @@ -127,7 +144,7 @@ func unescape(s string, mode encoding) (string, error) { if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { s = s[i:] if len(s) > 3 { - s = s[0:3] + s = s[:3] } return "", EscapeError(s) } @@ -224,16 +241,24 @@ func escape(s string, mode encoding) string { // 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, -// but when it is a client must use other routines to parse the raw URL or construct -// the parsed URL. For example, an HTTP server can consult req.RequestURI, and -// an HTTP client can use URL{Host: "example.com", Opaque: "//example.com/Go%2f"} -// instead of URL{Host: "example.com", Path: "/Go/"}. +// but when it is, code must not use Path directly. +// +// Go 1.5 introduced the RawPath field to hold the encoded form of Path. +// 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, +// by calling the EncodedPath method. +// +// In earlier versions of Go, the more indirect workarounds were that an +// HTTP server could consult req.RequestURI and an HTTP client could +// construct a URL struct directly and set the Opaque field instead of Path. +// These still work as well. type URL struct { Scheme string Opaque string // encoded opaque data User *Userinfo // username and password information Host string // host or host:port Path string + RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method) RawQuery string // encoded query values, without '?' Fragment string // fragment for references, without '#' } @@ -305,7 +330,7 @@ func getscheme(rawurl string) (scheme, path string, err error) { if i == 0 { return "", "", errors.New("missing protocol scheme") } - return rawurl[0:i], rawurl[i+1:], nil + return rawurl[:i], rawurl[i+1:], nil default: // we have encountered an invalid character, // so there is no valid scheme @@ -324,9 +349,9 @@ func split(s string, c string, cutc bool) (string, string) { return s, "" } if cutc { - return s[0:i], s[i+len(c):] + return s[:i], s[i+len(c):] } - return s[0:i], s[i:] + return s[:i], s[i:] } // Parse parses rawurl into a URL structure. @@ -401,14 +426,17 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) { if err != nil { goto Error } - if strings.Contains(url.Host, "%") { - err = errors.New("hexadecimal escape in host") - goto Error - } } if url.Path, err = unescape(rest, encodePath); err != nil { goto Error } + // RawPath is a hint as to the encoding of Path to use + // in url.EncodedPath. If that method already gets the + // right answer without RawPath, leave it empty. + // This will help make sure that people don't rely on it in general. + if url.EscapedPath() != rest && validEncodedPath(rest) { + url.RawPath = rest + } return url, nil Error: @@ -418,36 +446,157 @@ Error: func parseAuthority(authority string) (user *Userinfo, host string, err error) { i := strings.LastIndex(authority, "@") if i < 0 { - host = authority - return + host, err = parseHost(authority) + } else { + host, err = parseHost(authority[i+1:]) } - userinfo, host := authority[:i], authority[i+1:] + if err != nil { + return nil, "", err + } + if i < 0 { + return nil, host, nil + } + userinfo := authority[:i] if strings.Index(userinfo, ":") < 0 { if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil { - return + return nil, "", err } user = User(userinfo) } else { username, password := split(userinfo, ":", true) if username, err = unescape(username, encodeUserPassword); err != nil { - return + return nil, "", err } if password, err = unescape(password, encodeUserPassword); err != nil { - return + return nil, "", err } user = UserPassword(username, password) } - return + return user, host, nil +} + +// parseHost parses host as an authority without user +// information. That is, as host[:port]. +func parseHost(host string) (string, error) { + litOrName := host + if strings.HasPrefix(host, "[") { + // Parse an IP-Literal in RFC 3986 and RFC 6874. + // E.g., "[fe80::1], "[fe80::1%25en0]" + // + // RFC 4007 defines "%" as a delimiter character in + // the textual representation of IPv6 addresses. + // Per RFC 6874, in URIs that "%" is encoded as "%25". + i := strings.LastIndex(host, "]") + if i < 0 { + return "", errors.New("missing ']' in host") + } + colonPort := host[i+1:] + if !validOptionalPort(colonPort) { + return "", fmt.Errorf("invalid port %q after host", colonPort) + } + // Parse a host subcomponent without a ZoneID in RFC + // 6874 because the ZoneID is allowed to use the + // percent encoded form. + j := strings.Index(host[:i], "%25") + if j < 0 { + litOrName = host[1:i] + } else { + litOrName = host[1:j] + } + } + + // A URI containing an IP-Literal without a ZoneID or + // IPv4address in RFC 3986 and RFC 6847 must not be + // percent-encoded. + // + // A URI containing a DNS registered name in RFC 3986 is + // allowed to be percent-encoded, though we don't use it for + // now to avoid messing up with the gap between allowed + // characters in URI and allowed characters in DNS. + // See golang.org/issue/7991. + if strings.Contains(litOrName, "%") { + return "", errors.New("percent-encoded characters in host") + } + var err error + if host, err = unescape(host, encodeHost); err != nil { + return "", err + } + return host, nil +} + +// 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 + } + } + if u.Path == "*" { + return "*" // don't escape (Issue 11202) + } + return escape(u.Path, encodePath) +} + +// validEncodedPath reports whether s is a valid encoded path. +// It must not contain any bytes that require escaping during path encoding. +func validEncodedPath(s string) bool { + for i := 0; i < len(s); i++ { + // 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 + } + } + } + return true +} + +// validOptionalPort reports whether port is either an empty string +// or matches /^:\d+$/ +func validOptionalPort(port string) bool { + if port == "" { + return true + } + if port[0] != ':' || len(port) == 1 { + return false + } + for _, b := range port[1:] { + if b < '0' || b > '9' { + return false + } + } + return true } // String reassembles the URL into a valid URL string. // The general form of the result is one of: // -// scheme:opaque +// scheme:opaque?query#fragment // scheme://userinfo@host/path?query#fragment // // If u.Opaque is non-empty, String uses the first form; // otherwise it uses the second form. +// To obtain the path, String uses u.EncodedPath(). // // In the second form, the following rules apply: // - if u.Scheme is empty, scheme: is omitted. @@ -475,13 +624,14 @@ func (u *URL) String() string { buf.WriteByte('@') } if h := u.Host; h != "" { - buf.WriteString(h) + buf.WriteString(escape(h, encodeHost)) } } - if u.Path != "" && u.Path[0] != '/' && u.Host != "" { + path := u.EscapedPath() + if path != "" && path[0] != '/' && u.Host != "" { buf.WriteByte('/') } - buf.WriteString(escape(u.Path, encodePath)) + buf.WriteString(path) } if u.RawQuery != "" { buf.WriteByte('?') @@ -639,7 +789,7 @@ func resolvePath(base, ref string) string { return "/" + strings.TrimLeft(strings.Join(dst, "/"), "/") } -// IsAbs returns true if the URL is absolute. +// IsAbs reports whether the URL is absolute. func (u *URL) IsAbs() bool { return u.Scheme != "" } @@ -703,7 +853,7 @@ func (u *URL) Query() Values { func (u *URL) RequestURI() string { result := u.Opaque if result == "" { - result = escape(u.Path, encodePath) + result = u.EscapedPath() if result == "" { result = "/" } |

