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