mirror of
https://github.com/golang/go
synced 2024-11-22 00:44:39 -07:00
http: make Request.Body an io.ReadCloser, matching Response.Body.
R=rsc, rsc1 CC=golang-dev https://golang.org/cl/194046
This commit is contained in:
parent
60a6ec1c93
commit
8814555534
@ -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
|
|||||||
|
|
||||||
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 {
|
|||||||
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"
|
|||||||
// 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 {
|
|||||||
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) {
|
|||||||
// 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) {
|
|||||||
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
|
|||||||
// 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
|
|||||||
// 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 {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if b.resp == nil { // not reading trailer
|
if b.th == nil { // not reading trailer
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user