diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-02-14 13:23:51 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-02-14 13:23:51 +0100 |
commit | 758ff64c69e34965f8af5b2d6ffd65e8d7ab2150 (patch) | |
tree | 6d6b34f8c678862fe9b56c945a7b63f68502c245 /src/pkg/http/url.go | |
parent | 3e45412327a2654a77944249962b3652e6142299 (diff) | |
download | golang-upstream/2011-02-01.1.tar.gz |
Imported Upstream version 2011-02-01.1upstream/2011-02-01.1
Diffstat (limited to 'src/pkg/http/url.go')
-rw-r--r-- | src/pkg/http/url.go | 182 |
1 files changed, 124 insertions, 58 deletions
diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go index f0ac4c1df..efd90d81e 100644 --- a/src/pkg/http/url.go +++ b/src/pkg/http/url.go @@ -114,62 +114,6 @@ func shouldEscape(c byte, mode encoding) bool { return true } -// CanonicalPath applies the algorithm specified in RFC 2396 to -// simplify the path, removing unnecessary . and .. elements. -func CanonicalPath(path string) string { - buf := []byte(path) - a := buf[0:0] - // state helps to find /.. ^.. ^. and /. patterns. - // state == 1 - prev char is '/' or beginning of the string. - // state > 1 - prev state > 0 and prev char was '.' - // state == 0 - otherwise - state := 1 - cnt := 0 - for _, v := range buf { - switch v { - case '/': - s := state - state = 1 - switch s { - case 2: - a = a[0 : len(a)-1] - continue - case 3: - if cnt > 0 { - i := len(a) - 4 - for ; i >= 0 && a[i] != '/'; i-- { - } - a = a[0 : i+1] - cnt-- - continue - } - default: - if len(a) > 0 { - cnt++ - } - } - case '.': - if state > 0 { - state++ - } - default: - state = 0 - } - l := len(a) - a = a[0 : l+1] - a[l] = v - } - switch { - case state == 2: - a = a[0 : len(a)-1] - case state == 3 && cnt > 0: - i := len(a) - 4 - for ; i >= 0 && a[i] != '/'; i-- { - } - a = a[0 : i+1] - } - return string(a) -} // URLUnescape unescapes a string in ``URL encoded'' form, // converting %AB into the byte 0xAB and '+' into ' ' (space). @@ -385,7 +329,25 @@ func split(s string, c byte, cutc bool) (string, string) { // 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.) +// The rawurl may be relative or absolute. func ParseURL(rawurl string) (url *URL, err os.Error) { + return parseURL(rawurl, false) +} + +// ParseRequestURL parses rawurl into a URL structure. It assumes that +// rawurl was received from an HTTP request, so the rawurl is interpreted +// 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.) +func ParseRequestURL(rawurl string) (url *URL, err os.Error) { + return parseURL(rawurl, true) +} + +// parseURL parses a URL from a string in one of two contexts. If +// 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. +func parseURL(rawurl string, viaRequest bool) (url *URL, err os.Error) { if rawurl == "" { err = os.ErrorString("empty url") goto Error @@ -400,7 +362,9 @@ func ParseURL(rawurl string) (url *URL, err os.Error) { goto Error } - if url.Scheme != "" && (len(path) == 0 || path[0] != '/') { + leadingSlash := strings.HasPrefix(path, "/") + + if url.Scheme != "" && !leadingSlash { // RFC 2396: // Absolute URI (has scheme) with non-rooted path // is uninterpreted. It doesn't even have a ?query. @@ -412,6 +376,11 @@ func ParseURL(rawurl string) (url *URL, err os.Error) { } url.OpaquePath = true } else { + if viaRequest && !leadingSlash { + err = os.ErrorString("invalid URI for request") + goto Error + } + // Split off query before parsing path further. url.RawPath = path path, query := split(path, '?', false) @@ -420,7 +389,8 @@ func ParseURL(rawurl string) (url *URL, err os.Error) { } // Maybe path is //authority/path - if url.Scheme != "" && len(path) > 2 && path[0:2] == "//" { + if (url.Scheme != "" || !viaRequest) && + strings.HasPrefix(path, "//") && !strings.HasPrefix(path, "///") { url.RawAuthority, path = split(path[2:], '/', false) url.RawPath = url.RawPath[2+len(url.RawAuthority):] } @@ -527,3 +497,99 @@ func EncodeQuery(m map[string][]string) string { } return strings.Join(parts, "&") } + +// resolvePath applies special path segments from refs and applies +// them to base, per RFC 2396. +func resolvePath(basepath string, refpath string) string { + base := strings.Split(basepath, "/", -1) + refs := strings.Split(refpath, "/", -1) + if len(base) == 0 { + base = []string{""} + } + for idx, ref := range refs { + switch { + case ref == ".": + base[len(base)-1] = "" + case ref == "..": + newLen := len(base) - 1 + if newLen < 1 { + newLen = 1 + } + base = base[0:newLen] + base[len(base)-1] = "" + default: + if idx == 0 || base[len(base)-1] == "" { + base[len(base)-1] = ref + } else { + base = append(base, ref) + } + } + } + return strings.Join(base, "/") +} + +// IsAbs returns true if the URL is absolute. +func (url *URL) IsAbs() bool { + return url.Scheme != "" +} + +// ParseURL parses a URL in the context of a base URL. The URL in ref +// may be relative or absolute. ParseURL returns nil, err on parse +// failure, otherwise its return value is the same as ResolveReference. +func (base *URL) ParseURL(ref string) (*URL, os.Error) { + refurl, err := ParseURL(ref) + if err != nil { + return nil, err + } + return base.ResolveReference(refurl), nil +} + +// ResolveReference resolves a URI reference to an absolute URI from +// an absolute base URI, per RFC 2396 Section 5.2. The URI reference +// may be relative or absolute. ResolveReference always returns a new +// 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. +func (base *URL) ResolveReference(ref *URL) *URL { + url := new(URL) + switch { + case ref.IsAbs(): + *url = *ref + default: + // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] + *url = *base + if ref.RawAuthority != "" { + // The "net_path" case. + url.RawAuthority = ref.RawAuthority + url.Host = ref.Host + url.RawUserinfo = ref.RawUserinfo + } + switch { + case url.OpaquePath: + url.Path = ref.Path + url.RawPath = ref.RawPath + url.RawQuery = ref.RawQuery + case strings.HasPrefix(ref.Path, "/"): + // The "abs_path" case. + url.Path = ref.Path + url.RawPath = ref.RawPath + url.RawQuery = ref.RawQuery + default: + // The "rel_path" case. + path := resolvePath(base.Path, ref.Path) + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + url.Path = path + url.RawPath = url.Path + url.RawQuery = ref.RawQuery + if ref.RawQuery != "" { + url.RawPath += "?" + url.RawQuery + } + } + + url.Fragment = ref.Fragment + } + url.Raw = url.String() + return url +} |