github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/mime/mediatype_test.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package mime 6 7 import ( 8 "reflect" 9 "testing" 10 ) 11 12 func TestConsumeToken(t *testing.T) { 13 tests := [...][3]string{ 14 {"foo bar", "foo", " bar"}, 15 {"bar", "bar", ""}, 16 {"", "", ""}, 17 {" foo", "", " foo"}, 18 } 19 for _, test := range tests { 20 token, rest := consumeToken(test[0]) 21 expectedToken := test[1] 22 expectedRest := test[2] 23 if token != expectedToken { 24 t.Errorf("expected to consume token '%s', not '%s' from '%s'", 25 expectedToken, token, test[0]) 26 } else if rest != expectedRest { 27 t.Errorf("expected to have left '%s', not '%s' after reading token '%s' from '%s'", 28 expectedRest, rest, token, test[0]) 29 } 30 } 31 } 32 33 func TestConsumeValue(t *testing.T) { 34 tests := [...][3]string{ 35 {"foo bar", "foo", " bar"}, 36 {"bar", "bar", ""}, 37 {" bar ", "", " bar "}, 38 {`"My value"end`, "My value", "end"}, 39 {`"My value" end`, "My value", " end"}, 40 {`"\\" rest`, "\\", " rest"}, 41 {`"My \" value"end`, "My \" value", "end"}, 42 {`"\" rest`, "", `"\" rest`}, 43 {`"C:\dev\go\robots.txt"`, `C:\dev\go\robots.txt`, ""}, 44 {`"C:\新建文件件\中文第二次测试.mp4"`, `C:\新建文件件\中文第二次测试.mp4`, ""}, 45 } 46 for _, test := range tests { 47 value, rest := consumeValue(test[0]) 48 expectedValue := test[1] 49 expectedRest := test[2] 50 if value != expectedValue { 51 t.Errorf("expected to consume value [%s], not [%s] from [%s]", 52 expectedValue, value, test[0]) 53 } else if rest != expectedRest { 54 t.Errorf("expected to have left [%s], not [%s] after reading value [%s] from [%s]", 55 expectedRest, rest, value, test[0]) 56 } 57 } 58 } 59 60 func TestConsumeMediaParam(t *testing.T) { 61 tests := [...][4]string{ 62 {" ; foo=bar", "foo", "bar", ""}, 63 {"; foo=bar", "foo", "bar", ""}, 64 {";foo=bar", "foo", "bar", ""}, 65 {";FOO=bar", "foo", "bar", ""}, 66 {`;foo="bar"`, "foo", "bar", ""}, 67 {`;foo="bar"; `, "foo", "bar", "; "}, 68 {`;foo="bar"; foo=baz`, "foo", "bar", "; foo=baz"}, 69 {` ; boundary=----CUT;`, "boundary", "----CUT", ";"}, 70 {` ; key=value; blah="value";name="foo" `, "key", "value", `; blah="value";name="foo" `}, 71 {`; blah="value";name="foo" `, "blah", "value", `;name="foo" `}, 72 {`;name="foo" `, "name", "foo", ` `}, 73 } 74 for _, test := range tests { 75 param, value, rest := consumeMediaParam(test[0]) 76 expectedParam := test[1] 77 expectedValue := test[2] 78 expectedRest := test[3] 79 if param != expectedParam { 80 t.Errorf("expected to consume param [%s], not [%s] from [%s]", 81 expectedParam, param, test[0]) 82 } else if value != expectedValue { 83 t.Errorf("expected to consume value [%s], not [%s] from [%s]", 84 expectedValue, value, test[0]) 85 } else if rest != expectedRest { 86 t.Errorf("expected to have left [%s], not [%s] after reading [%s/%s] from [%s]", 87 expectedRest, rest, param, value, test[0]) 88 } 89 } 90 } 91 92 type mediaTypeTest struct { 93 in string 94 t string 95 p map[string]string 96 } 97 98 func TestParseMediaType(t *testing.T) { 99 // Convenience map initializer 100 m := func(s ...string) map[string]string { 101 sm := make(map[string]string) 102 for i := 0; i < len(s); i += 2 { 103 sm[s[i]] = s[i+1] 104 } 105 return sm 106 } 107 108 nameFoo := map[string]string{"name": "foo"} 109 tests := []mediaTypeTest{ 110 {`form-data; name="foo"`, "form-data", nameFoo}, 111 {` form-data ; name=foo`, "form-data", nameFoo}, 112 {`FORM-DATA;name="foo"`, "form-data", nameFoo}, 113 {` FORM-DATA ; name="foo"`, "form-data", nameFoo}, 114 {` FORM-DATA ; name="foo"`, "form-data", nameFoo}, 115 116 {`form-data; key=value; blah="value";name="foo" `, 117 "form-data", 118 m("key", "value", "blah", "value", "name", "foo")}, 119 120 {`foo; key=val1; key=the-key-appears-again-which-is-bogus`, 121 "", m()}, 122 123 // From RFC 2231: 124 {`application/x-stuff; title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A`, 125 "application/x-stuff", 126 m("title", "This is ***fun***")}, 127 128 {`message/external-body; access-type=URL; ` + 129 `URL*0="ftp://";` + 130 `URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"`, 131 "message/external-body", 132 m("access-type", "URL", 133 "url", "ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar")}, 134 135 {`application/x-stuff; ` + 136 `title*0*=us-ascii'en'This%20is%20even%20more%20; ` + 137 `title*1*=%2A%2A%2Afun%2A%2A%2A%20; ` + 138 `title*2="isn't it!"`, 139 "application/x-stuff", 140 m("title", "This is even more ***fun*** isn't it!")}, 141 142 // Tests from http://greenbytes.de/tech/tc2231/ 143 // Note: Backslash escape handling is a bit loose, like MSIE. 144 145 // #attonly 146 {`attachment`, 147 "attachment", 148 m()}, 149 // #attonlyucase 150 {`ATTACHMENT`, 151 "attachment", 152 m()}, 153 // #attwithasciifilename 154 {`attachment; filename="foo.html"`, 155 "attachment", 156 m("filename", "foo.html")}, 157 // #attwithasciifilename25 158 {`attachment; filename="0000000000111111111122222"`, 159 "attachment", 160 m("filename", "0000000000111111111122222")}, 161 // #attwithasciifilename35 162 {`attachment; filename="00000000001111111111222222222233333"`, 163 "attachment", 164 m("filename", "00000000001111111111222222222233333")}, 165 // #attwithasciifnescapedchar 166 {`attachment; filename="f\oo.html"`, 167 "attachment", 168 m("filename", "f\\oo.html")}, 169 // #attwithasciifnescapedquote 170 {`attachment; filename="\"quoting\" tested.html"`, 171 "attachment", 172 m("filename", `"quoting" tested.html`)}, 173 // #attwithquotedsemicolon 174 {`attachment; filename="Here's a semicolon;.html"`, 175 "attachment", 176 m("filename", "Here's a semicolon;.html")}, 177 // #attwithfilenameandextparam 178 {`attachment; foo="bar"; filename="foo.html"`, 179 "attachment", 180 m("foo", "bar", "filename", "foo.html")}, 181 // #attwithfilenameandextparamescaped 182 {`attachment; foo="\"\\";filename="foo.html"`, 183 "attachment", 184 m("foo", "\"\\", "filename", "foo.html")}, 185 // #attwithasciifilenameucase 186 {`attachment; FILENAME="foo.html"`, 187 "attachment", 188 m("filename", "foo.html")}, 189 // #attwithasciifilenamenq 190 {`attachment; filename=foo.html`, 191 "attachment", 192 m("filename", "foo.html")}, 193 // #attwithasciifilenamenqs 194 {`attachment; filename=foo.html ;`, 195 "attachment", 196 m("filename", "foo.html")}, 197 // #attwithfntokensq 198 {`attachment; filename='foo.html'`, 199 "attachment", 200 m("filename", "'foo.html'")}, 201 // #attwithisofnplain 202 {`attachment; filename="foo-ä.html"`, 203 "attachment", 204 m("filename", "foo-ä.html")}, 205 // #attwithutf8fnplain 206 {`attachment; filename="foo-ä.html"`, 207 "attachment", 208 m("filename", "foo-ä.html")}, 209 // #attwithfnrawpctenca 210 {`attachment; filename="foo-%41.html"`, 211 "attachment", 212 m("filename", "foo-%41.html")}, 213 // #attwithfnusingpct 214 {`attachment; filename="50%.html"`, 215 "attachment", 216 m("filename", "50%.html")}, 217 // #attwithfnrawpctencaq 218 {`attachment; filename="foo-%\41.html"`, 219 "attachment", 220 m("filename", "foo-%\\41.html")}, 221 // #attwithnamepct 222 {`attachment; name="foo-%41.html"`, 223 "attachment", 224 m("name", "foo-%41.html")}, 225 // #attwithfilenamepctandiso 226 {`attachment; name="ä-%41.html"`, 227 "attachment", 228 m("name", "ä-%41.html")}, 229 // #attwithfnrawpctenclong 230 {`attachment; filename="foo-%c3%a4-%e2%82%ac.html"`, 231 "attachment", 232 m("filename", "foo-%c3%a4-%e2%82%ac.html")}, 233 // #attwithasciifilenamews1 234 {`attachment; filename ="foo.html"`, 235 "attachment", 236 m("filename", "foo.html")}, 237 // #attmissingdisposition 238 {`filename=foo.html`, 239 "", m()}, 240 // #attmissingdisposition2 241 {`x=y; filename=foo.html`, 242 "", m()}, 243 // #attmissingdisposition3 244 {`"foo; filename=bar;baz"; filename=qux`, 245 "", m()}, 246 // #attmissingdisposition4 247 {`filename=foo.html, filename=bar.html`, 248 "", m()}, 249 // #emptydisposition 250 {`; filename=foo.html`, 251 "", m()}, 252 // #doublecolon 253 {`: inline; attachment; filename=foo.html`, 254 "", m()}, 255 // #attandinline 256 {`inline; attachment; filename=foo.html`, 257 "", m()}, 258 // #attandinline2 259 {`attachment; inline; filename=foo.html`, 260 "", m()}, 261 // #attbrokenquotedfn 262 {`attachment; filename="foo.html".txt`, 263 "", m()}, 264 // #attbrokenquotedfn2 265 {`attachment; filename="bar`, 266 "", m()}, 267 // #attbrokenquotedfn3 268 {`attachment; filename=foo"bar;baz"qux`, 269 "", m()}, 270 // #attmultinstances 271 {`attachment; filename=foo.html, attachment; filename=bar.html`, 272 "", m()}, 273 // #attmissingdelim 274 {`attachment; foo=foo filename=bar`, 275 "", m()}, 276 // #attmissingdelim2 277 {`attachment; filename=bar foo=foo`, 278 "", m()}, 279 // #attmissingdelim3 280 {`attachment filename=bar`, 281 "", m()}, 282 // #attreversed 283 {`filename=foo.html; attachment`, 284 "", m()}, 285 // #attconfusedparam 286 {`attachment; xfilename=foo.html`, 287 "attachment", 288 m("xfilename", "foo.html")}, 289 // #attcdate 290 {`attachment; creation-date="Wed, 12 Feb 1997 16:29:51 -0500"`, 291 "attachment", 292 m("creation-date", "Wed, 12 Feb 1997 16:29:51 -0500")}, 293 // #attmdate 294 {`attachment; modification-date="Wed, 12 Feb 1997 16:29:51 -0500"`, 295 "attachment", 296 m("modification-date", "Wed, 12 Feb 1997 16:29:51 -0500")}, 297 // #dispext 298 {`foobar`, "foobar", m()}, 299 // #dispextbadfn 300 {`attachment; example="filename=example.txt"`, 301 "attachment", 302 m("example", "filename=example.txt")}, 303 // #attwithfn2231utf8 304 {`attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html`, 305 "attachment", 306 m("filename", "foo-ä-€.html")}, 307 // #attwithfn2231noc 308 {`attachment; filename*=''foo-%c3%a4-%e2%82%ac.html`, 309 "attachment", 310 m()}, 311 // #attwithfn2231utf8comp 312 {`attachment; filename*=UTF-8''foo-a%cc%88.html`, 313 "attachment", 314 m("filename", "foo-ä.html")}, 315 // #attwithfn2231ws2 316 {`attachment; filename*= UTF-8''foo-%c3%a4.html`, 317 "attachment", 318 m("filename", "foo-ä.html")}, 319 // #attwithfn2231ws3 320 {`attachment; filename* =UTF-8''foo-%c3%a4.html`, 321 "attachment", 322 m("filename", "foo-ä.html")}, 323 // #attwithfn2231quot 324 {`attachment; filename*="UTF-8''foo-%c3%a4.html"`, 325 "attachment", 326 m("filename", "foo-ä.html")}, 327 // #attwithfn2231quot2 328 {`attachment; filename*="foo%20bar.html"`, 329 "attachment", 330 m()}, 331 // #attwithfn2231singleqmissing 332 {`attachment; filename*=UTF-8'foo-%c3%a4.html`, 333 "attachment", 334 m()}, 335 // #attwithfn2231nbadpct1 336 {`attachment; filename*=UTF-8''foo%`, 337 "attachment", 338 m()}, 339 // #attwithfn2231nbadpct2 340 {`attachment; filename*=UTF-8''f%oo.html`, 341 "attachment", 342 m()}, 343 // #attwithfn2231dpct 344 {`attachment; filename*=UTF-8''A-%2541.html`, 345 "attachment", 346 m("filename", "A-%41.html")}, 347 // #attfncont 348 {`attachment; filename*0="foo."; filename*1="html"`, 349 "attachment", 350 m("filename", "foo.html")}, 351 // #attfncontenc 352 {`attachment; filename*0*=UTF-8''foo-%c3%a4; filename*1=".html"`, 353 "attachment", 354 m("filename", "foo-ä.html")}, 355 // #attfncontlz 356 {`attachment; filename*0="foo"; filename*01="bar"`, 357 "attachment", 358 m("filename", "foo")}, 359 // #attfncontnc 360 {`attachment; filename*0="foo"; filename*2="bar"`, 361 "attachment", 362 m("filename", "foo")}, 363 // #attfnconts1 364 {`attachment; filename*1="foo."; filename*2="html"`, 365 "attachment", m()}, 366 // #attfncontord 367 {`attachment; filename*1="bar"; filename*0="foo"`, 368 "attachment", 369 m("filename", "foobar")}, 370 // #attfnboth 371 {`attachment; filename="foo-ae.html"; filename*=UTF-8''foo-%c3%a4.html`, 372 "attachment", 373 m("filename", "foo-ä.html")}, 374 // #attfnboth2 375 {`attachment; filename*=UTF-8''foo-%c3%a4.html; filename="foo-ae.html"`, 376 "attachment", 377 m("filename", "foo-ä.html")}, 378 // #attfnboth3 379 {`attachment; filename*0*=ISO-8859-15''euro-sign%3d%a4; filename*=ISO-8859-1''currency-sign%3d%a4`, 380 "attachment", 381 m()}, 382 // #attnewandfn 383 {`attachment; foobar=x; filename="foo.html"`, 384 "attachment", 385 m("foobar", "x", "filename", "foo.html")}, 386 387 // Browsers also just send UTF-8 directly without RFC 2231, 388 // at least when the source page is served with UTF-8. 389 {`form-data; firstname="Брэд"; lastname="Фицпатрик"`, 390 "form-data", 391 m("firstname", "Брэд", "lastname", "Фицпатрик")}, 392 393 // Empty string used to be mishandled. 394 {`foo; bar=""`, "foo", m("bar", "")}, 395 396 // Microsoft browers in intranet mode do not think they need to escape \ in file name. 397 {`form-data; name="file"; filename="C:\dev\go\robots.txt"`, "form-data", m("name", "file", "filename", `C:\dev\go\robots.txt`)}, 398 {`form-data; name="file"; filename="C:\新建文件件\中文第二次测试.mp4"`, "form-data", m("name", "file", "filename", `C:\新建文件件\中文第二次测试.mp4`)}, 399 } 400 for _, test := range tests { 401 mt, params, err := ParseMediaType(test.in) 402 if err != nil { 403 if test.t != "" { 404 t.Errorf("for input %#q, unexpected error: %v", test.in, err) 405 continue 406 } 407 continue 408 } 409 if g, e := mt, test.t; g != e { 410 t.Errorf("for input %#q, expected type %q, got %q", 411 test.in, e, g) 412 continue 413 } 414 if len(params) == 0 && len(test.p) == 0 { 415 continue 416 } 417 if !reflect.DeepEqual(params, test.p) { 418 t.Errorf("for input %#q, wrong params.\n"+ 419 "expected: %#v\n"+ 420 " got: %#v", 421 test.in, test.p, params) 422 } 423 } 424 } 425 426 type badMediaTypeTest struct { 427 in string 428 mt string 429 err string 430 } 431 432 var badMediaTypeTests = []badMediaTypeTest{ 433 {"bogus ;=========", "bogus", "mime: invalid media parameter"}, 434 // The following example is from real email delivered by gmail (error: missing semicolon) 435 // and it is there to check behavior described in #19498 436 {"application/pdf; x-mac-type=\"3F3F3F3F\"; x-mac-creator=\"3F3F3F3F\" name=\"a.pdf\";", 437 "application/pdf", "mime: invalid media parameter"}, 438 {"bogus/<script>alert</script>", "", "mime: expected token after slash"}, 439 {"bogus/bogus<script>alert</script>", "", "mime: unexpected content after media subtype"}, 440 // Tests from http://greenbytes.de/tech/tc2231/ 441 {`"attachment"`, "attachment", "mime: no media type"}, 442 {"attachment; filename=foo,bar.html", "attachment", "mime: invalid media parameter"}, 443 {"attachment; ;filename=foo", "attachment", "mime: invalid media parameter"}, 444 {"attachment; filename=foo bar.html", "attachment", "mime: invalid media parameter"}, 445 {`attachment; filename="foo.html"; filename="bar.html"`, "attachment", "mime: duplicate parameter name"}, 446 {"attachment; filename=foo[1](2).html", "attachment", "mime: invalid media parameter"}, 447 {"attachment; filename=foo-ä.html", "attachment", "mime: invalid media parameter"}, 448 {"attachment; filename=foo-ä.html", "attachment", "mime: invalid media parameter"}, 449 {`attachment; filename *=UTF-8''foo-%c3%a4.html`, "attachment", "mime: invalid media parameter"}, 450 } 451 452 func TestParseMediaTypeBogus(t *testing.T) { 453 for _, tt := range badMediaTypeTests { 454 mt, params, err := ParseMediaType(tt.in) 455 if err == nil { 456 t.Errorf("ParseMediaType(%q) = nil error; want parse error", tt.in) 457 continue 458 } 459 if err.Error() != tt.err { 460 t.Errorf("ParseMediaType(%q) = err %q; want %q", tt.in, err.Error(), tt.err) 461 } 462 if params != nil { 463 t.Errorf("ParseMediaType(%q): got non-nil params on error", tt.in) 464 } 465 if err != ErrInvalidMediaParameter && mt != "" { 466 t.Errorf("ParseMediaType(%q): got unexpected non-empty media type string", tt.in) 467 } 468 if err == ErrInvalidMediaParameter && mt != tt.mt { 469 t.Errorf("ParseMediaType(%q): in case of invalid parameters: expected type %q, got %q", tt.in, tt.mt, mt) 470 } 471 } 472 } 473 474 type formatTest struct { 475 typ string 476 params map[string]string 477 want string 478 } 479 480 var formatTests = []formatTest{ 481 {"noslash", map[string]string{"X": "Y"}, "noslash; x=Y"}, // e.g. Content-Disposition values (RFC 2183); issue 11289 482 {"foo bar/baz", nil, ""}, 483 {"foo/bar baz", nil, ""}, 484 {"foo/BAR", nil, "foo/bar"}, 485 {"foo/BAR", map[string]string{"X": "Y"}, "foo/bar; x=Y"}, 486 {"foo/BAR", map[string]string{"space": "With space"}, `foo/bar; space="With space"`}, 487 {"foo/BAR", map[string]string{"quote": `With "quote`}, `foo/bar; quote="With \"quote"`}, 488 {"foo/BAR", map[string]string{"bslash": `With \backslash`}, `foo/bar; bslash="With \\backslash"`}, 489 {"foo/BAR", map[string]string{"both": `With \backslash and "quote`}, `foo/bar; both="With \\backslash and \"quote"`}, 490 {"foo/BAR", map[string]string{"": "empty attribute"}, ""}, 491 {"foo/BAR", map[string]string{"bad attribute": "baz"}, ""}, 492 {"foo/BAR", map[string]string{"nonascii": "not an ascii character: ä"}, ""}, 493 {"foo/bar", map[string]string{"a": "av", "b": "bv", "c": "cv"}, "foo/bar; a=av; b=bv; c=cv"}, 494 {"foo/bar", map[string]string{"0": "'", "9": "'"}, "foo/bar; 0='; 9='"}, 495 {"foo", map[string]string{"bar": ""}, `foo; bar=""`}, 496 } 497 498 func TestFormatMediaType(t *testing.T) { 499 for i, tt := range formatTests { 500 got := FormatMediaType(tt.typ, tt.params) 501 if got != tt.want { 502 t.Errorf("%d. FormatMediaType(%q, %v) = %q; want %q", i, tt.typ, tt.params, got, tt.want) 503 } 504 } 505 }