summaryrefslogtreecommitdiff
path: root/src/pkg/net/url/url.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/net/url/url.go')
-rw-r--r--src/pkg/net/url/url.go92
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