Commit dafd9f0b authored by Gustavo Niemeyer's avatar Gustavo Niemeyer

net/url: cleaned up URL interface (v2)

Duplicated fields from URL were dropped so that its behavior
is simple and expected when being stringified and when being
operated by packages like http. Most of the preserved fields
are in unencoded form, except for RawQuery which continues to
exist and be more easily handled via url.Query().

The RawUserinfo field was also replaced since it wasn't practical
to use and had limitations when operating with empty usernames
and passwords which are allowed by the RFC. In its place the
Userinfo type was introduced and made accessible through the
url.User and url.UserPassword functions.

What was previous built as:

        url.URL{RawUserinfo: url.EncodeUserinfo("user", ""), ...}

Is now built as:

        url.URL{User: url.User("user"), ...}

R=rsc, bradfitz, gustavo
CC=golang-dev
https://golang.org/cl/5498076
parent a5aa4d33
......@@ -124,7 +124,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
"GATEWAY_INTERFACE=CGI/1.1",
"REQUEST_METHOD=" + req.Method,
"QUERY_STRING=" + req.URL.RawQuery,
"REQUEST_URI=" + req.URL.RawPath,
"REQUEST_URI=" + req.URL.RequestURI(),
"PATH_INFO=" + pathInfo,
"SCRIPT_NAME=" + root,
"SCRIPT_FILENAME=" + h.Path,
......
......@@ -121,9 +121,8 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
req.Header = make(Header)
}
info := req.URL.RawUserinfo
if len(info) > 0 {
req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(info)))
if u := req.URL.User; u != nil {
req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String())))
}
return t.RoundTrip(req)
}
......
......@@ -124,16 +124,8 @@ func DumpRequest(req *http.Request, body bool) (dump []byte, err error) {
var b bytes.Buffer
urlStr := req.URL.Raw
if urlStr == "" {
urlStr = valueOrDefault(req.URL.EncodedPath(), "/")
if req.URL.RawQuery != "" {
urlStr += "?" + req.URL.RawQuery
}
}
fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), urlStr,
req.ProtoMajor, req.ProtoMinor)
fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
req.URL.RequestURI(), req.ProtoMajor, req.ProtoMinor)
host := req.Host
if host == "" && req.URL != nil {
......
......@@ -59,11 +59,6 @@ func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if q := req.URL.RawQuery; q != "" {
req.URL.RawPath = req.URL.Path + "?" + q
} else {
req.URL.RawPath = req.URL.Path
}
req.URL.RawQuery = target.RawQuery
}
return &ReverseProxy{Director: director}
......
......@@ -44,15 +44,9 @@ var reqTests = []reqTest{
&Request{
Method: "GET",
URL: &url.URL{
Raw: "http://www.techcrunch.com/",
Scheme: "http",
RawPath: "/",
RawAuthority: "www.techcrunch.com",
RawUserinfo: "",
Host: "www.techcrunch.com",
Path: "/",
RawQuery: "",
Fragment: "",
Scheme: "http",
Host: "www.techcrunch.com",
Path: "/",
},
Proto: "HTTP/1.1",
ProtoMajor: 1,
......@@ -86,9 +80,7 @@ var reqTests = []reqTest{
&Request{
Method: "GET",
URL: &url.URL{
Raw: "/",
Path: "/",
RawPath: "/",
Path: "/",
},
Proto: "HTTP/1.1",
ProtoMajor: 1,
......@@ -113,15 +105,7 @@ var reqTests = []reqTest{
&Request{
Method: "GET",
URL: &url.URL{
Raw: "//user@host/is/actually/a/path/",
Scheme: "",
RawPath: "//user@host/is/actually/a/path/",
RawAuthority: "",
RawUserinfo: "",
Host: "",
Path: "//user@host/is/actually/a/path/",
RawQuery: "",
Fragment: "",
Path: "//user@host/is/actually/a/path/",
},
Proto: "HTTP/1.1",
ProtoMajor: 1,
......@@ -170,9 +154,7 @@ var reqTests = []reqTest{
&Request{
Method: "POST",
URL: &url.URL{
Raw: "/",
Path: "/",
RawPath: "/",
Path: "/",
},
TransferEncoding: []string{"chunked"},
Proto: "HTTP/1.1",
......
......@@ -302,26 +302,14 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
host = req.URL.Host
}
urlStr := req.URL.RawPath
if strings.HasPrefix(urlStr, "?") {
urlStr = "/" + urlStr // Issue 2344
}
if urlStr == "" {
urlStr = valueOrDefault(req.URL.RawPath, valueOrDefault(req.URL.EncodedPath(), "/"))
if req.URL.RawQuery != "" {
urlStr += "?" + req.URL.RawQuery
}
if usingProxy {
if urlStr == "" || urlStr[0] != '/' {
urlStr = "/" + urlStr
}
urlStr = req.URL.Scheme + "://" + host + urlStr
}
ruri := req.URL.RequestURI()
if usingProxy && req.URL.Scheme != "" && req.URL.Opaque == "" {
ruri = req.URL.Scheme + "://" + host + ruri
}
// TODO(bradfitz): escape at least newlines in urlStr?
// TODO(bradfitz): escape at least newlines in ruri?
bw := bufio.NewWriter(w)
fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), urlStr)
fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
// Header lines
fmt.Fprintf(bw, "Host: %s\r\n", host)
......
......@@ -32,15 +32,9 @@ var reqWriteTests = []reqWriteTest{
Req: Request{
Method: "GET",
URL: &url.URL{
Raw: "http://www.techcrunch.com/",
Scheme: "http",
RawPath: "http://www.techcrunch.com/",
RawAuthority: "www.techcrunch.com",
RawUserinfo: "",
Host: "www.techcrunch.com",
Path: "/",
RawQuery: "",
Fragment: "",
Scheme: "http",
Host: "www.techcrunch.com",
Path: "/",
},
Proto: "HTTP/1.1",
ProtoMajor: 1,
......@@ -60,7 +54,7 @@ var reqWriteTests = []reqWriteTest{
Form: map[string][]string{},
},
WantWrite: "GET http://www.techcrunch.com/ HTTP/1.1\r\n" +
WantWrite: "GET / HTTP/1.1\r\n" +
"Host: www.techcrunch.com\r\n" +
"User-Agent: Fake\r\n" +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" +
......@@ -198,7 +192,7 @@ var reqWriteTests = []reqWriteTest{
"\r\n" +
"abcdef",
WantProxy: "POST / HTTP/1.1\r\n" +
WantProxy: "POST http://example.com/ HTTP/1.1\r\n" +
"Host: example.com\r\n" +
"User-Agent: Go http package\r\n" +
"Content-Length: 6\r\n" +
......
......@@ -642,7 +642,7 @@ func TestServerExpect(t *testing.T) {
// Note using r.FormValue("readbody") because for POST
// requests that would read from r.Body, which we only
// conditionally want to do.
if strings.Contains(r.URL.RawPath, "readbody=true") {
if strings.Contains(r.URL.RawQuery, "readbody=true") {
ioutil.ReadAll(r.Body)
w.Write([]byte("Hi"))
} else {
......
......@@ -229,9 +229,8 @@ func (cm *connectMethod) proxyAuth() string {
if cm.proxyURL == nil {
return ""
}
proxyInfo := cm.proxyURL.RawUserinfo
if proxyInfo != "" {
return "Basic " + base64.URLEncoding.EncodeToString([]byte(proxyInfo))
if u := cm.proxyURL.User; u != nil {
return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String()))
}
return ""
}
......@@ -332,7 +331,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
case cm.targetScheme == "https":
connectReq := &Request{
Method: "CONNECT",
URL: &url.URL{RawPath: cm.targetAddr},
URL: &url.URL{Opaque: cm.targetAddr},
Host: cm.targetAddr,
Header: make(Header),
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -343,7 +343,7 @@ func hixie76ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer)
}
// 4.1. Opening handshake.
// Step 5. send a request line.
bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
// Step 6-14. push request headers in fields.
fields := []string{
......@@ -456,7 +456,7 @@ func hixie75ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer)
if config.Version != ProtocolVersionHixie75 {
panic("wrong protocol version.")
}
bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
bw.WriteString("Upgrade: WebSocket\r\n")
bw.WriteString("Connection: Upgrade\r\n")
bw.WriteString("Host: " + config.Location.Host + "\r\n")
......@@ -557,7 +557,7 @@ func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Req
} else {
scheme = "ws"
}
c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RequestURI())
if err != nil {
return http.StatusBadRequest, err
}
......@@ -653,7 +653,7 @@ func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Req
} else {
scheme = "ws"
}
c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RequestURI())
if err != nil {
return http.StatusBadRequest, err
}
......
......@@ -390,7 +390,7 @@ func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (er
panic("wrong protocol version.")
}
bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
bw.WriteString("Host: " + config.Location.Host + "\r\n")
bw.WriteString("Upgrade: websocket\r\n")
......@@ -505,7 +505,7 @@ func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Reques
} else {
scheme = "ws"
}
c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RequestURI())
if err != nil {
return http.StatusBadRequest, err
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment