data_url_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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/base/data_url.h"
  5. #include "base/memory/ref_counted.h"
  6. #include "net/base/net_errors.h"
  7. #include "net/http/http_response_headers.h"
  8. #include "net/http/http_version.h"
  9. #include "testing/gtest/include/gtest/gtest.h"
  10. #include "url/gurl.h"
  11. namespace net {
  12. namespace {
  13. struct ParseTestData {
  14. const char* url;
  15. bool is_valid;
  16. const char* mime_type;
  17. const char* charset;
  18. const std::string data;
  19. };
  20. } // namespace
  21. TEST(DataURLTest, Parse) {
  22. const ParseTestData tests[] = {
  23. {"data:", false, "", "", ""},
  24. {"data:,", true, "text/plain", "US-ASCII", ""},
  25. {"data:;base64,", true, "text/plain", "US-ASCII", ""},
  26. {"data:;charset=,test", false, "", "", ""},
  27. {"data:TeXt/HtMl,<b>x</b>", true, "text/html", "", "<b>x</b>"},
  28. {"data:,foo", true, "text/plain", "US-ASCII", "foo"},
  29. {"data:;base64,aGVsbG8gd29ybGQ=", true, "text/plain", "US-ASCII",
  30. "hello world"},
  31. // Allow invalid mediatype for backward compatibility but set mime_type to
  32. // "text/plain" instead of the invalid mediatype.
  33. {"data:foo,boo", true, "text/plain", "US-ASCII", "boo"},
  34. // When accepting an invalid mediatype, override charset with "US-ASCII"
  35. {"data:foo;charset=UTF-8,boo", true, "text/plain", "US-ASCII", "boo"},
  36. // Invalid mediatype. Includes a slash but the type part is not a token.
  37. {"data:f(oo/bar;baz=1;charset=kk,boo", true, "text/plain", "US-ASCII",
  38. "boo"},
  39. {"data:foo/bar;baz=1;charset=kk,boo", true, "foo/bar", "kk", "boo"},
  40. {"data:foo/bar;charset=kk;baz=1,boo", true, "foo/bar", "kk", "boo"},
  41. {"data:text/html,%3Chtml%3E%3Cbody%3E%3Cb%3Ehello%20world"
  42. "%3C%2Fb%3E%3C%2Fbody%3E%3C%2Fhtml%3E",
  43. true, "text/html", "", "<html><body><b>hello world</b></body></html>"},
  44. {"data:text/html,<html><body><b>hello world</b></body></html>", true,
  45. "text/html", "", "<html><body><b>hello world</b></body></html>"},
  46. // the comma cannot be url-escaped!
  47. {"data:%2Cblah", false, "", "", ""},
  48. // invalid base64 content
  49. {"data:;base64,aGVs_-_-", false, "", "", ""},
  50. // Spaces should be removed from non-text data URLs (we already tested
  51. // spaces above).
  52. {"data:image/fractal,a b c d e f g", true, "image/fractal", "",
  53. "abcdefg"},
  54. // Spaces should also be removed from anything base-64 encoded
  55. {"data:;base64,aGVs bG8gd2 9ybGQ=", true, "text/plain", "US-ASCII",
  56. "hello world"},
  57. // Other whitespace should also be removed from anything base-64 encoded.
  58. {"data:;base64,aGVs bG8gd2 \n9ybGQ=", true, "text/plain", "US-ASCII",
  59. "hello world"},
  60. // In base64 encoding, escaped whitespace should be stripped.
  61. // (This test was taken from acid3)
  62. // http://b/1054495
  63. {"data:text/javascript;base64,%20ZD%20Qg%0D%0APS%20An%20Zm91cic%0D%0A%207"
  64. "%20",
  65. true, "text/javascript", "", "d4 = 'four';"},
  66. // Only unescaped whitespace should be stripped in non-base64.
  67. // http://b/1157796
  68. {"data:img/png,A B %20 %0A C", true, "img/png", "", "AB \nC"},
  69. {"data:text/plain;charset=utf-8;base64,SGVsbMO2", true, "text/plain",
  70. "utf-8", "Hell\xC3\xB6"},
  71. // no mimetype
  72. {"data:;charset=utf-8;base64,SGVsbMO2", true, "text/plain", "utf-8",
  73. "Hell\xC3\xB6"},
  74. // Not sufficiently padded.
  75. {"data:;base64,aGVsbG8gd29ybGQ", true, "text/plain", "US-ASCII",
  76. "hello world"},
  77. // Not sufficiently padded with whitespace.
  78. {"data:;base64,aGV sbG8g d29ybGQ", true, "text/plain", "US-ASCII",
  79. "hello world"},
  80. // Not sufficiently padded with escaped whitespace.
  81. {"data:;base64,aGV%20sbG8g%20d29ybGQ", true, "text/plain", "US-ASCII",
  82. "hello world"},
  83. // Bad encoding (truncated).
  84. {"data:;base64,aGVsbG8gd29yb", false, "", "", ""},
  85. // BiDi control characters should be unescaped and preserved as is, and
  86. // should not be replaced with % versions. In the below case, \xE2\x80\x8F
  87. // is the RTL mark and the parsed text should preserve it as is.
  88. {"data:text/plain;charset=utf-8,\xE2\x80\x8Ftest", true, "text/plain",
  89. "utf-8", "\xE2\x80\x8Ftest"},
  90. // Same as above but with Arabic text after RTL mark.
  91. {"data:text/plain;charset=utf-8,"
  92. "\xE2\x80\x8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1",
  93. true, "text/plain", "utf-8",
  94. "\xE2\x80\x8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1"},
  95. // RTL mark encoded as %E2%80%8F should be unescaped too. Note that when
  96. // wrapped in a GURL, this URL and the next effectively become the same as
  97. // the previous two URLs.
  98. {"data:text/plain;charset=utf-8,%E2%80%8Ftest", true, "text/plain",
  99. "utf-8", "\xE2\x80\x8Ftest"},
  100. // Same as above but with Arabic text after RTL mark.
  101. {"data:text/plain;charset=utf-8,"
  102. "%E2%80%8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1",
  103. true, "text/plain", "utf-8",
  104. "\xE2\x80\x8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1"},
  105. // The 'data' of a data URI does not include any ref it has.
  106. {"data:text/plain,this/is/a/test/%23include/#dontinclude", true,
  107. "text/plain", "", "this/is/a/test/#include/"},
  108. // More unescaping tests and tests with nulls.
  109. {"data:%00text/plain%41,foo", true, "%00text/plain%41", "", "foo"},
  110. {"data:text/plain;charset=%00US-ASCII%41,foo", true, "text/plain",
  111. "%00US-ASCII%41", "foo"},
  112. {"data:text/plain,%00_%41", true, "text/plain", "",
  113. std::string("\x00_A", 3)},
  114. {"data:text/plain;base64,AA//", true, "text/plain", "",
  115. std::string("\x00\x0F\xFF", 3)},
  116. // "%62ase64" unescapes to base64, but should not be treated as such.
  117. {"data:text/plain;%62ase64,AA//", true, "text/plain", "", "AA//"},
  118. };
  119. for (const auto& test : tests) {
  120. SCOPED_TRACE(test.url);
  121. std::string mime_type;
  122. std::string charset;
  123. std::string data;
  124. bool ok = DataURL::Parse(GURL(test.url), &mime_type, &charset, &data);
  125. EXPECT_EQ(ok, test.is_valid);
  126. EXPECT_EQ(test.mime_type, mime_type);
  127. EXPECT_EQ(test.charset, charset);
  128. EXPECT_EQ(test.data, data);
  129. }
  130. }
  131. TEST(DataURLTest, BuildResponseSimple) {
  132. std::string mime_type;
  133. std::string charset;
  134. std::string data;
  135. scoped_refptr<HttpResponseHeaders> headers;
  136. ASSERT_EQ(OK, DataURL::BuildResponse(GURL("data:,Hello"), "GET", &mime_type,
  137. &charset, &data, &headers));
  138. EXPECT_EQ("text/plain", mime_type);
  139. EXPECT_EQ("US-ASCII", charset);
  140. EXPECT_EQ("Hello", data);
  141. ASSERT_TRUE(headers);
  142. const HttpVersion& version = headers->GetHttpVersion();
  143. EXPECT_EQ(1, version.major_value());
  144. EXPECT_EQ(1, version.minor_value());
  145. EXPECT_EQ("OK", headers->GetStatusText());
  146. std::string value;
  147. EXPECT_TRUE(headers->GetNormalizedHeader("Content-Type", &value));
  148. EXPECT_EQ(value, "text/plain;charset=US-ASCII");
  149. value.clear();
  150. }
  151. TEST(DataURLTest, BuildResponseHead) {
  152. for (const char* method : {"HEAD", "head", "hEaD"}) {
  153. SCOPED_TRACE(method);
  154. std::string mime_type;
  155. std::string charset;
  156. std::string data;
  157. scoped_refptr<HttpResponseHeaders> headers;
  158. ASSERT_EQ(OK,
  159. DataURL::BuildResponse(GURL("data:,Hello"), method, &mime_type,
  160. &charset, &data, &headers));
  161. EXPECT_EQ("text/plain", mime_type);
  162. EXPECT_EQ("US-ASCII", charset);
  163. EXPECT_EQ("", data);
  164. ASSERT_TRUE(headers);
  165. HttpVersion version = headers->GetHttpVersion();
  166. EXPECT_EQ(1, version.major_value());
  167. EXPECT_EQ(1, version.minor_value());
  168. EXPECT_EQ("OK", headers->GetStatusText());
  169. std::string content_type;
  170. EXPECT_TRUE(headers->GetNormalizedHeader("Content-Type", &content_type));
  171. EXPECT_EQ(content_type, "text/plain;charset=US-ASCII");
  172. }
  173. }
  174. TEST(DataURLTest, BuildResponseInput) {
  175. std::string mime_type;
  176. std::string charset;
  177. std::string data;
  178. scoped_refptr<HttpResponseHeaders> headers;
  179. ASSERT_EQ(ERR_INVALID_URL,
  180. DataURL::BuildResponse(GURL("bogus"), "GET", &mime_type, &charset,
  181. &data, &headers));
  182. EXPECT_FALSE(headers);
  183. EXPECT_TRUE(mime_type.empty());
  184. EXPECT_TRUE(charset.empty());
  185. EXPECT_TRUE(data.empty());
  186. }
  187. TEST(DataURLTest, BuildResponseInvalidMimeType) {
  188. std::string mime_type;
  189. std::string charset;
  190. std::string data;
  191. scoped_refptr<HttpResponseHeaders> headers;
  192. // MIME type contains delimiters. Must be accepted but Content-Type header
  193. // should be generated as if the mediatype was text/plain.
  194. ASSERT_EQ(OK, DataURL::BuildResponse(GURL("data:f(o/b)r,test"), "GET",
  195. &mime_type, &charset, &data, &headers));
  196. ASSERT_TRUE(headers);
  197. std::string value;
  198. EXPECT_TRUE(headers->GetNormalizedHeader("Content-Type", &value));
  199. EXPECT_EQ(value, "text/plain;charset=US-ASCII");
  200. }
  201. TEST(DataURLTest, InvalidCharset) {
  202. std::string mime_type;
  203. std::string charset;
  204. std::string data;
  205. scoped_refptr<HttpResponseHeaders> headers;
  206. // MIME type contains delimiters. Must be rejected.
  207. ASSERT_EQ(ERR_INVALID_URL, DataURL::BuildResponse(
  208. GURL("data:text/html;charset=(),test"), "GET",
  209. &mime_type, &charset, &data, &headers));
  210. EXPECT_FALSE(headers);
  211. EXPECT_TRUE(mime_type.empty());
  212. EXPECT_TRUE(charset.empty());
  213. EXPECT_TRUE(data.empty());
  214. }
  215. // Test a slightly larger data URL.
  216. TEST(DataURLTest, Image) {
  217. // Use our nice little Chrome logo.
  218. GURL image_url(
  219. "data:image/png;base64,"
  220. "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADVklEQVQ4jX2TfUwUB"
  221. "BjG3w1y+HGcd9dxhXR8T4awOccJGgOSWclHImznLkTlSw0DDQXkrmgYgbUYnlQTqQ"
  222. "xIEVxitD5UMCATRA1CEEg+Qjw3bWDxIauJv/5oumqs39/P827vnucRmYN0gyF01GI"
  223. "5MpCVdW0gO7tvNC+vqSEtbZefk5NuLv1jdJ46p/zw0HeH4+PHr3h7c1mjoV2t5rKz"
  224. "Mx1+fg9bAgK6zHq9cU5z+LpA3xOtx34+vTeT21onRuzssC3zxbbSwC13d/pFuC7Ck"
  225. "IMDxQpF7r/MWq12UctI1dWWm99ypqSYmRUBdKem8MkrO/kgaTt1O7YzlpzE5GIVd0"
  226. "WYUqt57yWf2McHTObYPbVD+ZwbtlLTVMZ3BW+TnLyXLaWtmEq6WJVbT3HBh3Svj2H"
  227. "QQcm43XwmtoYM6vVKleh0uoWvnzW3v3MpidruPTQPf0bia7sJOtBM0ufTWNvus/nk"
  228. "DFHF9ZS+uYVjRUasMeHUmyLYtcklTvzWGFZnNOXczThvpKIzjcahSqIzkvDLayDq6"
  229. "D3eOjtBbNUEIZYyqsvj4V4wY92eNJ4IoyhTbxXX1T5xsV9tm9r4TQwHLiZw/pdDZJ"
  230. "ea8TKmsmR/K0uLh/GwnCHghTja6lPhphezPfO5/5MrVvMzNaI3+ERHfrFzPKQukrQ"
  231. "GI4d/3EFD/3E2mVNYvi4at7CXWREaxZGD+3hg28zD3gVMd6q5c8GdosynKmSeRuGz"
  232. "pjyl1/9UDGtPR5HeaKT8Wjo17WXk579BXVUhN64ehF9fhRtq/uxxZKzNiZFGD0wRC"
  233. "3NFROZ5mwIPL/96K/rKMMLrIzF9uhHr+/sYH7DAbwlgC4J+R2Z7FUx1qLnV7MGF40"
  234. "smVSoJ/jvHRfYhQeUJd/SnYtGWhPHR0Sz+GE2F2yth0B36Vcz2KpnufBJbsysjjW4"
  235. "kblBUiIjiURUWqJY65zxbnTy57GQyH58zgy0QBtTQv5gH15XMdKkYu+TGaJMnlm2O"
  236. "34uI4b9tflqp1+QEFGzoW/ulmcofcpkZCYJhDfSpme7QcrHa+Xfji8paEQkTkSfmm"
  237. "oRWRNZr/F1KfVMjW+IKEnv2FwZfKdzt0BQR6lClcZR0EfEXEfv/G6W9iLiIyCoReV"
  238. "5EnhORIBHx+ufPj/gLB/zGI/G4Bk0AAAAASUVORK5CYII=");
  239. std::string mime_type;
  240. std::string charset;
  241. std::string data;
  242. scoped_refptr<HttpResponseHeaders> headers;
  243. EXPECT_EQ(OK, DataURL::BuildResponse(image_url, "GET", &mime_type, &charset,
  244. &data, &headers));
  245. EXPECT_EQ(911u, data.size());
  246. EXPECT_EQ("image/png", mime_type);
  247. EXPECT_TRUE(charset.empty());
  248. ASSERT_TRUE(headers);
  249. std::string value;
  250. EXPECT_EQ(headers->GetStatusLine(), "HTTP/1.1 200 OK");
  251. EXPECT_TRUE(headers->GetNormalizedHeader("Content-Type", &value));
  252. EXPECT_EQ(value, "image/png");
  253. }
  254. } // namespace net