Skip to content

Commit 90e4ece

Browse files
committed
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
1 parent 3d36a81 commit 90e4ece

File tree

2 files changed

+120
-14
lines changed

2 files changed

+120
-14
lines changed

Diff for: src/pkg/mime/mediatype.go

+33-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ import (
1010
"unicode"
1111
)
1212

13+
func validMediaTypeOrDisposition(s string) bool {
14+
typ, rest := consumeToken(s)
15+
if typ == "" {
16+
return false
17+
}
18+
if rest == "" {
19+
return true
20+
}
21+
if !strings.HasPrefix(rest, "/") {
22+
return false
23+
}
24+
subtype, rest := consumeToken(rest[1:])
25+
if subtype == "" {
26+
return false
27+
}
28+
return rest == ""
29+
}
30+
1331
// ParseMediaType parses a media type value and any optional
1432
// parameters, per RFC 1531. Media types are the values in
1533
// Content-Type and Content-Disposition headers (RFC 2183). On
@@ -22,6 +40,10 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
2240
i = len(v)
2341
}
2442
mediatype = strings.TrimSpace(strings.ToLower(v[0:i]))
43+
if !validMediaTypeOrDisposition(mediatype) {
44+
return "", nil
45+
}
46+
2547
params = make(map[string]string)
2648

