]> Cypherpunks repositories - gostls13.git/commitdiff
http Request parsing, plus a convenient accessor.
authorDavid Symonds <dsymonds@golang.org>
Fri, 26 Jun 2009 04:05:44 +0000 (21:05 -0700)
committerDavid Symonds <dsymonds@golang.org>
Fri, 26 Jun 2009 04:05:44 +0000 (21:05 -0700)
R=rsc
APPROVED=rsc
DELTA=95  (40 added, 14 deleted, 41 changed)
OCL=30727
CL=30784

src/pkg/http/request.go
src/pkg/http/request_test.go

index 86b37d4c87156ca026507b1c82a2746e023bbb8a..ef6dbe673480750a9ee1761088af5e6775875734 100644 (file)
@@ -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 ""
 }
index 230fe7bbb8b39e0f6c2aa481f5b4d752f0aefb35..1cd91717db23813b00b0f5caad01186459db215e 100644 (file)
@@ -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{ "<key: 0x90>" } },
        },
 }
 
 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);
+       }
+}