xref: /aosp_15_r20/external/cronet/net/base/mime_util_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/base/mime_util.h"
6 
7 #include <vector>
8 
9 #include "base/containers/contains.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "build/build_config.h"
13 #include "build/chromeos_buildflags.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace net {
18 
19 using testing::Contains;
20 
TEST(MimeUtilTest,GetWellKnownMimeTypeFromExtension)21 TEST(MimeUtilTest, GetWellKnownMimeTypeFromExtension) {
22   // String: png\0css
23   base::FilePath::StringType containsNullByte;
24   containsNullByte.append(FILE_PATH_LITERAL("png"));
25   containsNullByte.append(1, FILE_PATH_LITERAL('\0'));
26   containsNullByte.append(FILE_PATH_LITERAL("css"));
27 
28   const struct {
29     const base::FilePath::StringType extension;
30     const char* const mime_type;
31   } tests[] = {
32       {FILE_PATH_LITERAL("png"), "image/png"},
33       {FILE_PATH_LITERAL("PNG"), "image/png"},
34       {FILE_PATH_LITERAL("css"), "text/css"},
35       {FILE_PATH_LITERAL("pjp"), "image/jpeg"},
36       {FILE_PATH_LITERAL("pjpeg"), "image/jpeg"},
37       {FILE_PATH_LITERAL("json"), "application/json"},
38       {FILE_PATH_LITERAL("js"), "text/javascript"},
39       {FILE_PATH_LITERAL("webm"), "video/webm"},
40       {FILE_PATH_LITERAL("weba"), "audio/webm"},
41       {FILE_PATH_LITERAL("avif"), "image/avif"},
42       {FILE_PATH_LITERAL("epub"), "application/epub+zip"},
43       {FILE_PATH_LITERAL("apk"), "application/vnd.android.package-archive"},
44       {FILE_PATH_LITERAL("cer"), "application/x-x509-ca-cert"},
45       {FILE_PATH_LITERAL("crt"), "application/x-x509-ca-cert"},
46       {FILE_PATH_LITERAL("zip"), "application/zip"},
47       {FILE_PATH_LITERAL("ics"), "text/calendar"},
48       {FILE_PATH_LITERAL("m3u8"), "application/x-mpegurl"},
49       {FILE_PATH_LITERAL("csv"), "text/csv"},
50       {FILE_PATH_LITERAL("not an extension / for sure"), nullptr},
51       {containsNullByte, nullptr}};
52 
53   for (const auto& test : tests) {
54     std::string mime_type;
55     if (GetWellKnownMimeTypeFromExtension(test.extension, &mime_type))
56       EXPECT_EQ(test.mime_type, mime_type);
57     else
58       EXPECT_EQ(test.mime_type, nullptr);
59   }
60 }
61 
TEST(MimeUtilTest,ExtensionTest)62 TEST(MimeUtilTest, ExtensionTest) {
63   // String: png\0css
64   base::FilePath::StringType containsNullByte;
65   containsNullByte.append(FILE_PATH_LITERAL("png"));
66   containsNullByte.append(1, FILE_PATH_LITERAL('\0'));
67   containsNullByte.append(FILE_PATH_LITERAL("css"));
68 
69   const struct {
70     const base::FilePath::StringType extension;
71     const std::vector<std::string> mime_types;
72   } tests[] = {
73     {FILE_PATH_LITERAL("png"), {"image/png"}},
74     {FILE_PATH_LITERAL("PNG"), {"image/png"}},
75     {FILE_PATH_LITERAL("css"), {"text/css"}},
76     {FILE_PATH_LITERAL("pjp"), {"image/jpeg"}},
77     {FILE_PATH_LITERAL("pjpeg"), {"image/jpeg"}},
78     {FILE_PATH_LITERAL("json"), {"application/json"}},
79     {FILE_PATH_LITERAL("js"), {"text/javascript"}},
80     {FILE_PATH_LITERAL("webm"), {"video/webm"}},
81     {FILE_PATH_LITERAL("weba"), {"audio/webm"}},
82     {FILE_PATH_LITERAL("avif"), {"image/avif"}},
83 #if BUILDFLAG(IS_CHROMEOS_ASH)
84     // These are test cases for testing platform mime types on ChromeOS.
85     {FILE_PATH_LITERAL("epub"), {"application/epub+zip"}},
86     {FILE_PATH_LITERAL("apk"), {"application/vnd.android.package-archive"}},
87     {FILE_PATH_LITERAL("cer"),
88      {
89          "application/x-x509-ca-cert",
90          "application/pkix-cert",  // System override for ChromeOS.
91      }},
92     {FILE_PATH_LITERAL("crt"),
93      {
94          "application/x-x509-ca-cert",
95          "application/pkix-cert",  // System override for ChromeOS.
96      }},
97     {FILE_PATH_LITERAL("zip"), {"application/zip"}},
98     {FILE_PATH_LITERAL("ics"), {"text/calendar"}},
99 #endif
100     {FILE_PATH_LITERAL("m3u8"),
101      {
102          "application/x-mpegurl",  // Chrome's secondary mapping.
103          "audio/x-mpegurl",  // https://crbug.com/1273061, system override for
104                              // android-arm[64]-test and Linux. Possibly more.
105          "audio/mpegurl",                  // System override for mac.
106      }},
107     {FILE_PATH_LITERAL("csv"), {"text/csv"}},
108     {FILE_PATH_LITERAL("not an extension / for sure"), {}},
109     {containsNullByte, {}}
110   };
111 
112   for (const auto& test : tests) {
113     std::string mime_type;
114     if (GetMimeTypeFromExtension(test.extension, &mime_type))
115       EXPECT_THAT(test.mime_types, Contains(mime_type));
116     else
117       EXPECT_TRUE(test.mime_types.empty());
118   }
119 }
120 
121 // Behavior of GetPreferredExtensionForMimeType() is dependent on the host
122 // platform since the latter can override the mapping from file extensions to
123 // MIME types. The tests below would only work if the platform MIME mappings
124 // don't have mappings for or has an agreeing mapping for each MIME type
125 // mentioned.
TEST(MimeUtilTest,GetPreferredExtensionForMimeType)126 TEST(MimeUtilTest, GetPreferredExtensionForMimeType) {
127   const struct {
128     const std::string mime_type;
129     const base::FilePath::StringType expected_extension;
130   } kTestCases[] = {
131       {"application/wasm", FILE_PATH_LITERAL("wasm")},      // Primary
132       {"application/javascript", FILE_PATH_LITERAL("js")},  // Secondary
133       {"text/javascript", FILE_PATH_LITERAL("js")},         // Primary
134       {"video/webm", FILE_PATH_LITERAL("webm")},            // Primary
135   };
136 
137   for (const auto& test : kTestCases) {
138     base::FilePath::StringType extension;
139     auto rv = GetPreferredExtensionForMimeType(test.mime_type, &extension);
140     EXPECT_TRUE(rv);
141     EXPECT_EQ(test.expected_extension, extension);
142   }
143 }
144 
TEST(MimeUtilTest,FileTest)145 TEST(MimeUtilTest, FileTest) {
146   const struct {
147     const base::FilePath::CharType* file_path;
148     const char* const mime_type;
149     bool valid;
150   } tests[] = {
151       {FILE_PATH_LITERAL("c:\\foo\\bar.css"), "text/css", true},
152       {FILE_PATH_LITERAL("c:\\foo\\bar.CSS"), "text/css", true},
153       {FILE_PATH_LITERAL("c:\\blah"), "", false},
154       {FILE_PATH_LITERAL("/usr/local/bin/mplayer"), "", false},
155       {FILE_PATH_LITERAL("/home/foo/bar.css"), "text/css", true},
156       {FILE_PATH_LITERAL("/blah."), "", false},
157       {FILE_PATH_LITERAL("c:\\blah."), "", false},
158   };
159 
160   std::string mime_type;
161   bool rv;
162 
163   for (const auto& test : tests) {
164     rv = GetMimeTypeFromFile(base::FilePath(test.file_path), &mime_type);
165     EXPECT_EQ(test.valid, rv);
166     if (rv)
167       EXPECT_EQ(test.mime_type, mime_type);
168   }
169 }
170 
TEST(MimeUtilTest,MatchesMimeType)171 TEST(MimeUtilTest, MatchesMimeType) {
172   // MIME types are case insensitive.
173   EXPECT_TRUE(MatchesMimeType("VIDEO/*", "video/x-mpeg"));
174   EXPECT_TRUE(MatchesMimeType("video/*", "VIDEO/X-MPEG"));
175 
176   EXPECT_TRUE(MatchesMimeType("*", "video/x-mpeg"));
177   EXPECT_TRUE(MatchesMimeType("video/*", "video/x-mpeg"));
178   EXPECT_TRUE(MatchesMimeType("video/*", "video/*"));
179   EXPECT_TRUE(MatchesMimeType("video/x-mpeg", "video/x-mpeg"));
180   EXPECT_TRUE(MatchesMimeType("application/*+xml",
181                                    "application/html+xml"));
182   EXPECT_TRUE(MatchesMimeType("application/*+xml", "application/+xml"));
183   EXPECT_TRUE(MatchesMimeType("application/*+json",
184                                    "application/x-myformat+json"));
185   EXPECT_TRUE(MatchesMimeType("aaa*aaa", "aaaaaa"));
186   EXPECT_TRUE(MatchesMimeType("*", std::string()));
187   EXPECT_FALSE(MatchesMimeType("video/", "video/x-mpeg"));
188   EXPECT_FALSE(MatchesMimeType("VIDEO/", "Video/X-MPEG"));
189   EXPECT_FALSE(MatchesMimeType(std::string(), "video/x-mpeg"));
190   EXPECT_FALSE(MatchesMimeType(std::string(), std::string()));
191   EXPECT_FALSE(MatchesMimeType("video/x-mpeg", std::string()));
192   EXPECT_FALSE(MatchesMimeType("application/*+xml", "application/xml"));
193   EXPECT_FALSE(MatchesMimeType("application/*+xml",
194                                     "application/html+xmlz"));
195   EXPECT_FALSE(MatchesMimeType("application/*+xml",
196                                     "applcation/html+xml"));
197   EXPECT_FALSE(MatchesMimeType("aaa*aaa", "aaaaa"));
198 
199   EXPECT_TRUE(MatchesMimeType("*", "video/x-mpeg;param=val"));
200   EXPECT_TRUE(MatchesMimeType("*", "Video/X-MPEG;PARAM=VAL"));
201   EXPECT_TRUE(MatchesMimeType("video/*", "video/x-mpeg;param=val"));
202   EXPECT_FALSE(MatchesMimeType("video/*;param=val", "video/mpeg"));
203   EXPECT_FALSE(MatchesMimeType("Video/*;PARAM=VAL", "VIDEO/Mpeg"));
204   EXPECT_FALSE(MatchesMimeType("video/*;param=val", "video/mpeg;param=other"));
205   EXPECT_TRUE(MatchesMimeType("video/*;param=val", "video/mpeg;param=val"));
206   EXPECT_TRUE(MatchesMimeType("Video/*;PARAM=Val", "VIDEO/Mpeg;Param=Val"));
207   EXPECT_FALSE(MatchesMimeType("Video/*;PARAM=VAL", "VIDEO/Mpeg;Param=Val"));
208   EXPECT_TRUE(MatchesMimeType("video/x-mpeg", "video/x-mpeg;param=val"));
209   EXPECT_TRUE(MatchesMimeType("video/x-mpeg;param=val",
210                               "video/x-mpeg;param=val"));
211   EXPECT_FALSE(MatchesMimeType("video/x-mpeg;param2=val2",
212                                "video/x-mpeg;param=val"));
213   EXPECT_FALSE(MatchesMimeType("video/x-mpeg;param2=val2",
214                                "video/x-mpeg;param2=val"));
215   EXPECT_TRUE(MatchesMimeType("video/x-mpeg;param=val",
216                               "video/x-mpeg;param=val;param2=val2"));
217   EXPECT_TRUE(MatchesMimeType("Video/X-Mpeg;Param=Val",
218                               "VIDEO/X-MPEG;PARAM=Val;PARAM2=val2"));
219   EXPECT_TRUE(MatchesMimeType("Video/X-Mpeg;Param=VAL",
220                               "VIDEO/X-MPEG;PARAM=VAL;PARAM2=val2"));
221   EXPECT_FALSE(MatchesMimeType("Video/X-Mpeg;Param=val",
222                                "VIDEO/X-MPEG;PARAM=VAL;PARAM2=val2"));
223   EXPECT_FALSE(MatchesMimeType("video/x-mpeg;param=VAL;param2=val2",
224                                "video/x-mpeg;param=val;param2=val2"));
225   EXPECT_TRUE(MatchesMimeType("video/x-mpeg;param2=val2;param=val",
226                               "video/x-mpeg;param=val;param2=val2"));
227   EXPECT_FALSE(MatchesMimeType("video/x-mpeg;param3=val3;param=val",
228                                "video/x-mpeg;param=val;param2=val2"));
229   EXPECT_TRUE(MatchesMimeType("video/x-mpeg;param=val ;param2=val2 ",
230                               "video/x-mpeg;param=val;param2=val2"));
231 
232   EXPECT_TRUE(MatchesMimeType("*/*;param=val", "video/x-mpeg;param=val"));
233   EXPECT_FALSE(MatchesMimeType("*/*;param=val", "video/x-mpeg;param=val2"));
234 
235   EXPECT_TRUE(MatchesMimeType("*", "*"));
236   EXPECT_TRUE(MatchesMimeType("*", "*/*"));
237   EXPECT_TRUE(MatchesMimeType("*/*", "*/*"));
238   EXPECT_TRUE(MatchesMimeType("*/*", "*"));
239   EXPECT_TRUE(MatchesMimeType("video/*", "video/*"));
240   EXPECT_FALSE(MatchesMimeType("video/*", "*/*"));
241   EXPECT_FALSE(MatchesMimeType("video/*;param=val", "video/*"));
242   EXPECT_TRUE(MatchesMimeType("video/*;param=val", "video/*;param=val"));
243   EXPECT_FALSE(MatchesMimeType("video/*;param=val", "video/*;param=val2"));
244 
245   EXPECT_TRUE(MatchesMimeType("ab*cd", "abxxxcd"));
246   EXPECT_TRUE(MatchesMimeType("ab*cd", "abx/xcd"));
247   EXPECT_TRUE(MatchesMimeType("ab/*cd", "ab/xxxcd"));
248 }
249 
TEST(MimeUtilTest,TestParseMimeType)250 TEST(MimeUtilTest, TestParseMimeType) {
251   const struct {
252     std::string type_str;
253     std::string mime_type;
254     base::StringPairs params;
255   } tests[] = {
256       // Simple tests.
257       {"image/jpeg", "image/jpeg"},
258       {"application/octet-stream;foo=bar;name=\"test.jpg\"",
259        "application/octet-stream",
260        {{"foo", "bar"}, {"name", "test.jpg"}}},
261       // Quoted string parsing.
262       {"t/s;name=\"t\\\\est\\\".jpg\"", "t/s", {{"name", "t\\est\".jpg"}}},
263       {"t/s;name=\"test.jpg\"", "t/s", {{"name", "test.jpg"}}},
264       {"t/s;name=\"test;jpg\"", "t/s", {{"name", "test;jpg"}}},
265       // Lenient for no closing quote.
266       {"t/s;name=\"test.jpg", "t/s", {{"name", "test.jpg"}}},
267       {"t/s;name=\"ab\\\"", "t/s", {{"name", "ab\""}}},
268       // Strip whitespace from start/end of mime_type.
269       {" t/s", "t/s"},
270       {"t/s ", "t/s"},
271       {" t/s ", "t/s"},
272       {"t/=", "t/="},
273       // Generally ignore whitespace.
274       {"t/s;a=1;b=2", "t/s", {{"a", "1"}, {"b", "2"}}},
275       {"t/s ;a=1;b=2", "t/s", {{"a", "1"}, {"b", "2"}}},
276       {"t/s; a=1;b=2", "t/s", {{"a", "1"}, {"b", "2"}}},
277       // Special case, include whitespace after param name until equals.
278       {"t/s;a =1;b=2", "t/s", {{"a ", "1"}, {"b", "2"}}},
279       {"t/s;a= 1;b=2", "t/s", {{"a", "1"}, {"b", "2"}}},
280       {"t/s;a=1 ;b=2", "t/s", {{"a", "1"}, {"b", "2"}}},
281       {"t/s;a=1; b=2", "t/s", {{"a", "1"}, {"b", "2"}}},
282       {"t/s; a = 1;b=2", "t/s", {{"a ", "1"}, {"b", "2"}}},
283       // Do not trim whitespace from quoted-string param values.
284       {"t/s;a=\" 1\";b=2", "t/s", {{"a", " 1"}, {"b", "2"}}},
285       {"t/s;a=\"1 \";b=2", "t/s", {{"a", "1 "}, {"b", "2"}}},
286       {"t/s;a=\" 1 \";b=2", "t/s", {{"a", " 1 "}, {"b", "2"}}},
287       // Ignore incomplete params.
288       {"t/s;a", "t/s", {}},
289       {"t/s;a=", "t/s", {}},
290       {"t/s;a=1;", "t/s", {{"a", "1"}}},
291       {"t/s;a=1;b", "t/s", {{"a", "1"}}},
292       {"t/s;a=1;b=", "t/s", {{"a", "1"}}},
293       // Allow empty subtype.
294       {"t/", "t/", {}},
295       {"ts/", "ts/", {}},
296       {"t/;", "t/", {}},
297       {"t/ s", "t/", {}},
298       // Questionable: allow anything as long as there is a slash somewhere.
299       {"/ts", "/ts", {}},
300       {"/s", "/s", {}},
301       {"/", "/", {}},
302   };
303   for (const auto& test : tests) {
304     std::string mime_type;
305     base::StringPairs params;
306     EXPECT_TRUE(ParseMimeType(test.type_str, &mime_type, &params));
307     EXPECT_EQ(test.mime_type, mime_type);
308     EXPECT_EQ(test.params, params);
309   }
310   for (auto* type_str : {
311            // Must have slash in mime type.
312            "",
313            "ts",
314            "t / s",
315        }) {
316     EXPECT_FALSE(ParseMimeType(type_str, nullptr, nullptr));
317   }
318 }
319 
TEST(MimeUtilTest,TestParseMimeTypeWithoutParameter)320 TEST(MimeUtilTest, TestParseMimeTypeWithoutParameter) {
321   std::string nonAscii("application/nonutf8");
322   EXPECT_TRUE(ParseMimeTypeWithoutParameter(nonAscii, nullptr, nullptr));
323 #if BUILDFLAG(IS_WIN)
324   nonAscii.append(base::WideToUTF8(L"\u2603"));
325 #else
326   nonAscii.append("\u2603");  // unicode snowman
327 #endif
328   EXPECT_FALSE(ParseMimeTypeWithoutParameter(nonAscii, nullptr, nullptr));
329 
330   std::string top_level_type;
331   std::string subtype;
332   EXPECT_TRUE(ParseMimeTypeWithoutParameter(
333       "application/mime", &top_level_type, &subtype));
334   EXPECT_EQ("application", top_level_type);
335   EXPECT_EQ("mime", subtype);
336 
337   // Various allowed subtype forms.
338   EXPECT_TRUE(
339       ParseMimeTypeWithoutParameter("application/json", nullptr, nullptr));
340   EXPECT_TRUE(ParseMimeTypeWithoutParameter("application/x-suggestions+json",
341                                             nullptr, nullptr));
342   EXPECT_TRUE(
343       ParseMimeTypeWithoutParameter("application/+json", nullptr, nullptr));
344 
345   // Upper case letters are allowed.
346   EXPECT_TRUE(ParseMimeTypeWithoutParameter("text/mime", nullptr, nullptr));
347   EXPECT_TRUE(ParseMimeTypeWithoutParameter("TEXT/mime", nullptr, nullptr));
348   EXPECT_TRUE(ParseMimeTypeWithoutParameter("Text/mime", nullptr, nullptr));
349   EXPECT_TRUE(ParseMimeTypeWithoutParameter("TeXt/mime", nullptr, nullptr));
350 
351   // Experimental types are also considered to be valid.
352   EXPECT_TRUE(ParseMimeTypeWithoutParameter("x-video/mime", nullptr, nullptr));
353   EXPECT_TRUE(ParseMimeTypeWithoutParameter("X-Video/mime", nullptr, nullptr));
354 
355   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text", nullptr, nullptr));
356   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text/", nullptr, nullptr));
357   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text/ ", nullptr, nullptr));
358   EXPECT_FALSE(ParseMimeTypeWithoutParameter("te(xt/ ", nullptr, nullptr));
359   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text/()plain", nullptr, nullptr));
360 
361   EXPECT_FALSE(ParseMimeTypeWithoutParameter("x-video", nullptr, nullptr));
362   EXPECT_FALSE(ParseMimeTypeWithoutParameter("x-video/", nullptr, nullptr));
363 
364   EXPECT_FALSE(
365       ParseMimeTypeWithoutParameter("application/a/b/c", nullptr, nullptr));
366 
367   // Test leading and trailing whitespace
368   EXPECT_TRUE(ParseMimeTypeWithoutParameter(" text/plain", nullptr, nullptr));
369   EXPECT_TRUE(ParseMimeTypeWithoutParameter("text/plain ", nullptr, nullptr));
370   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text /plain", nullptr, nullptr));
371   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text/ plain ", nullptr, nullptr));
372 
373   EXPECT_TRUE(ParseMimeTypeWithoutParameter("\ttext/plain", nullptr, nullptr));
374   EXPECT_TRUE(ParseMimeTypeWithoutParameter("text/plain\t", nullptr, nullptr));
375   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text\t/plain", nullptr, nullptr));
376   EXPECT_FALSE(
377       ParseMimeTypeWithoutParameter("text/\tplain ", nullptr, nullptr));
378 
379   EXPECT_TRUE(ParseMimeTypeWithoutParameter("\vtext/plain", nullptr, nullptr));
380   EXPECT_TRUE(ParseMimeTypeWithoutParameter("text/plain\v", nullptr, nullptr));
381   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text\v/plain", nullptr, nullptr));
382   EXPECT_FALSE(
383       ParseMimeTypeWithoutParameter("text/\vplain ", nullptr, nullptr));
384 
385   EXPECT_TRUE(ParseMimeTypeWithoutParameter("\rtext/plain", nullptr, nullptr));
386   EXPECT_TRUE(ParseMimeTypeWithoutParameter("text/plain\r", nullptr, nullptr));
387   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text\r/plain", nullptr, nullptr));
388   EXPECT_FALSE(
389       ParseMimeTypeWithoutParameter("text/\rplain ", nullptr, nullptr));
390 
391   EXPECT_TRUE(ParseMimeTypeWithoutParameter("\ntext/plain", nullptr, nullptr));
392   EXPECT_TRUE(ParseMimeTypeWithoutParameter("text/plain\n", nullptr, nullptr));
393   EXPECT_FALSE(ParseMimeTypeWithoutParameter("text\n/plain", nullptr, nullptr));
394   EXPECT_FALSE(
395       ParseMimeTypeWithoutParameter("text/\nplain ", nullptr, nullptr));
396 }
397 
398 class ExtractMIMETypeTestInvalid : public testing::TestWithParam<std::string> {
399 };
400 
401 INSTANTIATE_TEST_SUITE_P(
402     InvalidMediaTypes,
403     ExtractMIMETypeTestInvalid,
404     testing::Values(
405         // Fails because it doesn't contain '/'.
406         "a",
407         "application",
408         // Space is not HTTP token code point.
409         //  https://mimesniff.spec.whatwg.org/#http-token-code-point
410         // U+2003, EM SPACE (UTF-8: E2 80 83).
411         "\xE2\x80\x83text/html",
412         "text\xE2\x80\x83/html",
413         "text / html",
414         "t e x t / h t m l",
415         "text\r\n/\nhtml",
416         "text\n/\nhtml",
417         ", text/html",
418         "; text/html"));
419 
TEST_P(ExtractMIMETypeTestInvalid,MustFail)420 TEST_P(ExtractMIMETypeTestInvalid, MustFail) {
421   // Parsing is expected to fail.
422   EXPECT_EQ(std::nullopt, net::ExtractMimeTypeFromMediaType(GetParam(), true));
423 }
424 
425 class ExtractMIMETypeTestValid : public testing::TestWithParam<std::string> {};
426 
427 INSTANTIATE_TEST_SUITE_P(
428     ValidMediaTypes,
429     ExtractMIMETypeTestValid,
430     testing::Values("text/html",
431                     "text/html; charset=iso-8859-1",
432                     // Quoted charset parameter.
433                     "text/html; charset=\"quoted\"",
434                     // Multiple parameters.
435                     "text/html; charset=x; foo=bar",
436                     // OWSes are trimmed.
437                     " text/html   ",
438                     "\ttext/html \t",
439                     "text/html ; charset=iso-8859-1"
440                     // Non-standard multiple type/subtype listing using a comma
441                     // as a separator is accepted.
442                     "text/html,text/plain",
443                     "text/html , text/plain",
444                     "text/html\t,\ttext/plain",
445                     "text/html,text/plain;charset=iso-8859-1",
446                     "\r\ntext/html\r\n",
447                     "text/html;wow",
448                     "text/html;;;;;;",
449                     "text/html; = = = "));
450 
TEST_P(ExtractMIMETypeTestValid,MustSucceed)451 TEST_P(ExtractMIMETypeTestValid, MustSucceed) {
452   //  net::ExtractMIMETypeFromMediaType parses well-formed headers correctly.
453   EXPECT_EQ("text/html",
454             net::ExtractMimeTypeFromMediaType(GetParam(), true).value_or(""));
455 }
456 
TEST(MimeUtilTest,TestIsValidTopLevelMimeType)457 TEST(MimeUtilTest, TestIsValidTopLevelMimeType) {
458   EXPECT_TRUE(IsValidTopLevelMimeType("application"));
459   EXPECT_TRUE(IsValidTopLevelMimeType("audio"));
460   EXPECT_TRUE(IsValidTopLevelMimeType("example"));
461   EXPECT_TRUE(IsValidTopLevelMimeType("font"));
462   EXPECT_TRUE(IsValidTopLevelMimeType("image"));
463   EXPECT_TRUE(IsValidTopLevelMimeType("message"));
464   EXPECT_TRUE(IsValidTopLevelMimeType("model"));
465   EXPECT_TRUE(IsValidTopLevelMimeType("multipart"));
466   EXPECT_TRUE(IsValidTopLevelMimeType("text"));
467   EXPECT_TRUE(IsValidTopLevelMimeType("video"));
468 
469   EXPECT_TRUE(IsValidTopLevelMimeType("TEXT"));
470   EXPECT_TRUE(IsValidTopLevelMimeType("Text"));
471   EXPECT_TRUE(IsValidTopLevelMimeType("TeXt"));
472 
473   EXPECT_FALSE(IsValidTopLevelMimeType("mime"));
474   EXPECT_FALSE(IsValidTopLevelMimeType(""));
475   EXPECT_FALSE(IsValidTopLevelMimeType("/"));
476   EXPECT_FALSE(IsValidTopLevelMimeType(" "));
477 
478   EXPECT_TRUE(IsValidTopLevelMimeType("x-video"));
479   EXPECT_TRUE(IsValidTopLevelMimeType("X-video"));
480 
481   EXPECT_FALSE(IsValidTopLevelMimeType("x-"));
482 }
483 
TEST(MimeUtilTest,TestGetExtensionsForMimeType)484 TEST(MimeUtilTest, TestGetExtensionsForMimeType) {
485   const struct {
486     const char* const mime_type;
487     size_t min_expected_size;
488     const char* const contained_result;
489     bool no_matches;
490   } tests[] = {
491       {"text/plain", 2, "txt"},
492       {"text/pl", 0, nullptr, true},
493       {"*", 0, nullptr},
494       {"", 0, nullptr, true},
495       {"message/*", 1, "eml"},
496       {"MeSsAge/*", 1, "eml"},
497       {"message/", 0, nullptr, true},
498       {"image/avif", 1, "avif"},
499       {"image/bmp", 1, "bmp"},
500       {"video/*", 6, "mp4"},
501       {"video/*", 6, "mpeg"},
502       {"audio/*", 6, "oga"},
503       {"aUDIo/*", 6, "wav"},
504   };
505 
506   for (const auto& test : tests) {
507     std::vector<base::FilePath::StringType> extensions;
508     GetExtensionsForMimeType(test.mime_type, &extensions);
509     ASSERT_LE(test.min_expected_size, extensions.size());
510 
511     if (test.no_matches)
512       ASSERT_EQ(0u, extensions.size());
513 
514     if (test.contained_result) {
515       // Convert ASCII to FilePath::StringType.
516       base::FilePath::StringType contained_result(
517           test.contained_result,
518           test.contained_result + strlen(test.contained_result));
519 
520       bool found = base::Contains(extensions, contained_result);
521 
522       ASSERT_TRUE(found) << "Must find at least the contained result within "
523                          << test.mime_type;
524     }
525   }
526 }
527 
TEST(MimeUtilTest,TestGenerateMimeMultipartBoundary)528 TEST(MimeUtilTest, TestGenerateMimeMultipartBoundary) {
529   std::string boundary1 = GenerateMimeMultipartBoundary();
530   std::string boundary2 = GenerateMimeMultipartBoundary();
531 
532   // RFC 1341 says: the boundary parameter [...] consists of 1 to 70 characters.
533   EXPECT_GE(70u, boundary1.size());
534   EXPECT_GE(70u, boundary2.size());
535 
536   // RFC 1341 asks to: exercise care to choose a unique boundary.
537   EXPECT_NE(boundary1, boundary2);
538   ASSERT_LE(16u, boundary1.size());
539   ASSERT_LE(16u, boundary2.size());
540 
541   // Expect that we don't pick '\0' character from the array/string
542   // where we take the characters from.
543   EXPECT_EQ(std::string::npos, boundary1.find('\0'));
544   EXPECT_EQ(std::string::npos, boundary2.find('\0'));
545 
546   // Asserts below are not RFC 1341 requirements, but are here
547   // to improve readability of generated MIME documents and to
548   // try to preserve some aspects of the old boundary generation code.
549   EXPECT_EQ("--", boundary1.substr(0, 2));
550   EXPECT_EQ("--", boundary2.substr(0, 2));
551   EXPECT_NE(std::string::npos, boundary1.find("MultipartBoundary"));
552   EXPECT_NE(std::string::npos, boundary2.find("MultipartBoundary"));
553   EXPECT_EQ("--", boundary1.substr(boundary1.size() - 2, 2));
554   EXPECT_EQ("--", boundary2.substr(boundary2.size() - 2, 2));
555 }
556 
TEST(MimeUtilTest,TestAddMultipartValueForUpload)557 TEST(MimeUtilTest, TestAddMultipartValueForUpload) {
558   const char ref_output[] =
559       "--boundary\r\nContent-Disposition: form-data;"
560       " name=\"value name\"\r\nContent-Type: content type"
561       "\r\n\r\nvalue\r\n"
562       "--boundary\r\nContent-Disposition: form-data;"
563       " name=\"value name\"\r\n\r\nvalue\r\n"
564       "--boundary--\r\n";
565   std::string post_data;
566   AddMultipartValueForUpload("value name", "value", "boundary",
567                              "content type", &post_data);
568   AddMultipartValueForUpload("value name", "value", "boundary",
569                              "", &post_data);
570   AddMultipartFinalDelimiterForUpload("boundary", &post_data);
571   EXPECT_STREQ(ref_output, post_data.c_str());
572 }
573 
TEST(MimeUtilTest,TestAddMultipartValueForUploadWithFileName)574 TEST(MimeUtilTest, TestAddMultipartValueForUploadWithFileName) {
575   const char ref_output[] =
576       "--boundary\r\nContent-Disposition: form-data;"
577       " name=\"value name\"; filename=\"file name\"\r\nContent-Type: content "
578       "type"
579       "\r\n\r\nvalue\r\n"
580       "--boundary\r\nContent-Disposition: form-data;"
581       " name=\"value name\"; filename=\"file name\"\r\n\r\nvalue\r\n"
582       "--boundary--\r\n";
583   std::string post_data;
584   AddMultipartValueForUploadWithFileName("value name", "file name", "value",
585                                          "boundary", "content type",
586                                          &post_data);
587   AddMultipartValueForUploadWithFileName("value name", "file name", "value",
588                                          "boundary", "", &post_data);
589   AddMultipartFinalDelimiterForUpload("boundary", &post_data);
590   EXPECT_STREQ(ref_output, post_data.c_str());
591 }
592 }  // namespace net
593