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 (
"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
......
......@@ -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)
}
}
}
......
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