]> Cypherpunks repositories - gostls13.git/commitdiff
mime: bunch more tests, few minor parsing fixes
authorBrad Fitzpatrick <bradfitz@golang.org>
Fri, 15 Apr 2011 23:33:52 +0000 (16:33 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Fri, 15 Apr 2011 23:33:52 +0000 (16:33 -0700)
Working towards issue 1119

Using test data from http://greenbytes.de/tech/tc2231/

R=r
CC=golang-dev
https://golang.org/cl/4430049

src/pkg/mime/mediatype.go
src/pkg/mime/mediatype_test.go

index eb629aa6f7fec2b5ff26f96964cfe1a97efa174f..e9e649f95077ce9bc073e842787899e3b32cbbc7 100644 (file)
@@ -10,6 +10,24 @@ import (
        "unicode"
 )
 
+func validMediaTypeOrDisposition(s string) bool {
+       typ, rest := consumeToken(s)
+       if typ == "" {
+               return false
+       }
+       if rest == "" {
+               return true
+       }
+       if !strings.HasPrefix(rest, "/") {
+               return false
+       }
+       subtype, rest := consumeToken(rest[1:])
+       if subtype == "" {
+               return false
+       }
+       return rest == ""
+}
+
 // ParseMediaType parses a media type value and any optional
 // parameters, per RFC 1531.  Media types are the values in
 // Content-Type and Content-Disposition headers (RFC 2183).  On
@@ -22,6 +40,10 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
                i = len(v)
        }
        mediatype = strings.TrimSpace(strings.ToLower(v[0:i]))
+       if !validMediaTypeOrDisposition(mediatype) {
+               return "", nil
+       }
+
        params = make(map[string]string)
 
        v = v[i:]
@@ -32,6 +54,11 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
                }
                key, value, rest := consumeMediaParam(v)
                if key == "" {
+                       if strings.TrimSpace(rest) == ";" {
+                               // Ignore trailing semicolons.
+                               // Not an error.
+                               return
+                       }
                        // Parse error.
                        return "", nil
                }
