From: David Symonds Date: Fri, 26 Jun 2009 04:05:44 +0000 (-0700) Subject: http Request parsing, plus a convenient accessor. X-Git-Tag: weekly.2009-11-06~1317 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a2a827542aa4aeb86f45e13527c24fc1debb5d36;p=gostls13.git http Request parsing, plus a convenient accessor. R=rsc APPROVED=rsc DELTA=95 (40 added, 14 deleted, 41 changed) OCL=30727 CL=30784 --- diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index 86b37d4c87..ef6dbe6734 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -103,8 +103,8 @@ type Request struct { // The User-Agent: header string, if sent in the request. UserAgent string; - // The parsed form data. Only available after ParseForm is called. - FormData map[string] *vector.StringVector + // The parsed form. Only available after ParseForm is called. + Form map[string] []string; } @@ -581,9 +581,9 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return req, nil } -func parseForm(body string) (data map[string] *vector.StringVector, err os.Error) { - data = make(map[string] *vector.StringVector); - for _, kv := range strings.Split(body, "&", 0) { +func parseForm(query string) (m map[string] []string, err os.Error) { + data := make(map[string] *vector.StringVector); + for _, kv := range strings.Split(query, "&", 0) { kvPair := strings.Split(kv, "=", 2); var key, value string; @@ -593,7 +593,7 @@ func parseForm(body string) (data map[string] *vector.StringVector, err os.Error value, e = URLUnescape(kvPair[1]); } if e != nil { - err := e; + err = e; } vec, ok := data[key]; @@ -603,26 +603,56 @@ func parseForm(body string) (data map[string] *vector.StringVector, err os.Error } vec.Push(value); } + + m = make(map[string] []string); + for k, vec := range data { + m[k] = vec.Data(); + } + return } -// ParseForm parses the request body as a form. -// TODO(dsymonds): Parse r.Url.RawQuery instead for GET requests. +// ParseForm parses the request body as a form for POST requests, or the raw query for GET requests. +// It is idempotent. func (r *Request) ParseForm() (err os.Error) { - if r.Body == nil { - return os.ErrorString("missing form body"); - } - ct, ok := r.Header["Content-Type"]; - if !ok { - ct = "application/x-www-form-urlencoded"; // default - } - switch ct { - case "text/plain", "application/x-www-form-urlencoded": - buf := new(io.ByteBuffer); - io.Copy(r.Body, buf); - r.FormData, err = parseForm(string(buf.Data())); - return err - // TODO(dsymonds): Handle multipart/form-data - } - return &badStringError{"unknown Content-Type", ct}; + if r.Form != nil { + return + } + + var query string; + + switch r.Method { + case "GET": + query = r.Url.RawQuery; + case "POST": + if r.Body == nil { + return os.ErrorString("missing form body") + } + ct, _ := r.Header["Content-Type"]; + switch ct { + case "text/plain", "application/x-www-form-urlencoded", "": + var b []byte; + if b, err = io.ReadAll(r.Body); err != nil { + return + } + query = string(b); + // TODO(dsymonds): Handle multipart/form-data + default: + return &badStringError{"unknown Content-Type", ct} + } + } + r.Form, err = parseForm(query); + return +} + +// FormValue returns the first value for the named component of the query. +// FormValue calls ParseForm if necessary. +func (r *Request) FormValue(key string) string { + if r.Form == nil { + r.ParseForm(); + } + if vs, ok := r.Form[key]; ok && len(vs) > 0 { + return vs[0] + } + return "" } diff --git a/src/pkg/http/request_test.go b/src/pkg/http/request_test.go index 230fe7bbb8..1cd91717db 100644 --- a/src/pkg/http/request_test.go +++ b/src/pkg/http/request_test.go @@ -7,56 +7,66 @@ package http import ( "fmt"; "http"; + "os"; "testing"; ) type stringMultimap map[string] []string type parseTest struct { - body string; + query string; out stringMultimap; } var parseTests = []parseTest{ parseTest{ - body: "a=1&b=2", + query: "a=1&b=2", out: stringMultimap{ "a": []string{ "1" }, "b": []string{ "2" } }, }, parseTest{ - body: "a=1&a=2&a=banana", + query: "a=1&a=2&a=banana", out: stringMultimap{ "a": []string{ "1", "2", "banana" } }, }, parseTest{ - body: "ascii=%3Ckey%3A+0x90%3E", + query: "ascii=%3Ckey%3A+0x90%3E", out: stringMultimap{ "ascii": []string{ "" } }, }, } func TestParseForm(t *testing.T) { for i, test := range parseTests { - data, err := parseForm(test.body); + form, err := parseForm(test.query); if err != nil { t.Errorf("test %d: Unexpected error: %v", i, err); continue } - if dlen, olen := len(data), len(test.out); dlen != olen { - t.Errorf("test %d: Have %d keys, want %d keys", i, dlen, olen); + if len(form) != len(test.out) { + t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out)); } - for k, vs := range test.out { - vec, ok := data[k]; + for k, evs := range test.out { + vs, ok := form[k]; if !ok { t.Errorf("test %d: Missing key %q", i, k); continue } - if dlen, olen := vec.Len(), len(vs); dlen != olen { - t.Errorf("test %d: key %q: Have %d keys, want %d keys", i, k, dlen, olen); + if len(vs) != len(evs) { + t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs)); continue } - for j, v := range vs { - if dv := vec.At(j); dv != v { - t.Errorf("test %d: key %q: val %d: Have %q, want %q", i, k, j, dv, v); + for j, ev := range evs { + if v := vs[j]; v != ev { + t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev); } } } } } + +func TestQuery(t *testing.T) { + var err os.Error; + req := &Request{ Method: "GET" }; + req.Url, err = ParseURL("http://www.google.com/search?q=foo&q=bar"); + if q := req.FormValue("q"); q != "foo" { + t.Errorf(`req.FormValue("q") = %q, want "foo"`, q); + } +}