summaryrefslogtreecommitdiff
path: root/src/net/http/request.go
diff options
context:
space:
mode:
authorQuentin Renard <contact@asticode.com>2016-03-06 17:27:50 +0100
committerBrad Fitzpatrick <bradfitz@golang.org>2016-10-04 20:05:02 +0000
commit59320c396e6448132a52cb5a5d96491eee1e0ad8 (patch)
treeafe98d8b4f7254367c58944f8a835ae25db088a8 /src/net/http/request.go
parente6051de0351bb05d7409ee0d483f932e3530f816 (diff)
downloadgo-git-59320c396e6448132a52cb5a5d96491eee1e0ad8.tar.gz
net/http: improve performance for parsePostForm
Remove the use of io.ReadAll in http.parsePostForm to avoid converting the whole input from []byte to string and not performing well space-allocated-wise. Instead a new function called parsePostFormURLEncoded is used and is fed directly an io.Reader that is parsed using a bufio.Reader. Benchmark: name old time/op new time/op delta PostQuery-4 2.90µs ± 6% 2.82µs ± 4% ~ (p=0.094 n=9+9) name old alloc/op new alloc/op delta PostQuery-4 1.05kB ± 0% 0.90kB ± 0% -14.49% (p=0.000 n=10+10) name old allocs/op new allocs/op delta PostQuery-4 6.00 ± 0% 7.00 ± 0% +16.67% (p=0.000 n=10+10) Fixes #14655 Change-Id: I112c263d4221d959ed6153cfe88bc57a2aa8ea73 Reviewed-on: https://go-review.googlesource.com/20301 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/net/http/request.go')
-rw-r--r--src/net/http/request.go60
1 files changed, 48 insertions, 12 deletions
diff --git a/src/net/http/request.go b/src/net/http/request.go
index c29af7fbe5..82a918c22e 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -1015,18 +1015,8 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
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
- }
- break
- }
- if int64(len(b)) > maxFormSize {
- err = errors.New("http: POST too large")
- return
- }
- vs, e = url.ParseQuery(string(b))
+ vs = make(url.Values)
+ e := parsePostFormURLEncoded(vs, reader, maxFormSize)
if err == nil {
err = e
}
@@ -1041,6 +1031,52 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
return
}
+// parsePostFormURLEncoded reads from r, the reader of a POST form to populate vs which is a url-type values.
+// maxFormSize indicates the maximum number of bytes that will be read from r.
+func parsePostFormURLEncoded(vs url.Values, r io.Reader, maxFormSize int64) error {
+ br := newBufioReader(r)
+ defer putBufioReader(br)
+
+ var readSize int64
+ for {
+ // Read next "key=value&" or "justkey&".
+ // If this is the last pair, b will contain just "key=value" or "justkey".
+ b, err := br.ReadBytes('&')
+ if err != nil && err != io.EOF && err != bufio.ErrBufferFull {
+ return err
+ }
+ isEOF := err == io.EOF
+ readSize += int64(len(b))
+ if readSize >= maxFormSize {
+ return errors.New("http: POST too large")
+ }
+
+ // Remove last delimiter
+ if len(b) > 0 && b[len(b)-1] == '&' {
+ b = b[:len(b)-1]
+ }
+
+ // Parse key and value
+ k := string(b)
+ var v string
+ if i := strings.Index(k, "="); i > -1 {
+ k, v = k[:i], k[i+1:]
+ }
+ if k, err = url.QueryUnescape(k); err != nil {
+ return err
+ }
+ if v, err = url.QueryUnescape(v); err != nil {
+ return err
+ }
+
+ // Populate vs
+ vs[k] = append(vs[k], v)
+ if isEOF {
+ return nil
+ }
+ }
+}
+
// ParseForm parses the raw query from the URL and updates r.Form.
//
// For POST or PUT requests, it also parses the request body as a form and