if r.Host != "" {
// Hostname is provided, so we can reasonably construct a URL,
// even if we have to assume 'http' for the scheme.
- r.RawURL = "http://" + r.Host + params["REQUEST_URI"]
- url, err := url.Parse(r.RawURL)
+ rawurl := "http://" + r.Host + params["REQUEST_URI"]
+ url, err := url.Parse(rawurl)
if err != nil {
- return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL)
+ return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
}
r.URL = url
}
// Fallback logic if we don't have a Host header or the URL
// failed to parse
if r.URL == nil {
- r.RawURL = params["REQUEST_URI"]
- url, err := url.Parse(r.RawURL)
+ uriStr := params["REQUEST_URI"]
+ url, err := url.Parse(uriStr)
if err != nil {
- return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL)
+ return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
}
r.URL = url
}
if g, e := req.Header.Get("Foo-Bar"), "baz"; e != g {
t.Errorf("expected Foo-Bar %q; got %q", e, g)
}
- if g, e := req.RawURL, "http://example.com/path?a=b"; e != g {
- t.Errorf("expected RawURL %q; got %q", e, g)
- }
if g, e := req.URL.String(), "http://example.com/path?a=b"; e != g {
t.Errorf("expected URL %q; got %q", e, g)
}
if err != nil {
t.Fatalf("RequestFromMap: %v", err)
}
- if g, e := req.RawURL, "/path?a=b"; e != g {
- t.Errorf("expected RawURL %q; got %q", e, g)
- }
if req.URL == nil {
t.Fatalf("unexpected nil URL")
}
newReq := &http.Request{
Method: "GET",
URL: url,
- RawURL: path,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
&Request{
Method: "GET",
- RawURL: "http://www.techcrunch.com/",
URL: &url.URL{
Raw: "http://www.techcrunch.com/",
Scheme: "http",
&Request{
Method: "GET",
- RawURL: "/",
URL: &url.URL{
Raw: "/",
Path: "/",
&Request{
Method: "GET",
- RawURL: "//user@host/is/actually/a/path/",
URL: &url.URL{
Raw: "//user@host/is/actually/a/path/",
Scheme: "",
// A Request represents a parsed HTTP request header.
type Request struct {
- Method string // GET, POST, PUT, etc.
- RawURL string // The raw URL given in the request.
- URL *url.URL // Parsed URL.
+ Method string // GET, POST, PUT, etc.
+ URL *url.URL
// The protocol version for incoming requests.
// Outgoing requests always use HTTP/1.1.
// Write writes an HTTP/1.1 request -- header and body -- in wire format.
// This method consults the following fields of req:
// Host
-// RawURL, if non-empty, or else URL
+// URL
// Method (defaults to "GET")
// Header
// ContentLength
// WriteProxy is like Write but writes the request in the form
// expected by an HTTP proxy. In particular, WriteProxy writes the
// initial Request-URI line of the request with an absolute URI, per
-// section 5.1.2 of RFC 2616, including the scheme and host. If
-// req.RawURL is non-empty, WriteProxy uses it unchanged. In either
-// case, WriteProxy also writes a Host header, using either req.Host
-// or req.URL.Host.
+// section 5.1.2 of RFC 2616, including the scheme and host. In
+// either case, WriteProxy also writes a Host header, using either
+// req.Host or req.URL.Host.
func (req *Request) WriteProxy(w io.Writer) os.Error {
return req.write(w, true)
}
func (req *Request) dumpWrite(w io.Writer) os.Error {
- urlStr := req.RawURL
- if urlStr == "" {
- urlStr = valueOrDefault(req.URL.EncodedPath(), "/")
- if req.URL.RawQuery != "" {
- urlStr += "?" + req.URL.RawQuery
- }
+ // TODO(bradfitz): RawPath here?
+ urlStr := valueOrDefault(req.URL.EncodedPath(), "/")
+ if req.URL.RawQuery != "" {
+ urlStr += "?" + req.URL.RawQuery
}
bw := bufio.NewWriter(w)
host = req.URL.Host
}
- urlStr := req.RawURL
+ urlStr := req.URL.RawPath
+ if strings.HasPrefix(urlStr, "?") {
+ urlStr = "/" + urlStr // Issue 2344
+ }
if urlStr == "" {
- urlStr = valueOrDefault(req.URL.EncodedPath(), "/")
+ urlStr = valueOrDefault(req.URL.RawPath, valueOrDefault(req.URL.EncodedPath(), "/"))
if req.URL.RawQuery != "" {
urlStr += "?" + req.URL.RawQuery
}
urlStr = req.URL.Scheme + "://" + host + urlStr
}
}
+ // TODO(bradfitz): escape at least newlines in urlStr?
bw := bufio.NewWriter(w)
fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), urlStr)
if f = strings.SplitN(s, " ", 3); len(f) < 3 {
return nil, &badStringError{"malformed HTTP request", s}
}
- req.Method, req.RawURL, req.Proto = f[0], f[1], f[2]
+ var rawurl string
+ req.Method, rawurl, req.Proto = f[0], f[1], f[2]
var ok bool
if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok {
return nil, &badStringError{"malformed HTTP version", req.Proto}
}
- if req.URL, err = url.ParseRequest(req.RawURL); err != nil {
+ if req.URL, err = url.ParseRequest(rawurl); err != nil {
return nil, err
}
{
Req: Request{
Method: "GET",
- RawURL: "http://www.techcrunch.com/",
URL: &url.URL{
Raw: "http://www.techcrunch.com/",
Scheme: "http",
{
Req: Request{
Method: "POST",
- RawURL: "http://example.com/",
+ URL: mustParseURL("http://example.com/"),
Host: "example.com",
Header: Header{
"Content-Length": []string{"10"}, // ignored
Body: []byte("abcdef"),
- WantWrite: "POST http://example.com/ HTTP/1.1\r\n" +
+ WantWrite: "POST / HTTP/1.1\r\n" +
"Host: example.com\r\n" +
"User-Agent: Go http package\r\n" +
"Content-Length: 6\r\n" +
"\r\n" +
"abcdef",
- WantProxy: "POST http://example.com/ HTTP/1.1\r\n" +
+ WantProxy: "POST / HTTP/1.1\r\n" +
"Host: example.com\r\n" +
"User-Agent: Go http package\r\n" +
"Content-Length: 6\r\n" +
{
Req: Request{
Method: "GET",
- RawURL: "/search",
+ URL: mustParseURL("/search"),
Host: "www.google.com",
},
"Host: www.google.com\r\n" +
"User-Agent: Go http package\r\n" +
"\r\n",
-
- // Looks weird but RawURL overrides what WriteProxy would choose.
- WantProxy: "GET /search HTTP/1.1\r\n" +
- "Host: www.google.com\r\n" +
- "User-Agent: Go http package\r\n" +
- "\r\n",
},
// Request with a 0 ContentLength and a 0 byte body.
{
Req: Request{
Method: "POST",
- RawURL: "/",
+ URL: mustParseURL("/"),
Host: "example.com",
ProtoMajor: 1,
ProtoMinor: 1,
{
Req: Request{
Method: "POST",
- RawURL: "/",
+ URL: mustParseURL("/"),
Host: "example.com",
ProtoMajor: 1,
ProtoMinor: 1,
{
Req: Request{
Method: "POST",
- RawURL: "/",
+ URL: mustParseURL("/"),
Host: "example.com",
ProtoMajor: 1,
ProtoMinor: 1,
{
Req: Request{
Method: "POST",
- RawURL: "/",
+ URL: mustParseURL("/"),
Host: "example.com",
ProtoMajor: 1,
ProtoMinor: 1,
{
Req: Request{
Method: "POST",
- RawURL: "/",
+ URL: mustParseURL("/"),
Host: "example.com",
ProtoMajor: 1,
ProtoMinor: 1,
{
Req: Request{
Method: "GET",
- RawURL: "/foo",
+ URL: mustParseURL("/foo"),
ProtoMajor: 1,
ProtoMinor: 0,
Header: Header{
// .. but we can't call Request.Write on it, due to its lack of Host header.
// TODO(bradfitz): there might be an argument to allow this, but for now I'd
// rather let HTTP/1.0 continue to die.
- WantError: os.NewError("http: Request.Write on Request with no Host or URL set"),
+ WantWrite: "GET /foo HTTP/1.1\r\n" +
+ "Host: \r\n" +
+ "User-Agent: Go http package\r\n" +
+ "X-Foo: X-Bar\r\n\r\n",
},
}
func chunk(s string) string {
return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
}
+
+func mustParseURL(s string) *url.URL {
+ u, err := url.Parse(s)
+ if err != nil {
+ panic(fmt.Sprintf("Error parsing URL %q: %v", s, err))
+ }
+ return u
+}
// RoundTrip implements the RoundTripper interface.
func (t *Transport) RoundTrip(req *Request) (resp *Response, err os.Error) {
if req.URL == nil {
- if req.URL, err = url.Parse(req.RawURL); err != nil {
- return
- }
+ return nil, os.NewError("http: nil Request.URL")
}
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
t.lk.Lock()
case cm.targetScheme == "https":
connectReq := &Request{
Method: "CONNECT",
- RawURL: cm.targetAddr,
+ URL: &url.URL{RawPath: cm.targetAddr},
Host: cm.targetAddr,
Header: make(Header),
}
fetch := func(n int) string {
req := new(Request)
var err os.Error
- req.URL, err = url.Parse(ts.URL + fmt.Sprintf("?close=%v", connectionClose))
+ req.URL, err = url.Parse(ts.URL + fmt.Sprintf("/?close=%v", connectionClose))
if err != nil {
t.Fatalf("URL parse error: %v", err)
}
}
}
-func TestTransportNilURL(t *testing.T) {
- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- fmt.Fprintf(w, "Hi")
- }))
- defer ts.Close()
-
- req := new(Request)
- req.URL = nil // what we're actually testing
- req.Method = "GET"
- req.RawURL = ts.URL
- req.Proto = "HTTP/1.1"
- req.ProtoMajor = 1
- req.ProtoMinor = 1
- req.Header = make(Header)
-
- tr := &Transport{}
- res, err := tr.RoundTrip(req)
- if err != nil {
- t.Fatalf("unexpected RoundTrip error: %v", err)
- }
- body, err := ioutil.ReadAll(res.Body)
- if g, e := string(body), "Hi"; g != e {
- t.Fatalf("Expected response body of %q; got %q", e, g)
- }
-}
-
var roundTripTests = []struct {
accept string
expectAccept string
c := &Client{Transport: &Transport{}}
// First fetch something large, but only read some of it.
- res, err := c.Get(ts.URL + "?body=large&chunked=" + chunked)
+ res, err := c.Get(ts.URL + "/?body=large&chunked=" + chunked)
if err != nil {
t.Fatalf("large get: %v", err)
}
}
// Then something small.
- res, err = c.Get(ts.URL + "?chunked=" + chunked)
+ res, err = c.Get(ts.URL + "/?chunked=" + chunked)
if err != nil {
t.Fatal(err)
}
}
req, err := http.ReadRequest(bufio.NewReader(b))
if err != nil {
- t.Errorf("read request: %v", err)
+ t.Fatalf("read request: %v", err)
}
if req.Method != "GET" {
t.Errorf("request method expected GET, but got %q", req.Method)
}
- if req.RawURL != "/demo" {
- t.Errorf("request path expected /demo, but got %q", req.RawURL)
+ if req.URL.Path != "/demo" {
+ t.Errorf("request path expected /demo, but got %q", req.URL.Path)
}
if req.Proto != "HTTP/1.1" {
t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto)
}
req, err := http.ReadRequest(bufio.NewReader(b))
if err != nil {
- t.Errorf("read request: %v", err)
+ t.Fatalf("read request: %v", err)
}
if req.Method != "GET" {
t.Errorf("request method expected GET, but got %q", req.Method)
}
- if req.RawURL != "/chat" {
- t.Errorf("request path expected /chat, but got %q", req.RawURL)
+ if req.URL.Path != "/chat" {
+ t.Errorf("request path expected /chat, but got %q", req.URL.Path)
}
if req.Proto != "HTTP/1.1" {
t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto)
}
req, err := http.ReadRequest(bufio.NewReader(b))
if err != nil {
- t.Errorf("read request: %v", err)
+ t.Fatalf("read request: %v", err)
}
if req.Method != "GET" {
t.Errorf("request method expected GET, but got %q", req.Method)
}
- if req.RawURL != "/chat" {
- t.Errorf("request path expected /demo, but got %q", req.RawURL)
+ if req.URL.Path != "/chat" {
+ t.Errorf("request path expected /demo, but got %q", req.URL.Path)
}
if req.Proto != "HTTP/1.1" {
t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto)