mirror of
https://github.com/golang/go
synced 2024-11-25 05:47:57 -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"
|
"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) {
|
|||||||
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) {
|
|||||||
}
|
}
|
||||||
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) {
|
|||||||
// 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) {
|
|||||||
}
|
}
|
||||||
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) {
|
|||||||
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 @@
|
|||||||
package mime
|
package mime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"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) {
|
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
|
continue
|
||||||
}
|
}
|
||||||
if params["name"] != "foo" {
|
if len(params) == 0 && len(test.p) == 0 {
|
||||||
t.Errorf("expected name=foo for %s", test)
|
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