mirror of
https://github.com/golang/go
synced 2024-11-21 20:34:40 -07:00
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
This commit is contained in:
parent
3d36a81fcc
commit
90e4ece365
@ -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 params["name"] != "foo" {
|
||||
t.Errorf("expected name=foo for %s", test)
|
||||
if len(params) == 0 && len(test.p) == 0 {
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(params, test.p) {
|
||||
t.Errorf("for input %q, wrong params.\n"+
|
||||
"expected: %#v\n"+
|
||||
" got: %#v",
|
||||
test.in, test.p, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user