summaryrefslogtreecommitdiff
path: root/src/pkg/http/request.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/http/request.go')
-rw-r--r--src/pkg/http/request.go79
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}
}