@@ -66,10 +93,12 @@ func consumeToken(v string) (token, rest string) {
 // quoted-string) and the rest of the string.  On failure, returns
 // ("", v).
 func consumeValue(v string) (value, rest string) {
-       if !strings.HasPrefix(v, `"`) {
+       if !strings.HasPrefix(v, `"`) && !strings.HasPrefix(v, `'`) {
                return consumeToken(v)
        }
 
+       leadQuote := int(v[0])
+
        // parse a quoted-string
        rest = v[1:] // consume the leading quote
        buffer := new(bytes.Buffer)
@@ -83,7 +112,7 @@ func consumeValue(v string) (value, rest string) {
                        }
                        buffer.WriteRune(rune)
                        nextIsLiteral = false
-               case rune == '"':
+               case rune == leadQuote:
                        return buffer.String(), rest[idx+1:]
                case IsQText(rune):
                        buffer.WriteRune(rune)
@@ -108,10 +137,12 @@ func consumeMediaParam(v string) (param, value, rest string) {
        if param == "" {
                return "", "", v
        }
+       rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
        if !strings.HasPrefix(rest, "=") {
                return "", "", v
        }
        rest = rest[1:] // consume equals sign
+       rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
        value, rest = consumeValue(rest)
        if value == "" {
                return "", "", v
index 4891e899d4c04090f06039be92d6e833a328a80e..f9603159575fa92b47ac086d88e592ff24b18e03 100644 (file)
@@ -5,6 +5,7 @@
 package mime
 
 import (
+       "reflect"
        "testing"
 )
 
@@ -85,23 +86,97 @@ func TestConsumeMediaParam(t *testing.T) {
        }
 }
 
+type mediaTypeTest struct {
+       in string
+       t  string
+       p  map[string]string
+}
+
 func TestParseMediaType(t *testing.T) {
-       tests := [...]string{
-               `form-data; name="foo"`,
-               ` form-data ; name=foo`,
-               `FORM-DATA;name="foo"`,
-               ` FORM-DATA ; name="foo"`,
-               ` FORM-DATA ; name="foo"`,
-               `form-data; key=value;  blah="value";name="foo" `,
+       // Convenience map initializer
+       m := func(s ...string) map[string]string {
+               sm := make(map[string]string)
+               for i := 0; i < len(s); i += 2 {
+                       sm[s[i]] = s[i+1]
+               }
+               return sm
+       }
+
+       nameFoo := map[string]string{"name": "foo"}
+       tests := []mediaTypeTest{
+               {`form-data; name="foo"`, "form-data", nameFoo},
+               {` form-data ; name=foo`, "form-data", nameFoo},
+               {`FORM-DATA;name="foo"`, "form-data", nameFoo},
+               {` FORM-DATA ; name="foo"`, "form-data", nameFoo},
+               {` FORM-DATA ; name="foo"`, "form-data", nameFoo},
+
+               {`form-data; key=value;  blah="value";name="foo" `,
+                       "form-data",
+                       m("key", "value", "blah", "value", "name", "foo")},
+
+               // Tests from http://greenbytes.de/tech/tc2231/
+               // TODO(bradfitz): add the rest of the tests from that site.
+               {`attachment; filename="f\oo.html"`,
+                       "attachment",
+                       m("filename", "foo.html")},
+               {`attachment; filename="\"quoting\" tested.html"`,
+                       "attachment",
+                       m("filename", `"quoting" tested.html`)},
+               {`attachment; filename="Here's a semicolon;.html"`,
+                       "attachment",
+                       m("filename", "Here's a semicolon;.html")},
+               {`attachment; foo="\"\\";filename="foo.html"`,
+                       "attachment",
+                       m("foo", "\"\\", "filename", "foo.html")},
+               {`attachment; filename=foo.html`,
+                       "attachment",
+                       m("filename", "foo.html")},
+               {`attachment; filename=foo.html ;`,
+                       "attachment",
+                       m("filename", "foo.html")},
+               {`attachment; filename='foo.html'`,
+                       "attachment",
+                       m("filename", "foo.html")},
+               {`attachment; filename="foo-%41.html"`,
+                       "attachment",
+                       m("filename", "foo-%41.html")},
+               {`attachment; filename="foo-%\41.html"`,
+                       "attachment",
+                       m("filename", "foo-%41.html")},
+               {`filename=foo.html`,
+                       "", m()},
+               {`x=y; filename=foo.html`,
+                       "", m()},
+               {`"foo; filename=bar;baz"; filename=qux`,
+                       "", m()},
+               {`inline; attachment; filename=foo.html`,
+                       "", m()},
+               {`attachment; filename="foo.html".txt`,
+                       "", m()},
+               {`attachment; filename="bar`,
+                       "", m()},
+               {`attachment; creation-date="Wed, 12 Feb 1997 16:29:51 -0500"`,
+                       "attachment",
+                       m("creation-date", "Wed, 12 Feb 1997 16:29:51 -0500")},
+               {`foobar`, "foobar", m()},
+               // TODO(bradfitz): rest of them, including RFC2231 encoded UTF-8 and
+               // other charsets.
        }
        for _, test := range tests {
-               mt, params := ParseMediaType(test)
-               if mt != "form-data" {
-                       t.Errorf("expected type form-data for %s, got [%s]", test, mt)
+               mt, params := ParseMediaType(test.in)
+               if g, e := mt, test.t; g != e {
+                       t.Errorf("for input %q, expected type %q, got %q",
+                               test.in, e, g)
+                       continue
+               }
+               if len(params) == 0 && len(test.p) == 0 {
                        continue
                }
-               if params["name"] != "foo" {
-                       t.Errorf("expected name=foo for %s", test)
+               if !reflect.DeepEqual(params, test.p) {
+                       t.Errorf("for input %q, wrong params.\n"+
+                               "expected: %#v\n"+
+                               "     got: %#v",
+                               test.in, test.p, params)
                }
        }
 }