2749
v = v[i:]
@@ -32,6 +54,11 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
3254
}
3355
key, value, rest := consumeMediaParam(v)
3456
if key == "" {
57+
if strings.TrimSpace(rest) == ";" {
58+
// Ignore trailing semicolons.
59+
// Not an error.
60+
return
61+
}
3562
// Parse error.
3663
return "", nil
3764
}
@@ -66,10 +93,12 @@ func consumeToken(v string) (token, rest string) {
6693
// quoted-string) and the rest of the string. On failure, returns
6794
// ("", v).
6895
func consumeValue(v string) (value, rest string) {
69-
if !strings.HasPrefix(v, `"`) {
96+
if !strings.HasPrefix(v, `"`) && !strings.HasPrefix(v, `'`) {
7097
return consumeToken(v)
7198
}
7299

100+
leadQuote := int(v[0])
101+
73102
// parse a quoted-string
74103
rest = v[1:] // consume the leading quote
75104
buffer := new(bytes.Buffer)
@@ -83,7 +112,7 @@ func consumeValue(v string) (value, rest string) {
83112
}
84113
buffer.WriteRune(rune)
85114
nextIsLiteral = false
86-
case rune == '"':
115+
case rune == leadQuote:
87116
return buffer.String(), rest[idx+1:]
88117
case IsQText(rune):
89118
buffer.WriteRune(rune)
@@ -108,10 +137,12 @@ func consumeMediaParam(v string) (param, value, rest string) {
108137
if param == "" {
109138
return "", "", v
110139
}
140+
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
111141
if !strings.HasPrefix(rest, "=") {
112142
return "", "", v
113143
}
114144
rest = rest[1:] // consume equals sign
145+
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
115146
value, rest = consumeValue(rest)
116147
if value == "" {
117148
return "", "", v

Diff for: src/pkg/mime/mediatype_test.go

+87-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package mime
66

77
import (
8+
"reflect"
89
"testing"
910
)
1011

@@ -85,23 +86,97 @@ func TestConsumeMediaParam(t *testing.T) {
8586
}
8687
}
8788

89+
type mediaTypeTest struct {
90+
in string
91+
t string
92+
p map[string]string
93+
}
94+
8895
func TestParseMediaType(t *testing.T) {
89-
tests := [...]string{
90-
`form-data; name="foo"`,
91-
` form-data ; name=foo`,
92-
`FORM-DATA;name="foo"`,
93-
` FORM-DATA ; name="foo"`,
94-
` FORM-DATA ; name="foo"`,
95-
`form-data; key=value; blah="value";name="foo" `,
96+
// Convenience map initializer
97+
m := func(s ...string) map[string]string {
98+
sm := make(map[string]string)
99+
for i := 0; i < len(s); i += 2 {
100+
sm[s[i]] = s[i+1]
101+
}
102+
return sm
103+
}
104+
105+
nameFoo := map[string]string{"name": "foo"}
106+
tests := []mediaTypeTest{
107+
{`form-data; name="foo"`, "form-data", nameFoo},
108+
{` form-data ; name=foo`, "form-data", nameFoo},
109+
{`FORM-DATA;name="foo"`, "form-data", nameFoo},
110+
{` FORM-DATA ; name="foo"`, "form-data", nameFoo},
111+
{` FORM-DATA ; name="foo"`, "form-data", nameFoo},
112+
113+
{`form-data; key=value; blah="value";name="foo" `,
114+
"form-data",
115+
m("key", "value", "blah", "value", "name", "foo")},
116+
117+
// Tests from http://greenbytes.de/tech/tc2231/
118+
// TODO(bradfitz): add the rest of the tests from that site.
119+
{`attachment; filename="f\oo.html"`,
120+
"attachment",
121+
m("filename", "foo.html")},
122+
{`attachment; filename="\"quoting\" tested.html"`,
123+
"attachment",
124+
m("filename", `"quoting" tested.html`)},
125+
{`attachment; filename="Here's a semicolon;.html"`,
126+
"attachment",
127+
m("filename", "Here's a semicolon;.html")},
128+
{`attachment; foo="\"\\";filename="foo.html"`,
129+
"attachment",
130+
m("foo", "\"\\", "filename", "foo.html")},
131+
{`attachment; filename=foo.html`,
132+
"attachment",
133+
m("filename", "foo.html")},
134+
{`attachment; filename=foo.html ;`,
135+
"attachment",
136+
m("filename", "foo.html")},
137+
{`attachment; filename='foo.html'`,
138+
"attachment",
139+
m("filename", "foo.html")},
140+
{`attachment; filename="foo-%41.html"`,
141+
"attachment",
142+
m("filename", "foo-%41.html")},
143+
{`attachment; filename="foo-%\41.html"`,
144+
"attachment",
145+
m("filename", "foo-%41.html")},
146+
{`filename=foo.html`,
147+
"", m()},
148+
{`x=y; filename=foo.html`,
149+
"", m()},
150+
{`"foo; filename=bar;baz"; filename=qux`,
151+
"", m()},
152+
{`inline; attachment; filename=foo.html`,
153+
"", m()},
154+
{`attachment; filename="foo.html".txt`,
155+
"", m()},
156+
{`attachment; filename="bar`,
157+
"", m()},
158+
{`attachment; creation-date="Wed, 12 Feb 1997 16:29:51 -0500"`,
159+
"attachment",
160+
m("creation-date", "Wed, 12 Feb 1997 16:29:51 -0500")},
161+
{`foobar`, "foobar", m()},
162+
// TODO(bradfitz): rest of them, including RFC2231 encoded UTF-8 and
163+
// other charsets.
96164
}
97165
for _, test := range tests {
98-
mt, params := ParseMediaType(test)
99-
if mt != "form-data" {
100-
t.Errorf("expected type form-data for %s, got [%s]", test, mt)
166+
mt, params := ParseMediaType(test.in)
167+
if g, e := mt, test.t; g != e {
168+
t.Errorf("for input %q, expected type %q, got %q",
169+
test.in, e, g)
170+
continue
171+
}
172+
if len(params) == 0 && len(test.p) == 0 {
101173
continue
102174
}
103-
if params["name"] != "foo" {
104-
t.Errorf("expected name=foo for %s", test)
175+
if !reflect.DeepEqual(params, test.p) {
176+
t.Errorf("for input %q, wrong params.\n"+
177+
"expected: %#v\n"+
178+
" got: %#v",
179+
test.in, test.p, params)
105180
}
106181
}
107182
}

0 commit comments

Comments
 (0)