Skip to content
File

Blob: src/workerd/api/data-url-test.c++

15.8 KB
1#include "data-url.h"
2 
3#include <workerd/jsg/url.h>
4 
5#include <kj/test.h>
6 
7namespace workerd::api {
8namespace {
9 
10KJ_TEST("DataUrl Basics") {
11 auto dataUrl =
12 KJ_ASSERT_NONNULL(DataUrl::tryParse("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="_kj));
13 KJ_ASSERT(dataUrl.getMimeType() == MimeType::PLAINTEXT);
14 KJ_ASSERT(dataUrl.getData().asChars() == "Hello, World!"_kj);
15}
16 
17KJ_TEST("DataUrl Leading/Trailing Whitespace") {
18 auto dataUrl = KJ_ASSERT_NONNULL(
19 DataUrl::tryParse(" data: \t text/plain \t;base64\t\t ,SGVsbG8sIFdvcmxkIQ== "_kj));
20 KJ_ASSERT(dataUrl.getMimeType() == MimeType::PLAINTEXT);
21 KJ_ASSERT(dataUrl.getData().asChars() == "Hello, World!"_kj);
22}
23 
24KJ_TEST("DataUrl base64 case-insensitive") {
25 auto dataUrl = KJ_ASSERT_NONNULL(
26 DataUrl::tryParse(" data: \t text/plain \t;BasE64\t\t ,SGVsbG8sIFdvcmxkIQ== "_kj));
27 KJ_ASSERT(dataUrl.getMimeType() == MimeType::PLAINTEXT);
28 KJ_ASSERT(dataUrl.getData().asChars() == "Hello, World!"_kj);
29}
30 
31KJ_TEST("DataUrl no-base64") {
32 auto dataUrl = KJ_ASSERT_NONNULL(
33 DataUrl::tryParse(" data: \t text/plain \t;a=b\t\t ,SGVsbG8sIFdvcmxkIQ== "_kj));
34 KJ_ASSERT(dataUrl.getMimeType() == MimeType::PLAINTEXT);
35 
36 auto& val = KJ_REQUIRE_NONNULL(dataUrl.getMimeType().params().find("a"_kj));
37 KJ_ASSERT(val == "b"_kj);
38 
39 KJ_ASSERT(dataUrl.getData().asChars() == "SGVsbG8sIFdvcmxkIQ=="_kj);
40}
41 
42KJ_TEST("DataUrl default mime type") {
43 auto dataUrl = KJ_ASSERT_NONNULL(DataUrl::tryParse("data:,Hello, World!"_kj));
44 KJ_ASSERT(dataUrl.getMimeType() == MimeType::PLAINTEXT);
45 KJ_ASSERT(dataUrl.getData().asChars() == "Hello, World!"_kj);
46}
47 
48KJ_TEST("DataUrl default mime type") {
49 auto dataUrl = KJ_ASSERT_NONNULL(DataUrl::tryParse("data:;,Hello, World!"_kj));
50 KJ_ASSERT(dataUrl.getMimeType() == MimeType::PLAINTEXT);
51 KJ_ASSERT(dataUrl.getData().asChars() == "Hello, World!"_kj);
52}
53 
54KJ_TEST("DataUrl default mime type") {
55 auto dataUrl = KJ_ASSERT_NONNULL(DataUrl::tryParse("data:;charset=UTF-8,Hello, World!"_kj));
56 KJ_ASSERT(dataUrl.getMimeType() == MimeType::PLAINTEXT);
57 KJ_ASSERT(dataUrl.getData().asChars() == "Hello, World!"_kj);
58 
59 auto& val = KJ_REQUIRE_NONNULL(dataUrl.getMimeType().params().find("charset"_kj));
60 KJ_ASSERT(val == "UTF-8"_kj);
61}
62 
63struct Test {
64 kj::StringPtr input;
65 kj::StringPtr mimeType;
66 kj::Array<const kj::byte> data;
67};
68 
69KJ_TEST("DataUrl Web Platform Tests") {
70 
71 Test tests[] = {{
72 "data://test/,X"_kj,
73 "text/plain;charset=US-ASCII"_kj,
74 kj::heapArray<kj::byte>({88}),
75 },
76 {
77 "data://test:test/,X"_kj,
78 nullptr,
79 kj::heapArray<kj::byte>(0),
80 },
81 {
82 "data:,X"_kj,
83 "text/plain;charset=US-ASCII"_kj,
84 kj::heapArray<kj::byte>({88}),
85 },
86 {
87 "data:"_kj,
88 nullptr,
89 kj::heapArray<kj::byte>(0),
90 },
91 {
92 "data:text/html"_kj,
93 nullptr,
94 kj::heapArray<kj::byte>(0),
95 },
96 {
97 "data:text/html ;charset=x "_kj,
98 nullptr,
99 kj::heapArray<kj::byte>(0),
100 },
101 {
102 "data:,"_kj,
103 "text/plain;charset=US-ASCII"_kj,
104 kj::heapArray<kj::byte>(0),
105 },
106 {
107 "data:,X#X"_kj,
108 "text/plain;charset=US-ASCII"_kj,
109 kj::heapArray<kj::byte>({88}),
110 },
111 {
112 "data:,%FF"_kj,
113 "text/plain;charset=US-ASCII"_kj,
114 kj::heapArray<kj::byte>({255}),
115 },
116 {
117 "data:text/plain,X"_kj,
118 "text/plain"_kj,
119 kj::heapArray<kj::byte>({88}),
120 },
121 {
122 "data:text/plain ,X"_kj,
123 "text/plain"_kj,
124 kj::heapArray<kj::byte>({88}),
125 },
126 {
127 "data:text/plain%20,X"_kj,
128 "text/plain%20"_kj,
129 kj::heapArray<kj::byte>({88}),
130 },
131 {
132 "data:text/plain\f,X"_kj,
133 "text/plain%0c"_kj,
134 kj::heapArray<kj::byte>({88}),
135 },
136 {
137 "data:text/plain%0C,X"_kj,
138 "text/plain%0c"_kj,
139 kj::heapArray<kj::byte>({88}),
140 },
141 {
142 "data:text/plain;,X"_kj,
143 "text/plain"_kj,
144 kj::heapArray<kj::byte>({88}),
145 },
146 {
147 "data:;x=x;charset=x,X"_kj,
148 "text/plain;x=x;charset=x"_kj,
149 kj::heapArray<kj::byte>({88}),
150 },
151 {
152 "data:;x=x,X"_kj,
153 "text/plain;x=x"_kj,
154 kj::heapArray<kj::byte>({88}),
155 },
156 {
157 "data:text/plain;charset=windows-1252,%C2%B1"_kj,
158 "text/plain;charset=windows-1252"_kj,
159 kj::heapArray<kj::byte>({194, 177}),
160 },
161 {
162 "data:text/plain;Charset=UTF-8,%C2%B1"_kj,
163 "text/plain;charset=UTF-8"_kj,
164 kj::heapArray<kj::byte>({194, 177}),
165 },
166 {
167 "data:text/plain;charset=windows-1252,áñçə💩"_kj,
168 "text/plain;charset=windows-1252"_kj,
169 kj::heapArray<kj::byte>({195, 161, 195, 177, 195, 167, 201, 153, 240, 159, 146, 169}),
170 },
171 {
172 "data:text/plain;charset=UTF-8,áñçə💩"_kj,
173 "text/plain;charset=UTF-8"_kj,
174 kj::heapArray<kj::byte>({195, 161, 195, 177, 195, 167, 201, 153, 240, 159, 146, 169}),
175 },
176 {
177 "data:image/gif,%C2%B1"_kj,
178 "image/gif"_kj,
179 kj::heapArray<kj::byte>({194, 177}),
180 },
181 {
182 "data:IMAGE/gif,%C2%B1"_kj,
183 "image/gif"_kj,
184 kj::heapArray<kj::byte>({194, 177}),
185 },
186 {
187 "data:IMAGE/gif;hi=x,%C2%B1"_kj,
188 "image/gif;hi=x"_kj,
189 kj::heapArray<kj::byte>({194, 177}),
190 },
191 {
192 "data:IMAGE/gif;CHARSET=x,%C2%B1"_kj,
193 "image/gif;charset=x"_kj,
194 kj::heapArray<kj::byte>({194, 177}),
195 },
196 {
197 "data: ,%FF"_kj,
198 "text/plain;charset=US-ASCII"_kj,
199 kj::heapArray<kj::byte>({255}),
200 },
201 {
202 "data:%20,%FF"_kj,
203 "text/plain;charset=US-ASCII"_kj,
204 kj::heapArray<kj::byte>({255}),
205 },
206 {
207 "data:\f,%FF"_kj,
208 "text/plain;charset=US-ASCII"_kj,
209 kj::heapArray<kj::byte>({255}),
210 },
211 {
212 "data:%1F,%FF"_kj,
213 "text/plain;charset=US-ASCII"_kj,
214 kj::heapArray<kj::byte>({255}),
215 },
216 {
217 "data:\u0000,%FF"_kj,
218 "text/plain;charset=US-ASCII"_kj,
219 kj::heapArray<kj::byte>({255}),
220 },
221 {
222 "data:%00,%FF"_kj,
223 "text/plain;charset=US-ASCII"_kj,
224 kj::heapArray<kj::byte>({255}),
225 },
226 {
227 "data:text/html ,X"_kj,
228 "text/html"_kj,
229 kj::heapArray<kj::byte>({88}),
230 },
231 {
232 "data:text / html,X"_kj,
233 "text/plain;charset=US-ASCII"_kj,
234 kj::heapArray<kj::byte>({88}),
235 },
236 {
237 "data:†,X"_kj,
238 "text/plain;charset=US-ASCII"_kj,
239 kj::heapArray<kj::byte>({88}),
240 },
241 {
242 "data:†/†,X"_kj,
243 "%e2%80%a0/%e2%80%a0"_kj,
244 kj::heapArray<kj::byte>({88}),
245 },
246 {
247 "data:X,X"_kj,
248 "text/plain;charset=US-ASCII"_kj,
249 kj::heapArray<kj::byte>({88}),
250 },
251 {
252 "data:image/png,X X"_kj,
253 "image/png"_kj,
254 kj::heapArray<kj::byte>({88, 32, 88}),
255 },
256 {
257 "data:application/javascript,X X"_kj,
258 "application/javascript"_kj,
259 kj::heapArray<kj::byte>({88, 32, 88}),
260 },
261 {
262 "data:application/xml,X X"_kj,
263 "application/xml"_kj,
264 kj::heapArray<kj::byte>({88, 32, 88}),
265 },
266 {
267 "data:text/javascript,X X"_kj,
268 "text/javascript"_kj,
269 kj::heapArray<kj::byte>({88, 32, 88}),
270 },
271 {
272 "data:text/plain,X X"_kj,
273 "text/plain"_kj,
274 kj::heapArray<kj::byte>({88, 32, 88}),
275 },
276 {
277 "data:unknown/unknown,X X"_kj,
278 "unknown/unknown"_kj,
279 kj::heapArray<kj::byte>({88, 32, 88}),
280 },
281 {
282 "data:text/plain;a=\",\",X"_kj,
283 "text/plain;a=\"\""_kj,
284 kj::heapArray<kj::byte>({34, 44, 88}),
285 },
286 {
287 "data:text/plain;a=%2C,X"_kj,
288 "text/plain;a=%2C"_kj,
289 kj::heapArray<kj::byte>({88}),
290 },
291 {
292 "data:;base64;base64,WA"_kj,
293 "text/plain"_kj,
294 kj::heapArray<kj::byte>({88}),
295 },
296 {
297 "data:x/x;base64;base64,WA"_kj,
298 "x/x"_kj,
299 kj::heapArray<kj::byte>({88}),
300 },
301 {
302 "data:x/x;base64;charset=x,WA"_kj,
303 "x/x;charset=x"_kj,
304 kj::heapArray<kj::byte>({87, 65}),
305 },
306 {
307 "data:x/x;base64;charset=x;base64,WA"_kj,
308 "x/x;charset=x"_kj,
309 kj::heapArray<kj::byte>({88}),
310 },
311 {
312 "data:x/x;base64;base64x,WA"_kj,
313 "x/x"_kj,
314 kj::heapArray<kj::byte>({87, 65}),
315 },
316 {
317 "data:;base64,W%20A"_kj,
318 "text/plain;charset=US-ASCII"_kj,
319 kj::heapArray<kj::byte>({88}),
320 },
321 {
322 "data:;base64,W%0CA"_kj,
323 "text/plain;charset=US-ASCII"_kj,
324 kj::heapArray<kj::byte>({88}),
325 },
326 {
327 "data:x;base64x,WA"_kj,
328 "text/plain;charset=US-ASCII"_kj,
329 kj::heapArray<kj::byte>({87, 65}),
330 },
331 {
332 "data:x;base64;x,WA"_kj,
333 "text/plain;charset=US-ASCII"_kj,
334 kj::heapArray<kj::byte>({87, 65}),
335 },
336 {
337 "data:x;base64=x,WA"_kj,
338 "text/plain;charset=US-ASCII"_kj,
339 kj::heapArray<kj::byte>({87, 65}),
340 },
341 {
342 "data:; base64,WA"_kj,
343 "text/plain;charset=US-ASCII"_kj,
344 kj::heapArray<kj::byte>({88}),
345 },
346 {
347 "data:; base64,WA"_kj,
348 "text/plain;charset=US-ASCII"_kj,
349 kj::heapArray<kj::byte>({88}),
350 },
351 {
352 "data: ;charset=x ; base64,WA"_kj,
353 "text/plain;charset=x"_kj,
354 kj::heapArray<kj::byte>({88}),
355 },
356 {
357 "data:;base64;,WA"_kj,
358 "text/plain"_kj,
359 kj::heapArray<kj::byte>({87, 65}),
360 },
361 {
362 "data:;base64 ,WA"_kj,
363 "text/plain;charset=US-ASCII"_kj,
364 kj::heapArray<kj::byte>({88}),
365 },
366 {
367 "data:;base64 ,WA"_kj,
368 "text/plain;charset=US-ASCII"_kj,
369 kj::heapArray<kj::byte>({88}),
370 },
371 {
372 "data:;base 64,WA"_kj,
373 "text/plain"_kj,
374 kj::heapArray<kj::byte>({87, 65}),
375 },
376 {
377 "data:;BASe64,WA"_kj,
378 "text/plain;charset=US-ASCII"_kj,
379 kj::heapArray<kj::byte>({88}),
380 },
381 {
382 "data:;%62ase64,WA"_kj,
383 "text/plain"_kj,
384 kj::heapArray<kj::byte>({87, 65}),
385 },
386 {
387 "data:%3Bbase64,WA"_kj,
388 "text/plain;charset=US-ASCII"_kj,
389 kj::heapArray<kj::byte>({87, 65}),
390 },
391 {
392 "data:;charset=x,X"_kj,
393 "text/plain;charset=x"_kj,
394 kj::heapArray<kj::byte>({88}),
395 },
396 {
397 "data:; charset=x,X"_kj,
398 "text/plain;charset=x"_kj,
399 kj::heapArray<kj::byte>({88}),
400 },
401 {
402 "data:;charset =x,X"_kj,
403 "text/plain"_kj,
404 kj::heapArray<kj::byte>({88}),
405 },
406 {
407 "data:;charset= x,X"_kj,
408 "text/plain;charset=\" x\""_kj,
409 kj::heapArray<kj::byte>({88}),
410 },
411 {
412 "data:;charset=,X"_kj,
413 "text/plain"_kj,
414 kj::heapArray<kj::byte>({88}),
415 },
416 {
417 "data:;charset,X"_kj,
418 "text/plain"_kj,
419 kj::heapArray<kj::byte>({88}),
420 },
421 {
422 "data:;charset=\"x\",X"_kj,
423 "text/plain;charset=x"_kj,
424 kj::heapArray<kj::byte>({88}),
425 },
426 {
427 "data:;CHARSET=\"X\",X"_kj,
428 "text/plain;charset=X"_kj,
429 kj::heapArray<kj::byte>({88}),
430 }};
431 
432 auto testPtr = kj::arrayPtr<Test>(tests, 72);
433 
434 for (auto& test: testPtr) {
435 if (test.mimeType == nullptr) {
436 KJ_ASSERT(DataUrl::tryParse(test.input) == kj::none);
437 } else {
438 auto parsed = KJ_ASSERT_NONNULL(DataUrl::tryParse(test.input));
439 KJ_ASSERT(parsed.getMimeType().toString() == test.mimeType);
440 KJ_ASSERT(parsed.getData() == test.data);
441 }
442 }
443}
444 
445struct Base64Test {
446 kj::StringPtr input;
447 kj::Array<kj::byte> expected;
448};
449 
450KJ_TEST("DataUrl base64") {
451 // Our base64 decoder is not very strict and way more forgiving than the
452 // web platform's forgiving base64 decoder. That's just fine for us.
453 // These cases were extracted from the Web Platform Tests for data urls
454 // See: https://github.com/web-platform-tests/wpt/blob/master/fetch/data-urls/resources/
455 Base64Test tests[] = {{""_kj, nullptr}, {"abcd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
456 {" abcd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
457 {"abcd "_kj, kj::heapArray<kj::byte>({105, 183, 29})},
458 {" abcd==="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
459 {"abcd=== "_kj, kj::heapArray<kj::byte>({105, 183, 29})},
460 {"abcd ==="_kj, kj::heapArray<kj::byte>({105, 183, 29})}, {"a"_kj, nullptr},
461 {"ab"_kj, kj::heapArray<kj::byte>({105})}, {"abc"_kj, kj::heapArray<kj::byte>({105, 183})},
462 {"abcde"_kj, kj::heapArray<kj::byte>({105, 183, 29})}, {"𐀀"_kj, nullptr}, {"="_kj, nullptr},
463 {"=="_kj, nullptr}, {"==="_kj, nullptr}, {"===="_kj, nullptr}, {"====="_kj, nullptr},
464 {"a="_kj, nullptr}, {"a=="_kj, nullptr}, {"a==="_kj, nullptr}, {"a===="_kj, nullptr},
465 {"a====="_kj, nullptr}, {"ab="_kj, kj::heapArray<kj::byte>({105})},
466 {"ab=="_kj, kj::heapArray<kj::byte>({105})}, {"ab==="_kj, kj::heapArray<kj::byte>({105})},
467 {"ab===="_kj, kj::heapArray<kj::byte>({105})}, {"ab====="_kj, kj::heapArray<kj::byte>({105})},
468 {"abc="_kj, kj::heapArray<kj::byte>({105, 183})},
469 {"abc=="_kj, kj::heapArray<kj::byte>({105, 183})},
470 {"abc==="_kj, kj::heapArray<kj::byte>({105, 183})},
471 {"abc===="_kj, kj::heapArray<kj::byte>({105, 183})},
472 {"abc====="_kj, kj::heapArray<kj::byte>({105, 183})},
473 {"abcd="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
474 {"abcd=="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
475 {"abcd==="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
476 {"abcd===="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
477 {"abcd====="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
478 {"abcde="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
479 {"abcde=="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
480 {"abcde==="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
481 {"abcde===="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
482 {"abcde====="_kj, kj::heapArray<kj::byte>({105, 183, 29})}, {"=a"_kj, nullptr},
483 {"=a="_kj, nullptr}, {"a=b"_kj, kj::heapArray<kj::byte>({105})},
484 {"a=b="_kj, kj::heapArray<kj::byte>({105})}, {"ab=c"_kj, kj::heapArray<kj::byte>({105, 183})},
485 {"ab=c="_kj, kj::heapArray<kj::byte>({105, 183})},
486 {"abc=d"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
487 {"abc=d="_kj, kj::heapArray<kj::byte>({105, 183, 29})},
488 {"ab\u000Bcd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
489 {"ab\u3000cd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
490 {"ab\u3001cd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
491 {"ab\tcd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
492 {"ab\ncd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
493 {"ab\fcd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
494 {"ab\rcd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
495 {"ab cd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
496 {"ab\u00a0cd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
497 {"ab\t\n\f\r cd"_kj, kj::heapArray<kj::byte>({105, 183, 29})},
498 {" \t\n\f\r ab\t\n\f\r cd\t\n\f\r "_kj, kj::heapArray<kj::byte>({105, 183, 29})},
499 {"ab\t\n\f\r =\t\n\f\r =\t\n\f\r "_kj, kj::heapArray<kj::byte>({105})}, {"A"_kj, nullptr},
500 {"/A"_kj, kj::heapArray<kj::byte>({252})}, {"//A"_kj, kj::heapArray<kj::byte>({255, 240})},
501 {"///A"_kj, kj::heapArray<kj::byte>({255, 255, 192})},
502 {"////A"_kj, kj::heapArray<kj::byte>({255, 255, 255})}, {"/"_kj, nullptr},
503 {"A/"_kj, kj::heapArray<kj::byte>({3})}, {"AA/"_kj, kj::heapArray<kj::byte>({0, 15})},
504 {"AAAA/"_kj, kj::heapArray<kj::byte>({0, 0, 0})},
505 {"AAA/"_kj, kj::heapArray<kj::byte>({0, 0, 63})},
506 {"\u0000nonsense"_kj, kj::heapArray<kj::byte>({158, 137, 236, 122, 123, 30})},
507 {"abcd\u0000nonsense"_kj, kj::heapArray<kj::byte>({105, 183, 29, 158, 137, 236, 122, 123, 30})},
508 {"YQ"_kj, kj::heapArray<kj::byte>({97})}, {"YR"_kj, kj::heapArray<kj::byte>({97})},
509 {"~~"_kj, nullptr}, {".."_kj, nullptr}, {"--"_kj, nullptr}, {"__"_kj, nullptr}};
510 
511 auto testPtr = kj::arrayPtr<Base64Test>(tests, 80);
512 
513 for (auto& test: testPtr) {
514 auto input = kj::str("data:;base64,", test.input);
515 
516 auto url = KJ_ASSERT_NONNULL(DataUrl::tryParse(input));
517 
518 KJ_ASSERT(url.getData() == test.expected, test.input);
519 }
520}
521 
522KJ_TEST("Large Data URL") {
523 auto str = kj::str(kj::repeat('a', 6000));
524 auto url = kj::str("data:,", str);
525 auto parsed = KJ_ASSERT_NONNULL(DataUrl::tryParse(url));
526 KJ_ASSERT(parsed.getData().asChars() == str);
527}
528 
529} // namespace
530} // namespace workerd::api