From 9e4e1b6ae33991bcfecfc3f0b34021f5457dfc1b Mon Sep 17 00:00:00 2001
From: Will McCutchen <will@mccutch.org>
Date: Fri, 16 Jun 2017 17:32:37 -0700
Subject: [PATCH] Use middleware to limit request body size

---
 httpbin/handlers.go   |  2 +-
 httpbin/helpers.go    | 15 +++++++++------
 httpbin/httpbin.go    |  8 +++++++-
 httpbin/middleware.go |  9 +++++++++
 4 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/httpbin/handlers.go b/httpbin/handlers.go
index 159e403..d987dbb 100644
--- a/httpbin/handlers.go
+++ b/httpbin/handlers.go
@@ -63,7 +63,7 @@ func (h *HTTPBin) RequestWithBody(w http.ResponseWriter, r *http.Request) {
 		URL:     getURL(r).String(),
 	}
 
-	err := parseBody(w, r, resp, h.options.MaxMemory)
+	err := parseBody(w, r, resp)
 	if err != nil {
 		http.Error(w, fmt.Sprintf("error parsing request body: %s", err), http.StatusBadRequest)
 		return
diff --git a/httpbin/helpers.go b/httpbin/helpers.go
index 2fa7a92..f513e39 100644
--- a/httpbin/helpers.go
+++ b/httpbin/helpers.go
@@ -70,14 +70,14 @@ func writeHTML(w http.ResponseWriter, body []byte, status int) {
 
 // parseBody handles parsing a request body into our standard API response,
 // taking care to only consume the request body once based on the Content-Type
-// of the request. The given Resp will be updated.
-func parseBody(w http.ResponseWriter, r *http.Request, resp *bodyResponse, maxMemory int64) error {
+// of the request. The given bodyResponse will be modified.
+//
+// Note: this function expects callers to limit the the maximum size of the
+// request body. See, e.g., the limitRequestSize middleware.
+func parseBody(w http.ResponseWriter, r *http.Request, resp *bodyResponse) error {
 	if r.Body == nil {
 		return nil
 	}
-
-	// Restrict size of request body
-	r.Body = http.MaxBytesReader(w, r.Body, maxMemory)
 	defer r.Body.Close()
 
 	ct := r.Header.Get("Content-Type")
@@ -89,7 +89,10 @@ func parseBody(w http.ResponseWriter, r *http.Request, resp *bodyResponse, maxMe
 		}
 		resp.Form = r.PostForm
 	case strings.HasPrefix(ct, "multipart/form-data"):
-		err := r.ParseMultipartForm(maxMemory)
+		// The memory limit here only restricts how many parts will be kept in
+		// memory before overflowing to disk:
+		// http://localhost:8080/pkg/net/http/#Request.ParseMultipartForm
+		err := r.ParseMultipartForm(1024 * 1024)
 		if err != nil {
 			return err
 		}
diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go
index 1c2a462..93d2690 100644
--- a/httpbin/httpbin.go
+++ b/httpbin/httpbin.go
@@ -157,7 +157,13 @@ func (h *HTTPBin) Handler() http.Handler {
 	mux.HandleFunc("/stream-bytes", http.NotFound)
 	mux.HandleFunc("/links", http.NotFound)
 
-	return logger(cors(mux))
+	// Apply global middleware
+	var handler http.Handler
+	handler = mux
+	handler = limitRequestSize(h.options.MaxMemory, handler)
+	handler = logger(handler)
+	handler = cors(handler)
+	return handler
 }
 
 // NewHTTPBin creates a new HTTPBin
diff --git a/httpbin/middleware.go b/httpbin/middleware.go
index ec04086..5e896a0 100644
--- a/httpbin/middleware.go
+++ b/httpbin/middleware.go
@@ -47,3 +47,12 @@ func methods(h http.HandlerFunc, methods ...string) http.HandlerFunc {
 		h.ServeHTTP(w, r)
 	}
 }
+
+func limitRequestSize(maxSize int64, h http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.Body != nil {
+			r.Body = http.MaxBytesReader(w, r.Body, maxSize)
+		}
+		h.ServeHTTP(w, r)
+	})
+}
-- 
GitLab