diff options
Diffstat (limited to 'src/pkg/http/request.go')
| -rw-r--r-- | src/pkg/http/request.go | 79 |
1 files changed, 67 insertions, 12 deletions
diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index ed41fa45c..610223139 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -234,8 +234,8 @@ func (r *Request) multipartReader() (*multipart.Reader, os.Error) { if v == "" { return nil, ErrNotMultipart } - d, params := mime.ParseMediaType(v) - if d != "multipart/form-data" { + d, params, err := mime.ParseMediaType(v) + if err != nil || d != "multipart/form-data" { return nil, ErrNotMultipart } boundary, ok := params["boundary"] @@ -608,27 +608,77 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return req, nil } -// ParseForm parses the raw query. -// For POST requests, it also parses the request body as a form. +// MaxBytesReader is similar to io.LimitReader, but is intended for +// limiting the size of incoming request bodies. In contrast to +// io.LimitReader, MaxBytesReader is a ReadCloser, returns a non-EOF +// error if the body is too large, and also takes care of closing the +// underlying io.ReadCloser connection (if applicable, usually a TCP +// connection) when the limit is hit. This prevents clients from +// accidentally or maliciously sending a large request and wasting +// server resources. +func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { + return &maxBytesReader{w: w, r: r, n: n} +} + +type maxBytesReader struct { + w ResponseWriter + r io.ReadCloser // underlying reader + n int64 // max bytes remaining + stopped bool +} + +func (l *maxBytesReader) Read(p []byte) (n int, err os.Error) { + if l.n <= 0 { + if !l.stopped { + l.stopped = true + if res, ok := l.w.(*response); ok { + res.requestTooLarge() + } + } + return 0, os.NewError("http: request body too large") + } + if int64(len(p)) > l.n { + p = p[:l.n] + } + n, err = l.r.Read(p) + l.n -= int64(n) + return +} + +func (l *maxBytesReader) Close() os.Error { + return l.r.Close() +} + +// ParseForm parses the raw query from the URL. +// +// For POST or PUT requests, it also parses the request body as a form. +// If the request Body's size has not already been limited by MaxBytesReader, +// the size is capped at 10MB. +// // ParseMultipartForm calls ParseForm automatically. // It is idempotent. func (r *Request) ParseForm() (err os.Error) { if r.Form != nil { return } - if r.URL != nil { r.Form, err = url.ParseQuery(r.URL.RawQuery) } - if r.Method == "POST" { + if r.Method == "POST" || r.Method == "PUT" { if r.Body == nil { return os.NewError("missing form body") } ct := r.Header.Get("Content-Type") - switch strings.SplitN(ct, ";", 2)[0] { - case "text/plain", "application/x-www-form-urlencoded", "": - const maxFormSize = int64(10 << 20) // 10 MB is a lot of text. - b, e := ioutil.ReadAll(io.LimitReader(r.Body, maxFormSize+1)) + ct, _, err := mime.ParseMediaType(ct) + switch { + case ct == "text/plain" || ct == "application/x-www-form-urlencoded" || ct == "": + var reader io.Reader = r.Body + maxFormSize := int64((1 << 63) - 1) + if _, ok := r.Body.(*maxBytesReader); !ok { + maxFormSize = int64(10 << 20) // 10 MB is a lot of text. + reader = io.LimitReader(r.Body, maxFormSize+1) + } + b, e := ioutil.ReadAll(reader) if e != nil { if err == nil { err = e @@ -652,8 +702,13 @@ func (r *Request) ParseForm() (err os.Error) { r.Form.Add(k, value) } } - case "multipart/form-data": - // handled by ParseMultipartForm + case ct == "multipart/form-data": + // handled by ParseMultipartForm (which is calling us, or should be) + // TODO(bradfitz): there are too many possible + // orders to call too many functions here. + // Clean this up and write more tests. + // request_test.go contains the start of this, + // in TestRequestMultipartCallOrder. default: return &badStringError{"unknown Content-Type", ct} } |
