http_content_disposition_unittest.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/http/http_content_disposition.h"
  5. #include "base/strings/utf_string_conversions.h"
  6. #include "testing/gtest/include/gtest/gtest.h"
  7. namespace net {
  8. namespace {
  9. struct FileNameCDCase {
  10. const char* header;
  11. const char* referrer_charset;
  12. const wchar_t* expected;
  13. };
  14. } // anonymous namespace
  15. TEST(HttpContentDispositionTest, Filename) {
  16. const FileNameCDCase tests[] = {
  17. // Test various forms of C-D header fields emitted by web servers.
  18. {"inline; filename=\"abcde.pdf\"", "", L"abcde.pdf"},
  19. {"attachment; filename=abcde.pdf", "", L"abcde.pdf"},
  20. {"attachment; filename=abc,de.pdf", "", L"abc,de.pdf"},
  21. {"filename=abcde.pdf", "", L"abcde.pdf"},
  22. {"filename= abcde.pdf", "", L"abcde.pdf"},
  23. {"filename =abcde.pdf", "", L"abcde.pdf"},
  24. {"filename = abcde.pdf", "", L"abcde.pdf"},
  25. {"filename\t=abcde.pdf", "", L"abcde.pdf"},
  26. {"filename \t\t =abcde.pdf", "", L"abcde.pdf"},
  27. {"inline; filename=\"abc%20de.pdf\"", "",
  28. L"abc de.pdf"},
  29. // Name values are no longer synonyms for filename.
  30. {"inline; name=\"abcde.pdf\"", "", L""},
  31. {"attachment; name=abcde.pdf", "", L""},
  32. {"name=abcde.pdf", "", L""},
  33. // Unbalanced quotation mark
  34. {"filename=\"abcdef.pdf", "", L"abcdef.pdf"},
  35. // Whitespaces are converted to a space.
  36. {"inline; filename=\"abc \t\nde.pdf\"", "",
  37. L"abc de.pdf"},
  38. // %-escaped UTF-8
  39. {"attachment; filename=\"%EC%98%88%EC%88%A0%20"
  40. "%EC%98%88%EC%88%A0.jpg\"", "", L"\xc608\xc220 \xc608\xc220.jpg"},
  41. {"attachment; filename=\"%F0%90%8C%B0%F0%90%8C%B1"
  42. "abc.jpg\"", "", L"\U00010330\U00010331abc.jpg"},
  43. {"attachment; filename=\"%EC%98%88%EC%88%A0 \n"
  44. "%EC%98%88%EC%88%A0.jpg\"", "", L"\xc608\xc220 \xc608\xc220.jpg"},
  45. // Characters that are not supposed to be displayed should still be decoded.
  46. {"attachment; filename=%E5%B2%A1%E3%80%80%E5%B2%A1.txt", "",
  47. L"\u5ca1\u3000\u5ca1.txt"},
  48. // RFC 2047 with various charsets and Q/B encodings
  49. {"attachment; filename=\"=?EUC-JP?Q?=B7=DD=BD="
  50. "D13=2Epng?=\"", "", L"\x82b8\x8853" L"3.png"},
  51. {"attachment; filename==?eUc-Kr?b?v7m8+iAzLnBuZw==?=",
  52. "", L"\xc608\xc220 3.png"},
  53. {"attachment; filename==?utf-8?Q?=E8=8A=B8=E8"
  54. "=A1=93_3=2Epng?=", "", L"\x82b8\x8853 3.png"},
  55. {"attachment; filename==?utf-8?Q?=F0=90=8C=B0"
  56. "_3=2Epng?=", "", L"\U00010330 3.png"},
  57. {"inline; filename=\"=?iso88591?Q?caf=e9_=2epng?=\"",
  58. "", L"caf\x00e9 .png"},
  59. // Space after an encoded word should be removed.
  60. {"inline; filename=\"=?iso88591?Q?caf=E9_?= .png\"",
  61. "", L"caf\x00e9 .png"},
  62. // Two encoded words with different charsets (not very likely to be emitted
  63. // by web servers in the wild). Spaces between them are removed.
  64. {"inline; filename=\"=?euc-kr?b?v7m8+iAz?="
  65. " =?ksc5601?q?=BF=B9=BC=FA=2Epng?=\"", "",
  66. L"\xc608\xc220 3\xc608\xc220.png"},
  67. {"attachment; filename=\"=?windows-1252?Q?caf=E9?="
  68. " =?iso-8859-7?b?4eI=?= .png\"", "", L"caf\x00e9\x03b1\x03b2.png"},
  69. // Non-ASCII string is passed through and treated as UTF-8 as long as
  70. // it's valid as UTF-8 and regardless of |referrer_charset|.
  71. {"attachment; filename=caf\xc3\xa9.png",
  72. "iso-8859-1", L"caf\x00e9.png"},
  73. {"attachment; filename=caf\xc3\xa9.png",
  74. "", L"caf\x00e9.png"},
  75. // Non-ASCII/Non-UTF-8 string. Fall back to the referrer charset.
  76. {"attachment; filename=caf\xe5.png",
  77. "windows-1253", L"caf\x03b5.png"},
  78. #if 0
  79. // Non-ASCII/Non-UTF-8 string. Fall back to the native codepage.
  80. // TODO(jungshik): We need to set the OS default codepage
  81. // to a specific value before testing. On Windows, we can use
  82. // SetThreadLocale().
  83. {"attachment; filename=\xb0\xa1\xb0\xa2.png",
  84. "", L"\xac00\xac01.png"},
  85. #endif
  86. // Failure cases
  87. // Invalid hex-digit "G"
  88. {"attachment; filename==?iiso88591?Q?caf=EG?=", "",
  89. L""},
  90. // Incomplete RFC 2047 encoded-word (missing '='' at the end)
  91. {"attachment; filename==?iso88591?Q?caf=E3?", "", L""},
  92. // Extra character at the end of an encoded word
  93. {"attachment; filename==?iso88591?Q?caf=E3?==",
  94. "", L""},
  95. // Extra token at the end of an encoded word
  96. {"attachment; filename==?iso88591?Q?caf=E3?=?",
  97. "", L""},
  98. {"attachment; filename==?iso88591?Q?caf=E3?=?=",
  99. "", L""},
  100. // Incomplete hex-escaped chars
  101. {"attachment; filename==?windows-1252?Q?=63=61=E?=",
  102. "", L""},
  103. {"attachment; filename=%EC%98%88%EC%88%A", "", L""},
  104. // %-escaped non-UTF-8 encoding is an "error"
  105. {"attachment; filename=%B7%DD%BD%D1.png", "", L""},
  106. // Two RFC 2047 encoded words in a row without a space is an error.
  107. {"attachment; filename==?windows-1252?Q?caf=E3?="
  108. "=?iso-8859-7?b?4eIucG5nCg==?=", "", L""},
  109. // RFC 5987 tests with Filename* : see http://tools.ietf.org/html/rfc5987
  110. {"attachment; filename*=foo.html", "", L""},
  111. {"attachment; filename*=foo'.html", "", L""},
  112. {"attachment; filename*=''foo'.html", "", L""},
  113. {"attachment; filename*=''foo.html'", "", L""},
  114. {"attachment; filename*=''f\"oo\".html'", "", L""},
  115. {"attachment; filename*=bogus_charset''foo.html'",
  116. "", L""},
  117. {"attachment; filename*='en'foo.html'", "", L""},
  118. {"attachment; filename*=iso-8859-1'en'foo.html", "",
  119. L"foo.html"},
  120. {"attachment; filename*=utf-8'en'foo.html", "",
  121. L"foo.html"},
  122. {"attachment; filename*=utf-8'en'%E5%B2%A1%E3%80%80%E5%B2%A1.txt", "",
  123. L"\u5ca1\u3000\u5ca1.txt"},
  124. // charset cannot be omitted.
  125. {"attachment; filename*='es'f\xfa.html'", "", L""},
  126. // Non-ASCII bytes are not allowed.
  127. {"attachment; filename*=iso-8859-1'es'f\xfa.html", "",
  128. L""},
  129. {"attachment; filename*=utf-8'es'f\xce\xba.html", "",
  130. L""},
  131. // TODO(jshin): Space should be %-encoded, but currently, we allow
  132. // spaces.
  133. {"inline; filename*=iso88591''cafe foo.png", "",
  134. L"cafe foo.png"},
  135. // Filename* tests converted from Q-encoded tests above.
  136. {"attachment; filename*=EUC-JP''%B7%DD%BD%D13%2Epng",
  137. "", L"\x82b8\x8853" L"3.png"},
  138. {"attachment; filename*=utf-8''"
  139. "%E8%8A%B8%E8%A1%93%203%2Epng", "", L"\x82b8\x8853 3.png"},
  140. {"attachment; filename*=utf-8''%F0%90%8C%B0 3.png", "",
  141. L"\U00010330 3.png"},
  142. {"inline; filename*=Euc-Kr'ko'%BF%B9%BC%FA%2Epng", "",
  143. L"\xc608\xc220.png"},
  144. {"attachment; filename*=windows-1252''caf%E9.png", "",
  145. L"caf\x00e9.png"},
  146. // Multiple filename, filename*, name parameters specified.
  147. {"attachment; name=\"foo\"; filename=\"bar\"", "", L"bar"},
  148. {"attachment; filename=\"bar\"; name=\"foo\"", "", L"bar"},
  149. {"attachment; filename=\"bar\"; filename*=utf-8''baz", "", L"baz"},
  150. // http://greenbytes.de/tech/tc2231/ filename* test cases.
  151. // attwithisofn2231iso
  152. {"attachment; filename*=iso-8859-1''foo-%E4.html", "",
  153. L"foo-\xe4.html"},
  154. // attwithfn2231utf8
  155. {"attachment; filename*="
  156. "UTF-8''foo-%c3%a4-%e2%82%ac.html", "", L"foo-\xe4-\x20ac.html"},
  157. // attwithfn2231noc : no encoding specified but UTF-8 is used.
  158. {"attachment; filename*=''foo-%c3%a4-%e2%82%ac.html",
  159. "", L""},
  160. // attwithfn2231utf8comp
  161. {"attachment; filename*=UTF-8''foo-a%cc%88.html", "",
  162. L"foo-\xe4.html"},
  163. #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
  164. // This does not work because we treat ISO-8859-1 synonymous with
  165. // Windows-1252 per HTML5. For HTTP, in theory, we're not
  166. // supposed to.
  167. // attwithfn2231utf8-bad
  168. {"attachment; filename*="
  169. "iso-8859-1''foo-%c3%a4-%e2%82%ac.html", "", L""},
  170. #endif
  171. // attwithfn2231ws1
  172. {"attachment; filename *=UTF-8''foo-%c3%a4.html", "",
  173. L""},
  174. // attwithfn2231ws2
  175. {"attachment; filename*= UTF-8''foo-%c3%a4.html", "",
  176. L"foo-\xe4.html"},
  177. // attwithfn2231ws3
  178. {"attachment; filename* =UTF-8''foo-%c3%a4.html", "",
  179. L"foo-\xe4.html"},
  180. // attwithfn2231quot
  181. {"attachment; filename*=\"UTF-8''foo-%c3%a4.html\"",
  182. "", L""},
  183. // attfnboth
  184. {"attachment; filename=\"foo-ae.html\"; "
  185. "filename*=UTF-8''foo-%c3%a4.html", "", L"foo-\xe4.html"},
  186. // attfnboth2
  187. {"attachment; filename*=UTF-8''foo-%c3%a4.html; "
  188. "filename=\"foo-ae.html\"", "", L"foo-\xe4.html"},
  189. // attnewandfn
  190. {"attachment; foobar=x; filename=\"foo.html\"", "",
  191. L"foo.html"},
  192. };
  193. for (const auto& test : tests) {
  194. HttpContentDisposition header(test.header, test.referrer_charset);
  195. EXPECT_EQ(test.expected, base::UTF8ToWide(header.filename()))
  196. << "Failed on input: " << test.header;
  197. }
  198. }
  199. // Test cases from http://greenbytes.de/tech/tc2231/
  200. TEST(HttpContentDispositionTest, tc2231) {
  201. const struct FileNameCDCase {
  202. const char* header;
  203. HttpContentDisposition::Type expected_type;
  204. const wchar_t* expected_filename;
  205. } tests[] = {
  206. // http://greenbytes.de/tech/tc2231/#inlonly
  207. {"inline", HttpContentDisposition::INLINE, L""},
  208. // http://greenbytes.de/tech/tc2231/#inlonlyquoted
  209. {"\"inline\"", HttpContentDisposition::INLINE, L""},
  210. // http://greenbytes.de/tech/tc2231/#inlwithasciifilename
  211. {"inline; filename=\"foo.html\"", HttpContentDisposition::INLINE,
  212. L"foo.html"},
  213. // http://greenbytes.de/tech/tc2231/#inlwithfnattach
  214. {"inline; filename=\"Not an attachment!\"",
  215. HttpContentDisposition::INLINE, L"Not an attachment!"},
  216. // http://greenbytes.de/tech/tc2231/#inlwithasciifilenamepdf
  217. {"inline; filename=\"foo.pdf\"", HttpContentDisposition::INLINE,
  218. L"foo.pdf"},
  219. // http://greenbytes.de/tech/tc2231/#attonly
  220. {"attachment", HttpContentDisposition::ATTACHMENT, L""},
  221. // http://greenbytes.de/tech/tc2231/#attonlyquoted
  222. {"\"attachment\"", HttpContentDisposition::INLINE, L""},
  223. // http://greenbytes.de/tech/tc2231/#attonly403
  224. // TODO(abarth): This isn't testable in this unit test.
  225. // http://greenbytes.de/tech/tc2231/#attonlyucase
  226. {"ATTACHMENT", HttpContentDisposition::ATTACHMENT, L""},
  227. // http://greenbytes.de/tech/tc2231/#attwithasciifilename
  228. {"attachment; filename=\"foo.html\"", HttpContentDisposition::ATTACHMENT,
  229. L"foo.html"},
  230. // http://greenbytes.de/tech/tc2231/#attwithasciifnescapedchar
  231. {"attachment; filename=\"f\\oo.html\"",
  232. HttpContentDisposition::ATTACHMENT, L"foo.html"},
  233. // http://greenbytes.de/tech/tc2231/#attwithasciifnescapedquote
  234. {"attachment; filename=\"\\\"quoting\\\" tested.html\"",
  235. HttpContentDisposition::ATTACHMENT, L"\"quoting\" tested.html"},
  236. // http://greenbytes.de/tech/tc2231/#attwithquotedsemicolon
  237. {"attachment; filename=\"Here's a semicolon;.html\"",
  238. HttpContentDisposition::ATTACHMENT, L"Here's a semicolon;.html"},
  239. // http://greenbytes.de/tech/tc2231/#attwithfilenameandextparam
  240. {"attachment; foo=\"bar\"; filename=\"foo.html\"",
  241. HttpContentDisposition::ATTACHMENT, L"foo.html"},
  242. // http://greenbytes.de/tech/tc2231/#attwithfilenameandextparamescaped
  243. {"attachment; foo=\"\\\"\\\\\";filename=\"foo.html\"",
  244. HttpContentDisposition::ATTACHMENT, L"foo.html"},
  245. // http://greenbytes.de/tech/tc2231/#attwithasciifilenameucase
  246. {"attachment; FILENAME=\"foo.html\"", HttpContentDisposition::ATTACHMENT,
  247. L"foo.html"},
  248. // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenq
  249. {"attachment; filename=foo.html", HttpContentDisposition::ATTACHMENT,
  250. L"foo.html"},
  251. // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenqs
  252. // Note: tc2231 says we should fail to parse this header.
  253. {"attachment; filename=foo.html ;", HttpContentDisposition::ATTACHMENT,
  254. L"foo.html"},
  255. // http://greenbytes.de/tech/tc2231/#attemptyparam
  256. // Note: tc2231 says we should fail to parse this header.
  257. {"attachment; ;filename=foo", HttpContentDisposition::ATTACHMENT, L"foo"},
  258. // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenqws
  259. // Note: tc2231 says we should fail to parse this header.
  260. {"attachment; filename=foo bar.html", HttpContentDisposition::ATTACHMENT,
  261. L"foo bar.html"},
  262. // http://greenbytes.de/tech/tc2231/#attwithfntokensq
  263. {"attachment; filename='foo.bar'", HttpContentDisposition::ATTACHMENT,
  264. L"'foo.bar'"},
  265. #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
  266. // http://greenbytes.de/tech/tc2231/#attwithisofnplain
  267. {
  268. "attachment; filename=\"foo-\xE4html\"",
  269. HttpContentDisposition::ATTACHMENT,
  270. L"" // Should be L"foo-\xE4.html"
  271. },
  272. #endif
  273. // http://greenbytes.de/tech/tc2231/#attwithutf8fnplain
  274. // Note: We'll UTF-8 decode the file name, even though tc2231 says not to.
  275. {"attachment; filename=\"foo-\xC3\xA4.html\"",
  276. HttpContentDisposition::ATTACHMENT, L"foo-\xE4.html"},
  277. // http://greenbytes.de/tech/tc2231/#attwithfnrawpctenca
  278. {
  279. "attachment; filename=\"foo-%41.html\"",
  280. HttpContentDisposition::ATTACHMENT,
  281. L"foo-A.html" // Should be L"foo-%41.html"
  282. },
  283. // http://greenbytes.de/tech/tc2231/#attwithfnusingpct
  284. {"attachment; filename=\"50%.html\"", HttpContentDisposition::ATTACHMENT,
  285. L"50%.html"},
  286. // http://greenbytes.de/tech/tc2231/#attwithfnrawpctencaq
  287. {
  288. "attachment; filename=\"foo-%\\41.html\"",
  289. HttpContentDisposition::ATTACHMENT,
  290. L"foo-A.html" // Should be L"foo-%41.html"
  291. },
  292. // http://greenbytes.de/tech/tc2231/#attwithnamepct
  293. // Value is skipped like other UAs.
  294. {"attachment; name=\"foo-%41.html\"", HttpContentDisposition::ATTACHMENT,
  295. L""},
  296. #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
  297. // http://greenbytes.de/tech/tc2231/#attwithfilenamepctandiso
  298. {
  299. "attachment; filename=\"\xE4-%41.html\"",
  300. HttpContentDisposition::ATTACHMENT,
  301. L"" // Should be L"\xE4-%41.htm"
  302. },
  303. #endif
  304. // http://greenbytes.de/tech/tc2231/#attwithfnrawpctenclong
  305. {
  306. "attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\"",
  307. HttpContentDisposition::ATTACHMENT,
  308. L"foo-\xE4-\u20AC.html" // Should be L"foo-%c3%a4-%e2%82%ac.html"
  309. },
  310. // http://greenbytes.de/tech/tc2231/#attwithasciifilenamews1
  311. {"attachment; filename =\"foo.html\"", HttpContentDisposition::ATTACHMENT,
  312. L"foo.html"},
  313. // http://greenbytes.de/tech/tc2231/#attwith2filenames
  314. // Note: tc2231 says we should fail to parse this header.
  315. {"attachment; filename=\"foo.html\"; filename=\"bar.html\"",
  316. HttpContentDisposition::ATTACHMENT, L"foo.html"},
  317. // http://greenbytes.de/tech/tc2231/#attfnbrokentoken
  318. // Note: tc2231 says we should fail to parse this header.
  319. {"attachment; filename=foo[1](2).html",
  320. HttpContentDisposition::ATTACHMENT, L"foo[1](2).html"},
  321. #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
  322. // http://greenbytes.de/tech/tc2231/#attfnbrokentokeniso
  323. // Note: tc2231 says we should fail to parse this header.
  324. {"attachment; filename=foo-\xE4.html", HttpContentDisposition::ATTACHMENT,
  325. L""},
  326. #endif
  327. // http://greenbytes.de/tech/tc2231/#attfnbrokentokenutf
  328. // Note: tc2231 says we should fail to parse this header.
  329. {"attachment; filename=foo-\xC3\xA4.html",
  330. HttpContentDisposition::ATTACHMENT, L"foo-\xE4.html"},
  331. // http://greenbytes.de/tech/tc2231/#attmissingdisposition
  332. // Note: tc2231 says we should fail to parse this header.
  333. {"filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
  334. // http://greenbytes.de/tech/tc2231/#attmissingdisposition2
  335. // Note: tc2231 says we should fail to parse this header.
  336. {"x=y; filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
  337. // http://greenbytes.de/tech/tc2231/#attmissingdisposition3
  338. // Note: tc2231 says we should fail to parse this header.
  339. {
  340. "\"foo; filename=bar;baz\"; filename=qux",
  341. HttpContentDisposition::INLINE,
  342. L"" // Firefox gets qux
  343. },
  344. // http://greenbytes.de/tech/tc2231/#attmissingdisposition4
  345. // Note: tc2231 says we should fail to parse this header.
  346. {"filename=foo.html, filename=bar.html", HttpContentDisposition::INLINE,
  347. L"foo.html, filename=bar.html"},
  348. // http://greenbytes.de/tech/tc2231/#emptydisposition
  349. // Note: tc2231 says we should fail to parse this header.
  350. {"; filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
  351. // http://greenbytes.de/tech/tc2231/#attandinline
  352. // Note: tc2231 says we should fail to parse this header.
  353. {"inline; attachment; filename=foo.html", HttpContentDisposition::INLINE,
  354. L""},
  355. // http://greenbytes.de/tech/tc2231/#attandinline2
  356. // Note: tc2231 says we should fail to parse this header.
  357. {"attachment; inline; filename=foo.html",
  358. HttpContentDisposition::ATTACHMENT, L""},
  359. // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn
  360. // Note: tc2231 says we should fail to parse this header.
  361. {"attachment; filename=\"foo.html\".txt",
  362. HttpContentDisposition::ATTACHMENT, L"foo.html\".txt"},
  363. // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn2
  364. // Note: tc2231 says we should fail to parse this header.
  365. {"attachment; filename=\"bar", HttpContentDisposition::ATTACHMENT,
  366. L"bar"},
  367. // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn3
  368. // Note: tc2231 says we should fail to parse this header.
  369. {"attachment; filename=foo\"bar;baz\"qux",
  370. HttpContentDisposition::ATTACHMENT, L"foo\"bar;baz\"qux"},
  371. // http://greenbytes.de/tech/tc2231/#attmultinstances
  372. // Note: tc2231 says we should fail to parse this header.
  373. {"attachment; filename=foo.html, attachment; filename=bar.html",
  374. HttpContentDisposition::ATTACHMENT, L"foo.html, attachment"},
  375. // http://greenbytes.de/tech/tc2231/#attmissingdelim
  376. {"attachment; foo=foo filename=bar", HttpContentDisposition::ATTACHMENT,
  377. L""},
  378. // http://greenbytes.de/tech/tc2231/#attreversed
  379. // Note: tc2231 says we should fail to parse this header.
  380. {"filename=foo.html; attachment", HttpContentDisposition::INLINE,
  381. L"foo.html"},
  382. // http://greenbytes.de/tech/tc2231/#attconfusedparam
  383. {"attachment; xfilename=foo.html", HttpContentDisposition::ATTACHMENT,
  384. L""},
  385. // http://greenbytes.de/tech/tc2231/#attabspath
  386. {"attachment; filename=\"/foo.html\"", HttpContentDisposition::ATTACHMENT,
  387. L"/foo.html"},
  388. // http://greenbytes.de/tech/tc2231/#attabspathwin
  389. {"attachment; filename=\"\\\\foo.html\"",
  390. HttpContentDisposition::ATTACHMENT, L"\\foo.html"},
  391. // http://greenbytes.de/tech/tc2231/#dispext
  392. {"foobar", HttpContentDisposition::ATTACHMENT, L""},
  393. // http://greenbytes.de/tech/tc2231/#dispextbadfn
  394. {"attachment; example=\"filename=example.txt\"",
  395. HttpContentDisposition::ATTACHMENT, L""},
  396. // http://greenbytes.de/tech/tc2231/#attnewandfn
  397. {"attachment; foobar=x; filename=\"foo.html\"",
  398. HttpContentDisposition::ATTACHMENT, L"foo.html"},
  399. // TODO(abarth): Add the filename* tests, but check
  400. // HttpContentDispositionTest.Filename for overlap.
  401. // TODO(abarth): http://greenbytes.de/tech/tc2231/#attrfc2047token
  402. // TODO(abarth): http://greenbytes.de/tech/tc2231/#attrfc2047quoted
  403. };
  404. for (const auto& test : tests) {
  405. HttpContentDisposition header(test.header, std::string());
  406. EXPECT_EQ(test.expected_type, header.type())
  407. << "Failed on input: " << test.header;
  408. EXPECT_EQ(test.expected_filename, base::UTF8ToWide(header.filename()))
  409. << "Failed on input: " << test.header;
  410. }
  411. }
  412. TEST(HttpContentDispositionTest, ParseResult) {
  413. const struct ParseResultTestCase {
  414. const char* header;
  415. int expected_flags;
  416. } kTestCases[] = {
  417. // Basic feature tests
  418. {"", HttpContentDisposition::INVALID},
  419. {"example=x", HttpContentDisposition::INVALID},
  420. {"attachment; filename=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
  421. {"attachment; name=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
  422. {"attachment; filename*=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
  423. {"attachment; filename==?utf-8?Q?\?=",
  424. HttpContentDisposition::HAS_DISPOSITION_TYPE},
  425. {"filename=x", HttpContentDisposition::HAS_FILENAME},
  426. {"example; filename=x",
  427. HttpContentDisposition::HAS_DISPOSITION_TYPE |
  428. HttpContentDisposition::HAS_UNKNOWN_DISPOSITION_TYPE |
  429. HttpContentDisposition::HAS_FILENAME},
  430. {"attachment; filename=x", HttpContentDisposition::HAS_DISPOSITION_TYPE |
  431. HttpContentDisposition::HAS_FILENAME},
  432. {"attachment; filename='x'",
  433. HttpContentDisposition::HAS_DISPOSITION_TYPE |
  434. HttpContentDisposition::HAS_FILENAME |
  435. HttpContentDisposition::HAS_SINGLE_QUOTED_FILENAME},
  436. {"attachment; filename=x; name=y",
  437. HttpContentDisposition::HAS_DISPOSITION_TYPE |
  438. HttpContentDisposition::HAS_FILENAME},
  439. {"attachment; name=y; filename*=utf-8''foo; name=x",
  440. HttpContentDisposition::HAS_DISPOSITION_TYPE |
  441. HttpContentDisposition::HAS_EXT_FILENAME},
  442. // Feature tests for 'filename' attribute.
  443. {"filename=foo\xcc\x88",
  444. HttpContentDisposition::HAS_FILENAME |
  445. HttpContentDisposition::HAS_NON_ASCII_STRINGS},
  446. {"filename=foo%cc%88",
  447. HttpContentDisposition::HAS_FILENAME |
  448. HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS},
  449. {"filename==?utf-8?Q?foo?=",
  450. HttpContentDisposition::HAS_FILENAME |
  451. HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
  452. {"filename=\"=?utf-8?Q?foo?=\"",
  453. HttpContentDisposition::HAS_FILENAME |
  454. HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
  455. {"filename==?utf-8?Q?foo?", HttpContentDisposition::INVALID},
  456. // Test 'name' isn't a synonym for 'filename'.
  457. {"name=foo\xcc\x88", HttpContentDisposition::INVALID},
  458. // Shouldn't set |has_non_ascii_strings| based on 'name' attribute.
  459. {"filename=x; name=foo\xcc\x88", HttpContentDisposition::HAS_FILENAME},
  460. {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?=",
  461. HttpContentDisposition::HAS_FILENAME |
  462. HttpContentDisposition::HAS_NON_ASCII_STRINGS |
  463. HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS |
  464. HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
  465. // If 'filename' attribute is invalid, should set any flags based on it.
  466. {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?",
  467. HttpContentDisposition::INVALID},
  468. {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?; name=x",
  469. HttpContentDisposition::INVALID},
  470. };
  471. for (size_t i = 0; i < std::size(kTestCases); ++i) {
  472. const ParseResultTestCase& test_case = kTestCases[i];
  473. HttpContentDisposition content_disposition(test_case.header, "utf-8");
  474. int result = content_disposition.parse_result_flags();
  475. SCOPED_TRACE(testing::Message() << "Test case " << i
  476. << " with header " << test_case.header);
  477. EXPECT_EQ(test_case.expected_flags, result);
  478. }
  479. }
  480. TEST(HttpContentDispositionTest, ContainsNul) {
  481. const char kHeader[] = "filename=ab\0c";
  482. const char kExpectedFilename[] = "ab\0c";
  483. // Note: both header and expected_filename include the trailing NUL.
  484. std::string header{kHeader, sizeof(kHeader)};
  485. std::string expected_filename{kExpectedFilename, sizeof(kExpectedFilename)};
  486. HttpContentDisposition content_disposition(header, "utf-8");
  487. EXPECT_EQ(expected_filename, content_disposition.filename());
  488. }
  489. } // namespace net