diff options
Diffstat (limited to 'src/pkg/net/url/url.go')
-rw-r--r-- | src/pkg/net/url/url.go | 92 |
1 files changed, 70 insertions, 22 deletions
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go index 17bf0d3a3..a39964ea1 100644 --- a/src/pkg/net/url/url.go +++ b/src/pkg/net/url/url.go @@ -7,7 +7,9 @@ package url import ( + "bytes" "errors" + "sort" "strconv" "strings" ) @@ -218,11 +220,18 @@ func escape(s string, mode encoding) string { // // scheme:opaque[?query][#fragment] // +// 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/"}. type URL struct { Scheme string Opaque string // encoded opaque data User *Userinfo // username and password information - Host string + Host string // host or host:port Path string RawQuery string // encoded query values, without '?' Fragment string // fragment for references, without '#' @@ -359,11 +368,17 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) { } url = new(URL) + if rawurl == "*" { + url.Path = "*" + return + } + // Split off possible leading "http:", "mailto:", etc. // Cannot contain escaped characters. if url.Scheme, rest, err = getscheme(rawurl); err != nil { goto Error } + url.Scheme = strings.ToLower(url.Scheme) rest, url.RawQuery = split(rest, '?', true) @@ -379,7 +394,7 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) { } } - if (url.Scheme != "" || !viaRequest) && strings.HasPrefix(rest, "//") && !strings.HasPrefix(rest, "///") { + if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") { var authority string authority, rest = split(rest[2:], '/', false) url.User, url.Host, err = parseAuthority(authority) @@ -427,30 +442,35 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { // String reassembles the URL into a valid URL string. func (u *URL) String() string { - // TODO: Rewrite to use bytes.Buffer - result := "" + var buf bytes.Buffer if u.Scheme != "" { - result += u.Scheme + ":" + buf.WriteString(u.Scheme) + buf.WriteByte(':') } if u.Opaque != "" { - result += u.Opaque + buf.WriteString(u.Opaque) } else { - if u.Host != "" || u.User != nil { - result += "//" + if u.Scheme != "" || u.Host != "" || u.User != nil { + buf.WriteString("//") if u := u.User; u != nil { - result += u.String() + "@" + buf.WriteString(u.String()) + buf.WriteByte('@') + } + if h := u.Host; h != "" { + buf.WriteString(h) } - result += u.Host } - result += escape(u.Path, encodePath) + buf.WriteString(escape(u.Path, encodePath)) } if u.RawQuery != "" { - result += "?" + u.RawQuery + buf.WriteByte('?') + buf.WriteString(u.RawQuery) } if u.Fragment != "" { - result += "#" + escape(u.Fragment, encodeFragment) + buf.WriteByte('#') + buf.WriteString(escape(u.Fragment, encodeFragment)) } - return result + return buf.String() } // Values maps a string key to a list of values. @@ -519,12 +539,16 @@ func parseQuery(m Values, query string) (err error) { } key, err1 := QueryUnescape(key) if err1 != nil { - err = err1 + if err == nil { + err = err1 + } continue } value, err1 = QueryUnescape(value) if err1 != nil { - err = err1 + if err == nil { + err = err1 + } continue } m[key] = append(m[key], value) @@ -538,14 +562,24 @@ func (v Values) Encode() string { if v == nil { return "" } - parts := make([]string, 0, len(v)) // will be large enough for most uses - for k, vs := range v { + var buf bytes.Buffer + keys := make([]string, 0, len(v)) + for k := range v { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := v[k] prefix := QueryEscape(k) + "=" for _, v := range vs { - parts = append(parts, prefix+QueryEscape(v)) + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(prefix) + buf.WriteString(QueryEscape(v)) } } - return strings.Join(parts, "&") + return buf.String() } // resolvePath applies special path segments from refs and applies @@ -556,23 +590,33 @@ func resolvePath(basepath string, refpath string) string { if len(base) == 0 { base = []string{""} } + + rm := true for idx, ref := range refs { switch { case ref == ".": - base[len(base)-1] = "" + if idx == 0 { + base[len(base)-1] = "" + rm = true + } else { + rm = false + } case ref == "..": newLen := len(base) - 1 if newLen < 1 { newLen = 1 } base = base[0:newLen] - base[len(base)-1] = "" + if rm { + base[len(base)-1] = "" + } default: if idx == 0 || base[len(base)-1] == "" { base[len(base)-1] = ref } else { base = append(base, ref) } + rm = false } } return strings.Join(base, "/") @@ -650,6 +694,10 @@ func (u *URL) RequestURI() string { if result == "" { result = "/" } + } else { + if strings.HasPrefix(result, "//") { + result = u.Scheme + ":" + result + } } if u.RawQuery != "" { result += "?" + u.RawQuery |