diff options
Diffstat (limited to 'src/pkg/http/url.go')
-rw-r--r-- | src/pkg/http/url.go | 72 |
1 files changed, 50 insertions, 22 deletions
diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go index 156f3ad01..87612a96a 100644 --- a/src/pkg/http/url.go +++ b/src/pkg/http/url.go @@ -9,12 +9,19 @@ package http import ( "os"; - "strings" + "strconv"; + "strings"; ) -// Errors introduced by ParseURL. -type BadURL struct { - os.ErrorString +// URLError reports an error and the operation and URL that caused it. +type URLError struct { + Op string; + URL string; + Error os.Error; +} + +func (e *URLError) String() string { + return e.Op + " " + e.URL + ": " + e.Error.String(); } func ishex(c byte) bool { @@ -41,6 +48,11 @@ func unhex(c byte) byte { return 0 } +type URLEscapeError string +func (e URLEscapeError) String() string { + return "invalid URL escape " + strconv.Quote(string(e)); +} + // Return true if the specified character should be escaped when appearing in a // URL string. // @@ -56,7 +68,7 @@ func shouldEscape(c byte) bool { // URLUnescape unescapes a URL-encoded string, // converting %AB into the byte 0xAB and '+' into ' ' (space). -// It returns a BadURL error if any % is not followed +// It returns an error if any % is not followed // by two hexadecimal digits. func URLUnescape(s string) (string, os.Error) { // Count %, check that they're well-formed. @@ -67,7 +79,11 @@ func URLUnescape(s string) (string, os.Error) { case '%': n++; if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { - return "", BadURL{"invalid hexadecimal escape"} + s = s[i:len(s)]; + if len(s) > 3 { + s = s[0:3]; + } + return "", URLEscapeError(s); } i += 3; case '+': @@ -124,18 +140,18 @@ func URLEscape(s string) string { t := make([]byte, len(s)+2*hexCount); j := 0; for i := 0; i < len(s); i++ { - c := s[i]; - if !shouldEscape(c) { - t[j] = s[i]; - j++; - } else if c == ' ' { + switch c := s[i]; { + case c == ' ': t[j] = '+'; j++; - } else { + case shouldEscape(c): t[j] = '%'; t[j+1] = "0123456789abcdef"[c>>4]; t[j+2] = "0123456789abcdef"[c&15]; j += 3; + default: + t[j] = s[i]; + j++; } } return string(t); @@ -177,7 +193,7 @@ func getscheme(rawurl string) (scheme, path string, err os.Error) { } case c == ':': if i == 0 { - return "", "", BadURL{"missing protocol scheme"} + return "", "", os.ErrorString("missing protocol scheme") } return rawurl[0:i], rawurl[i+1:len(rawurl)], nil default: @@ -204,15 +220,21 @@ func split(s string, c byte, cutc bool) (string, string) { return s, "" } +// TODO(rsc): The BUG comment is supposed to appear in the godoc output +// in a BUGS section, but that got lost in the transition to godoc. + // BUG(rsc): ParseURL should canonicalize the path, // removing unnecessary . and .. elements. + + // ParseURL parses rawurl into a URL structure. // The string rawurl is assumed not to have a #fragment suffix. // (Web browsers strip #fragment before sending the URL to a web server.) func ParseURL(rawurl string) (url *URL, err os.Error) { if rawurl == "" { - return nil, BadURL{"empty url"} + err = os.ErrorString("empty url"); + goto Error; } url = new(URL); url.Raw = rawurl; @@ -220,7 +242,7 @@ func ParseURL(rawurl string) (url *URL, err os.Error) { // split off possible leading "http:", "mailto:", etc. var path string; if url.Scheme, path, err = getscheme(rawurl); err != nil { - return nil, err + goto Error; } url.RawPath = path; @@ -245,25 +267,31 @@ func ParseURL(rawurl string) (url *URL, err os.Error) { // What's left is the path. // TODO: Canonicalize (remove . and ..)? if url.Path, err = URLUnescape(path); err != nil { - return nil, err + goto Error; } // Remove escapes from the Authority and Userinfo fields, and verify // that Scheme and Host contain no escapes (that would be illegal). if url.Authority, err = URLUnescape(url.Authority); err != nil { - return nil, err + goto Error; } if url.Userinfo, err = URLUnescape(url.Userinfo); err != nil { - return nil, err + goto Error; } if strings.Index(url.Scheme, "%") >= 0 { - return nil, BadURL{"hexadecimal escape in scheme"} + err = os.ErrorString("hexadecimal escape in scheme"); + goto Error; } if strings.Index(url.Host, "%") >= 0 { - return nil, BadURL{"hexadecimal escape in host"} + err = os.ErrorString("hexadecimal escape in host"); + goto Error; } - return url, nil + return url, nil; + +Error: + return nil, &URLError{"parse", rawurl, err} + } // ParseURLReference is like ParseURL but allows a trailing #fragment. @@ -274,7 +302,7 @@ func ParseURLReference(rawurlref string) (url *URL, err os.Error) { return nil, err } if url.Fragment, err = URLUnescape(frag); err != nil { - return nil, err + return nil, &URLError{"parse", rawurl, err} } return url, nil } |