Commit 90e4ece3 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

mime: bunch more tests, few minor parsing fixes

Working towards issue 1119

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

R=r
CC=golang-dev
https://golang.org/cl/4430049
parent 3d36a81f
...@@ -10,6 +10,24 @@ import ( ...@@ -10,6 +10,24 @@ import (
"unicode" "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 // ParseMediaType parses a media type value and any optional
// parameters, per RFC 1531. Media types are the values in // parameters, per RFC 1531. Media types are the values in
// Content-Type and Content-Disposition headers (RFC 2183). On // Content-Type and Content-Disposition headers (RFC 2183). On
...@@ -22,6 +40,10 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) { ...@@ -22,6 +40,10 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
i = len(v) i = len(v)
} }
mediatype = strings.TrimSpace(strings.ToLower(v[0:i])) mediatype = strings.TrimSpace(strings.ToLower(v[0:i]))
if !validMediaTypeOrDisposition(mediatype) {
return "", nil
}
params = make(map[string]string) params = make(map[string]string)
v = v[i:] v = v[i:]
...@@ -32,6 +54,11 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) { ...@@ -32,6 +54,11 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
} }
key, value, rest := consumeMediaParam(v) key, value, rest := consumeMediaParam(v)
if key == "" { if key == "" {
if strings.TrimSpace(rest) == ";" {
// Ignore trailing semicolons.
// Not an error.
return
}
// Parse error. // Parse error.
return "", nil return "", nil
} }
...@@ -66,10 +93,12 @@ func consumeToken(v string) (token, rest string) { ...@@ -66,10 +93,12 @@ func consumeToken(v string) (token, rest string) {
// quoted-string) and the rest of the string. On failure, returns // quoted-string) and the rest of the string. On failure, returns
// ("", v). // ("", v).
func consumeValue(v string) (value, rest string) { func consumeValue(v string) (value, rest string) {
if !strings.HasPrefix(v, `"`) { if !strings.HasPrefix(v, `"`) && !strings.HasPrefix(v, `'`) {
return consumeToken(v) return consumeToken(v)
} }
leadQuote := int(v[0])
// parse a quoted-string // parse a quoted-string
rest = v[1:] // consume the leading quote rest = v[1:] // consume the leading quote
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
...@@ -83,7 +112,7 @@ func consumeValue(v string) (value, rest string) { ...@@ -83,7 +112,7 @@ func consumeValue(v string) (value, rest string) {
} }
buffer.WriteRune(rune) buffer.WriteRune(rune)
nextIsLiteral = false nextIsLiteral = false
case rune == '"': case rune == leadQuote:
return buffer.String(), rest[idx+1:] return buffer.String(), rest[idx+1:]
case IsQText(rune): case IsQText(rune):
buffer.WriteRune(rune) buffer.WriteRune(rune)
...@@ -108,10 +137,12 @@ func consumeMediaParam(v string) (param, value, rest string) { ...@@ -108,10 +137,12 @@ func consumeMediaParam(v string) (param, value, rest string) {
if param == "" { if param == "" {
return "", "", v return "", "", v
} }
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
if !strings.HasPrefix(rest, "=") { if !strings.HasPrefix(rest, "=") {
return "", "", v return "", "", v
} }
rest = rest[1:] // consume equals sign rest = rest[1:] // consume equals sign
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
value, rest = consumeValue(rest) value, rest = consumeValue(rest)
if value == "" { if value == "" {
return "", "", v return "", "", v
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package mime package mime
import ( import (
"reflect"
"testing" "testing"
) )
...@@ -85,23 +86,97 @@ func TestConsumeMediaParam(t *testing.T) { ...@@ -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) { func TestParseMediaType(t *testing.T) {
tests := [...]string{ // Convenience map initializer
`form-data; name="foo"`, m := func(s ...string) map[string]string {
` form-data ; name=foo`, sm := make(map[string]string)
`FORM-DATA;name="foo"`, for i := 0; i < len(s); i += 2 {
` FORM-DATA ; name="foo"`, sm[s[i]] = s[i+1]
` FORM-DATA ; name="foo"`, }
`form-data; key=value; blah="value";name="foo" `, 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 { for _, test := range tests {
mt, params := ParseMediaType(test) mt, params := ParseMediaType(test.in)
if mt != "form-data" { if g, e := mt, test.t; g != e {
t.Errorf("expected type form-data for %s, got [%s]", test, mt) t.Errorf("for input %q, expected type %q, got %q",
test.in, e, g)
continue
}
if len(params) == 0 && len(test.p) == 0 {
continue continue
} }
if params["name"] != "foo" { if !reflect.DeepEqual(params, test.p) {
t.Errorf("expected name=foo for %s", test) t.Errorf("for input %q, wrong params.\n"+
"expected: %#v\n"+
" got: %#v",
test.in, test.p, params)
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment