Skip to content

Commit f6b4c88

Browse files
committed
Revert "net/http: improve performance for parsePostForm"
This reverts commit 59320c3. Reasons: This CL was causing failures on a large regression test that we run within Google. The issues arises from two bugs in the CL: * The CL dropped support for ';' as a delimiter (see https://golang.org/issue/2210) * The handling of an empty string caused an empty record to be added when no record was added (see https://golang.org/cl/30454 for my attempted fix) The logic being added is essentially a variation of url.ParseQuery, but altered to accept an io.Reader instead of a string. Since it is duplicated (but modified) logic, there needs to be good tests to ensure that it's implementation doesn't drift in functionality from url.ParseQuery. Fixing the above issues and adding the associated regression tests leads to >100 lines of codes. For a 4% reduction in CPU time, I think this complexity and duplicated logic is not worth the effort. As such, I am abandoning my efforts to fix the existing issues and believe that reverting CL/20301 is the better course of action. Updates #14655 Change-Id: Ibb5be0a5b48a16c46337e213b79467fcafee69df Reviewed-on: https://go-review.googlesource.com/30470 Run-TryBot: Joe Tsai <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent a9b4953 commit f6b4c88

File tree

2 files changed

+16
-70
lines changed

2 files changed

+16
-70
lines changed

src/net/http/request.go

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,8 +1015,18 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
10151015
maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
10161016
reader = io.LimitReader(r.Body, maxFormSize+1)
10171017
}
1018-
vs = make(url.Values)
1019-
e := parsePostFormURLEncoded(vs, reader, maxFormSize)
1018+
b, e := ioutil.ReadAll(reader)
1019+
if e != nil {
1020+
if err == nil {
1021+
err = e
1022+
}
1023+
break
1024+
}
1025+
if int64(len(b)) > maxFormSize {
1026+
err = errors.New("http: POST too large")
1027+
return
1028+
}
1029+
vs, e = url.ParseQuery(string(b))
10201030
if err == nil {
10211031
err = e
10221032
}
@@ -1031,52 +1041,6 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
10311041
return
10321042
}
10331043

1034-
// parsePostFormURLEncoded reads from r, the reader of a POST form to populate vs which is a url-type values.
1035-
// maxFormSize indicates the maximum number of bytes that will be read from r.
1036-
func parsePostFormURLEncoded(vs url.Values, r io.Reader, maxFormSize int64) error {
1037-
br := newBufioReader(r)
1038-
defer putBufioReader(br)
1039-
1040-
var readSize int64
1041-
for {
1042-
// Read next "key=value&" or "justkey&".
1043-
// If this is the last pair, b will contain just "key=value" or "justkey".
1044-
b, err := br.ReadBytes('&')
1045-
if err != nil && err != io.EOF && err != bufio.ErrBufferFull {
1046-
return err
1047-
}
1048-
isEOF := err == io.EOF
1049-
readSize += int64(len(b))
1050-
if readSize >= maxFormSize {
1051-
return errors.New("http: POST too large")
1052-
}
1053-
1054-
// Remove last delimiter
1055-
if len(b) > 0 && b[len(b)-1] == '&' {
1056-
b = b[:len(b)-1]
1057-
}
1058-
1059-
// Parse key and value
1060-
k := string(b)
1061-
var v string
1062-
if i := strings.Index(k, "="); i > -1 {
1063-
k, v = k[:i], k[i+1:]
1064-
}
1065-
if k, err = url.QueryUnescape(k); err != nil {
1066-
return err
1067-
}
1068-
if v, err = url.QueryUnescape(v); err != nil {
1069-
return err
1070-
}
1071-
1072-
// Populate vs
1073-
vs[k] = append(vs[k], v)
1074-
if isEOF {
1075-
return nil
1076-
}
1077-
}
1078-
}
1079-
10801044
// ParseForm parses the raw query from the URL and updates r.Form.
10811045
//
10821046
// For POST or PUT requests, it also parses the request body as a form and

src/net/http/request_test.go

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ func TestQuery(t *testing.T) {
3030
}
3131

3232
func TestPostQuery(t *testing.T) {
33-
req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not&orphan=nope",
34-
strings.NewReader("z=post&both=y&prio=2&orphan&empty="))
33+
req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not",
34+
strings.NewReader("z=post&both=y&prio=2&empty="))
3535
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
3636

3737
if q := req.FormValue("q"); q != "foo" {
@@ -58,26 +58,11 @@ func TestPostQuery(t *testing.T) {
5858
if empty := req.FormValue("empty"); empty != "" {
5959
t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
6060
}
61-
if orphan := req.FormValue("orphan"); orphan != "" {
62-
t.Errorf(`req.FormValue("orphan") = %q, want "" (from body)`, orphan)
63-
}
64-
}
65-
66-
func BenchmarkPostQuery(b *testing.B) {
67-
req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not&orphan=nope",
68-
strings.NewReader("z=post&both=y&prio=2&orphan&empty="))
69-
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
70-
b.ReportAllocs()
71-
b.ResetTimer()
72-
for i := 0; i < b.N; i++ {
73-
req.PostForm = nil
74-
req.ParseForm()
75-
}
7661
}
7762

7863
func TestPatchQuery(t *testing.T) {
79-
req, _ := NewRequest("PATCH", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not&orphan=nope",
80-
strings.NewReader("z=post&both=y&prio=2&orphan&empty="))
64+
req, _ := NewRequest("PATCH", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not",
65+
strings.NewReader("z=post&both=y&prio=2&empty="))
8166
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
8267

8368
if q := req.FormValue("q"); q != "foo" {
@@ -104,9 +89,6 @@ func TestPatchQuery(t *testing.T) {
10489
if empty := req.FormValue("empty"); empty != "" {
10590
t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
10691
}
107-
if orphan := req.FormValue("orphan"); orphan != "" {
108-
t.Errorf(`req.FormValue("orphan") = %q, want "" (from body)`, orphan)
109-
}
11092
}
11193

11294
type stringMap map[string][]string

0 commit comments

Comments
 (0)