|
|
|
@ -17,7 +17,7 @@ func HasAsciiControlBytes(raw string) bool {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ParseScheme attempts to parse a scheme from a raw url
|
|
|
|
|
func ParseScheme(raw string) (string, string, errors.Error) {
|
|
|
|
|
func ParseScheme(raw string) (string, string, error) {
|
|
|
|
|
// If first char is:
|
|
|
|
|
// - valid ascii (but non-scheme), return here no errors
|
|
|
|
|
// - end of scheme char, return bad request error
|
|
|
|
@ -26,7 +26,7 @@ func ParseScheme(raw string) (string, string, errors.Error) {
|
|
|
|
|
case ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'):
|
|
|
|
|
// All good, continue
|
|
|
|
|
case c == ':':
|
|
|
|
|
return "", "", ErrInvalidRequest
|
|
|
|
|
return "", "", errors.Wrap(raw, ErrParsingScheme)
|
|
|
|
|
default:
|
|
|
|
|
// Invalid scheme char (or scheme first-char) return
|
|
|
|
|
return "", raw, nil
|
|
|
|
@ -104,7 +104,10 @@ func shouldHostEscape(b byte) bool {
|
|
|
|
|
|
|
|
|
|
func shouldPathEscape(b byte) bool {
|
|
|
|
|
switch b {
|
|
|
|
|
// Reserved character in path
|
|
|
|
|
// Reserved character in path.
|
|
|
|
|
// Bear in mind ;, ARE allowed in a URL path,
|
|
|
|
|
// but when converting from a filesystem-->URL path
|
|
|
|
|
// (how this will be used), it will need escaping.
|
|
|
|
|
case '?', ';', ',':
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
@ -136,7 +139,7 @@ func unescape(raw string, count int) string {
|
|
|
|
|
return t.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unescapeHost(raw string) (string, errors.Error) {
|
|
|
|
|
func unescapeHost(raw string) (string, error) {
|
|
|
|
|
// Count all the percent signs
|
|
|
|
|
count := 0
|
|
|
|
|
for i := 0; i < len(raw); {
|
|
|
|
@ -147,14 +150,14 @@ func unescapeHost(raw string) (string, errors.Error) {
|
|
|
|
|
|
|
|
|
|
// If not a valid % encoded hex value, return with error
|
|
|
|
|
if i+2 >= len(raw) || !isHex(raw[i+1]) || !isHex(raw[i+2]) {
|
|
|
|
|
return "", ErrInvalidRequest.Extend("unescaping host info " + raw)
|
|
|
|
|
return "", errors.Wrap(raw, ErrUnescapingHost)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// In the host component % encoding can only be used
|
|
|
|
|
// for non-ASCII bytes. And rfc6874 introduces %25 for
|
|
|
|
|
// escaped percent sign in IPv6 literals
|
|
|
|
|
if unHex(raw[i+1]) < 8 && raw[i:i+3] != "%25" {
|
|
|
|
|
return "", ErrInvalidRequest.Extend("unescaping host " + raw)
|
|
|
|
|
return "", errors.Wrap(raw, ErrUnescapingHost)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Skip iteration past the
|
|
|
|
@ -163,7 +166,7 @@ func unescapeHost(raw string) (string, errors.Error) {
|
|
|
|
|
default:
|
|
|
|
|
// If within ASCII range, and shoud be escaped, return error
|
|
|
|
|
if raw[i] < 0x80 && shouldHostEscape(raw[i]) {
|
|
|
|
|
return "", ErrInvalidRequest.Extend("unescaping host " + raw)
|
|
|
|
|
return "", errors.Wrap(raw, ErrUnescapingHost)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iter
|
|
|
|
@ -178,7 +181,7 @@ func unescapeHost(raw string) (string, errors.Error) {
|
|
|
|
|
return unescape(raw, count), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unescapePath(raw string) (string, errors.Error) {
|
|
|
|
|
func unescapePath(raw string) (string, error) {
|
|
|
|
|
// Count all the percent signs
|
|
|
|
|
count := 0
|
|
|
|
|
length := len(raw)
|
|
|
|
@ -190,7 +193,7 @@ func unescapePath(raw string) (string, errors.Error) {
|
|
|
|
|
|
|
|
|
|
// If not a valid % encoded hex value, return with error
|
|
|
|
|
if i+2 >= length || !isHex(raw[i+1]) || !isHex(raw[i+2]) {
|
|
|
|
|
return "", ErrInvalidRequest.Extend("unescaping path " + raw)
|
|
|
|
|
return "", errors.Wrap(raw, ErrUnescapingPath)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Skip iteration past the
|
|
|
|
@ -240,11 +243,11 @@ func EscapePath(path string) string {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ParseEncodedHost parses encoded host info, safely returning unescape host and port
|
|
|
|
|
func ParseEncodedHost(raw string) (string, string, errors.Error) {
|
|
|
|
|
func ParseEncodedHost(raw string) (string, string, error) {
|
|
|
|
|
// Unescape the host info
|
|
|
|
|
raw, err := unescapeHost(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", "", err
|
|
|
|
|
return "", "", errors.WrapError(ErrParsingHost, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Split by last ':' and return
|
|
|
|
@ -253,14 +256,14 @@ func ParseEncodedHost(raw string) (string, string, errors.Error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ParseEncodedURI parses encoded URI, safely returning unescaped path and still-escaped query
|
|
|
|
|
func ParseEncodedURI(received string) (string, string, errors.Error) {
|
|
|
|
|
func ParseEncodedURI(received string) (string, string, error) {
|
|
|
|
|
// Split into path and query
|
|
|
|
|
rawPath, query := SplitBy(received, "?")
|
|
|
|
|
|
|
|
|
|
// Unescape path, query is up-to CGI scripts
|
|
|
|
|
rawPath, err := unescapePath(rawPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", "", ErrInvalidRequest.Wrap(err)
|
|
|
|
|
return "", "", errors.WrapError(ErrParsingURI, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the raw path and query
|
|
|
|
|