Commit 88145555 authored by Petar Maymounkov's avatar Petar Maymounkov Committed by Russ Cox

http: make Request.Body an io.ReadCloser, matching Response.Body.

R=rsc, rsc1
CC=golang-dev
https://golang.org/cl/194046
parent 60a6ec1c
...@@ -137,7 +137,7 @@ func Get(url string) (r *Response, finalURL string, err os.Error) { ...@@ -137,7 +137,7 @@ func Get(url string) (r *Response, finalURL string, err os.Error) {
func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) { func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
var req Request var req Request
req.Method = "POST" req.Method = "POST"
req.Body = body req.Body = nopCloser{body}
req.Header = map[string]string{ req.Header = map[string]string{
"Content-Type": bodyType, "Content-Type": bodyType,
"Transfer-Encoding": "chunked", "Transfer-Encoding": "chunked",
...@@ -150,3 +150,9 @@ func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Erro ...@@ -150,3 +150,9 @@ func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Erro
return send(&req) return send(&req)
} }
type nopCloser struct {
io.Reader
}
func (nopCloser) Close() os.Error { return nil }
...@@ -80,7 +80,7 @@ type Request struct { ...@@ -80,7 +80,7 @@ type Request struct {
Header map[string]string Header map[string]string
// The message body. // The message body.
Body io.Reader Body io.ReadCloser
// Whether to close the connection after replying to this request. // Whether to close the connection after replying to this request.
Close bool Close bool
...@@ -135,7 +135,8 @@ const defaultUserAgent = "Go http package" ...@@ -135,7 +135,8 @@ const defaultUserAgent = "Go http package"
// Header // Header
// Body // Body
// //
// If Body is present, "Transfer-Encoding: chunked" is forced as a header. // If Body is present, Write forces "Transfer-Encoding: chunked" as a header
// and then closes Body when finished sending it.
func (req *Request) Write(w io.Writer) os.Error { func (req *Request) Write(w io.Writer) os.Error {
uri := urlEscape(req.URL.Path, false) uri := urlEscape(req.URL.Path, false)
if req.URL.RawQuery != "" { if req.URL.RawQuery != "" {
...@@ -198,6 +199,7 @@ func (req *Request) Write(w io.Writer) os.Error { ...@@ -198,6 +199,7 @@ func (req *Request) Write(w io.Writer) os.Error {
return io.ErrShortWrite return io.ErrShortWrite
} }
} }
req.Body.Close()
// last-chunk CRLF // last-chunk CRLF
fmt.Fprint(w, "0\r\n\r\n") fmt.Fprint(w, "0\r\n\r\n")
} }
...@@ -572,19 +574,14 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { ...@@ -572,19 +574,14 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) {
// A message body exists when either Content-Length or Transfer-Encoding // A message body exists when either Content-Length or Transfer-Encoding
// headers are present. Transfer-Encoding trumps Content-Length. // headers are present. Transfer-Encoding trumps Content-Length.
if v, present := req.Header["Transfer-Encoding"]; present && v == "chunked" { if v, present := req.Header["Transfer-Encoding"]; present && v == "chunked" {
req.Body = newChunkedReader(b) req.Body = &body{Reader: newChunkedReader(b), th: req, r: b, closing: req.Close}
} else if v, present := req.Header["Content-Length"]; present { } else if v, present := req.Header["Content-Length"]; present {
length, err := strconv.Btoui64(v, 10) length, err := strconv.Btoi64(v, 10)
if err != nil { if err != nil {
return nil, &badStringError{"invalid Content-Length", v} return nil, &badStringError{"invalid Content-Length", v}
} }
// TODO: limit the Content-Length. This is an easy DoS vector. // TODO: limit the Content-Length. This is an easy DoS vector.
raw := make([]byte, length) req.Body = &body{Reader: io.LimitReader(b, length), closing: req.Close}
n, err := b.Read(raw)
if err != nil || uint64(n) < length {
return nil, ErrShortBody
}
req.Body = bytes.NewBuffer(raw)
} }
return req, nil return req, nil
......
...@@ -90,7 +90,7 @@ func TestPostContentTypeParsing(t *testing.T) { ...@@ -90,7 +90,7 @@ func TestPostContentTypeParsing(t *testing.T) {
req := &Request{ req := &Request{
Method: "POST", Method: "POST",
Header: test.contentType, Header: test.contentType,
Body: bytes.NewBufferString("body"), Body: nopCloser{bytes.NewBufferString("body")},
} }
err := req.ParseForm() err := req.ParseForm()
if !test.error && err != nil { if !test.error && err != nil {
......
...@@ -134,7 +134,7 @@ func ReadResponse(r *bufio.Reader, requestMethod string) (resp *Response, err os ...@@ -134,7 +134,7 @@ func ReadResponse(r *bufio.Reader, requestMethod string) (resp *Response, err os
// or close connection when finished, since multipart is not supported yet // or close connection when finished, since multipart is not supported yet
switch { switch {
case chunked(resp.TransferEncoding): case chunked(resp.TransferEncoding):
resp.Body = &body{Reader: newChunkedReader(r), resp: resp, r: r, closing: resp.Close} resp.Body = &body{Reader: newChunkedReader(r), th: resp, r: r, closing: resp.Close}
case resp.ContentLength >= 0: case resp.ContentLength >= 0:
resp.Body = &body{Reader: io.LimitReader(r, resp.ContentLength), closing: resp.Close} resp.Body = &body{Reader: io.LimitReader(r, resp.ContentLength), closing: resp.Close}
default: default:
...@@ -149,13 +149,13 @@ func ReadResponse(r *bufio.Reader, requestMethod string) (resp *Response, err os ...@@ -149,13 +149,13 @@ func ReadResponse(r *bufio.Reader, requestMethod string) (resp *Response, err os
// and then reads the trailer if necessary. // and then reads the trailer if necessary.
type body struct { type body struct {
io.Reader io.Reader
resp *Response // non-nil value means read trailer th interface{} // non-nil (Response or Request) value means read trailer
r *bufio.Reader // underlying wire-format reader for the trailer r *bufio.Reader // underlying wire-format reader for the trailer
closing bool // is the connection to be closed after reading body? closing bool // is the connection to be closed after reading body?
} }
func (b *body) Close() os.Error { func (b *body) Close() os.Error {
if b.resp == nil && b.closing { if b.th == nil && b.closing {
// no trailer and closing the connection next. // no trailer and closing the connection next.
// no point in reading to EOF. // no point in reading to EOF.
return nil return nil
...@@ -172,7 +172,7 @@ func (b *body) Close() os.Error { ...@@ -172,7 +172,7 @@ func (b *body) Close() os.Error {
} }
return err return err
} }
if b.resp == nil { // not reading trailer if b.th == nil { // not reading trailer
return nil return nil
} }
......
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