Skip to content

Commit

Permalink
http: parse query string always, not just in GET
Browse files Browse the repository at this point in the history
Fixes #985.

R=dsymonds, dsymonds1
CC=golang-dev
https://golang.org/cl/1963044
  • Loading branch information
rsc committed Aug 18, 2010
1 parent 8dc4c0b commit 3bf6563
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 17 deletions.
48 changes: 31 additions & 17 deletions src/pkg/http/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,9 +588,22 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) {
return req, nil
}

// ParseQuery parses the URL-encoded query string and returns
// a map listing the values specified for each key.
// ParseQuery always returns a non-nil map containing all the
// valid query parameters found; err describes the first decoding error
// encountered, if any.
func ParseQuery(query string) (m map[string][]string, err os.Error) {
m = make(map[string][]string)
err = parseQuery(m, query)
return
}

func parseQuery(m map[string][]string, query string) (err os.Error) {
for _, kv := range strings.Split(query, "&", -1) {
if len(kv) == 0 {
continue
}
kvPair := strings.Split(kv, "=", 2)

var key, value string
Expand All @@ -601,14 +614,13 @@ func ParseQuery(query string) (m map[string][]string, err os.Error) {
}
if e != nil {
err = e
continue
}

vec := vector.StringVector(m[key])
vec.Push(value)
m[key] = vec
}

return
return err
}

// ParseForm parses the request body as a form for POST requests, or the raw query for GET requests.
Expand All @@ -618,32 +630,34 @@ func (r *Request) ParseForm() (err os.Error) {
return
}

var query string
switch r.Method {
case "GET":
query = r.URL.RawQuery
case "POST":
r.Form = make(map[string][]string)
if r.URL != nil {
err = parseQuery(r.Form, r.URL.RawQuery)
}
if r.Method == "POST" {
if r.Body == nil {
r.Form = make(map[string][]string)
return os.ErrorString("missing form body")
}
ct := r.Header["Content-Type"]
switch strings.Split(ct, ";", 2)[0] {
case "text/plain", "application/x-www-form-urlencoded", "":
var b []byte
if b, err = ioutil.ReadAll(r.Body); err != nil {
r.Form = make(map[string][]string)
return err
b, e := ioutil.ReadAll(r.Body)
if e != nil {
if err == nil {
err = e
}
break
}
e = parseQuery(r.Form, string(b))
if err == nil {
err = e
}
query = string(b)
// TODO(dsymonds): Handle multipart/form-data
default:
r.Form = make(map[string][]string)
return &badStringError{"unknown Content-Type", ct}
}
}
r.Form, err = ParseQuery(query)
return
return err
}

// FormValue returns the first value for the named component of the query.
Expand Down
18 changes: 18 additions & 0 deletions src/pkg/http/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package http

import (
"bytes"
"reflect"
"strings"
"testing"
)

Expand Down Expand Up @@ -68,6 +70,22 @@ func TestQuery(t *testing.T) {
}
}

func TestPostQuery(t *testing.T) {
req := &Request{Method: "POST"}
req.URL, _ = ParseURL("http://www.google.com/search?q=foo&q=bar&both=x")
req.Header = map[string]string{"Content-Type": "application/x-www-form-urlencoded; boo!"}
req.Body = nopCloser{strings.NewReader("z=post&both=y")}
if q := req.FormValue("q"); q != "foo" {
t.Errorf(`req.FormValue("q") = %q, want "foo"`, q)
}
if z := req.FormValue("z"); z != "post" {
t.Errorf(`req.FormValue("z") = %q, want "post"`, z)
}
if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"x", "y"}) {
t.Errorf(`req.FormValue("both") = %q, want ["x", "y"]`, both)
}
}

type stringMap map[string]string
type parseContentTypeTest struct {
contentType stringMap
Expand Down

0 comments on commit 3bf6563

Please sign in to comment.