Skip to content
File

Blob: src/workerd/api/node/buffer-test.c++

16.3 KB
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#include <workerd/tests/test-fixture.h>
6 
7#include <kj/test.h>
8 
9namespace workerd::api {
10namespace {
11 
12KJ_TEST("node:buffer import without capability") {
13 KJ_EXPECT_LOG(ERROR, "script startup threw exception");
14 
15 try {
16 TestFixture fixture({.mainModuleSource = R"SCRIPT(
17 import { Buffer } from 'node:buffer';
18 
19 export default {
20 fetch(request) {
21 return new Response(new Buffer("test").toString());
22 },
23 };
24 )SCRIPT"_kj});
25 
26 KJ_UNREACHABLE;
27 } catch (kj::Exception& e) {
28 KJ_EXPECT(e.getDescription() == "script startup threw exception"_kj);
29 }
30}
31 
32KJ_TEST("Verify maximum Buffer size") {
33 capnp::MallocMessageBuilder message;
34 auto flags = message.initRoot<CompatibilityFlags>();
35 flags.setNodeJsCompat(true);
36 flags.setWorkerdExperimental(true);
37 
38 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
39 import { Buffer, kMaxLength } from 'node:buffer';
40 
41 try {
42 Buffer.alloc(kMaxLength + 1);
43 throw new Error('alloc should have failed');
44 } catch (err) {
45 if (!err.message.startsWith("The value of \"size\" is out of range"))
46 throw err;
47 }
48 
49 export default {
50 fetch(request) {
51 return new Response("test");
52 },
53 };
54 )SCRIPT"_kj});
55 
56 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
57 
58 KJ_EXPECT(response.statusCode == 200);
59 KJ_EXPECT(response.body == "test");
60}
61 
62KJ_TEST("Create 0-length buffers") {
63 capnp::MallocMessageBuilder message;
64 auto flags = message.initRoot<CompatibilityFlags>();
65 flags.setNodeJsCompat(true);
66 flags.setWorkerdExperimental(true);
67 
68 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
69 import { Buffer } from 'node:buffer';
70 export default {
71 fetch(request) {
72 Buffer.from('');
73 Buffer.from('', 'ascii');
74 Buffer.from('', 'latin1');
75 Buffer.alloc(0);
76 Buffer.allocUnsafe(0);
77 new Buffer('');
78 new Buffer('', 'ascii');
79 new Buffer('', 'latin1');
80 new Buffer('', 'binary');
81 Buffer(0);
82 return new Response("test");
83 },
84 };
85 )SCRIPT"_kj});
86 
87 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
88 
89 KJ_EXPECT(response.statusCode == 200);
90 KJ_EXPECT(response.body == "test");
91}
92 
93KJ_TEST("new Buffer(string)") {
94 capnp::MallocMessageBuilder message;
95 auto flags = message.initRoot<CompatibilityFlags>();
96 flags.setNodeJsCompat(true);
97 flags.setWorkerdExperimental(true);
98 
99 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
100 import { Buffer } from 'node:buffer';
101 export default {
102 fetch(request) {
103 const b = new Buffer("test");
104 return new Response(b.toString("utf8"));
105 },
106 };
107 )SCRIPT"_kj});
108 
109 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
110 
111 KJ_EXPECT(response.statusCode == 200);
112 KJ_EXPECT(response.body == "test");
113}
114 
115KJ_TEST("Buffer.allocUnsafe(), Buffer.alloc(), Buffer.allocUnsafeSlow()") {
116 capnp::MallocMessageBuilder message;
117 auto flags = message.initRoot<CompatibilityFlags>();
118 flags.setNodeJsCompat(true);
119 flags.setWorkerdExperimental(true);
120 
121 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
122 import { Buffer } from 'node:buffer';
123 
124 export default {
125 fetch(request) {
126 [
127 'alloc',
128 'allocUnsafe',
129 'allocUnsafeSlow'
130 ].forEach((alloc) => {
131 const b = Buffer[alloc](1024);
132 if (b.length !== 1024) {
133 throw new Error(`Incorrect buffer length [${b.length}]`);
134 }
135 
136 // In Node.js' implementation, the Buffer is sliced off a larger pool.
137 // We don't do that, so the underlying ArrayBuffer length and offsets
138 // should be what we expect.
139 if (b.length !== b.buffer.byteLength) {
140 throw new Error('b.buffer.byteLength does not match');
141 }
142 if (b.byteOffset !== 0) {
143 throw new Error(`Incorrect b.byteOffset [${b.byteOffset}]`);
144 }
145 
146 // In Node.js' implementation of allocUnsafe, and allocUnsafeSlow(),
147 // the Buffer is filled with uninitialized memory. We don't do that,
148 // so everything should be zeroes.
149 for (const i of b) {
150 if (i !== 0) {
151 throw new Error(`Index should be zeroed out [${i}]`);
152 }
153 }
154 
155 b[0] = -1;
156 if (b[0] !== 255) {
157 throw new Error(`Incorrect index value [${b[0]}]`);
158 }
159 });
160 
161 return new Response("test");
162 },
163 };
164 )SCRIPT"_kj});
165 
166 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
167 
168 KJ_EXPECT(response.statusCode == 200);
169 KJ_EXPECT(response.body == "test");
170}
171 
172KJ_TEST("Buffer.from(string)") {
173 capnp::MallocMessageBuilder message;
174 auto flags = message.initRoot<CompatibilityFlags>();
175 flags.setNodeJsCompat(true);
176 flags.setWorkerdExperimental(true);
177 
178 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
179 import { Buffer } from 'node:buffer';
180 
181 export default {
182 fetch(request) {
183 return new Response(Buffer.from("test").toString());
184 },
185 };
186 )SCRIPT"_kj});
187 
188 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
189 
190 KJ_EXPECT(response.statusCode == 200);
191 KJ_EXPECT(response.body == "test");
192}
193 
194KJ_TEST("Buffer.from(string, 'utf8')") {
195 capnp::MallocMessageBuilder message;
196 auto flags = message.initRoot<CompatibilityFlags>();
197 flags.setNodeJsCompat(true);
198 flags.setWorkerdExperimental(true);
199 
200 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
201 import { Buffer } from 'node:buffer';
202 
203 export default {
204 fetch(request) {
205 return new Response(Buffer.from("test", 'utf8').toString('utf8'));
206 },
207 };
208 )SCRIPT"_kj});
209 
210 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
211 
212 KJ_EXPECT(response.statusCode == 200);
213 KJ_EXPECT(response.body == "test");
214}
215 
216KJ_TEST("Buffer.from(string, 'ucs2')") {
217 capnp::MallocMessageBuilder message;
218 auto flags = message.initRoot<CompatibilityFlags>();
219 flags.setNodeJsCompat(true);
220 flags.setWorkerdExperimental(true);
221 
222 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
223 import { Buffer } from 'node:buffer';
224 
225 export default {
226 fetch(request) {
227 return new Response(Buffer.from("test", 'ucs2').toString('ucs2'));
228 },
229 };
230 )SCRIPT"_kj});
231 
232 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
233 
234 KJ_EXPECT(response.statusCode == 200);
235 KJ_EXPECT(response.body == "test");
236}
237 
238KJ_TEST("Buffer.from(string, 'hex')") {
239 capnp::MallocMessageBuilder message;
240 auto flags = message.initRoot<CompatibilityFlags>();
241 flags.setNodeJsCompat(true);
242 flags.setWorkerdExperimental(true);
243 
244 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
245 import { Buffer } from 'node:buffer';
246 
247 export default {
248 fetch(request) {
249 // Only the valid hex in the input will be decoded. Anything after
250 // the first invalid hex pair will be ignored.
251 const buf = Buffer.from("74657374 invalid from here", 'hex');
252 if (buf.length !== 4) {
253 throw new Error(`invalid buffer length [${buf.length}]`);
254 }
255 return new Response(Buffer.from("74657374 invalid from here", 'hex').toString());
256 },
257 };
258 )SCRIPT"_kj});
259 
260 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
261 
262 KJ_EXPECT(response.statusCode == 200);
263 KJ_EXPECT(response.body == "test");
264}
265 
266KJ_TEST("Buffer.from(string, 'base64')") {
267 capnp::MallocMessageBuilder message;
268 auto flags = message.initRoot<CompatibilityFlags>();
269 flags.setNodeJsCompat(true);
270 flags.setWorkerdExperimental(true);
271 
272 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
273 import { Buffer } from 'node:buffer';
274 
275 export default {
276 fetch(request) {
277 // Invalid characters within the encoding are ignored...
278 return new Response(Buffer.from("dGV^^^^^^zdA==", 'base64').toString());
279 },
280 };
281 )SCRIPT"_kj});
282 
283 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
284 
285 KJ_EXPECT(response.statusCode == 200);
286 KJ_EXPECT(response.body == "test");
287}
288 
289KJ_TEST("new Buffer(string, 'base64')") {
290 capnp::MallocMessageBuilder message;
291 auto flags = message.initRoot<CompatibilityFlags>();
292 flags.setNodeJsCompat(true);
293 flags.setWorkerdExperimental(true);
294 
295 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
296 import { Buffer } from 'node:buffer';
297 
298 export default {
299 fetch(request) {
300 return new Response(new Buffer("dGVzdA==", 'base64').toString());
301 },
302 };
303 )SCRIPT"_kj});
304 
305 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
306 
307 KJ_EXPECT(response.statusCode == 200);
308 KJ_EXPECT(response.body == "test");
309}
310 
311KJ_TEST("Buffer.from(string, 'base64url')") {
312 capnp::MallocMessageBuilder message;
313 auto flags = message.initRoot<CompatibilityFlags>();
314 flags.setNodeJsCompat(true);
315 flags.setWorkerdExperimental(true);
316 
317 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
318 import { Buffer } from 'node:buffer';
319 
320 export default {
321 fetch(request) {
322 return new Response(Buffer.from("dGVzdA", 'base64').toString());
323 },
324 };
325 )SCRIPT"_kj});
326 
327 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
328 
329 KJ_EXPECT(response.statusCode == 200);
330 KJ_EXPECT(response.body == "test");
331}
332 
333KJ_TEST("Buffer.from(Uint8Array)") {
334 capnp::MallocMessageBuilder message;
335 auto flags = message.initRoot<CompatibilityFlags>();
336 flags.setNodeJsCompat(true);
337 flags.setWorkerdExperimental(true);
338 
339 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
340 import { Buffer } from 'node:buffer';
341 
342 export default {
343 fetch(request) {
344 const u8 = new Uint8Array([74, 65, 73, 74]);
345 const buffer = Buffer.from(u8);
346 if (buffer.length !== 4) {
347 throw new Error(`Unexpected buffer length [${buffer.length}]`);
348 }
349 if (buffer[0] !== 74) {
350 throw new Error(`Unexpected buffer value [${buffer[0]}]`);
351 }
352 u8.fill(0);
353 return new Response(buffer);
354 },
355 };
356 )SCRIPT"_kj});
357 
358 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
359 
360 KJ_EXPECT(response.statusCode == 200);
361 KJ_EXPECT(response.body == "JAIJ");
362}
363 
364KJ_TEST("new Buffer(Uint8Array)") {
365 capnp::MallocMessageBuilder message;
366 auto flags = message.initRoot<CompatibilityFlags>();
367 flags.setNodeJsCompat(true);
368 flags.setWorkerdExperimental(true);
369 
370 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
371 import { Buffer } from 'node:buffer';
372 
373 export default {
374 fetch(request) {
375 const u8 = new Uint8Array([74, 65, 73, 74]);
376 const buffer = new Buffer(u8);
377 if (buffer.length !== 4) {
378 throw new Error(`Unexpected buffer length [${buffer.length}]`);
379 }
380 if (buffer[0] !== 74) {
381 throw new Error(`Unexpected buffer value [${buffer[0]}]`);
382 }
383 u8.fill(0);
384 return new Response(buffer);
385 },
386 };
387 )SCRIPT"_kj});
388 
389 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
390 
391 KJ_EXPECT(response.statusCode == 200);
392 KJ_EXPECT(response.body == "JAIJ");
393}
394 
395KJ_TEST("Buffer.from(Uint32Array)") {
396 capnp::MallocMessageBuilder message;
397 auto flags = message.initRoot<CompatibilityFlags>();
398 flags.setNodeJsCompat(true);
399 flags.setWorkerdExperimental(true);
400 
401 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
402 import { Buffer } from 'node:buffer';
403 
404 export default {
405 fetch(request) {
406 const u32 = new Uint32Array([1953719668]);
407 const buffer = Buffer.from(u32);
408 if (buffer.length !== 1) {
409 throw new Error(`Unexpected buffer length [${buffer.length}]`);
410 }
411 if (buffer[0] !== 116) {
412 throw new Error(`Unexpected buffer value [${buffer[0]}]`);
413 }
414 u32.fill(0);
415 return new Response(buffer);
416 },
417 };
418 )SCRIPT"_kj});
419 
420 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
421 
422 KJ_EXPECT(response.statusCode == 200);
423 KJ_EXPECT(response.body == "t");
424}
425 
426KJ_TEST("Buffer.from(ArrayBuffer)") {
427 capnp::MallocMessageBuilder message;
428 auto flags = message.initRoot<CompatibilityFlags>();
429 flags.setNodeJsCompat(true);
430 flags.setWorkerdExperimental(true);
431 
432 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
433 import { Buffer } from 'node:buffer';
434 
435 export default {
436 fetch(request) {
437 const u32 = new Uint32Array([1953719668]);
438 const buffer = Buffer.from(u32.buffer);
439 if (buffer.length !== 4) {
440 throw new Error(`Unexpected buffer length [${buffer.length}]`);
441 }
442 if (buffer[0] !== 116) {
443 throw new Error(`Unexpected buffer value [${buffer[0]}]`);
444 }
445 return new Response(buffer);
446 },
447 };
448 )SCRIPT"_kj});
449 
450 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
451 
452 KJ_EXPECT(response.statusCode == 200);
453 KJ_EXPECT(response.body == "test");
454}
455 
456KJ_TEST("new Buffer(ArrayBuffer)") {
457 capnp::MallocMessageBuilder message;
458 auto flags = message.initRoot<CompatibilityFlags>();
459 flags.setNodeJsCompat(true);
460 flags.setWorkerdExperimental(true);
461 
462 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
463 import { Buffer } from 'node:buffer';
464 
465 export default {
466 fetch(request) {
467 const u32 = new Uint32Array([1953719668]);
468 const buffer = new Buffer(u32.buffer);
469 if (buffer.length !== 4) {
470 throw new Error(`Unexpected buffer length [${buffer.length}]`);
471 }
472 if (buffer[0] !== 116) {
473 throw new Error(`Unexpected buffer value [${buffer[0]}]`);
474 }
475 return new Response(buffer);
476 },
477 };
478 )SCRIPT"_kj});
479 
480 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
481 
482 KJ_EXPECT(response.statusCode == 200);
483 KJ_EXPECT(response.body == "test");
484}
485 
486KJ_TEST("Buffer.prototype.indexOf/lastIndexOf") {
487 capnp::MallocMessageBuilder message;
488 auto flags = message.initRoot<CompatibilityFlags>();
489 flags.setNodeJsCompat(true);
490 flags.setWorkerdExperimental(true);
491 
492 TestFixture fixture({.featureFlags = flags.asReader(), .mainModuleSource = R"SCRIPT(
493 import { Buffer } from 'node:buffer';
494 
495 export default {
496 fetch(request) {
497 
498 const b = Buffer.from('helloabcabcthere');
499 
500 if (b.indexOf('abc') !== 5) {
501 throw new Error('Incorrect index');
502 }
503 
504 if (b.indexOf('abc', -8) !== 8) {
505 throw new Error('Incorrect index');
506 }
507 
508 if (b.indexOf('abc', 6) !== 8) {
509 throw new Error('Incorrect index');
510 }
511 
512 if (b.lastIndexOf('abc') !== 8) {
513 throw new Error('Incorrect last index');
514 }
515 
516 if (b.indexOf(Buffer.from('abc')) !== 5) {
517 throw new Error('Incorrect index');
518 }
519 
520 return new Response('test');
521 },
522 };
523 )SCRIPT"_kj});
524 
525 auto response = fixture.runRequest(kj::HttpMethod::POST, "http://www.example.com"_kj, ""_kj);
526 
527 KJ_EXPECT(response.statusCode == 200);
528 KJ_EXPECT(response.body == "test");
529}
530 
531} // namespace
532} // namespace workerd::api