aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/http/request.go
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2011-08-23 12:17:21 +0400
committerBrad Fitzpatrick <bradfitz@golang.org>2011-08-23 12:17:21 +0400
commitf0ef4f474620ed95a7572c579689f262e79a724f (patch)
tree80d2caa7f7067ff1434a8cee500772181ef5fb99 /src/pkg/http/request.go
parent6731d47f99d993522aa41990b46498d20ba646f1 (diff)
downloadgo-f0ef4f474620ed95a7572c579689f262e79a724f.tar.xz
http: add MaxBytesReader to limit request body size
This adds http.MaxBytesReader, similar to io.LimitReader, but specific to http, and for preventing a class of DoS attacks. This also makes the 10MB ParseForm limit optional (if not already set by a MaxBytesReader), documents it, and also adds "PUT" as a valid verb for parsing forms in the request body. Improves issue 2093 (DoS protection) Fixes #2165 (PUT form parsing) R=golang-dev, adg CC=golang-dev https://golang.org/cl/4921049
Diffstat (limited to 'src/pkg/http/request.go')
-rw-r--r--src/pkg/http/request.go61
1 files changed, 55 insertions, 6 deletions
diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go
index d45de8e2e4..6102231392 100644
--- a/src/pkg/http/request.go
+++ b/src/pkg/http/request.go
@@ -608,19 +608,63 @@ 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")
}
@@ -628,8 +672,13 @@ func (r *Request) ParseForm() (err os.Error) {
ct, _, err := mime.ParseMediaType(ct)
switch {
case ct == "text/plain" || ct == "application/x-www-form-urlencoded" || ct == "":
- const maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
- b, e := ioutil.ReadAll(io.LimitReader(r.Body, maxFormSize+1))
+ 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