castrum 0.9.6 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +4 -3
- package/Cargo.toml +1 -1
- package/castrum.darwin-arm64.node +0 -0
- package/castrum.darwin-x64.node +0 -0
- package/castrum.linux-arm64-gnu.node +0 -0
- package/castrum.linux-arm64-musl.node +0 -0
- package/castrum.linux-x64-gnu.node +0 -0
- package/castrum.linux-x64-musl.node +0 -0
- package/castrum.linux-x64-v3-gnu.node +0 -0
- package/castrum.win32-x64-msvc.node +0 -0
- package/dist/index.d.ts +501 -5
- package/dist/index.js +1658 -1314
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/BENCHMARKS.md +13 -0
- package/docs/CASE_STUDY.md +2 -2
- package/docs/ENVIRONMENT.md +1 -1
- package/docs/FFI_BUN_GUIDE.md +13 -1
- package/docs/HOW_TO_ADD_AN_OP.md +24 -0
- package/docs/INGRESS.md +4 -4
- package/docs/REPO_MAP.md +8 -6
- package/docs/superpowers/plans/2026-09-18-castrum-hardening-cleanup.md +617 -0
- package/index.ts +1 -0
- package/package.json +3 -2
- package/rust/crypto/aead.rs +44 -0
- package/rust/crypto/argon2.rs +32 -2
- package/rust/crypto/jwt/tests.rs +19 -0
- package/rust/crypto/jwt/token.rs +43 -28
- package/rust/crypto/session.rs +170 -7
- package/rust/ffi/crypto.rs +182 -61
- package/rust/ffi/hashing.rs +48 -33
- package/rust/ffi/http.rs +62 -36
- package/rust/ffi/metrics.rs +6 -2
- package/rust/ffi/payload.rs +47 -27
- package/rust/ffi/task.rs +51 -32
- package/rust/ffi/tests.rs +197 -0
- package/rust/ffi/util.rs +40 -0
- package/rust/http/headers.rs +13 -0
- package/rust/http/multipart.rs +139 -0
- package/rust/ingress/native_route.rs +21 -3
- package/rust/task/completion.rs +7 -0
- package/rust/task/mod.rs +1 -1
- package/rust/task/tests.rs +30 -0
- package/src/ingress/decode/baked-result.ts +13 -5
- package/src/ingress/decode/fast-result.ts +1 -1
- package/src/ingress/decode/result-base.ts +16 -2
- package/src/ingress/handlers.ts +103 -31
- package/src/ingress/headers/memoized-headers.ts +40 -0
- package/src/ingress/native-route.ts +25 -6
- package/src/ingress/options.ts +26 -2
- package/src/ingress/packing/gather-raw-headers.ts +24 -8
- package/src/ingress/response/baked-response.ts +118 -28
- package/src/ingress/router.ts +14 -2
- package/src/ingress/routes/common.ts +26 -10
- package/src/ingress/routes/echo.ts +50 -7
- package/src/ingress/routes/json-write.ts +21 -8
- package/src/ingress/routes/responder.ts +4 -4
- package/src/ingress/server-node.ts +33 -14
- package/src/ingress/server.ts +3 -4
- package/src/ingress/shared.ts +1 -1
- package/src/ingress/types.ts +15 -0
- package/src/loader/ops.ts +1 -1
- package/src/native/ffi/build/codecs-selftest.ts +305 -0
- package/src/native/ffi/build/codecs.ts +0 -296
- package/src/native/ffi/build/instances-selftest.ts +264 -0
- package/src/native/ffi/build/instances.ts +39 -241
- package/src/native/ffi/build/metrics.ts +0 -7
- package/src/native/ffi/build/parse-selftest.ts +238 -0
- package/src/native/ffi/build/parse.ts +11 -234
- package/src/native/ffi/build.ts +5 -1
- package/src/native/ffi/selftest.ts +3 -3
- package/src/native/ffi/symbols.ts +460 -0
- package/src/native/ffi/types.ts +17 -0
- package/src/native/ffi.ts +2 -422
- package/src/native/ingress-binding.ts +39 -0
- package/src/runtime/codec.ts +24 -0
- package/src/runtime/types.ts +11 -0
- package/src/rust-ffi/batch/build.ts +18 -8
- package/src/rust-ffi/batch/types.ts +15 -0
- package/src/rust-ffi/options.ts +19 -0
- package/src/rust-ffi/scalar/crypto.ts +8 -0
- package/src/rust-ffi/scalar/factories.ts +47 -20
- package/src/rust-ffi/scalar/http.ts +60 -10
- package/src/rust-ffi/scalar/interface.ts +4 -1
- package/src/rust-ffi/scalar/payload.ts +27 -1
- package/src/shared/buffer-pool.ts +44 -9
- package/src/shared/codec.ts +26 -0
- package/src/shared/packed/parsers.ts +64 -7
- package/src/task/runtime.ts +29 -28
package/dist/index.js
CHANGED
|
@@ -87,7 +87,8 @@ function createBunCodec() {
|
|
|
87
87
|
},
|
|
88
88
|
decodeUtf8: decodeUtf8Bun,
|
|
89
89
|
decodeUtf8Fatal: decodeUtf8Bun,
|
|
90
|
-
decodeUtf8Range: decodeUtf8RangeBun
|
|
90
|
+
decodeUtf8Range: decodeUtf8RangeBun,
|
|
91
|
+
decodeUtf8RangeView: decodeUtf8RangeViewBun
|
|
91
92
|
};
|
|
92
93
|
}
|
|
93
94
|
function bunEncodeUtf8Into(s, dest, offset = 0) {
|
|
@@ -122,6 +123,14 @@ function decodeUtf8RangeBun(bytes, start, end) {
|
|
|
122
123
|
}
|
|
123
124
|
return view.toString("latin1", base + start, base + end);
|
|
124
125
|
}
|
|
126
|
+
function decodeUtf8RangeViewBun(view, start, end) {
|
|
127
|
+
for (let i = start;i < end; i++) {
|
|
128
|
+
if ((view[i] ?? 0) >= 128) {
|
|
129
|
+
return view.toString("utf8", start, end);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return view.toString("latin1", start, end);
|
|
133
|
+
}
|
|
125
134
|
function createNodeCodec() {
|
|
126
135
|
return {
|
|
127
136
|
encodeUtf8(s) {
|
|
@@ -144,6 +153,14 @@ function createNodeCodec() {
|
|
|
144
153
|
}
|
|
145
154
|
}
|
|
146
155
|
return view.toString("latin1", start, end);
|
|
156
|
+
},
|
|
157
|
+
decodeUtf8RangeView(view, start, end) {
|
|
158
|
+
for (let i = start;i < end; i++) {
|
|
159
|
+
if ((view[i] ?? 0) >= 128) {
|
|
160
|
+
return view.toString("utf8", start, end);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return view.toString("latin1", start, end);
|
|
147
164
|
}
|
|
148
165
|
};
|
|
149
166
|
}
|
|
@@ -264,6 +281,362 @@ var init_builtins = __esm(() => {
|
|
|
264
281
|
runtimeBuiltins = createBuiltins(detectedRuntime);
|
|
265
282
|
});
|
|
266
283
|
|
|
284
|
+
// src/native/ffi/symbols.ts
|
|
285
|
+
function buildFFISymbolMap(useBufferLength) {
|
|
286
|
+
const inputAbi = useBufferLength ? ["buffer", "buffer_length"] : ["ptr", "usize"];
|
|
287
|
+
const abi = (shape) => {
|
|
288
|
+
if (!useBufferLength)
|
|
289
|
+
return shape;
|
|
290
|
+
const out = [];
|
|
291
|
+
for (let i = 0;i < shape.length; i++) {
|
|
292
|
+
const t = shape[i];
|
|
293
|
+
if (t === "ptr") {
|
|
294
|
+
out.push("buffer");
|
|
295
|
+
out.push("buffer_length");
|
|
296
|
+
i++;
|
|
297
|
+
} else {
|
|
298
|
+
out.push(t);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
};
|
|
303
|
+
const U64_FAST = "u64_fast";
|
|
304
|
+
return {
|
|
305
|
+
castrum_crc32: { args: inputAbi, returns: "u32" },
|
|
306
|
+
castrum_fnv1a64: { args: inputAbi, returns: "u64" },
|
|
307
|
+
castrum_xxh3: { args: inputAbi, returns: "u64" },
|
|
308
|
+
castrum_json_valid: { args: inputAbi, returns: "u8" },
|
|
309
|
+
castrum_utf8_valid: { args: inputAbi, returns: "u8" },
|
|
310
|
+
castrum_hex_encode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
311
|
+
castrum_hex_decode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
312
|
+
castrum_url_encode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
313
|
+
castrum_url_decode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
314
|
+
castrum_validate_email: { args: ["cstring"], returns: "u8" },
|
|
315
|
+
castrum_validate_uuid: { args: ["cstring"], returns: "u8" },
|
|
316
|
+
castrum_validate_ipv4: { args: ["cstring"], returns: "u8" },
|
|
317
|
+
castrum_validate_ipv6: { args: ["cstring"], returns: "u8" },
|
|
318
|
+
castrum_validate_email_bytes: { args: inputAbi, returns: "u8" },
|
|
319
|
+
castrum_validate_uuid_bytes: { args: inputAbi, returns: "u8" },
|
|
320
|
+
castrum_validate_ipv4_bytes: { args: inputAbi, returns: "u8" },
|
|
321
|
+
castrum_validate_ipv6_bytes: { args: inputAbi, returns: "u8" },
|
|
322
|
+
castrum_json_sum_ids: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
323
|
+
castrum_hmac_sha256_verify: {
|
|
324
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
325
|
+
returns: "u8"
|
|
326
|
+
},
|
|
327
|
+
castrum_csrf_verify: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "u8" },
|
|
328
|
+
castrum_password_verify: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "u8" },
|
|
329
|
+
castrum_password_verify_bcrypt: {
|
|
330
|
+
args: abi(["ptr", "usize", "cstring"]),
|
|
331
|
+
returns: "u8"
|
|
332
|
+
},
|
|
333
|
+
castrum_ws_accept_key: { args: ["cstring"], returns: "cstring" },
|
|
334
|
+
castrum_ws_accept_key_into: {
|
|
335
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
336
|
+
returns: U64_FAST
|
|
337
|
+
},
|
|
338
|
+
castrum_etag: { args: abi(["ptr", "usize", "u8"]), returns: "cstring" },
|
|
339
|
+
castrum_etag_into: { args: abi(["ptr", "usize", "u8", "ptr", "usize"]), returns: U64_FAST },
|
|
340
|
+
castrum_conditional_is_not_modified: {
|
|
341
|
+
args: abi(["usize", "ptr", "usize", "ptr", "usize", "u8"]),
|
|
342
|
+
returns: "u8"
|
|
343
|
+
},
|
|
344
|
+
castrum_media_type_matcher_matches: {
|
|
345
|
+
args: abi(["usize", "ptr", "usize"]),
|
|
346
|
+
returns: "u8"
|
|
347
|
+
},
|
|
348
|
+
castrum_accept_negotiator_negotiate: {
|
|
349
|
+
args: abi(["usize", "ptr", "usize"]),
|
|
350
|
+
returns: "cstring"
|
|
351
|
+
},
|
|
352
|
+
castrum_accept_negotiator_negotiate_server: {
|
|
353
|
+
args: ["usize", "cstring"],
|
|
354
|
+
returns: "cstring"
|
|
355
|
+
},
|
|
356
|
+
castrum_jwt_signer_sign: {
|
|
357
|
+
args: abi(["usize", "ptr", "usize", "i64", "ptr", "usize"]),
|
|
358
|
+
returns: U64_FAST
|
|
359
|
+
},
|
|
360
|
+
castrum_jwt_signer_verify: {
|
|
361
|
+
args: abi(["usize", "ptr", "usize", "i64", "ptr", "usize"]),
|
|
362
|
+
returns: U64_FAST
|
|
363
|
+
},
|
|
364
|
+
castrum_template_render: {
|
|
365
|
+
args: abi(["usize", "ptr", "usize", "ptr", "usize"]),
|
|
366
|
+
returns: U64_FAST
|
|
367
|
+
},
|
|
368
|
+
castrum_schema_validator_validate: {
|
|
369
|
+
args: abi(["usize", "ptr", "usize"]),
|
|
370
|
+
returns: "u8"
|
|
371
|
+
},
|
|
372
|
+
castrum_rate_limiter_check: {
|
|
373
|
+
args: abi(["usize", "cstring", "i64", "ptr", "usize"]),
|
|
374
|
+
returns: U64_FAST
|
|
375
|
+
},
|
|
376
|
+
castrum_rate_limiter_check_key: {
|
|
377
|
+
args: abi(["usize", "i64", "i64", "ptr", "usize"]),
|
|
378
|
+
returns: U64_FAST
|
|
379
|
+
},
|
|
380
|
+
castrum_random_token: { args: ["u32"], returns: "cstring" },
|
|
381
|
+
castrum_random_token_into: { args: ["u32", ...abi(["ptr", "usize"])], returns: U64_FAST },
|
|
382
|
+
castrum_base64_encode: {
|
|
383
|
+
args: abi(["ptr", "usize", "ptr", "usize", "u8", "u8"]),
|
|
384
|
+
returns: U64_FAST
|
|
385
|
+
},
|
|
386
|
+
castrum_base64_decode: {
|
|
387
|
+
args: abi(["ptr", "usize", "ptr", "usize", "u8", "u8"]),
|
|
388
|
+
returns: U64_FAST
|
|
389
|
+
},
|
|
390
|
+
castrum_hmac_sha256: {
|
|
391
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
392
|
+
returns: U64_FAST
|
|
393
|
+
},
|
|
394
|
+
castrum_sign_cookie: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "cstring" },
|
|
395
|
+
castrum_sign_cookie_into: {
|
|
396
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
397
|
+
returns: U64_FAST
|
|
398
|
+
},
|
|
399
|
+
castrum_verify_cookie: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "cstring" },
|
|
400
|
+
castrum_verify_cookie_into: {
|
|
401
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
402
|
+
returns: U64_FAST
|
|
403
|
+
},
|
|
404
|
+
castrum_csrf_token: { args: abi(["ptr", "usize"]), returns: "cstring" },
|
|
405
|
+
castrum_csrf_token_into: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
406
|
+
castrum_password_hash: {
|
|
407
|
+
args: abi(["ptr", "usize", "ptr", "usize", "u32", "u32", "u32", "u32", "ptr", "usize"]),
|
|
408
|
+
returns: U64_FAST
|
|
409
|
+
},
|
|
410
|
+
castrum_password_hash_bcrypt: {
|
|
411
|
+
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
412
|
+
returns: U64_FAST
|
|
413
|
+
},
|
|
414
|
+
castrum_pbkdf2_sha256: {
|
|
415
|
+
args: abi(["ptr", "usize", "ptr", "usize", "u32", "u32", "ptr", "usize"]),
|
|
416
|
+
returns: U64_FAST
|
|
417
|
+
},
|
|
418
|
+
castrum_aead_encrypt: {
|
|
419
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize", "u8", "ptr", "usize"]),
|
|
420
|
+
returns: U64_FAST
|
|
421
|
+
},
|
|
422
|
+
castrum_aead_decrypt: {
|
|
423
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize", "u8", "ptr", "usize"]),
|
|
424
|
+
returns: U64_FAST
|
|
425
|
+
},
|
|
426
|
+
castrum_ws_frame_encode: {
|
|
427
|
+
args: abi(["u8", "ptr", "usize", "u8", "u8", "ptr", "usize"]),
|
|
428
|
+
returns: U64_FAST
|
|
429
|
+
},
|
|
430
|
+
castrum_json_patch: {
|
|
431
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
432
|
+
returns: U64_FAST
|
|
433
|
+
},
|
|
434
|
+
castrum_gzip_compress: {
|
|
435
|
+
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
436
|
+
returns: U64_FAST
|
|
437
|
+
},
|
|
438
|
+
castrum_gzip_decompress: {
|
|
439
|
+
args: abi(["ptr", "usize", "usize", "ptr", "usize"]),
|
|
440
|
+
returns: U64_FAST
|
|
441
|
+
},
|
|
442
|
+
castrum_brotli_compress: {
|
|
443
|
+
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
444
|
+
returns: U64_FAST
|
|
445
|
+
},
|
|
446
|
+
castrum_brotli_decompress: {
|
|
447
|
+
args: abi(["ptr", "usize", "usize", "ptr", "usize"]),
|
|
448
|
+
returns: U64_FAST
|
|
449
|
+
},
|
|
450
|
+
castrum_gzip_isize: { args: abi(["ptr", "usize"]), returns: "u32" },
|
|
451
|
+
castrum_http_parse_request_packed: {
|
|
452
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
453
|
+
returns: U64_FAST
|
|
454
|
+
},
|
|
455
|
+
castrum_query_parse_packed: {
|
|
456
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
457
|
+
returns: U64_FAST
|
|
458
|
+
},
|
|
459
|
+
castrum_cookie_parse_packed: {
|
|
460
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
461
|
+
returns: U64_FAST
|
|
462
|
+
},
|
|
463
|
+
castrum_http_date_into: { args: ["f64", ...abi(["ptr", "usize"])], returns: U64_FAST },
|
|
464
|
+
castrum_sse_encode_into: {
|
|
465
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize", "u8", "u32", "ptr", "usize"]),
|
|
466
|
+
returns: U64_FAST
|
|
467
|
+
},
|
|
468
|
+
castrum_jwt_sign_bytes: {
|
|
469
|
+
args: abi(["ptr", "usize", "ptr", "usize", "i64", "i64"]),
|
|
470
|
+
returns: "cstring"
|
|
471
|
+
},
|
|
472
|
+
castrum_jwt_sign_bytes_into: {
|
|
473
|
+
args: abi(["ptr", "usize", "ptr", "usize", "i64", "i64", "ptr", "usize"]),
|
|
474
|
+
returns: U64_FAST
|
|
475
|
+
},
|
|
476
|
+
castrum_ed25519_generate_keypair: {
|
|
477
|
+
args: abi(["ptr", "usize"]),
|
|
478
|
+
returns: U64_FAST
|
|
479
|
+
},
|
|
480
|
+
castrum_ed25519_sign: {
|
|
481
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
482
|
+
returns: U64_FAST
|
|
483
|
+
},
|
|
484
|
+
castrum_ed25519_verify: {
|
|
485
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
486
|
+
returns: "u32"
|
|
487
|
+
},
|
|
488
|
+
castrum_jwt_eddsa_sign: {
|
|
489
|
+
args: abi(["ptr", "usize", "ptr", "usize", "i64", "i64"]),
|
|
490
|
+
returns: "cstring"
|
|
491
|
+
},
|
|
492
|
+
castrum_jwt_eddsa_verify: {
|
|
493
|
+
args: abi(["ptr", "usize", "ptr", "usize", "i64"]),
|
|
494
|
+
returns: "cstring"
|
|
495
|
+
},
|
|
496
|
+
castrum_ws_frame_decode_packed: {
|
|
497
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
498
|
+
returns: U64_FAST
|
|
499
|
+
},
|
|
500
|
+
castrum_multipart_parse_packed: {
|
|
501
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
502
|
+
returns: U64_FAST
|
|
503
|
+
},
|
|
504
|
+
castrum_form_parse_packed: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
505
|
+
castrum_json_parse_packed: {
|
|
506
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
507
|
+
returns: U64_FAST
|
|
508
|
+
},
|
|
509
|
+
castrum_parse_media_type: {
|
|
510
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
511
|
+
returns: U64_FAST
|
|
512
|
+
},
|
|
513
|
+
castrum_parse_http_date: {
|
|
514
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
515
|
+
returns: U64_FAST
|
|
516
|
+
},
|
|
517
|
+
castrum_parse_accept_encoding: {
|
|
518
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
519
|
+
returns: U64_FAST
|
|
520
|
+
},
|
|
521
|
+
castrum_url_encode_query: { args: abi(["ptr", "usize"]), returns: "cstring" },
|
|
522
|
+
castrum_url_resolve: {
|
|
523
|
+
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
524
|
+
returns: "cstring"
|
|
525
|
+
},
|
|
526
|
+
castrum_url_builder_resolve: {
|
|
527
|
+
args: abi(["usize", "ptr", "usize", "ptr", "usize"]),
|
|
528
|
+
returns: U64_FAST
|
|
529
|
+
},
|
|
530
|
+
castrum_mime_from_extension: { args: ["cstring"], returns: "cstring" },
|
|
531
|
+
castrum_jwt_verify: {
|
|
532
|
+
args: abi(["ptr", "usize", "ptr", "usize", "i64"]),
|
|
533
|
+
returns: "cstring"
|
|
534
|
+
},
|
|
535
|
+
castrum_ingress_handle_packed: {
|
|
536
|
+
args: abi(["usize", "ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
537
|
+
returns: U64_FAST
|
|
538
|
+
},
|
|
539
|
+
castrum_ingress_handle_components: {
|
|
540
|
+
args: abi([
|
|
541
|
+
"usize",
|
|
542
|
+
"u8",
|
|
543
|
+
"cstring",
|
|
544
|
+
"cstring",
|
|
545
|
+
"ptr",
|
|
546
|
+
"usize",
|
|
547
|
+
"ptr",
|
|
548
|
+
"usize",
|
|
549
|
+
"ptr",
|
|
550
|
+
"usize",
|
|
551
|
+
"ptr",
|
|
552
|
+
"usize"
|
|
553
|
+
]),
|
|
554
|
+
returns: U64_FAST
|
|
555
|
+
},
|
|
556
|
+
castrum_ingress_layout: { args: abi(["ptr", "usize"]), returns: U64_FAST },
|
|
557
|
+
castrum_route_compile: { args: abi(["ptr", "usize"]), returns: U64_FAST },
|
|
558
|
+
castrum_route_run: {
|
|
559
|
+
args: abi(["usize", "ptr", "usize", "ptr", "usize"]),
|
|
560
|
+
returns: U64_FAST
|
|
561
|
+
},
|
|
562
|
+
castrum_route_destroy: { args: ["usize"], returns: "void" },
|
|
563
|
+
castrum_hex_validate_batch: {
|
|
564
|
+
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
565
|
+
returns: U64_FAST
|
|
566
|
+
},
|
|
567
|
+
castrum_regex_escape: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
568
|
+
castrum_metrics_create: { args: [], returns: U64_FAST },
|
|
569
|
+
castrum_metrics_counter: { args: ["usize", "cstring", "cstring"], returns: "u32" },
|
|
570
|
+
castrum_metrics_gauge: { args: ["usize", "cstring", "cstring"], returns: "u32" },
|
|
571
|
+
castrum_metrics_histogram: {
|
|
572
|
+
args: ["usize", "cstring", "cstring", "cstring"],
|
|
573
|
+
returns: "u32"
|
|
574
|
+
},
|
|
575
|
+
castrum_metrics_record: {
|
|
576
|
+
args: abi(["usize", "u32", "ptr", "usize", "f64"]),
|
|
577
|
+
returns: "u8"
|
|
578
|
+
},
|
|
579
|
+
castrum_metrics_gauge_set: {
|
|
580
|
+
args: abi(["usize", "u32", "ptr", "usize", "f64"]),
|
|
581
|
+
returns: "u8"
|
|
582
|
+
},
|
|
583
|
+
castrum_metrics_record_str: {
|
|
584
|
+
args: ["usize", "u32", "cstring", "f64"],
|
|
585
|
+
returns: "u8"
|
|
586
|
+
},
|
|
587
|
+
castrum_metrics_gauge_set_str: {
|
|
588
|
+
args: ["usize", "u32", "cstring", "f64"],
|
|
589
|
+
returns: "u8"
|
|
590
|
+
},
|
|
591
|
+
castrum_regex_escape_str: { args: ["cstring"], returns: "cstring" },
|
|
592
|
+
castrum_hex_validate_batch_str: {
|
|
593
|
+
args: abi(["cstring", "u32", "ptr", "usize"]),
|
|
594
|
+
returns: U64_FAST
|
|
595
|
+
},
|
|
596
|
+
castrum_metrics_render: { args: abi(["usize", "ptr", "usize"]), returns: U64_FAST },
|
|
597
|
+
castrum_metrics_snapshot: { args: abi(["usize", "ptr", "usize"]), returns: U64_FAST },
|
|
598
|
+
castrum_metrics_record_batch: {
|
|
599
|
+
args: abi(["usize", "ptr", "usize"]),
|
|
600
|
+
returns: "u8"
|
|
601
|
+
},
|
|
602
|
+
castrum_query_validate: { args: ["usize", "cstring"], returns: "u8" },
|
|
603
|
+
castrum_cookie_validate: { args: ["usize", "cstring"], returns: "u8" },
|
|
604
|
+
castrum_session_seal: {
|
|
605
|
+
args: ["cstring", "cstring", "i64", "cstring"],
|
|
606
|
+
returns: "cstring"
|
|
607
|
+
},
|
|
608
|
+
castrum_session_open: {
|
|
609
|
+
args: abi(["cstring", "cstring", "ptr", "usize"]),
|
|
610
|
+
returns: U64_FAST
|
|
611
|
+
},
|
|
612
|
+
castrum_session_seal_bytes: {
|
|
613
|
+
args: abi(["ptr", "usize", "ptr", "usize", "i64", "ptr", "usize"]),
|
|
614
|
+
returns: "cstring"
|
|
615
|
+
},
|
|
616
|
+
castrum_session_open_bytes: {
|
|
617
|
+
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
618
|
+
returns: U64_FAST
|
|
619
|
+
},
|
|
620
|
+
castrum_task_init: { args: ["u32"], returns: "u32" },
|
|
621
|
+
castrum_task_submit: { args: abi(["u32", "ptr", "usize", "usize"]), returns: "u32" },
|
|
622
|
+
castrum_task_submit_slice: {
|
|
623
|
+
args: abi(["u32", "ptr", "usize", "ptr", "usize", "usize"]),
|
|
624
|
+
returns: "u32"
|
|
625
|
+
},
|
|
626
|
+
castrum_task_submit_out: {
|
|
627
|
+
args: abi(["u32", "ptr", "usize", "usize", "ptr", "usize"]),
|
|
628
|
+
returns: "u32"
|
|
629
|
+
},
|
|
630
|
+
castrum_task_drain: { args: abi(["ptr", "usize"]), returns: U64_FAST },
|
|
631
|
+
castrum_task_pending: { args: [], returns: "u32" },
|
|
632
|
+
castrum_task_cancel: { args: ["usize"], returns: "u32" },
|
|
633
|
+
castrum_task_set_doorbell: { args: ["ptr"], returns: "u32" },
|
|
634
|
+
castrum_task_shutdown: { args: [], returns: "u32" },
|
|
635
|
+
castrum_task_threads: { args: [], returns: "u32" },
|
|
636
|
+
castrum_metrics_destroy: { args: ["usize"], returns: "void" }
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
267
640
|
// src/shared/runtime.ts
|
|
268
641
|
var isBun;
|
|
269
642
|
var init_runtime = __esm(() => {
|
|
@@ -272,29 +645,17 @@ var init_runtime = __esm(() => {
|
|
|
272
645
|
});
|
|
273
646
|
|
|
274
647
|
// src/shared/codec.ts
|
|
275
|
-
var encodeUtf8, encodeUtf8Into, decodeUtf8, decodeUtf8Range, decodeUtf8Fatal;
|
|
648
|
+
var encodeUtf8, encodeUtf8Into, decodeUtf8, decodeUtf8Range, decodeUtf8RangeView, decodeUtf8Fatal;
|
|
276
649
|
var init_codec2 = __esm(() => {
|
|
277
650
|
init_codec();
|
|
278
651
|
encodeUtf8 = runtimeCodec.encodeUtf8;
|
|
279
652
|
encodeUtf8Into = runtimeCodec.encodeUtf8Into;
|
|
280
653
|
decodeUtf8 = runtimeCodec.decodeUtf8;
|
|
281
654
|
decodeUtf8Range = runtimeCodec.decodeUtf8Range;
|
|
655
|
+
decodeUtf8RangeView = runtimeCodec.decodeUtf8RangeView ? runtimeCodec.decodeUtf8RangeView : (view, start, end) => decodeUtf8Range(view, start, end);
|
|
282
656
|
decodeUtf8Fatal = runtimeCodec.decodeUtf8Fatal;
|
|
283
657
|
});
|
|
284
658
|
|
|
285
|
-
// src/native/ffi/constants.ts
|
|
286
|
-
var MAX_DECOMPRESSED, MAX_JSON_PATCH_OUTPUT, COMPRESS_INITIAL_CAP, COMPRESS_MAX_CAP, COMPRESS_HEADROOM = 64, DECOMPRESS_GUESS_MULTIPLIER_GZIP = 16, DECOMPRESS_GUESS_MULTIPLIER_BROTLI = 32, DECOMPRESS_FALLBACK_CAP, DECOMPRESS_MIN_INITIAL = 1024, SELFTEST_HEX, SELFTEST_JSON, EMPTY_VIEW;
|
|
287
|
-
var init_constants = __esm(() => {
|
|
288
|
-
MAX_DECOMPRESSED = 64 * 1024 * 1024;
|
|
289
|
-
MAX_JSON_PATCH_OUTPUT = 128 * 1024 * 1024;
|
|
290
|
-
COMPRESS_INITIAL_CAP = 16 * 1024;
|
|
291
|
-
COMPRESS_MAX_CAP = 1024 * 1024;
|
|
292
|
-
DECOMPRESS_FALLBACK_CAP = 4 * 1024 * 1024;
|
|
293
|
-
SELFTEST_HEX = new Uint8Array([104, 101, 108, 108, 111]);
|
|
294
|
-
SELFTEST_JSON = new Uint8Array([123, 34, 97, 34, 58, 49, 125]);
|
|
295
|
-
EMPTY_VIEW = new Uint8Array(0);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
659
|
// src/shared/bytes.ts
|
|
299
660
|
function toBytes(v) {
|
|
300
661
|
return typeof v === "string" ? encodeUtf8(v) : v;
|
|
@@ -696,365 +1057,160 @@ function buildCodecs(sym, ctx) {
|
|
|
696
1057
|
}
|
|
697
1058
|
};
|
|
698
1059
|
}
|
|
699
|
-
|
|
1060
|
+
var init_codecs = __esm(() => {
|
|
1061
|
+
init_codec2();
|
|
1062
|
+
init_util();
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
// src/native/ffi/constants.ts
|
|
1066
|
+
var MAX_DECOMPRESSED, MAX_JSON_PATCH_OUTPUT, COMPRESS_INITIAL_CAP, COMPRESS_MAX_CAP, COMPRESS_HEADROOM = 64, DECOMPRESS_GUESS_MULTIPLIER_GZIP = 16, DECOMPRESS_GUESS_MULTIPLIER_BROTLI = 32, DECOMPRESS_FALLBACK_CAP, DECOMPRESS_MIN_INITIAL = 1024, SELFTEST_HEX, SELFTEST_JSON, EMPTY_VIEW;
|
|
1067
|
+
var init_constants = __esm(() => {
|
|
1068
|
+
MAX_DECOMPRESSED = 64 * 1024 * 1024;
|
|
1069
|
+
MAX_JSON_PATCH_OUTPUT = 128 * 1024 * 1024;
|
|
1070
|
+
COMPRESS_INITIAL_CAP = 16 * 1024;
|
|
1071
|
+
COMPRESS_MAX_CAP = 1024 * 1024;
|
|
1072
|
+
DECOMPRESS_FALLBACK_CAP = 4 * 1024 * 1024;
|
|
1073
|
+
SELFTEST_HEX = new Uint8Array([104, 101, 108, 108, 111]);
|
|
1074
|
+
SELFTEST_JSON = new Uint8Array([123, 34, 97, 34, 58, 49, 125]);
|
|
1075
|
+
EMPTY_VIEW = new Uint8Array(0);
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
// src/native/ffi/build/compress.ts
|
|
1079
|
+
function buildCompress(sym, ctx) {
|
|
1080
|
+
const { lenOrView, scratchFor } = ctx;
|
|
1081
|
+
const gzipCompress = sym.castrum_gzip_compress;
|
|
1082
|
+
const gzipDecompress = sym.castrum_gzip_decompress;
|
|
1083
|
+
const brotliCompress = sym.castrum_brotli_compress;
|
|
1084
|
+
const brotliDecompress = sym.castrum_brotli_decompress;
|
|
1085
|
+
const gzipIsize = sym.castrum_gzip_isize;
|
|
1086
|
+
const jsonPatch = sym.castrum_json_patch;
|
|
1087
|
+
return {
|
|
1088
|
+
jsonPatch(doc, patch) {
|
|
1089
|
+
let out = scratchFor(Math.min(Math.max(doc.length, patch.length) + 16, 64 * 1024));
|
|
1090
|
+
let w = Number(jsonPatch(doc, lenOrView(doc), patch, lenOrView(patch), out, lenOrView(out)));
|
|
1091
|
+
if (w > out.length && w <= MAX_JSON_PATCH_OUTPUT) {
|
|
1092
|
+
out = new Uint8Array(w);
|
|
1093
|
+
w = Number(jsonPatch(doc, lenOrView(doc), patch, lenOrView(patch), out, lenOrView(out)));
|
|
1094
|
+
}
|
|
1095
|
+
if (w === 0 || w > out.length) {
|
|
1096
|
+
throw new Error("json patch: output buffer too small or patch inapplicable");
|
|
1097
|
+
}
|
|
1098
|
+
return decodeUtf8(out.subarray(0, w));
|
|
1099
|
+
},
|
|
1100
|
+
gzipCompress(data, level = 6) {
|
|
1101
|
+
return growExact((out) => Number(gzipCompress(data, lenOrView(data), Math.min(level, 9), out, lenOrView(out))), Math.min(data.length + COMPRESS_HEADROOM, COMPRESS_INITIAL_CAP), Math.max(data.length * 2 + COMPRESS_HEADROOM, COMPRESS_MAX_CAP), "gzip compress: output buffer too small");
|
|
1102
|
+
},
|
|
1103
|
+
gzipCompressInto(data, output, level = 6) {
|
|
1104
|
+
const w = Number(gzipCompress(data, lenOrView(data), Math.min(level, 9), output, lenOrView(output)));
|
|
1105
|
+
if (w === 0) {
|
|
1106
|
+
throw new Error("gzip compress: invalid input");
|
|
1107
|
+
}
|
|
1108
|
+
if (w > output.length) {
|
|
1109
|
+
throw new Error("gzip compress: output buffer too small");
|
|
1110
|
+
}
|
|
1111
|
+
return w;
|
|
1112
|
+
},
|
|
1113
|
+
gzipDecompress(data, maxDecompressed) {
|
|
1114
|
+
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
1115
|
+
const isize = Number(gzipIsize(data, lenOrView(data)));
|
|
1116
|
+
const initial = isize !== 0 ? Math.min(isize, max) : Math.min(data.length * DECOMPRESS_GUESS_MULTIPLIER_GZIP, DECOMPRESS_FALLBACK_CAP);
|
|
1117
|
+
return growExact((out) => Number(gzipDecompress(data, lenOrView(data), max, out, lenOrView(out))), initial, max, "gzip decompress: invalid stream or exceeded max decompressed size");
|
|
1118
|
+
},
|
|
1119
|
+
gzipDecompressInto(data, output, maxDecompressed) {
|
|
1120
|
+
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
1121
|
+
const w = Number(gzipDecompress(data, lenOrView(data), max, output, lenOrView(output)));
|
|
1122
|
+
if (w === 0) {
|
|
1123
|
+
throw new Error("gzip decompress: invalid stream or exceeded max decompressed size");
|
|
1124
|
+
}
|
|
1125
|
+
if (w > output.length) {
|
|
1126
|
+
throw new Error("gzip decompress: output buffer too small");
|
|
1127
|
+
}
|
|
1128
|
+
return w;
|
|
1129
|
+
},
|
|
1130
|
+
gzipIsize(data) {
|
|
1131
|
+
return Number(gzipIsize(data, lenOrView(data)));
|
|
1132
|
+
},
|
|
1133
|
+
brotliCompress(data, quality = 5) {
|
|
1134
|
+
return growExact((out) => Number(brotliCompress(data, lenOrView(data), Math.min(quality, 11), out, lenOrView(out))), Math.min(data.length + COMPRESS_HEADROOM, COMPRESS_INITIAL_CAP), Math.max(data.length * 2 + COMPRESS_HEADROOM, COMPRESS_MAX_CAP), "brotli compress: output buffer too small");
|
|
1135
|
+
},
|
|
1136
|
+
brotliCompressInto(data, output, quality = 5) {
|
|
1137
|
+
const w = Number(brotliCompress(data, lenOrView(data), Math.min(quality, 11), output, lenOrView(output)));
|
|
1138
|
+
if (w === 0) {
|
|
1139
|
+
throw new Error("brotli compress: invalid input");
|
|
1140
|
+
}
|
|
1141
|
+
if (w > output.length) {
|
|
1142
|
+
throw new Error("brotli compress: output buffer too small");
|
|
1143
|
+
}
|
|
1144
|
+
return w;
|
|
1145
|
+
},
|
|
1146
|
+
brotliDecompress(data, maxDecompressed) {
|
|
1147
|
+
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
1148
|
+
return growExact((out) => Number(brotliDecompress(data, lenOrView(data), max, out, lenOrView(out))), Math.min(Math.max(data.length * DECOMPRESS_GUESS_MULTIPLIER_BROTLI, DECOMPRESS_MIN_INITIAL), DECOMPRESS_FALLBACK_CAP), max, "brotli decompress: invalid stream or exceeded max decompressed size");
|
|
1149
|
+
},
|
|
1150
|
+
brotliDecompressInto(data, output, maxDecompressed) {
|
|
1151
|
+
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
1152
|
+
const w = Number(brotliDecompress(data, lenOrView(data), max, output, lenOrView(output)));
|
|
1153
|
+
if (w === 0) {
|
|
1154
|
+
throw new Error("brotli decompress: invalid stream or exceeded max decompressed size");
|
|
1155
|
+
}
|
|
1156
|
+
if (w > output.length) {
|
|
1157
|
+
throw new Error("brotli decompress: output buffer too small");
|
|
1158
|
+
}
|
|
1159
|
+
return w;
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
function selfTestCompress(b) {
|
|
700
1164
|
const enc = { encode: encodeUtf8 };
|
|
701
1165
|
const dec = { decode: decodeUtf8 };
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
if (b.fnv1a64(enc.encode("foobar")) !== 0x85944171f73967e8n)
|
|
705
|
-
return false;
|
|
706
|
-
if (b.xxh3(new Uint8Array(0)) !== 0x2d06800538d394c2n)
|
|
707
|
-
return false;
|
|
708
|
-
if (b.jsonValid(SELFTEST_JSON) !== true || b.jsonValid(enc.encode("{not json")) !== false) {
|
|
709
|
-
return false;
|
|
710
|
-
}
|
|
711
|
-
if (!b.utf8Valid(enc.encode("héllo")) || b.utf8Valid(new Uint8Array([255, 254]))) {
|
|
712
|
-
return false;
|
|
713
|
-
}
|
|
714
|
-
const hexOut = new Uint8Array(SELFTEST_HEX.length * 2);
|
|
715
|
-
if (b.hexEncodeInto(SELFTEST_HEX, hexOut) !== 10 || dec.decode(hexOut) !== "68656c6c6f") {
|
|
716
|
-
return false;
|
|
717
|
-
}
|
|
718
|
-
const urlInput = enc.encode("a b/c");
|
|
719
|
-
const urlOut = new Uint8Array(9);
|
|
720
|
-
if (b.urlEncodeInto(urlInput, urlOut) !== 9 || dec.decode(urlOut) !== "a%20b%2Fc") {
|
|
1166
|
+
const patched = b.jsonPatch(enc.encode(`{"a":"b"}`), enc.encode(`[{"op":"add","path":"/c","value":"d"}]`));
|
|
1167
|
+
if (!patched.includes(`"c":"d"`)) {
|
|
721
1168
|
return false;
|
|
722
1169
|
}
|
|
723
|
-
|
|
1170
|
+
const gz = b.gzipCompress(SELFTEST_HEX);
|
|
1171
|
+
if (dec.decode(b.gzipDecompress(gz)) !== "hello") {
|
|
724
1172
|
return false;
|
|
725
1173
|
}
|
|
726
|
-
|
|
1174
|
+
const br = b.brotliCompress(SELFTEST_HEX);
|
|
1175
|
+
if (dec.decode(b.brotliDecompress(br)) !== "hello") {
|
|
727
1176
|
return false;
|
|
728
1177
|
}
|
|
729
|
-
|
|
730
|
-
return false;
|
|
731
|
-
if (b.jsonSumIds(enc.encode(`[{"id":0},{"id":0}]`)) !== 0n)
|
|
732
|
-
return false;
|
|
733
|
-
let sumInvalidThrew = false;
|
|
1178
|
+
let decompressThrew = false;
|
|
734
1179
|
try {
|
|
735
|
-
b.
|
|
1180
|
+
b.gzipDecompress(enc.encode("not-a-gzip-stream"));
|
|
736
1181
|
} catch {
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
if (!sumInvalidThrew)
|
|
740
|
-
return false;
|
|
741
|
-
{
|
|
742
|
-
const ids = enc.encode(`507f1f77bcf86cd799439011
|
|
743
|
-
zz
|
|
744
|
-
507F1F77BCF86CD799439012`);
|
|
745
|
-
const tiny = new Uint8Array(2);
|
|
746
|
-
const needed = b.hexValidateBatchInto(ids, 24, tiny);
|
|
747
|
-
if (needed !== 3)
|
|
748
|
-
return false;
|
|
749
|
-
const out = new Uint8Array(needed);
|
|
750
|
-
if (b.hexValidateBatchInto(ids, 24, out) !== 3)
|
|
751
|
-
return false;
|
|
752
|
-
if (out[0] !== 1 || out[1] !== 0 || out[2] !== 1)
|
|
753
|
-
return false;
|
|
754
|
-
let widthThrew = false;
|
|
755
|
-
try {
|
|
756
|
-
b.hexValidateBatchInto(ids, 0, new Uint8Array(3));
|
|
757
|
-
} catch {
|
|
758
|
-
widthThrew = true;
|
|
759
|
-
}
|
|
760
|
-
if (!widthThrew)
|
|
761
|
-
return false;
|
|
762
|
-
}
|
|
763
|
-
{
|
|
764
|
-
const out = new Uint8Array(64);
|
|
765
|
-
const w = b.regexEscapeInto(enc.encode("a.c(x)"), out);
|
|
766
|
-
if (w !== 9 || dec.decode(out.subarray(0, w)) !== "a\\.c\\(x\\)")
|
|
767
|
-
return false;
|
|
768
|
-
const plain = new Uint8Array(16);
|
|
769
|
-
const wp = b.regexEscapeInto(enc.encode("hello"), plain);
|
|
770
|
-
if (wp !== 5 || dec.decode(plain.subarray(0, wp)) !== "hello")
|
|
771
|
-
return false;
|
|
772
|
-
if (b.regexEscapeInto(enc.encode("a.b"), new Uint8Array(2)) !== 4)
|
|
773
|
-
return false;
|
|
774
|
-
if (b.regexEscapeStr("a.c(x)") !== "a\\.c\\(x\\)")
|
|
775
|
-
return false;
|
|
776
|
-
if (b.regexEscapeStr("plain text") !== "plain text")
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
{
|
|
780
|
-
const out = new Uint8Array(8);
|
|
781
|
-
const w = b.hexValidateBatchStr(`507f1f77bcf86cd799439011
|
|
782
|
-
zz`, 24, out);
|
|
783
|
-
if (w !== 2 || out[0] !== 1 || out[1] !== 0)
|
|
784
|
-
return false;
|
|
1182
|
+
decompressThrew = true;
|
|
785
1183
|
}
|
|
786
|
-
|
|
787
|
-
const hmacData = enc.encode("Hi There");
|
|
788
|
-
const hmacSig = enc.encode("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");
|
|
789
|
-
if (b.hmacSha256(hmacKey, hmacData) !== "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7") {
|
|
1184
|
+
if (!decompressThrew) {
|
|
790
1185
|
return false;
|
|
791
1186
|
}
|
|
792
|
-
|
|
793
|
-
|
|
1187
|
+
decompressThrew = false;
|
|
1188
|
+
try {
|
|
1189
|
+
b.brotliDecompress(enc.encode("not-brotli-stream"));
|
|
1190
|
+
} catch {
|
|
1191
|
+
decompressThrew = true;
|
|
794
1192
|
}
|
|
795
|
-
|
|
796
|
-
if (dec.decode(decoded) !== "hello" || dec.decode(b.urlDecode(enc.encode("a%20b%2Fc"))) !== "a b/c") {
|
|
1193
|
+
if (!decompressThrew) {
|
|
797
1194
|
return false;
|
|
798
1195
|
}
|
|
799
|
-
|
|
1196
|
+
const gzInto = new Uint8Array(64);
|
|
1197
|
+
const gzW = b.gzipCompressInto(SELFTEST_HEX, gzInto);
|
|
1198
|
+
if (gzW === 0 || dec.decode(b.gzipDecompress(gzInto.subarray(0, gzW))) !== "hello") {
|
|
800
1199
|
return false;
|
|
801
1200
|
}
|
|
802
|
-
|
|
1201
|
+
const brInto = new Uint8Array(64);
|
|
1202
|
+
const brW = b.brotliCompressInto(SELFTEST_HEX, brInto);
|
|
1203
|
+
if (brW === 0 || dec.decode(b.brotliDecompress(brInto.subarray(0, brW))) !== "hello") {
|
|
803
1204
|
return false;
|
|
804
1205
|
}
|
|
805
|
-
|
|
1206
|
+
const gzDst = new Uint8Array(16);
|
|
1207
|
+
const gzDW = b.gzipDecompressInto(gzInto.subarray(0, gzW), gzDst);
|
|
1208
|
+
if (gzDW === 0 || dec.decode(gzDst.subarray(0, gzDW)) !== "hello") {
|
|
806
1209
|
return false;
|
|
807
1210
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
const secret = enc.encode("s3cr3t-secret");
|
|
812
|
-
const signed = b.signCookie(SELFTEST_HEX, secret);
|
|
813
|
-
const verified = b.verifyCookie(enc.encode(signed), secret);
|
|
814
|
-
if (verified === null || verified !== "hello") {
|
|
815
|
-
return false;
|
|
816
|
-
}
|
|
817
|
-
if (b.verifyCookie(enc.encode("tampered.0000"), secret) !== null) {
|
|
818
|
-
return false;
|
|
819
|
-
}
|
|
820
|
-
const csrfTokenStr = b.csrfToken(secret);
|
|
821
|
-
if (csrfTokenStr.length !== 129 || !b.csrfVerify(enc.encode(csrfTokenStr), secret)) {
|
|
822
|
-
return false;
|
|
823
|
-
}
|
|
824
|
-
const pw = enc.encode("correct horse battery staple");
|
|
825
|
-
const salt = enc.encode("salty-salt-16b");
|
|
826
|
-
const phc = b.passwordHash(pw, salt, 8, 1, 1, 16);
|
|
827
|
-
if (phc.length === 0 || !b.passwordVerify(pw, enc.encode(phc))) {
|
|
828
|
-
return false;
|
|
829
|
-
}
|
|
830
|
-
const bcryptPhc = b.passwordHashBcrypt(pw, 4);
|
|
831
|
-
if (bcryptPhc.length === 0 || !b.passwordVerifyBcrypt(pw, bcryptPhc)) {
|
|
832
|
-
return false;
|
|
833
|
-
}
|
|
834
|
-
const dk = b.pbkdf2Sha256(enc.encode("password"), enc.encode("salt"), 1, 32);
|
|
835
|
-
if (b.hexEncode(dk) !== "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b") {
|
|
836
|
-
return false;
|
|
837
|
-
}
|
|
838
|
-
const aeadKey = new Uint8Array(32).fill(66);
|
|
839
|
-
const nonce = new Uint8Array(12).fill(7);
|
|
840
|
-
const ct = b.aeadEncrypt(aeadKey, nonce, SELFTEST_HEX, 0);
|
|
841
|
-
const pt = b.aeadDecrypt(aeadKey, nonce, ct, 0);
|
|
842
|
-
if (pt === null || dec.decode(pt) !== "hello") {
|
|
843
|
-
return false;
|
|
844
|
-
}
|
|
845
|
-
const rtOut = new Uint8Array(32);
|
|
846
|
-
const rtW = b.randomTokenInto(16, rtOut);
|
|
847
|
-
if (rtW !== 32 || !/^[0-9a-f]{32}$/.test(dec.decode(rtOut))) {
|
|
848
|
-
return false;
|
|
849
|
-
}
|
|
850
|
-
try {
|
|
851
|
-
b.randomTokenInto(16, new Uint8Array(8));
|
|
852
|
-
return false;
|
|
853
|
-
} catch {}
|
|
854
|
-
const waOut = new Uint8Array(28);
|
|
855
|
-
const waW = b.wsAcceptKeyInto(enc.encode("dGhlIHNhbXBsZSBub25jZQ=="), waOut);
|
|
856
|
-
const waExpected = b.wsAcceptKey("dGhlIHNhbXBsZSBub25jZQ==");
|
|
857
|
-
if (waW !== 28 || dec.decode(waOut) !== waExpected) {
|
|
858
|
-
return false;
|
|
859
|
-
}
|
|
860
|
-
const etagData = enc.encode("hello");
|
|
861
|
-
const etagOut = new Uint8Array(16);
|
|
862
|
-
const etagW = b.etagInto(etagData, etagOut);
|
|
863
|
-
if (etagW !== 10 || dec.decode(etagOut.subarray(0, etagW)) !== b.etag(etagData)) {
|
|
864
|
-
return false;
|
|
865
|
-
}
|
|
866
|
-
const csrfOut = new Uint8Array(129);
|
|
867
|
-
const csrfW = b.csrfTokenInto(enc.encode("csrf-secret"), csrfOut);
|
|
868
|
-
if (csrfW !== 129 || csrfOut[64] !== 46) {
|
|
869
|
-
return false;
|
|
870
|
-
}
|
|
871
|
-
const ckVal = enc.encode("session-value");
|
|
872
|
-
const ckSec = enc.encode("s3cr3t-secret");
|
|
873
|
-
const ckOut = new Uint8Array(256);
|
|
874
|
-
const ckW = b.signCookieInto(ckVal, ckSec, ckOut);
|
|
875
|
-
if (ckW !== ckVal.length + 65) {
|
|
876
|
-
return false;
|
|
877
|
-
}
|
|
878
|
-
const ckVerifyOut = new Uint8Array(256);
|
|
879
|
-
const ckV = b.verifyCookieInto(ckOut.subarray(0, ckW), ckSec, ckVerifyOut);
|
|
880
|
-
if (ckV !== ckVal.length || dec.decode(ckVerifyOut.subarray(0, ckV)) !== "session-value") {
|
|
881
|
-
return false;
|
|
882
|
-
}
|
|
883
|
-
if (b.urlEncode(enc.encode("a b/c")) !== "a%20b%2Fc") {
|
|
884
|
-
return false;
|
|
885
|
-
}
|
|
886
|
-
const hdInto = new Uint8Array(8);
|
|
887
|
-
if (b.hexDecodeInto(enc.encode("68656c6c6f"), hdInto) !== 5 || dec.decode(hdInto.subarray(0, 5)) !== "hello") {
|
|
888
|
-
return false;
|
|
889
|
-
}
|
|
890
|
-
const udInto = new Uint8Array(16);
|
|
891
|
-
if (b.urlDecodeInto(enc.encode("a%20b%2Fc"), udInto) !== 5 || dec.decode(udInto.subarray(0, 5)) !== "a b/c") {
|
|
892
|
-
return false;
|
|
893
|
-
}
|
|
894
|
-
const b64Into = new Uint8Array(16);
|
|
895
|
-
if (b.base64EncodeInto(SELFTEST_HEX, b64Into) !== 8 || dec.decode(b64Into.subarray(0, 8)) !== "aGVsbG8=") {
|
|
896
|
-
return false;
|
|
897
|
-
}
|
|
898
|
-
const bdInto = new Uint8Array(8);
|
|
899
|
-
if (b.base64DecodeInto(enc.encode("aGVsbG8="), bdInto) !== 5 || dec.decode(bdInto.subarray(0, 5)) !== "hello") {
|
|
900
|
-
return false;
|
|
901
|
-
}
|
|
902
|
-
if (!/^[0-9a-f]{32}$/.test(b.randomToken(16))) {
|
|
903
|
-
return false;
|
|
904
|
-
}
|
|
905
|
-
const hmacInto = new Uint8Array(64);
|
|
906
|
-
if (b.hmacSha256Into(hmacKey, hmacData, hmacInto) !== 64 || dec.decode(hmacInto) !== "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7") {
|
|
907
|
-
return false;
|
|
908
|
-
}
|
|
909
|
-
const ctInto = new Uint8Array(64);
|
|
910
|
-
const ctW = b.aeadEncryptInto(aeadKey, nonce, SELFTEST_HEX, ctInto, 0);
|
|
911
|
-
if (ctW === 0 || b.aeadDecrypt(aeadKey, nonce, ctInto.subarray(0, ctW), 0) === null) {
|
|
912
|
-
return false;
|
|
913
|
-
}
|
|
914
|
-
return true;
|
|
915
|
-
}
|
|
916
|
-
var init_codecs = __esm(() => {
|
|
917
|
-
init_codec2();
|
|
918
|
-
init_constants();
|
|
919
|
-
init_util();
|
|
920
|
-
});
|
|
921
|
-
|
|
922
|
-
// src/native/ffi/build/compress.ts
|
|
923
|
-
function buildCompress(sym, ctx) {
|
|
924
|
-
const { lenOrView, scratchFor } = ctx;
|
|
925
|
-
const gzipCompress = sym.castrum_gzip_compress;
|
|
926
|
-
const gzipDecompress = sym.castrum_gzip_decompress;
|
|
927
|
-
const brotliCompress = sym.castrum_brotli_compress;
|
|
928
|
-
const brotliDecompress = sym.castrum_brotli_decompress;
|
|
929
|
-
const gzipIsize = sym.castrum_gzip_isize;
|
|
930
|
-
const jsonPatch = sym.castrum_json_patch;
|
|
931
|
-
return {
|
|
932
|
-
jsonPatch(doc, patch) {
|
|
933
|
-
let out = scratchFor(Math.min(Math.max(doc.length, patch.length) + 16, 64 * 1024));
|
|
934
|
-
let w = Number(jsonPatch(doc, lenOrView(doc), patch, lenOrView(patch), out, lenOrView(out)));
|
|
935
|
-
if (w > out.length && w <= MAX_JSON_PATCH_OUTPUT) {
|
|
936
|
-
out = new Uint8Array(w);
|
|
937
|
-
w = Number(jsonPatch(doc, lenOrView(doc), patch, lenOrView(patch), out, lenOrView(out)));
|
|
938
|
-
}
|
|
939
|
-
if (w === 0 || w > out.length) {
|
|
940
|
-
throw new Error("json patch: output buffer too small or patch inapplicable");
|
|
941
|
-
}
|
|
942
|
-
return decodeUtf8(out.subarray(0, w));
|
|
943
|
-
},
|
|
944
|
-
gzipCompress(data, level = 6) {
|
|
945
|
-
return growExact((out) => Number(gzipCompress(data, lenOrView(data), Math.min(level, 9), out, lenOrView(out))), Math.min(data.length + COMPRESS_HEADROOM, COMPRESS_INITIAL_CAP), Math.max(data.length * 2 + COMPRESS_HEADROOM, COMPRESS_MAX_CAP), "gzip compress: output buffer too small");
|
|
946
|
-
},
|
|
947
|
-
gzipCompressInto(data, output, level = 6) {
|
|
948
|
-
const w = Number(gzipCompress(data, lenOrView(data), Math.min(level, 9), output, lenOrView(output)));
|
|
949
|
-
if (w === 0) {
|
|
950
|
-
throw new Error("gzip compress: invalid input");
|
|
951
|
-
}
|
|
952
|
-
if (w > output.length) {
|
|
953
|
-
throw new Error("gzip compress: output buffer too small");
|
|
954
|
-
}
|
|
955
|
-
return w;
|
|
956
|
-
},
|
|
957
|
-
gzipDecompress(data, maxDecompressed) {
|
|
958
|
-
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
959
|
-
const isize = Number(gzipIsize(data, lenOrView(data)));
|
|
960
|
-
const initial = isize !== 0 ? Math.min(isize, max) : Math.min(data.length * DECOMPRESS_GUESS_MULTIPLIER_GZIP, DECOMPRESS_FALLBACK_CAP);
|
|
961
|
-
return growExact((out) => Number(gzipDecompress(data, lenOrView(data), max, out, lenOrView(out))), initial, max, "gzip decompress: invalid stream or exceeded max decompressed size");
|
|
962
|
-
},
|
|
963
|
-
gzipDecompressInto(data, output, maxDecompressed) {
|
|
964
|
-
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
965
|
-
const w = Number(gzipDecompress(data, lenOrView(data), max, output, lenOrView(output)));
|
|
966
|
-
if (w === 0) {
|
|
967
|
-
throw new Error("gzip decompress: invalid stream or exceeded max decompressed size");
|
|
968
|
-
}
|
|
969
|
-
if (w > output.length) {
|
|
970
|
-
throw new Error("gzip decompress: output buffer too small");
|
|
971
|
-
}
|
|
972
|
-
return w;
|
|
973
|
-
},
|
|
974
|
-
gzipIsize(data) {
|
|
975
|
-
return Number(gzipIsize(data, lenOrView(data)));
|
|
976
|
-
},
|
|
977
|
-
brotliCompress(data, quality = 5) {
|
|
978
|
-
return growExact((out) => Number(brotliCompress(data, lenOrView(data), Math.min(quality, 11), out, lenOrView(out))), Math.min(data.length + COMPRESS_HEADROOM, COMPRESS_INITIAL_CAP), Math.max(data.length * 2 + COMPRESS_HEADROOM, COMPRESS_MAX_CAP), "brotli compress: output buffer too small");
|
|
979
|
-
},
|
|
980
|
-
brotliCompressInto(data, output, quality = 5) {
|
|
981
|
-
const w = Number(brotliCompress(data, lenOrView(data), Math.min(quality, 11), output, lenOrView(output)));
|
|
982
|
-
if (w === 0) {
|
|
983
|
-
throw new Error("brotli compress: invalid input");
|
|
984
|
-
}
|
|
985
|
-
if (w > output.length) {
|
|
986
|
-
throw new Error("brotli compress: output buffer too small");
|
|
987
|
-
}
|
|
988
|
-
return w;
|
|
989
|
-
},
|
|
990
|
-
brotliDecompress(data, maxDecompressed) {
|
|
991
|
-
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
992
|
-
return growExact((out) => Number(brotliDecompress(data, lenOrView(data), max, out, lenOrView(out))), Math.min(Math.max(data.length * DECOMPRESS_GUESS_MULTIPLIER_BROTLI, DECOMPRESS_MIN_INITIAL), DECOMPRESS_FALLBACK_CAP), max, "brotli decompress: invalid stream or exceeded max decompressed size");
|
|
993
|
-
},
|
|
994
|
-
brotliDecompressInto(data, output, maxDecompressed) {
|
|
995
|
-
const max = maxDecompressed ?? MAX_DECOMPRESSED;
|
|
996
|
-
const w = Number(brotliDecompress(data, lenOrView(data), max, output, lenOrView(output)));
|
|
997
|
-
if (w === 0) {
|
|
998
|
-
throw new Error("brotli decompress: invalid stream or exceeded max decompressed size");
|
|
999
|
-
}
|
|
1000
|
-
if (w > output.length) {
|
|
1001
|
-
throw new Error("brotli decompress: output buffer too small");
|
|
1002
|
-
}
|
|
1003
|
-
return w;
|
|
1004
|
-
}
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
function selfTestCompress(b) {
|
|
1008
|
-
const enc = { encode: encodeUtf8 };
|
|
1009
|
-
const dec = { decode: decodeUtf8 };
|
|
1010
|
-
const patched = b.jsonPatch(enc.encode(`{"a":"b"}`), enc.encode(`[{"op":"add","path":"/c","value":"d"}]`));
|
|
1011
|
-
if (!patched.includes(`"c":"d"`)) {
|
|
1012
|
-
return false;
|
|
1013
|
-
}
|
|
1014
|
-
const gz = b.gzipCompress(SELFTEST_HEX);
|
|
1015
|
-
if (dec.decode(b.gzipDecompress(gz)) !== "hello") {
|
|
1016
|
-
return false;
|
|
1017
|
-
}
|
|
1018
|
-
const br = b.brotliCompress(SELFTEST_HEX);
|
|
1019
|
-
if (dec.decode(b.brotliDecompress(br)) !== "hello") {
|
|
1020
|
-
return false;
|
|
1021
|
-
}
|
|
1022
|
-
let decompressThrew = false;
|
|
1023
|
-
try {
|
|
1024
|
-
b.gzipDecompress(enc.encode("not-a-gzip-stream"));
|
|
1025
|
-
} catch {
|
|
1026
|
-
decompressThrew = true;
|
|
1027
|
-
}
|
|
1028
|
-
if (!decompressThrew) {
|
|
1029
|
-
return false;
|
|
1030
|
-
}
|
|
1031
|
-
decompressThrew = false;
|
|
1032
|
-
try {
|
|
1033
|
-
b.brotliDecompress(enc.encode("not-brotli-stream"));
|
|
1034
|
-
} catch {
|
|
1035
|
-
decompressThrew = true;
|
|
1036
|
-
}
|
|
1037
|
-
if (!decompressThrew) {
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
|
-
const gzInto = new Uint8Array(64);
|
|
1041
|
-
const gzW = b.gzipCompressInto(SELFTEST_HEX, gzInto);
|
|
1042
|
-
if (gzW === 0 || dec.decode(b.gzipDecompress(gzInto.subarray(0, gzW))) !== "hello") {
|
|
1043
|
-
return false;
|
|
1044
|
-
}
|
|
1045
|
-
const brInto = new Uint8Array(64);
|
|
1046
|
-
const brW = b.brotliCompressInto(SELFTEST_HEX, brInto);
|
|
1047
|
-
if (brW === 0 || dec.decode(b.brotliDecompress(brInto.subarray(0, brW))) !== "hello") {
|
|
1048
|
-
return false;
|
|
1049
|
-
}
|
|
1050
|
-
const gzDst = new Uint8Array(16);
|
|
1051
|
-
const gzDW = b.gzipDecompressInto(gzInto.subarray(0, gzW), gzDst);
|
|
1052
|
-
if (gzDW === 0 || dec.decode(gzDst.subarray(0, gzDW)) !== "hello") {
|
|
1053
|
-
return false;
|
|
1054
|
-
}
|
|
1055
|
-
const brDst = new Uint8Array(16);
|
|
1056
|
-
const brDW = b.brotliDecompressInto(brInto.subarray(0, brW), brDst);
|
|
1057
|
-
if (brDW === 0 || dec.decode(brDst.subarray(0, brDW)) !== "hello") {
|
|
1211
|
+
const brDst = new Uint8Array(16);
|
|
1212
|
+
const brDW = b.brotliDecompressInto(brInto.subarray(0, brW), brDst);
|
|
1213
|
+
if (brDW === 0 || dec.decode(brDst.subarray(0, brDW)) !== "hello") {
|
|
1058
1214
|
return false;
|
|
1059
1215
|
}
|
|
1060
1216
|
return true;
|
|
@@ -1085,6 +1241,8 @@ function buildInstances(sym, ctx) {
|
|
|
1085
1241
|
const cookieValidateSym = sym.castrum_cookie_validate;
|
|
1086
1242
|
const sessionSealSym = sym.castrum_session_seal;
|
|
1087
1243
|
const sessionOpenSym = sym.castrum_session_open;
|
|
1244
|
+
const sessionSealBytesSym = sym.castrum_session_seal_bytes;
|
|
1245
|
+
const sessionOpenBytesSym = sym.castrum_session_open_bytes;
|
|
1088
1246
|
const rateLimiterCheckRaw = sym.castrum_rate_limiter_check;
|
|
1089
1247
|
const rateLimiterCheckKeyRaw = sym.castrum_rate_limiter_check_key;
|
|
1090
1248
|
const jwtSignBytes = sym.castrum_jwt_sign_bytes;
|
|
@@ -1111,7 +1269,10 @@ function buildInstances(sym, ctx) {
|
|
|
1111
1269
|
return acceptNegotiatorNegotiateRaw(inner, header, lenOrView(header));
|
|
1112
1270
|
},
|
|
1113
1271
|
acceptNegotiatorNegotiateServer(inner, header) {
|
|
1114
|
-
|
|
1272
|
+
const text = decodeUtf8(header);
|
|
1273
|
+
if (hasNul(text))
|
|
1274
|
+
return null;
|
|
1275
|
+
return acceptNegotiatorNegotiateServerRaw(inner, text);
|
|
1115
1276
|
},
|
|
1116
1277
|
jwtSignerSign(inner, claimsJson, nowSeconds) {
|
|
1117
1278
|
return growExact((out) => Number(jwtSignerSignRaw(inner, claimsJson, lenOrView(claimsJson), BigInt(nowSeconds), out, lenOrView(out))), Math.min(claimsJson.length + 128, 64 * 1024), 1024 * 1024, "jwt signer: invalid claims JSON or output buffer too small");
|
|
@@ -1151,7 +1312,15 @@ function buildInstances(sym, ctx) {
|
|
|
1151
1312
|
const w = Number(sessionOpenSym(token, secret, output, lenOrView(output)));
|
|
1152
1313
|
return w;
|
|
1153
1314
|
},
|
|
1315
|
+
sessionSealBytes(id, dataJson, expSecs, secret) {
|
|
1316
|
+
return sessionSealBytesSym(id, lenOrView(id), dataJson, lenOrView(dataJson), BigInt(expSecs), secret, lenOrView(secret));
|
|
1317
|
+
},
|
|
1318
|
+
sessionOpenBytes(token, secret, output) {
|
|
1319
|
+
return Number(sessionOpenBytesSym(token, lenOrView(token), secret, lenOrView(secret), output, lenOrView(output)));
|
|
1320
|
+
},
|
|
1154
1321
|
rateLimiterCheck(inner, key, nowMs) {
|
|
1322
|
+
if (hasNul(key))
|
|
1323
|
+
return { allowed: false, remaining: 0, resetMs: 0 };
|
|
1155
1324
|
const out = rateScratch;
|
|
1156
1325
|
const w = Number(rateLimiterCheckRaw(inner, key, BigInt(Math.trunc(nowMs)), out, lenOrView(out)));
|
|
1157
1326
|
if (w === 0)
|
|
@@ -1244,178 +1413,6 @@ function buildInstances(sym, ctx) {
|
|
|
1244
1413
|
}
|
|
1245
1414
|
};
|
|
1246
1415
|
}
|
|
1247
|
-
function selfTestInstances(b) {
|
|
1248
|
-
const enc = { encode: encodeUtf8 };
|
|
1249
|
-
const dec = { decode: decodeUtf8 };
|
|
1250
|
-
const layoutBuf = new Uint8Array(38 * 4);
|
|
1251
|
-
b.ingressLayout(layoutBuf);
|
|
1252
|
-
const layoutView = new DataView(layoutBuf.buffer, layoutBuf.byteOffset, layoutBuf.byteLength);
|
|
1253
|
-
if (layoutView.getUint32(0, true) !== 0 || layoutView.getUint32(2 * 4, true) !== 2 || layoutView.getUint32(12 * 4, true) !== 48 || layoutView.getUint32(13 * 4, true) !== 1 || layoutView.getUint32(28 * 4, true) !== 32 || layoutView.getUint32(37 * 4, true) !== 8) {
|
|
1254
|
-
return false;
|
|
1255
|
-
}
|
|
1256
|
-
try {
|
|
1257
|
-
b.ingressHandlePacked(0, enc.encode("/"), null, new Uint8Array(64));
|
|
1258
|
-
return false;
|
|
1259
|
-
} catch {}
|
|
1260
|
-
try {
|
|
1261
|
-
b.ingressHandleComponents(0, 0, "/", "", enc.encode("rid"), new Uint8Array(2), null, new Uint8Array(64));
|
|
1262
|
-
return false;
|
|
1263
|
-
} catch {}
|
|
1264
|
-
const routeDesc = new Uint8Array(33);
|
|
1265
|
-
const rd = new DataView(routeDesc.buffer);
|
|
1266
|
-
rd.setUint32(0, 1380930900, true);
|
|
1267
|
-
rd.setUint32(4, 3, true);
|
|
1268
|
-
rd.setUint32(8, 2 * 1024 * 1024, true);
|
|
1269
|
-
rd.setUint32(12, 8192, true);
|
|
1270
|
-
rd.setUint32(16, 8192, true);
|
|
1271
|
-
rd.setUint32(20, 0, true);
|
|
1272
|
-
rd.setUint32(24, 1, true);
|
|
1273
|
-
routeDesc[28] = 0;
|
|
1274
|
-
rd.setUint32(29, 0, true);
|
|
1275
|
-
const routeHandle = b.routeCompile(routeDesc);
|
|
1276
|
-
if (routeHandle === 0) {
|
|
1277
|
-
return false;
|
|
1278
|
-
}
|
|
1279
|
-
const routeFrame = new Uint8Array(15);
|
|
1280
|
-
const rf = new DataView(routeFrame.buffer);
|
|
1281
|
-
rf.setUint32(0, 0, true);
|
|
1282
|
-
rf.setUint32(4, 3, true);
|
|
1283
|
-
routeFrame.set(enc.encode("a=1"), 8);
|
|
1284
|
-
rf.setUint32(11, 0, true);
|
|
1285
|
-
const routeOut = new Uint8Array(64);
|
|
1286
|
-
const routeW = b.routeRun(routeHandle, routeFrame, routeOut);
|
|
1287
|
-
if (routeW <= 8) {
|
|
1288
|
-
return false;
|
|
1289
|
-
}
|
|
1290
|
-
const rv = new DataView(routeOut.buffer);
|
|
1291
|
-
const rFlags = rv.getUint32(0, true);
|
|
1292
|
-
if ((rFlags & 1) === 0 || (rFlags & 4) === 0) {
|
|
1293
|
-
return false;
|
|
1294
|
-
}
|
|
1295
|
-
b.routeDestroy(routeHandle);
|
|
1296
|
-
if (b.conditionalIsNotModified(0, null, null) !== false) {
|
|
1297
|
-
return false;
|
|
1298
|
-
}
|
|
1299
|
-
if (b.mediaTypeMatcherMatches(0, enc.encode("x")) !== false)
|
|
1300
|
-
return false;
|
|
1301
|
-
if (b.acceptNegotiatorNegotiate(0, enc.encode("gzip")) !== null)
|
|
1302
|
-
return false;
|
|
1303
|
-
if (b.acceptNegotiatorNegotiateServer(0, enc.encode("gzip")) !== null)
|
|
1304
|
-
return false;
|
|
1305
|
-
if (b.schemaValidatorValidate(0, enc.encode("{}")) !== false)
|
|
1306
|
-
return false;
|
|
1307
|
-
if (b.jwtSignerVerify(0, enc.encode("a.b.c"), 0) !== null)
|
|
1308
|
-
return false;
|
|
1309
|
-
let phase6Threw = false;
|
|
1310
|
-
try {
|
|
1311
|
-
b.jwtSignerSign(0, enc.encode("{}"), 0);
|
|
1312
|
-
} catch {
|
|
1313
|
-
phase6Threw = true;
|
|
1314
|
-
}
|
|
1315
|
-
if (!phase6Threw)
|
|
1316
|
-
return false;
|
|
1317
|
-
phase6Threw = false;
|
|
1318
|
-
try {
|
|
1319
|
-
b.templateRender(0, enc.encode("{}"));
|
|
1320
|
-
} catch {
|
|
1321
|
-
phase6Threw = true;
|
|
1322
|
-
}
|
|
1323
|
-
if (!phase6Threw)
|
|
1324
|
-
return false;
|
|
1325
|
-
phase6Threw = false;
|
|
1326
|
-
try {
|
|
1327
|
-
b.rateLimiterCheck(0, "k", 0);
|
|
1328
|
-
} catch {
|
|
1329
|
-
phase6Threw = true;
|
|
1330
|
-
}
|
|
1331
|
-
if (!phase6Threw)
|
|
1332
|
-
return false;
|
|
1333
|
-
const jwtClaims = enc.encode('{"sub":"user-1"}');
|
|
1334
|
-
const jwtSecret2 = enc.encode("my-secret");
|
|
1335
|
-
const jwtOut = new Uint8Array(512);
|
|
1336
|
-
const jwtW = b.jwtSignBytesInto(jwtClaims, jwtSecret2, 0, 0, jwtOut);
|
|
1337
|
-
if (jwtW === 0 || dec.decode(jwtOut.subarray(0, jwtW)).split(".").length !== 3) {
|
|
1338
|
-
return false;
|
|
1339
|
-
}
|
|
1340
|
-
const jwtSecret = enc.encode("my-secret");
|
|
1341
|
-
const jwt = b.jwtSignBytes(enc.encode('{"sub":"user-1"}'), jwtSecret, 0, 0);
|
|
1342
|
-
const jwtStr = jwt;
|
|
1343
|
-
const segs = jwtStr.split(".");
|
|
1344
|
-
if (segs.length !== 3 || segs[0] === "" || segs[1] === "" || segs[2] === "") {
|
|
1345
|
-
return false;
|
|
1346
|
-
}
|
|
1347
|
-
const signingInput = enc.encode(`${segs[0]}.${segs[1]}`);
|
|
1348
|
-
const sigHex = b.hmacSha256(jwtSecret, signingInput);
|
|
1349
|
-
const sigBytes = b.base64Decode(enc.encode(segs[2] ?? ""), true, false);
|
|
1350
|
-
if (sigBytes === null || b.hexEncode(sigBytes) !== sigHex) {
|
|
1351
|
-
return false;
|
|
1352
|
-
}
|
|
1353
|
-
const vjwtSecret = enc.encode("verify-secret");
|
|
1354
|
-
const vjwt = b.jwtSignBytes(enc.encode('{"sub":"u-1"}'), vjwtSecret, 0, 0);
|
|
1355
|
-
const vjwtClaims = b.jwtVerify(enc.encode(vjwt), vjwtSecret, 0);
|
|
1356
|
-
if (vjwtClaims === null || !vjwtClaims.includes('"sub":"u-1"')) {
|
|
1357
|
-
return false;
|
|
1358
|
-
}
|
|
1359
|
-
if (b.jwtVerify(enc.encode("tampered.token.value"), vjwtSecret, 0) !== null) {
|
|
1360
|
-
return false;
|
|
1361
|
-
}
|
|
1362
|
-
const edKp = b.ed25519GenerateKeypair();
|
|
1363
|
-
const edMsg = enc.encode("ed25519 bind-time self-test");
|
|
1364
|
-
const edSig = b.ed25519Sign(edMsg, edKp.privateKey);
|
|
1365
|
-
if (edSig.byteLength !== 64 || !b.ed25519Verify(edMsg, edSig, edKp.publicKey)) {
|
|
1366
|
-
return false;
|
|
1367
|
-
}
|
|
1368
|
-
const edTampered = edSig.slice();
|
|
1369
|
-
edTampered[0] = (edTampered[0] ?? 0) ^ 255;
|
|
1370
|
-
if (b.ed25519Verify(edMsg, edTampered, edKp.publicKey)) {
|
|
1371
|
-
return false;
|
|
1372
|
-
}
|
|
1373
|
-
const edToken = b.jwtEdDSASign(enc.encode('{"sub":"ffi-self-test"}'), edKp.privateKey, 0, 0);
|
|
1374
|
-
if (edToken === null || edToken.split(".").length !== 3) {
|
|
1375
|
-
return false;
|
|
1376
|
-
}
|
|
1377
|
-
const edClaims = b.jwtEdDSAVerify(enc.encode(edToken), edKp.publicKey, 0);
|
|
1378
|
-
if (edClaims === null || !edClaims.includes('"sub":"ffi-self-test"')) {
|
|
1379
|
-
return false;
|
|
1380
|
-
}
|
|
1381
|
-
if (b.jwtEdDSAVerify(enc.encode("tampered.token.value"), edKp.publicKey, 0) !== null) {
|
|
1382
|
-
return false;
|
|
1383
|
-
}
|
|
1384
|
-
let urlBuilderThrew = false;
|
|
1385
|
-
try {
|
|
1386
|
-
b.urlBuilderResolve(0, enc.encode("g"));
|
|
1387
|
-
} catch {
|
|
1388
|
-
urlBuilderThrew = true;
|
|
1389
|
-
}
|
|
1390
|
-
if (!urlBuilderThrew) {
|
|
1391
|
-
return false;
|
|
1392
|
-
}
|
|
1393
|
-
if (b.queryValidate(0, "route=/a") !== false)
|
|
1394
|
-
return false;
|
|
1395
|
-
if (b.cookieValidate(0, "route=/a") !== false)
|
|
1396
|
-
return false;
|
|
1397
|
-
{
|
|
1398
|
-
const tok = b.sessionSeal("sess-9", '{"n":1}', 1234567, "sekrit");
|
|
1399
|
-
if (tok === null || !tok.includes(".") || !tok.startsWith('{"id":"sess-9"'))
|
|
1400
|
-
return false;
|
|
1401
|
-
const out = new Uint8Array(256);
|
|
1402
|
-
const w = b.sessionOpen(tok, "sekrit", out);
|
|
1403
|
-
if (w <= 13 || out[0] !== 1)
|
|
1404
|
-
return false;
|
|
1405
|
-
if (b.sessionOpen(tok, "wrong", out) !== 0)
|
|
1406
|
-
return false;
|
|
1407
|
-
}
|
|
1408
|
-
let rlKeyThrew = false;
|
|
1409
|
-
try {
|
|
1410
|
-
b.rateLimiterCheckKey(0, 12345, 0);
|
|
1411
|
-
} catch {
|
|
1412
|
-
rlKeyThrew = true;
|
|
1413
|
-
}
|
|
1414
|
-
if (!rlKeyThrew) {
|
|
1415
|
-
return false;
|
|
1416
|
-
}
|
|
1417
|
-
return true;
|
|
1418
|
-
}
|
|
1419
1416
|
var init_instances = __esm(() => {
|
|
1420
1417
|
init_codec2();
|
|
1421
1418
|
init_constants();
|
|
@@ -1528,7 +1525,6 @@ function selfTestMetrics(b) {
|
|
|
1528
1525
|
console.error("FAIL: recordStr");
|
|
1529
1526
|
return false;
|
|
1530
1527
|
}
|
|
1531
|
-
console.error("CK: counter");
|
|
1532
1528
|
const gauge = b.metricsGauge(handle, "ct_selftest_depth", "q");
|
|
1533
1529
|
if (gauge === METRICS_DECLARE_ERR)
|
|
1534
1530
|
return false;
|
|
@@ -1536,7 +1532,6 @@ function selfTestMetrics(b) {
|
|
|
1536
1532
|
return false;
|
|
1537
1533
|
if (!b.metricsGaugeSetStr(handle, gauge, "io", 6))
|
|
1538
1534
|
return false;
|
|
1539
|
-
console.error("CK: gauge");
|
|
1540
1535
|
const hist = b.metricsHistogram(handle, "ct_selftest_latency", "", "0.1,0.5");
|
|
1541
1536
|
if (hist === METRICS_DECLARE_ERR)
|
|
1542
1537
|
return false;
|
|
@@ -1544,10 +1539,8 @@ function selfTestMetrics(b) {
|
|
|
1544
1539
|
return false;
|
|
1545
1540
|
if (b.metricsRecord(handle, hist, EMPTY, -1))
|
|
1546
1541
|
return false;
|
|
1547
|
-
console.error("CK: hist");
|
|
1548
1542
|
if (b.metricsRecord(handle, counter, encodeUtf8("/a"), 1))
|
|
1549
1543
|
return false;
|
|
1550
|
-
console.error("CK: arity");
|
|
1551
1544
|
const probe = new Uint8Array(16);
|
|
1552
1545
|
const needed = b.metricsRender(handle, probe);
|
|
1553
1546
|
if (needed <= probe.length)
|
|
@@ -1566,7 +1559,6 @@ function selfTestMetrics(b) {
|
|
|
1566
1559
|
`)) {
|
|
1567
1560
|
return false;
|
|
1568
1561
|
}
|
|
1569
|
-
console.error("CK: render");
|
|
1570
1562
|
{
|
|
1571
1563
|
const probe2 = new Uint8Array(16);
|
|
1572
1564
|
const need = b.metricsSnapshot(handle, probe2);
|
|
@@ -1581,7 +1573,6 @@ function selfTestMetrics(b) {
|
|
|
1581
1573
|
if (sv.getUint32(4, true) < 3)
|
|
1582
1574
|
return false;
|
|
1583
1575
|
}
|
|
1584
|
-
console.error("CK: snapshot");
|
|
1585
1576
|
{
|
|
1586
1577
|
const c2 = b.metricsCounter(handle, "ct_selftest_batch", "k");
|
|
1587
1578
|
const buf = new Uint8Array(64);
|
|
@@ -1601,7 +1592,6 @@ function selfTestMetrics(b) {
|
|
|
1601
1592
|
return false;
|
|
1602
1593
|
}
|
|
1603
1594
|
}
|
|
1604
|
-
console.error("CK: batch");
|
|
1605
1595
|
try {
|
|
1606
1596
|
b.metricsCounter(0, "ct_selftest_x", "");
|
|
1607
1597
|
return false;
|
|
@@ -1610,15 +1600,702 @@ function selfTestMetrics(b) {
|
|
|
1610
1600
|
} catch (err) {
|
|
1611
1601
|
console.error("METRICS SELFTEST THROW:", err);
|
|
1612
1602
|
return false;
|
|
1613
|
-
} finally {
|
|
1614
|
-
b.metricsDestroy(handle);
|
|
1603
|
+
} finally {
|
|
1604
|
+
b.metricsDestroy(handle);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
var METRICS_DECLARE_ERR = 4294967295, EMPTY;
|
|
1608
|
+
var init_metrics = __esm(() => {
|
|
1609
|
+
init_codec2();
|
|
1610
|
+
init_util();
|
|
1611
|
+
EMPTY = new Uint8Array(0);
|
|
1612
|
+
});
|
|
1613
|
+
|
|
1614
|
+
// src/native/ffi/build/parse.ts
|
|
1615
|
+
function sseMemoEncode(slot, s) {
|
|
1616
|
+
if (s === slot.str)
|
|
1617
|
+
return slot.bytes;
|
|
1618
|
+
const bytes = encodeUtf8(s);
|
|
1619
|
+
slot.str = s;
|
|
1620
|
+
slot.bytes = bytes;
|
|
1621
|
+
return bytes;
|
|
1622
|
+
}
|
|
1623
|
+
function buildParse(sym, ctx) {
|
|
1624
|
+
const { lenOrView, scratchFor, dateScratch, dateScratchView } = ctx;
|
|
1625
|
+
const httpParsePacked = sym.castrum_http_parse_request_packed;
|
|
1626
|
+
const queryParsePacked = sym.castrum_query_parse_packed;
|
|
1627
|
+
const cookieParsePacked = sym.castrum_cookie_parse_packed;
|
|
1628
|
+
const httpDateIntoRaw = sym.castrum_http_date_into;
|
|
1629
|
+
const sseEncodeIntoRaw = sym.castrum_sse_encode_into;
|
|
1630
|
+
const jsonParsePackedSym = sym.castrum_json_parse_packed;
|
|
1631
|
+
const wsFrameDecodePacked = sym.castrum_ws_frame_decode_packed;
|
|
1632
|
+
const multipartParsePacked = sym.castrum_multipart_parse_packed;
|
|
1633
|
+
const formParsePacked = sym.castrum_form_parse_packed;
|
|
1634
|
+
const wsFrameEncode = sym.castrum_ws_frame_encode;
|
|
1635
|
+
const parseMediaTypeSym = sym.castrum_parse_media_type;
|
|
1636
|
+
const parseHttpDateSym = sym.castrum_parse_http_date;
|
|
1637
|
+
const parseAcceptEncodingSym = sym.castrum_parse_accept_encoding;
|
|
1638
|
+
const urlEncodeQuerySym = sym.castrum_url_encode_query;
|
|
1639
|
+
const urlResolveSym = sym.castrum_url_resolve;
|
|
1640
|
+
const mimeFromExtensionSym = sym.castrum_mime_from_extension;
|
|
1641
|
+
return {
|
|
1642
|
+
httpParseRequestPackedInto(input, output) {
|
|
1643
|
+
const w = Number(httpParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1644
|
+
if (w === 0 && input.length !== 0) {
|
|
1645
|
+
throw new Error("http parse: output buffer too small or malformed request");
|
|
1646
|
+
}
|
|
1647
|
+
return w;
|
|
1648
|
+
},
|
|
1649
|
+
queryParsePackedInto(input, output) {
|
|
1650
|
+
const w = Number(queryParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1651
|
+
if (w === 0 || w > output.length) {
|
|
1652
|
+
throw new Error("query parse: output buffer too small");
|
|
1653
|
+
}
|
|
1654
|
+
return w;
|
|
1655
|
+
},
|
|
1656
|
+
cookieParsePackedInto(input, output) {
|
|
1657
|
+
const w = Number(cookieParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1658
|
+
if (w === 0 || w > output.length) {
|
|
1659
|
+
throw new Error("cookie parse: output buffer too small");
|
|
1660
|
+
}
|
|
1661
|
+
return w;
|
|
1662
|
+
},
|
|
1663
|
+
wsFrameEncode(opcode, payload, mask, fin) {
|
|
1664
|
+
const out = allocOut(payload.length + 14);
|
|
1665
|
+
const w = Number(wsFrameEncode(opcode, payload, lenOrView(payload), flag(mask), flag(fin), out, lenOrView(out)));
|
|
1666
|
+
if (w === 0) {
|
|
1667
|
+
throw new Error("ws frame encode: output buffer too small");
|
|
1668
|
+
}
|
|
1669
|
+
return out.subarray(0, w);
|
|
1670
|
+
},
|
|
1671
|
+
wsFrameEncodeInto(opcode, payload, mask, fin, output) {
|
|
1672
|
+
const need = payload.length + 14;
|
|
1673
|
+
if (output.length < need) {
|
|
1674
|
+
throw new Error("ws frame encode: output buffer too small");
|
|
1675
|
+
}
|
|
1676
|
+
const w = Number(wsFrameEncode(opcode, payload, lenOrView(payload), flag(mask), flag(fin), output, lenOrView(output)));
|
|
1677
|
+
if (w === 0) {
|
|
1678
|
+
throw new Error("ws frame encode: output buffer too small");
|
|
1679
|
+
}
|
|
1680
|
+
return w;
|
|
1681
|
+
},
|
|
1682
|
+
wsFrameDecodePacked(data) {
|
|
1683
|
+
const out = new Uint8Array(data.length + 6);
|
|
1684
|
+
const w = Number(wsFrameDecodePacked(data, lenOrView(data), out, lenOrView(out)));
|
|
1685
|
+
return w === 0 ? null : out.subarray(0, w);
|
|
1686
|
+
},
|
|
1687
|
+
wsFrameDecodePackedInto(data, output) {
|
|
1688
|
+
const w = Number(wsFrameDecodePacked(data, lenOrView(data), output, lenOrView(output)));
|
|
1689
|
+
return w === 0 ? null : w;
|
|
1690
|
+
},
|
|
1691
|
+
multipartParsePacked(body, boundary) {
|
|
1692
|
+
return growExact((out) => Number(multipartParsePacked(body, lenOrView(body), boundary, lenOrView(boundary), out, lenOrView(out))), Math.min(body.length + boundary.length + 64, 64 * 1024), 128 * 1024 * 1024, "multipart parse: output buffer too small");
|
|
1693
|
+
},
|
|
1694
|
+
multipartParsePackedInto(body, boundary, output) {
|
|
1695
|
+
const w = Number(multipartParsePacked(body, lenOrView(body), boundary, lenOrView(boundary), output, lenOrView(output)));
|
|
1696
|
+
if (w === 0 || w > output.length) {
|
|
1697
|
+
throw new Error("multipart parse: output buffer too small or malformed body");
|
|
1698
|
+
}
|
|
1699
|
+
return w;
|
|
1700
|
+
},
|
|
1701
|
+
formParsePackedInto(input, output) {
|
|
1702
|
+
const w = Number(formParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1703
|
+
if (w === 0 || w > output.length) {
|
|
1704
|
+
throw new Error("form parse: output buffer too small");
|
|
1705
|
+
}
|
|
1706
|
+
return w;
|
|
1707
|
+
},
|
|
1708
|
+
jsonParsePacked(input) {
|
|
1709
|
+
return growExact((out) => Number(jsonParsePackedSym(input, lenOrView(input), out, lenOrView(out))), Math.min(input.length + (input.length >> 1), 16 * 1024 * 1024), Math.max(1024 * 1024, input.length * 16), "json parse packed: invalid JSON or output buffer too small");
|
|
1710
|
+
},
|
|
1711
|
+
parseMediaType(input) {
|
|
1712
|
+
let out = scratchFor(input.length * 2 + 128);
|
|
1713
|
+
let w = Number(parseMediaTypeSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1714
|
+
if (w > out.length) {
|
|
1715
|
+
out = new Uint8Array(w);
|
|
1716
|
+
w = Number(parseMediaTypeSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1717
|
+
}
|
|
1718
|
+
if (w === 0 || w > out.length) {
|
|
1719
|
+
throw new Error("media type parse: invalid media type or output buffer too small");
|
|
1720
|
+
}
|
|
1721
|
+
return out.subarray(0, w);
|
|
1722
|
+
},
|
|
1723
|
+
parseHttpDate(input) {
|
|
1724
|
+
const out = dateScratch;
|
|
1725
|
+
const w = Number(parseHttpDateSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1726
|
+
if (w === 0) {
|
|
1727
|
+
throw new Error("http date parse: output buffer too small");
|
|
1728
|
+
}
|
|
1729
|
+
if (out[0] === 0)
|
|
1730
|
+
return null;
|
|
1731
|
+
return dateScratchView.getBigInt64(1, true);
|
|
1732
|
+
},
|
|
1733
|
+
parseAcceptEncoding(input) {
|
|
1734
|
+
let out = scratchFor(input.length * 2 + 128);
|
|
1735
|
+
let w = Number(parseAcceptEncodingSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1736
|
+
if (w > out.length) {
|
|
1737
|
+
out = new Uint8Array(w);
|
|
1738
|
+
w = Number(parseAcceptEncodingSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1739
|
+
}
|
|
1740
|
+
if (w === 0 || w > out.length) {
|
|
1741
|
+
throw new Error("accept encoding parse: output buffer too small");
|
|
1742
|
+
}
|
|
1743
|
+
return out.subarray(0, w);
|
|
1744
|
+
},
|
|
1745
|
+
urlEncodeQuery(input) {
|
|
1746
|
+
return urlEncodeQuerySym(input, lenOrView(input));
|
|
1747
|
+
},
|
|
1748
|
+
urlResolve(base, reference) {
|
|
1749
|
+
return urlResolveSym(base, lenOrView(base), reference, lenOrView(reference));
|
|
1750
|
+
},
|
|
1751
|
+
mimeFromExtension(ext) {
|
|
1752
|
+
return mimeFromExtensionSym(ext);
|
|
1753
|
+
},
|
|
1754
|
+
httpDateInto(secs, output) {
|
|
1755
|
+
const w = Number(httpDateIntoRaw(secs, output, lenOrView(output)));
|
|
1756
|
+
if (w === 0) {
|
|
1757
|
+
throw new Error("http date: output buffer too small or year out of range");
|
|
1758
|
+
}
|
|
1759
|
+
return w;
|
|
1760
|
+
},
|
|
1761
|
+
httpDate(secs) {
|
|
1762
|
+
const out = scratchFor(32);
|
|
1763
|
+
const w = Number(httpDateIntoRaw(secs ?? 0, out, lenOrView(out)));
|
|
1764
|
+
if (w !== 0)
|
|
1765
|
+
return decodeUtf8(out.subarray(0, w));
|
|
1766
|
+
return new Date((secs ?? 0) * 1000).toUTCString();
|
|
1767
|
+
},
|
|
1768
|
+
sseEncodeEvent(event, data, id, retry) {
|
|
1769
|
+
const ev = event === null ? EMPTY_VIEW : sseMemoEncode(sseMemoEvent, event);
|
|
1770
|
+
const idv = id === null ? EMPTY_VIEW : sseMemoEncode(sseMemoId, id);
|
|
1771
|
+
const flags = (event === null ? 0 : 1) | (id === null ? 0 : 2) | (retry === null ? 0 : 4);
|
|
1772
|
+
return growExact((out) => Number(sseEncodeIntoRaw(ev, lenOrView(ev), data, lenOrView(data), idv, lenOrView(idv), flags, retry ?? 0, out, lenOrView(out))), data.length + 64, 1 << 20, "sse encode: output buffer too small");
|
|
1773
|
+
},
|
|
1774
|
+
sseEncodeEventInto(event, data, id, retry, output) {
|
|
1775
|
+
const ev = event === null ? EMPTY_VIEW : sseMemoEncode(sseMemoEvent, event);
|
|
1776
|
+
const idv = id === null ? EMPTY_VIEW : sseMemoEncode(sseMemoId, id);
|
|
1777
|
+
const flags = (event === null ? 0 : 1) | (id === null ? 0 : 2) | (retry === null ? 0 : 4);
|
|
1778
|
+
const w = Number(sseEncodeIntoRaw(ev, lenOrView(ev), data, lenOrView(data), idv, lenOrView(idv), flags, retry ?? 0, output, lenOrView(output)));
|
|
1779
|
+
if (w === 0) {
|
|
1780
|
+
throw new Error("sse encode: invalid UTF-8 in event/id");
|
|
1781
|
+
}
|
|
1782
|
+
if (w > output.length) {
|
|
1783
|
+
throw new Error("sse encode: output buffer too small");
|
|
1784
|
+
}
|
|
1785
|
+
return w;
|
|
1786
|
+
}
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
var sseMemoEvent, sseMemoId;
|
|
1790
|
+
var init_parse = __esm(() => {
|
|
1791
|
+
init_codec2();
|
|
1792
|
+
init_constants();
|
|
1793
|
+
init_util();
|
|
1794
|
+
sseMemoEvent = { str: null, bytes: EMPTY_VIEW };
|
|
1795
|
+
sseMemoId = { str: null, bytes: EMPTY_VIEW };
|
|
1796
|
+
});
|
|
1797
|
+
|
|
1798
|
+
// src/native/ffi/build/task.ts
|
|
1799
|
+
function buildTask(sym, ctx) {
|
|
1800
|
+
const { lenOrView } = ctx;
|
|
1801
|
+
const init = sym.castrum_task_init;
|
|
1802
|
+
const submit = sym.castrum_task_submit;
|
|
1803
|
+
const submitOut = sym.castrum_task_submit_out;
|
|
1804
|
+
const submitSlice = sym.castrum_task_submit_slice;
|
|
1805
|
+
const drain = sym.castrum_task_drain;
|
|
1806
|
+
const pending = sym.castrum_task_pending;
|
|
1807
|
+
const cancel = sym.castrum_task_cancel;
|
|
1808
|
+
const setDoorbell = sym.castrum_task_set_doorbell;
|
|
1809
|
+
const shutdown = sym.castrum_task_shutdown;
|
|
1810
|
+
const threads = sym.castrum_task_threads;
|
|
1811
|
+
return {
|
|
1812
|
+
taskInit: (n) => Number(init(n)),
|
|
1813
|
+
taskSubmit: (op, args, taskId) => Number(submit(op, args, lenOrView(args), taskId)),
|
|
1814
|
+
taskSubmitOut: (op, args, taskId, output) => Number(submitOut(op, args, lenOrView(args), taskId, output, lenOrView(output))),
|
|
1815
|
+
taskSubmitSlice: (op, hdr, data, taskId) => Number(submitSlice(op, hdr, lenOrView(hdr), data, lenOrView(data), taskId)),
|
|
1816
|
+
taskDrain: (output) => Number(drain(output, lenOrView(output))),
|
|
1817
|
+
taskPending: () => Number(pending()),
|
|
1818
|
+
taskCancel: (taskId) => Number(cancel(taskId)),
|
|
1819
|
+
taskSetDoorbell: (ptr) => Number(setDoorbell(ptr)),
|
|
1820
|
+
taskShutdown: () => Number(shutdown()),
|
|
1821
|
+
taskThreads: () => Number(threads())
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
function selfTestTask(b) {
|
|
1825
|
+
try {
|
|
1826
|
+
if (typeof b.taskPending() !== "number")
|
|
1827
|
+
return false;
|
|
1828
|
+
if (typeof b.taskThreads() !== "number" || b.taskThreads() < 0)
|
|
1829
|
+
return false;
|
|
1830
|
+
if (b.taskSetDoorbell(0) !== 1)
|
|
1831
|
+
return false;
|
|
1832
|
+
if (typeof b.taskDrain(new Uint8Array(4)) !== "number")
|
|
1833
|
+
return false;
|
|
1834
|
+
if (typeof b.taskSubmitOut !== "function")
|
|
1835
|
+
return false;
|
|
1836
|
+
if (typeof b.taskSubmitSlice !== "function")
|
|
1837
|
+
return false;
|
|
1838
|
+
return true;
|
|
1839
|
+
} catch {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
// src/native/ffi/build.ts
|
|
1845
|
+
function build(sym, useBufferLength) {
|
|
1846
|
+
const oneArg = (raw, v) => useBufferLength ? raw(v, v) : raw(v, v.length);
|
|
1847
|
+
const lenOrView = (v) => useBufferLength ? v : v.length;
|
|
1848
|
+
let encodeScratch = new Uint8Array(0);
|
|
1849
|
+
const scratchFor = (size) => {
|
|
1850
|
+
if (encodeScratch.byteLength < size)
|
|
1851
|
+
encodeScratch = new Uint8Array(size);
|
|
1852
|
+
return encodeScratch;
|
|
1853
|
+
};
|
|
1854
|
+
const jsonSumOut = new Uint8Array(9);
|
|
1855
|
+
const jsonSumView = new DataView(jsonSumOut.buffer);
|
|
1856
|
+
const dateScratch = new Uint8Array(9);
|
|
1857
|
+
const dateScratchView = new DataView(dateScratch.buffer);
|
|
1858
|
+
const rateScratch = new Uint8Array(13);
|
|
1859
|
+
const rateScratchView = new DataView(rateScratch.buffer);
|
|
1860
|
+
const ctx = {
|
|
1861
|
+
useBufferLength,
|
|
1862
|
+
lenOrView,
|
|
1863
|
+
oneArg,
|
|
1864
|
+
scratchFor,
|
|
1865
|
+
jsonSumOut,
|
|
1866
|
+
jsonSumView,
|
|
1867
|
+
dateScratch,
|
|
1868
|
+
dateScratchView,
|
|
1869
|
+
rateScratch,
|
|
1870
|
+
rateScratchView
|
|
1871
|
+
};
|
|
1872
|
+
return {
|
|
1873
|
+
...buildCodecs(sym, ctx),
|
|
1874
|
+
...buildCompress(sym, ctx),
|
|
1875
|
+
...buildParse(sym, ctx),
|
|
1876
|
+
...buildInstances(sym, ctx),
|
|
1877
|
+
...buildMetrics(sym, ctx),
|
|
1878
|
+
...buildTask(sym, ctx)
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
var init_build = __esm(() => {
|
|
1882
|
+
init_codecs();
|
|
1883
|
+
init_compress();
|
|
1884
|
+
init_instances();
|
|
1885
|
+
init_metrics();
|
|
1886
|
+
init_parse();
|
|
1887
|
+
});
|
|
1888
|
+
|
|
1889
|
+
// src/native/ffi/build/codecs-selftest.ts
|
|
1890
|
+
function selfTestCodecs(b) {
|
|
1891
|
+
const enc = { encode: encodeUtf8 };
|
|
1892
|
+
const dec = { decode: decodeUtf8 };
|
|
1893
|
+
if (b.crc32(enc.encode("123456789")) !== 3421780262)
|
|
1894
|
+
return false;
|
|
1895
|
+
if (b.fnv1a64(enc.encode("foobar")) !== 0x85944171f73967e8n)
|
|
1896
|
+
return false;
|
|
1897
|
+
if (b.xxh3(new Uint8Array(0)) !== 0x2d06800538d394c2n)
|
|
1898
|
+
return false;
|
|
1899
|
+
if (b.jsonValid(SELFTEST_JSON) !== true || b.jsonValid(enc.encode("{not json")) !== false) {
|
|
1900
|
+
return false;
|
|
1901
|
+
}
|
|
1902
|
+
if (!b.utf8Valid(enc.encode("héllo")) || b.utf8Valid(new Uint8Array([255, 254]))) {
|
|
1903
|
+
return false;
|
|
1904
|
+
}
|
|
1905
|
+
const hexOut = new Uint8Array(SELFTEST_HEX.length * 2);
|
|
1906
|
+
if (b.hexEncodeInto(SELFTEST_HEX, hexOut) !== 10 || dec.decode(hexOut) !== "68656c6c6f") {
|
|
1907
|
+
return false;
|
|
1908
|
+
}
|
|
1909
|
+
const urlInput = enc.encode("a b/c");
|
|
1910
|
+
const urlOut = new Uint8Array(9);
|
|
1911
|
+
if (b.urlEncodeInto(urlInput, urlOut) !== 9 || dec.decode(urlOut) !== "a%20b%2Fc") {
|
|
1912
|
+
return false;
|
|
1913
|
+
}
|
|
1914
|
+
if (!b.validateEmail("a@b.com") || !b.validateUuid("550e8400-e29b-41d4-a716-446655440000") || !b.validateIpv4("192.168.0.1") || !b.validateIpv6("2001:db8::1") || b.validateEmail("not-an-email") || b.validateUuid("not-a-uuid")) {
|
|
1915
|
+
return false;
|
|
1916
|
+
}
|
|
1917
|
+
if (!b.validateEmailBytes(enc.encode("a@b.com")) || !b.validateUuidBytes(enc.encode("550e8400-e29b-41d4-a716-446655440000")) || !b.validateIpv4Bytes(enc.encode("192.168.0.1")) || !b.validateIpv6Bytes(enc.encode("2001:db8::1")) || b.validateEmailBytes(enc.encode("not-an-email")) || b.validateIpv6Bytes(enc.encode("999"))) {
|
|
1918
|
+
return false;
|
|
1919
|
+
}
|
|
1920
|
+
if (b.jsonSumIds(enc.encode(`[{"id":1},{"id":2}]`)) !== 3n)
|
|
1921
|
+
return false;
|
|
1922
|
+
if (b.jsonSumIds(enc.encode(`[{"id":0},{"id":0}]`)) !== 0n)
|
|
1923
|
+
return false;
|
|
1924
|
+
let sumInvalidThrew = false;
|
|
1925
|
+
try {
|
|
1926
|
+
b.jsonSumIds(enc.encode("nope"));
|
|
1927
|
+
} catch {
|
|
1928
|
+
sumInvalidThrew = true;
|
|
1929
|
+
}
|
|
1930
|
+
if (!sumInvalidThrew)
|
|
1931
|
+
return false;
|
|
1932
|
+
{
|
|
1933
|
+
const ids = enc.encode(`507f1f77bcf86cd799439011
|
|
1934
|
+
zz
|
|
1935
|
+
507F1F77BCF86CD799439012`);
|
|
1936
|
+
const tiny = new Uint8Array(2);
|
|
1937
|
+
const needed = b.hexValidateBatchInto(ids, 24, tiny);
|
|
1938
|
+
if (needed !== 3)
|
|
1939
|
+
return false;
|
|
1940
|
+
const out = new Uint8Array(needed);
|
|
1941
|
+
if (b.hexValidateBatchInto(ids, 24, out) !== 3)
|
|
1942
|
+
return false;
|
|
1943
|
+
if (out[0] !== 1 || out[1] !== 0 || out[2] !== 1)
|
|
1944
|
+
return false;
|
|
1945
|
+
let widthThrew = false;
|
|
1946
|
+
try {
|
|
1947
|
+
b.hexValidateBatchInto(ids, 0, new Uint8Array(3));
|
|
1948
|
+
} catch {
|
|
1949
|
+
widthThrew = true;
|
|
1950
|
+
}
|
|
1951
|
+
if (!widthThrew)
|
|
1952
|
+
return false;
|
|
1953
|
+
}
|
|
1954
|
+
{
|
|
1955
|
+
const out = new Uint8Array(64);
|
|
1956
|
+
const w = b.regexEscapeInto(enc.encode("a.c(x)"), out);
|
|
1957
|
+
if (w !== 9 || dec.decode(out.subarray(0, w)) !== "a\\.c\\(x\\)")
|
|
1958
|
+
return false;
|
|
1959
|
+
const plain = new Uint8Array(16);
|
|
1960
|
+
const wp = b.regexEscapeInto(enc.encode("hello"), plain);
|
|
1961
|
+
if (wp !== 5 || dec.decode(plain.subarray(0, wp)) !== "hello")
|
|
1962
|
+
return false;
|
|
1963
|
+
if (b.regexEscapeInto(enc.encode("a.b"), new Uint8Array(2)) !== 4)
|
|
1964
|
+
return false;
|
|
1965
|
+
if (b.regexEscapeStr("a.c(x)") !== "a\\.c\\(x\\)")
|
|
1966
|
+
return false;
|
|
1967
|
+
if (b.regexEscapeStr("plain text") !== "plain text")
|
|
1968
|
+
return false;
|
|
1969
|
+
}
|
|
1970
|
+
{
|
|
1971
|
+
const out = new Uint8Array(8);
|
|
1972
|
+
const w = b.hexValidateBatchStr(`507f1f77bcf86cd799439011
|
|
1973
|
+
zz`, 24, out);
|
|
1974
|
+
if (w !== 2 || out[0] !== 1 || out[1] !== 0)
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
const hmacKey = new Uint8Array(20).fill(11);
|
|
1978
|
+
const hmacData = enc.encode("Hi There");
|
|
1979
|
+
const hmacSig = enc.encode("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");
|
|
1980
|
+
if (b.hmacSha256(hmacKey, hmacData) !== "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7") {
|
|
1981
|
+
return false;
|
|
1982
|
+
}
|
|
1983
|
+
if (!b.hmacSha256Verify(hmacKey, hmacData, hmacSig)) {
|
|
1984
|
+
return false;
|
|
1985
|
+
}
|
|
1986
|
+
const decoded = b.hexDecode(enc.encode("68656c6c6f"));
|
|
1987
|
+
if (dec.decode(decoded) !== "hello" || dec.decode(b.urlDecode(enc.encode("a%20b%2Fc"))) !== "a b/c") {
|
|
1988
|
+
return false;
|
|
1989
|
+
}
|
|
1990
|
+
if (b.wsAcceptKey("dGhlIHNhbXBsZSBub25jZQ==") !== "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") {
|
|
1991
|
+
return false;
|
|
1992
|
+
}
|
|
1993
|
+
if (b.etag(SELFTEST_HEX).length !== 10 || b.etag(SELFTEST_HEX, true).length !== 12) {
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1996
|
+
if (b.base64Encode(SELFTEST_HEX) !== "aGVsbG8=") {
|
|
1997
|
+
return false;
|
|
1998
|
+
}
|
|
1999
|
+
if (dec.decode(b.base64Decode(enc.encode("aGVsbG8="))) !== "hello") {
|
|
2000
|
+
return false;
|
|
2001
|
+
}
|
|
2002
|
+
const secret = enc.encode("s3cr3t-secret");
|
|
2003
|
+
const signed = b.signCookie(SELFTEST_HEX, secret);
|
|
2004
|
+
const verified = b.verifyCookie(enc.encode(signed), secret);
|
|
2005
|
+
if (verified === null || verified !== "hello") {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
if (b.verifyCookie(enc.encode("tampered.0000"), secret) !== null) {
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
const csrfTokenStr = b.csrfToken(secret);
|
|
2012
|
+
if (csrfTokenStr.length !== 129 || !b.csrfVerify(enc.encode(csrfTokenStr), secret)) {
|
|
2013
|
+
return false;
|
|
2014
|
+
}
|
|
2015
|
+
const pw = enc.encode("correct horse battery staple");
|
|
2016
|
+
const salt = enc.encode("salty-salt-16b");
|
|
2017
|
+
const phc = b.passwordHash(pw, salt, 8, 1, 1, 16);
|
|
2018
|
+
if (phc.length === 0 || !b.passwordVerify(pw, enc.encode(phc))) {
|
|
2019
|
+
return false;
|
|
2020
|
+
}
|
|
2021
|
+
const bcryptPhc = b.passwordHashBcrypt(pw, 4);
|
|
2022
|
+
if (bcryptPhc.length === 0 || !b.passwordVerifyBcrypt(pw, bcryptPhc)) {
|
|
2023
|
+
return false;
|
|
2024
|
+
}
|
|
2025
|
+
const dk = b.pbkdf2Sha256(enc.encode("password"), enc.encode("salt"), 1, 32);
|
|
2026
|
+
if (b.hexEncode(dk) !== "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b") {
|
|
2027
|
+
return false;
|
|
2028
|
+
}
|
|
2029
|
+
const aeadKey = new Uint8Array(32).fill(66);
|
|
2030
|
+
const nonce = new Uint8Array(12).fill(7);
|
|
2031
|
+
const ct = b.aeadEncrypt(aeadKey, nonce, SELFTEST_HEX, 0);
|
|
2032
|
+
const pt = b.aeadDecrypt(aeadKey, nonce, ct, 0);
|
|
2033
|
+
if (pt === null || dec.decode(pt) !== "hello") {
|
|
2034
|
+
return false;
|
|
2035
|
+
}
|
|
2036
|
+
const rtOut = new Uint8Array(32);
|
|
2037
|
+
const rtW = b.randomTokenInto(16, rtOut);
|
|
2038
|
+
if (rtW !== 32 || !/^[0-9a-f]{32}$/.test(dec.decode(rtOut))) {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
2041
|
+
try {
|
|
2042
|
+
b.randomTokenInto(16, new Uint8Array(8));
|
|
2043
|
+
return false;
|
|
2044
|
+
} catch {}
|
|
2045
|
+
const waOut = new Uint8Array(28);
|
|
2046
|
+
const waW = b.wsAcceptKeyInto(enc.encode("dGhlIHNhbXBsZSBub25jZQ=="), waOut);
|
|
2047
|
+
const waExpected = b.wsAcceptKey("dGhlIHNhbXBsZSBub25jZQ==");
|
|
2048
|
+
if (waW !== 28 || dec.decode(waOut) !== waExpected) {
|
|
2049
|
+
return false;
|
|
2050
|
+
}
|
|
2051
|
+
const etagData = enc.encode("hello");
|
|
2052
|
+
const etagOut = new Uint8Array(16);
|
|
2053
|
+
const etagW = b.etagInto(etagData, etagOut);
|
|
2054
|
+
if (etagW !== 10 || dec.decode(etagOut.subarray(0, etagW)) !== b.etag(etagData)) {
|
|
2055
|
+
return false;
|
|
2056
|
+
}
|
|
2057
|
+
const csrfOut = new Uint8Array(129);
|
|
2058
|
+
const csrfW = b.csrfTokenInto(enc.encode("csrf-secret"), csrfOut);
|
|
2059
|
+
if (csrfW !== 129 || csrfOut[64] !== 46) {
|
|
2060
|
+
return false;
|
|
2061
|
+
}
|
|
2062
|
+
const ckVal = enc.encode("session-value");
|
|
2063
|
+
const ckSec = enc.encode("s3cr3t-secret");
|
|
2064
|
+
const ckOut = new Uint8Array(256);
|
|
2065
|
+
const ckW = b.signCookieInto(ckVal, ckSec, ckOut);
|
|
2066
|
+
if (ckW !== ckVal.length + 65) {
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
const ckVerifyOut = new Uint8Array(256);
|
|
2070
|
+
const ckV = b.verifyCookieInto(ckOut.subarray(0, ckW), ckSec, ckVerifyOut);
|
|
2071
|
+
if (ckV !== ckVal.length || dec.decode(ckVerifyOut.subarray(0, ckV)) !== "session-value") {
|
|
2072
|
+
return false;
|
|
2073
|
+
}
|
|
2074
|
+
if (b.urlEncode(enc.encode("a b/c")) !== "a%20b%2Fc") {
|
|
2075
|
+
return false;
|
|
2076
|
+
}
|
|
2077
|
+
const hdInto = new Uint8Array(8);
|
|
2078
|
+
if (b.hexDecodeInto(enc.encode("68656c6c6f"), hdInto) !== 5 || dec.decode(hdInto.subarray(0, 5)) !== "hello") {
|
|
2079
|
+
return false;
|
|
2080
|
+
}
|
|
2081
|
+
const udInto = new Uint8Array(16);
|
|
2082
|
+
if (b.urlDecodeInto(enc.encode("a%20b%2Fc"), udInto) !== 5 || dec.decode(udInto.subarray(0, 5)) !== "a b/c") {
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
const b64Into = new Uint8Array(16);
|
|
2086
|
+
if (b.base64EncodeInto(SELFTEST_HEX, b64Into) !== 8 || dec.decode(b64Into.subarray(0, 8)) !== "aGVsbG8=") {
|
|
2087
|
+
return false;
|
|
2088
|
+
}
|
|
2089
|
+
const bdInto = new Uint8Array(8);
|
|
2090
|
+
if (b.base64DecodeInto(enc.encode("aGVsbG8="), bdInto) !== 5 || dec.decode(bdInto.subarray(0, 5)) !== "hello") {
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
if (!/^[0-9a-f]{32}$/.test(b.randomToken(16))) {
|
|
2094
|
+
return false;
|
|
2095
|
+
}
|
|
2096
|
+
const hmacInto = new Uint8Array(64);
|
|
2097
|
+
if (b.hmacSha256Into(hmacKey, hmacData, hmacInto) !== 64 || dec.decode(hmacInto) !== "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7") {
|
|
2098
|
+
return false;
|
|
2099
|
+
}
|
|
2100
|
+
const ctInto = new Uint8Array(64);
|
|
2101
|
+
const ctW = b.aeadEncryptInto(aeadKey, nonce, SELFTEST_HEX, ctInto, 0);
|
|
2102
|
+
if (ctW === 0 || b.aeadDecrypt(aeadKey, nonce, ctInto.subarray(0, ctW), 0) === null) {
|
|
2103
|
+
return false;
|
|
2104
|
+
}
|
|
2105
|
+
return true;
|
|
2106
|
+
}
|
|
2107
|
+
var init_codecs_selftest = __esm(() => {
|
|
2108
|
+
init_codec2();
|
|
2109
|
+
init_constants();
|
|
2110
|
+
});
|
|
2111
|
+
|
|
2112
|
+
// src/native/ffi/build/instances-selftest.ts
|
|
2113
|
+
function selfTestInstances(b) {
|
|
2114
|
+
const enc = { encode: encodeUtf8 };
|
|
2115
|
+
const dec = { decode: decodeUtf8 };
|
|
2116
|
+
const layoutBuf = new Uint8Array(38 * 4);
|
|
2117
|
+
b.ingressLayout(layoutBuf);
|
|
2118
|
+
const layoutView = new DataView(layoutBuf.buffer, layoutBuf.byteOffset, layoutBuf.byteLength);
|
|
2119
|
+
if (layoutView.getUint32(0, true) !== 0 || layoutView.getUint32(2 * 4, true) !== 2 || layoutView.getUint32(12 * 4, true) !== 48 || layoutView.getUint32(13 * 4, true) !== 1 || layoutView.getUint32(28 * 4, true) !== 32 || layoutView.getUint32(37 * 4, true) !== 8) {
|
|
2120
|
+
return false;
|
|
2121
|
+
}
|
|
2122
|
+
try {
|
|
2123
|
+
b.ingressHandlePacked(0, enc.encode("/"), null, new Uint8Array(64));
|
|
2124
|
+
return false;
|
|
2125
|
+
} catch {}
|
|
2126
|
+
try {
|
|
2127
|
+
b.ingressHandleComponents(0, 0, "/", "", enc.encode("rid"), new Uint8Array(2), null, new Uint8Array(64));
|
|
2128
|
+
return false;
|
|
2129
|
+
} catch {}
|
|
2130
|
+
const routeDesc = new Uint8Array(33);
|
|
2131
|
+
const rd = new DataView(routeDesc.buffer);
|
|
2132
|
+
rd.setUint32(0, 1380930900, true);
|
|
2133
|
+
rd.setUint32(4, 3, true);
|
|
2134
|
+
rd.setUint32(8, 2 * 1024 * 1024, true);
|
|
2135
|
+
rd.setUint32(12, 8192, true);
|
|
2136
|
+
rd.setUint32(16, 8192, true);
|
|
2137
|
+
rd.setUint32(20, 0, true);
|
|
2138
|
+
rd.setUint32(24, 1, true);
|
|
2139
|
+
routeDesc[28] = 0;
|
|
2140
|
+
rd.setUint32(29, 0, true);
|
|
2141
|
+
const routeHandle = b.routeCompile(routeDesc);
|
|
2142
|
+
if (routeHandle === 0) {
|
|
2143
|
+
return false;
|
|
2144
|
+
}
|
|
2145
|
+
const routeFrame = new Uint8Array(15);
|
|
2146
|
+
const rf = new DataView(routeFrame.buffer);
|
|
2147
|
+
rf.setUint32(0, 0, true);
|
|
2148
|
+
rf.setUint32(4, 3, true);
|
|
2149
|
+
routeFrame.set(enc.encode("a=1"), 8);
|
|
2150
|
+
rf.setUint32(11, 0, true);
|
|
2151
|
+
const routeOut = new Uint8Array(64);
|
|
2152
|
+
const routeW = b.routeRun(routeHandle, routeFrame, routeOut);
|
|
2153
|
+
if (routeW <= 8) {
|
|
2154
|
+
return false;
|
|
2155
|
+
}
|
|
2156
|
+
const rv = new DataView(routeOut.buffer);
|
|
2157
|
+
const rFlags = rv.getUint32(0, true);
|
|
2158
|
+
if ((rFlags & 1) === 0 || (rFlags & 4) === 0) {
|
|
2159
|
+
return false;
|
|
2160
|
+
}
|
|
2161
|
+
b.routeDestroy(routeHandle);
|
|
2162
|
+
if (b.conditionalIsNotModified(0, null, null) !== false) {
|
|
2163
|
+
return false;
|
|
2164
|
+
}
|
|
2165
|
+
if (b.mediaTypeMatcherMatches(0, enc.encode("x")) !== false)
|
|
2166
|
+
return false;
|
|
2167
|
+
if (b.acceptNegotiatorNegotiate(0, enc.encode("gzip")) !== null)
|
|
2168
|
+
return false;
|
|
2169
|
+
if (b.acceptNegotiatorNegotiateServer(0, enc.encode("gzip")) !== null)
|
|
2170
|
+
return false;
|
|
2171
|
+
if (b.schemaValidatorValidate(0, enc.encode("{}")) !== false)
|
|
2172
|
+
return false;
|
|
2173
|
+
if (b.jwtSignerVerify(0, enc.encode("a.b.c"), 0) !== null)
|
|
2174
|
+
return false;
|
|
2175
|
+
let phase6Threw = false;
|
|
2176
|
+
try {
|
|
2177
|
+
b.jwtSignerSign(0, enc.encode("{}"), 0);
|
|
2178
|
+
} catch {
|
|
2179
|
+
phase6Threw = true;
|
|
2180
|
+
}
|
|
2181
|
+
if (!phase6Threw)
|
|
2182
|
+
return false;
|
|
2183
|
+
phase6Threw = false;
|
|
2184
|
+
try {
|
|
2185
|
+
b.templateRender(0, enc.encode("{}"));
|
|
2186
|
+
} catch {
|
|
2187
|
+
phase6Threw = true;
|
|
2188
|
+
}
|
|
2189
|
+
if (!phase6Threw)
|
|
2190
|
+
return false;
|
|
2191
|
+
phase6Threw = false;
|
|
2192
|
+
try {
|
|
2193
|
+
b.rateLimiterCheck(0, "k", 0);
|
|
2194
|
+
} catch {
|
|
2195
|
+
phase6Threw = true;
|
|
2196
|
+
}
|
|
2197
|
+
if (!phase6Threw)
|
|
2198
|
+
return false;
|
|
2199
|
+
const jwtClaims = enc.encode('{"sub":"user-1"}');
|
|
2200
|
+
const jwtSecret2 = enc.encode("my-secret");
|
|
2201
|
+
const jwtOut = new Uint8Array(512);
|
|
2202
|
+
const jwtW = b.jwtSignBytesInto(jwtClaims, jwtSecret2, 0, 0, jwtOut);
|
|
2203
|
+
if (jwtW === 0 || dec.decode(jwtOut.subarray(0, jwtW)).split(".").length !== 3) {
|
|
2204
|
+
return false;
|
|
2205
|
+
}
|
|
2206
|
+
const jwtSecret = enc.encode("my-secret");
|
|
2207
|
+
const jwt = b.jwtSignBytes(enc.encode('{"sub":"user-1"}'), jwtSecret, 0, 0);
|
|
2208
|
+
const jwtStr = jwt;
|
|
2209
|
+
const segs = jwtStr.split(".");
|
|
2210
|
+
if (segs.length !== 3 || segs[0] === "" || segs[1] === "" || segs[2] === "") {
|
|
2211
|
+
return false;
|
|
2212
|
+
}
|
|
2213
|
+
const signingInput = enc.encode(`${segs[0]}.${segs[1]}`);
|
|
2214
|
+
const sigHex = b.hmacSha256(jwtSecret, signingInput);
|
|
2215
|
+
const sigBytes = b.base64Decode(enc.encode(segs[2] ?? ""), true, false);
|
|
2216
|
+
if (sigBytes === null || b.hexEncode(sigBytes) !== sigHex) {
|
|
2217
|
+
return false;
|
|
2218
|
+
}
|
|
2219
|
+
const vjwtSecret = enc.encode("verify-secret");
|
|
2220
|
+
const vjwt = b.jwtSignBytes(enc.encode('{"sub":"u-1"}'), vjwtSecret, 0, 0);
|
|
2221
|
+
const vjwtClaims = b.jwtVerify(enc.encode(vjwt), vjwtSecret, 0);
|
|
2222
|
+
if (vjwtClaims === null || !vjwtClaims.includes('"sub":"u-1"')) {
|
|
2223
|
+
return false;
|
|
2224
|
+
}
|
|
2225
|
+
if (b.jwtVerify(enc.encode("tampered.token.value"), vjwtSecret, 0) !== null) {
|
|
2226
|
+
return false;
|
|
2227
|
+
}
|
|
2228
|
+
const edKp = b.ed25519GenerateKeypair();
|
|
2229
|
+
const edMsg = enc.encode("ed25519 bind-time self-test");
|
|
2230
|
+
const edSig = b.ed25519Sign(edMsg, edKp.privateKey);
|
|
2231
|
+
if (edSig.byteLength !== 64 || !b.ed25519Verify(edMsg, edSig, edKp.publicKey)) {
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2234
|
+
const edTampered = edSig.slice();
|
|
2235
|
+
edTampered[0] = (edTampered[0] ?? 0) ^ 255;
|
|
2236
|
+
if (b.ed25519Verify(edMsg, edTampered, edKp.publicKey)) {
|
|
2237
|
+
return false;
|
|
2238
|
+
}
|
|
2239
|
+
const edToken = b.jwtEdDSASign(enc.encode('{"sub":"ffi-self-test"}'), edKp.privateKey, 0, 0);
|
|
2240
|
+
if (edToken === null || edToken.split(".").length !== 3) {
|
|
2241
|
+
return false;
|
|
2242
|
+
}
|
|
2243
|
+
const edClaims = b.jwtEdDSAVerify(enc.encode(edToken), edKp.publicKey, 0);
|
|
2244
|
+
if (edClaims === null || !edClaims.includes('"sub":"ffi-self-test"')) {
|
|
2245
|
+
return false;
|
|
2246
|
+
}
|
|
2247
|
+
if (b.jwtEdDSAVerify(enc.encode("tampered.token.value"), edKp.publicKey, 0) !== null) {
|
|
2248
|
+
return false;
|
|
2249
|
+
}
|
|
2250
|
+
let urlBuilderThrew = false;
|
|
2251
|
+
try {
|
|
2252
|
+
b.urlBuilderResolve(0, enc.encode("g"));
|
|
2253
|
+
} catch {
|
|
2254
|
+
urlBuilderThrew = true;
|
|
2255
|
+
}
|
|
2256
|
+
if (!urlBuilderThrew) {
|
|
2257
|
+
return false;
|
|
2258
|
+
}
|
|
2259
|
+
if (b.queryValidate(0, "route=/a") !== false)
|
|
2260
|
+
return false;
|
|
2261
|
+
if (b.cookieValidate(0, "route=/a") !== false)
|
|
2262
|
+
return false;
|
|
2263
|
+
{
|
|
2264
|
+
const tok = b.sessionSeal("sess-9", '{"n":1}', 1234567, "sekrit");
|
|
2265
|
+
if (tok === null || !tok.includes(".") || !tok.startsWith('{"id":"sess-9"'))
|
|
2266
|
+
return false;
|
|
2267
|
+
const out = new Uint8Array(256);
|
|
2268
|
+
const w = b.sessionOpen(tok, "sekrit", out);
|
|
2269
|
+
if (w <= 13 || out[0] !== 1)
|
|
2270
|
+
return false;
|
|
2271
|
+
if (b.sessionOpen(tok, "wrong", out) !== 0)
|
|
2272
|
+
return false;
|
|
2273
|
+
const enc = new TextEncoder;
|
|
2274
|
+
const tokB = b.sessionSealBytes(enc.encode("sess-9"), enc.encode('{"n":1}'), 1234567, enc.encode("sekrit"));
|
|
2275
|
+
if (tokB === null || tokB !== tok)
|
|
2276
|
+
return false;
|
|
2277
|
+
const outB = new Uint8Array(256);
|
|
2278
|
+
const wB = b.sessionOpenBytes(enc.encode(tokB), enc.encode("sekrit"), outB);
|
|
2279
|
+
if (wB !== w || outB[0] !== 1)
|
|
2280
|
+
return false;
|
|
2281
|
+
if (b.sessionOpenBytes(enc.encode(tokB), enc.encode("wrong"), outB) !== 0)
|
|
2282
|
+
return false;
|
|
2283
|
+
if (b.sessionOpenBytes(enc.encode(tokB), enc.encode("sekrit"), outB.subarray(0, 4)) !== wB)
|
|
2284
|
+
return false;
|
|
2285
|
+
}
|
|
2286
|
+
let rlKeyThrew = false;
|
|
2287
|
+
try {
|
|
2288
|
+
b.rateLimiterCheckKey(0, 12345, 0);
|
|
2289
|
+
} catch {
|
|
2290
|
+
rlKeyThrew = true;
|
|
2291
|
+
}
|
|
2292
|
+
if (!rlKeyThrew) {
|
|
2293
|
+
return false;
|
|
1615
2294
|
}
|
|
2295
|
+
return true;
|
|
1616
2296
|
}
|
|
1617
|
-
var
|
|
1618
|
-
var init_metrics = __esm(() => {
|
|
2297
|
+
var init_instances_selftest = __esm(() => {
|
|
1619
2298
|
init_codec2();
|
|
1620
|
-
init_util();
|
|
1621
|
-
EMPTY = new Uint8Array(0);
|
|
1622
2299
|
});
|
|
1623
2300
|
|
|
1624
2301
|
// src/rust-ffi/scalar/json-packed.ts
|
|
@@ -1730,181 +2407,7 @@ var init_json_packed = __esm(() => {
|
|
|
1730
2407
|
init_runtime();
|
|
1731
2408
|
});
|
|
1732
2409
|
|
|
1733
|
-
// src/native/ffi/build/parse.ts
|
|
1734
|
-
function sseMemoEncode(slot, s) {
|
|
1735
|
-
if (s === slot.str)
|
|
1736
|
-
return slot.bytes;
|
|
1737
|
-
const bytes = encodeUtf8(s);
|
|
1738
|
-
slot.str = s;
|
|
1739
|
-
slot.bytes = bytes;
|
|
1740
|
-
return bytes;
|
|
1741
|
-
}
|
|
1742
|
-
function buildParse(sym, ctx) {
|
|
1743
|
-
const { lenOrView, scratchFor, dateScratch, dateScratchView } = ctx;
|
|
1744
|
-
const httpParsePacked = sym.castrum_http_parse_request_packed;
|
|
1745
|
-
const queryParsePacked = sym.castrum_query_parse_packed;
|
|
1746
|
-
const cookieParsePacked = sym.castrum_cookie_parse_packed;
|
|
1747
|
-
const httpDateIntoRaw = sym.castrum_http_date_into;
|
|
1748
|
-
const sseEncodeIntoRaw = sym.castrum_sse_encode_into;
|
|
1749
|
-
const jsonParsePackedSym = sym.castrum_json_parse_packed;
|
|
1750
|
-
const wsFrameDecodePacked = sym.castrum_ws_frame_decode_packed;
|
|
1751
|
-
const multipartParsePacked = sym.castrum_multipart_parse_packed;
|
|
1752
|
-
const formParsePacked = sym.castrum_form_parse_packed;
|
|
1753
|
-
const wsFrameEncode = sym.castrum_ws_frame_encode;
|
|
1754
|
-
const parseMediaTypeSym = sym.castrum_parse_media_type;
|
|
1755
|
-
const parseHttpDateSym = sym.castrum_parse_http_date;
|
|
1756
|
-
const parseAcceptEncodingSym = sym.castrum_parse_accept_encoding;
|
|
1757
|
-
const urlEncodeQuerySym = sym.castrum_url_encode_query;
|
|
1758
|
-
const urlResolveSym = sym.castrum_url_resolve;
|
|
1759
|
-
const mimeFromExtensionSym = sym.castrum_mime_from_extension;
|
|
1760
|
-
return {
|
|
1761
|
-
httpParseRequestPackedInto(input, output) {
|
|
1762
|
-
const w = Number(httpParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1763
|
-
if (w === 0 && input.length !== 0) {
|
|
1764
|
-
throw new Error("http parse: output buffer too small or malformed request");
|
|
1765
|
-
}
|
|
1766
|
-
return w;
|
|
1767
|
-
},
|
|
1768
|
-
queryParsePackedInto(input, output) {
|
|
1769
|
-
const w = Number(queryParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1770
|
-
if (w === 0 || w > output.length) {
|
|
1771
|
-
throw new Error("query parse: output buffer too small");
|
|
1772
|
-
}
|
|
1773
|
-
return w;
|
|
1774
|
-
},
|
|
1775
|
-
cookieParsePackedInto(input, output) {
|
|
1776
|
-
const w = Number(cookieParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1777
|
-
if (w === 0 || w > output.length) {
|
|
1778
|
-
throw new Error("cookie parse: output buffer too small");
|
|
1779
|
-
}
|
|
1780
|
-
return w;
|
|
1781
|
-
},
|
|
1782
|
-
wsFrameEncode(opcode, payload, mask, fin) {
|
|
1783
|
-
const out = allocOut(payload.length + 14);
|
|
1784
|
-
const w = Number(wsFrameEncode(opcode, payload, lenOrView(payload), flag(mask), flag(fin), out, lenOrView(out)));
|
|
1785
|
-
if (w === 0) {
|
|
1786
|
-
throw new Error("ws frame encode: output buffer too small");
|
|
1787
|
-
}
|
|
1788
|
-
return out.subarray(0, w);
|
|
1789
|
-
},
|
|
1790
|
-
wsFrameEncodeInto(opcode, payload, mask, fin, output) {
|
|
1791
|
-
const need = payload.length + 14;
|
|
1792
|
-
if (output.length < need) {
|
|
1793
|
-
throw new Error("ws frame encode: output buffer too small");
|
|
1794
|
-
}
|
|
1795
|
-
const w = Number(wsFrameEncode(opcode, payload, lenOrView(payload), flag(mask), flag(fin), output, lenOrView(output)));
|
|
1796
|
-
if (w === 0) {
|
|
1797
|
-
throw new Error("ws frame encode: output buffer too small");
|
|
1798
|
-
}
|
|
1799
|
-
return w;
|
|
1800
|
-
},
|
|
1801
|
-
wsFrameDecodePacked(data) {
|
|
1802
|
-
const out = new Uint8Array(data.length + 6);
|
|
1803
|
-
const w = Number(wsFrameDecodePacked(data, lenOrView(data), out, lenOrView(out)));
|
|
1804
|
-
return w === 0 ? null : out.subarray(0, w);
|
|
1805
|
-
},
|
|
1806
|
-
wsFrameDecodePackedInto(data, output) {
|
|
1807
|
-
const w = Number(wsFrameDecodePacked(data, lenOrView(data), output, lenOrView(output)));
|
|
1808
|
-
return w === 0 ? null : w;
|
|
1809
|
-
},
|
|
1810
|
-
multipartParsePacked(body, boundary) {
|
|
1811
|
-
return growExact((out) => Number(multipartParsePacked(body, lenOrView(body), boundary, lenOrView(boundary), out, lenOrView(out))), Math.min(body.length + boundary.length + 64, 64 * 1024), 128 * 1024 * 1024, "multipart parse: output buffer too small");
|
|
1812
|
-
},
|
|
1813
|
-
multipartParsePackedInto(body, boundary, output) {
|
|
1814
|
-
const w = Number(multipartParsePacked(body, lenOrView(body), boundary, lenOrView(boundary), output, lenOrView(output)));
|
|
1815
|
-
if (w === 0 || w > output.length) {
|
|
1816
|
-
throw new Error("multipart parse: output buffer too small or malformed body");
|
|
1817
|
-
}
|
|
1818
|
-
return w;
|
|
1819
|
-
},
|
|
1820
|
-
formParsePackedInto(input, output) {
|
|
1821
|
-
const w = Number(formParsePacked(input, lenOrView(input), output, lenOrView(output)));
|
|
1822
|
-
if (w === 0 || w > output.length) {
|
|
1823
|
-
throw new Error("form parse: output buffer too small");
|
|
1824
|
-
}
|
|
1825
|
-
return w;
|
|
1826
|
-
},
|
|
1827
|
-
jsonParsePacked(input) {
|
|
1828
|
-
return growExact((out) => Number(jsonParsePackedSym(input, lenOrView(input), out, lenOrView(out))), Math.min(input.length + (input.length >> 1), 16 * 1024 * 1024), Math.max(1024 * 1024, input.length * 16), "json parse packed: invalid JSON or output buffer too small");
|
|
1829
|
-
},
|
|
1830
|
-
parseMediaType(input) {
|
|
1831
|
-
let out = scratchFor(input.length * 2 + 128);
|
|
1832
|
-
let w = Number(parseMediaTypeSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1833
|
-
if (w > out.length) {
|
|
1834
|
-
out = new Uint8Array(w);
|
|
1835
|
-
w = Number(parseMediaTypeSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1836
|
-
}
|
|
1837
|
-
if (w === 0 || w > out.length) {
|
|
1838
|
-
throw new Error("media type parse: invalid media type or output buffer too small");
|
|
1839
|
-
}
|
|
1840
|
-
return out.subarray(0, w);
|
|
1841
|
-
},
|
|
1842
|
-
parseHttpDate(input) {
|
|
1843
|
-
const out = dateScratch;
|
|
1844
|
-
const w = Number(parseHttpDateSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1845
|
-
if (w === 0) {
|
|
1846
|
-
throw new Error("http date parse: output buffer too small");
|
|
1847
|
-
}
|
|
1848
|
-
if (out[0] === 0)
|
|
1849
|
-
return null;
|
|
1850
|
-
return dateScratchView.getBigInt64(1, true);
|
|
1851
|
-
},
|
|
1852
|
-
parseAcceptEncoding(input) {
|
|
1853
|
-
let out = scratchFor(input.length * 2 + 128);
|
|
1854
|
-
let w = Number(parseAcceptEncodingSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1855
|
-
if (w > out.length) {
|
|
1856
|
-
out = new Uint8Array(w);
|
|
1857
|
-
w = Number(parseAcceptEncodingSym(input, lenOrView(input), out, lenOrView(out)));
|
|
1858
|
-
}
|
|
1859
|
-
if (w === 0 || w > out.length) {
|
|
1860
|
-
throw new Error("accept encoding parse: output buffer too small");
|
|
1861
|
-
}
|
|
1862
|
-
return out.subarray(0, w);
|
|
1863
|
-
},
|
|
1864
|
-
urlEncodeQuery(input) {
|
|
1865
|
-
return urlEncodeQuerySym(input, lenOrView(input));
|
|
1866
|
-
},
|
|
1867
|
-
urlResolve(base, reference) {
|
|
1868
|
-
return urlResolveSym(base, lenOrView(base), reference, lenOrView(reference));
|
|
1869
|
-
},
|
|
1870
|
-
mimeFromExtension(ext) {
|
|
1871
|
-
return mimeFromExtensionSym(ext);
|
|
1872
|
-
},
|
|
1873
|
-
httpDateInto(secs, output) {
|
|
1874
|
-
const w = Number(httpDateIntoRaw(secs, output, lenOrView(output)));
|
|
1875
|
-
if (w === 0) {
|
|
1876
|
-
throw new Error("http date: output buffer too small or year out of range");
|
|
1877
|
-
}
|
|
1878
|
-
return w;
|
|
1879
|
-
},
|
|
1880
|
-
httpDate(secs) {
|
|
1881
|
-
const out = scratchFor(32);
|
|
1882
|
-
const w = Number(httpDateIntoRaw(secs ?? 0, out, lenOrView(out)));
|
|
1883
|
-
if (w !== 0)
|
|
1884
|
-
return decodeUtf8(out.subarray(0, w));
|
|
1885
|
-
return new Date((secs ?? 0) * 1000).toUTCString();
|
|
1886
|
-
},
|
|
1887
|
-
sseEncodeEvent(event, data, id, retry) {
|
|
1888
|
-
const ev = event === null ? EMPTY_VIEW : sseMemoEncode(sseMemoEvent, event);
|
|
1889
|
-
const idv = id === null ? EMPTY_VIEW : sseMemoEncode(sseMemoId, id);
|
|
1890
|
-
const flags = (event === null ? 0 : 1) | (id === null ? 0 : 2) | (retry === null ? 0 : 4);
|
|
1891
|
-
return growExact((out) => Number(sseEncodeIntoRaw(ev, lenOrView(ev), data, lenOrView(data), idv, lenOrView(idv), flags, retry ?? 0, out, lenOrView(out))), data.length + 64, 1 << 20, "sse encode: output buffer too small");
|
|
1892
|
-
},
|
|
1893
|
-
sseEncodeEventInto(event, data, id, retry, output) {
|
|
1894
|
-
const ev = event === null ? EMPTY_VIEW : sseMemoEncode(sseMemoEvent, event);
|
|
1895
|
-
const idv = id === null ? EMPTY_VIEW : sseMemoEncode(sseMemoId, id);
|
|
1896
|
-
const flags = (event === null ? 0 : 1) | (id === null ? 0 : 2) | (retry === null ? 0 : 4);
|
|
1897
|
-
const w = Number(sseEncodeIntoRaw(ev, lenOrView(ev), data, lenOrView(data), idv, lenOrView(idv), flags, retry ?? 0, output, lenOrView(output)));
|
|
1898
|
-
if (w === 0) {
|
|
1899
|
-
throw new Error("sse encode: invalid UTF-8 in event/id");
|
|
1900
|
-
}
|
|
1901
|
-
if (w > output.length) {
|
|
1902
|
-
throw new Error("sse encode: output buffer too small");
|
|
1903
|
-
}
|
|
1904
|
-
return w;
|
|
1905
|
-
}
|
|
1906
|
-
};
|
|
1907
|
-
}
|
|
2410
|
+
// src/native/ffi/build/parse-selftest.ts
|
|
1908
2411
|
function selfTestParse(b) {
|
|
1909
2412
|
const enc = { encode: encodeUtf8 };
|
|
1910
2413
|
const dec = { decode: decodeUtf8 };
|
|
@@ -2070,118 +2573,23 @@ data: hello
|
|
|
2070
2573
|
}
|
|
2071
2574
|
if (b.mimeFromExtension(".js") !== "text/javascript") {
|
|
2072
2575
|
return false;
|
|
2073
|
-
}
|
|
2074
|
-
if (b.mimeFromExtension("nope") !== "application/octet-stream") {
|
|
2075
|
-
return false;
|
|
2076
|
-
}
|
|
2077
|
-
if (b.httpDate(784111777) !== "Sun, 06 Nov 1994 08:49:37 GMT") {
|
|
2078
|
-
return false;
|
|
2079
|
-
}
|
|
2080
|
-
const frameInto = new Uint8Array(64);
|
|
2081
|
-
if (b.wsFrameEncodeInto(1, SELFTEST_HEX, false, true, frameInto) === 0 || frameInto[0] !== 129) {
|
|
2082
|
-
return false;
|
|
2083
|
-
}
|
|
2084
|
-
return true;
|
|
2085
|
-
}
|
|
2086
|
-
var sseMemoEvent, sseMemoId;
|
|
2087
|
-
var init_parse = __esm(() => {
|
|
2088
|
-
init_json_packed();
|
|
2089
|
-
init_codec2();
|
|
2090
|
-
init_constants();
|
|
2091
|
-
init_util();
|
|
2092
|
-
sseMemoEvent = { str: null, bytes: EMPTY_VIEW };
|
|
2093
|
-
sseMemoId = { str: null, bytes: EMPTY_VIEW };
|
|
2094
|
-
});
|
|
2095
|
-
|
|
2096
|
-
// src/native/ffi/build/task.ts
|
|
2097
|
-
function buildTask(sym, ctx) {
|
|
2098
|
-
const { lenOrView } = ctx;
|
|
2099
|
-
const init = sym.castrum_task_init;
|
|
2100
|
-
const submit = sym.castrum_task_submit;
|
|
2101
|
-
const submitOut = sym.castrum_task_submit_out;
|
|
2102
|
-
const submitSlice = sym.castrum_task_submit_slice;
|
|
2103
|
-
const drain = sym.castrum_task_drain;
|
|
2104
|
-
const pending = sym.castrum_task_pending;
|
|
2105
|
-
const cancel = sym.castrum_task_cancel;
|
|
2106
|
-
const setDoorbell = sym.castrum_task_set_doorbell;
|
|
2107
|
-
const shutdown = sym.castrum_task_shutdown;
|
|
2108
|
-
const threads = sym.castrum_task_threads;
|
|
2109
|
-
return {
|
|
2110
|
-
taskInit: (n) => Number(init(n)),
|
|
2111
|
-
taskSubmit: (op, args, taskId) => Number(submit(op, args, lenOrView(args), taskId)),
|
|
2112
|
-
taskSubmitOut: (op, args, taskId, output) => Number(submitOut(op, args, lenOrView(args), taskId, output, lenOrView(output))),
|
|
2113
|
-
taskSubmitSlice: (op, hdr, data, taskId) => Number(submitSlice(op, hdr, lenOrView(hdr), data, lenOrView(data), taskId)),
|
|
2114
|
-
taskDrain: (output) => Number(drain(output, lenOrView(output))),
|
|
2115
|
-
taskPending: () => Number(pending()),
|
|
2116
|
-
taskCancel: (taskId) => Number(cancel(taskId)),
|
|
2117
|
-
taskSetDoorbell: (ptr) => Number(setDoorbell(ptr)),
|
|
2118
|
-
taskShutdown: () => Number(shutdown()),
|
|
2119
|
-
taskThreads: () => Number(threads())
|
|
2120
|
-
};
|
|
2121
|
-
}
|
|
2122
|
-
function selfTestTask(b) {
|
|
2123
|
-
try {
|
|
2124
|
-
if (typeof b.taskPending() !== "number")
|
|
2125
|
-
return false;
|
|
2126
|
-
if (typeof b.taskThreads() !== "number" || b.taskThreads() < 0)
|
|
2127
|
-
return false;
|
|
2128
|
-
if (b.taskSetDoorbell(0) !== 1)
|
|
2129
|
-
return false;
|
|
2130
|
-
if (typeof b.taskDrain(new Uint8Array(4)) !== "number")
|
|
2131
|
-
return false;
|
|
2132
|
-
if (typeof b.taskSubmitOut !== "function")
|
|
2133
|
-
return false;
|
|
2134
|
-
if (typeof b.taskSubmitSlice !== "function")
|
|
2135
|
-
return false;
|
|
2136
|
-
return true;
|
|
2137
|
-
} catch {
|
|
2576
|
+
}
|
|
2577
|
+
if (b.mimeFromExtension("nope") !== "application/octet-stream") {
|
|
2138
2578
|
return false;
|
|
2139
2579
|
}
|
|
2580
|
+
if (b.httpDate(784111777) !== "Sun, 06 Nov 1994 08:49:37 GMT") {
|
|
2581
|
+
return false;
|
|
2582
|
+
}
|
|
2583
|
+
const frameInto = new Uint8Array(64);
|
|
2584
|
+
if (b.wsFrameEncodeInto(1, SELFTEST_HEX, false, true, frameInto) === 0 || frameInto[0] !== 129) {
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2587
|
+
return true;
|
|
2140
2588
|
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
const lenOrView = (v) => useBufferLength ? v : v.length;
|
|
2146
|
-
let encodeScratch = new Uint8Array(0);
|
|
2147
|
-
const scratchFor = (size) => {
|
|
2148
|
-
if (encodeScratch.byteLength < size)
|
|
2149
|
-
encodeScratch = new Uint8Array(size);
|
|
2150
|
-
return encodeScratch;
|
|
2151
|
-
};
|
|
2152
|
-
const jsonSumOut = new Uint8Array(9);
|
|
2153
|
-
const jsonSumView = new DataView(jsonSumOut.buffer);
|
|
2154
|
-
const dateScratch = new Uint8Array(9);
|
|
2155
|
-
const dateScratchView = new DataView(dateScratch.buffer);
|
|
2156
|
-
const rateScratch = new Uint8Array(13);
|
|
2157
|
-
const rateScratchView = new DataView(rateScratch.buffer);
|
|
2158
|
-
const ctx = {
|
|
2159
|
-
useBufferLength,
|
|
2160
|
-
lenOrView,
|
|
2161
|
-
oneArg,
|
|
2162
|
-
scratchFor,
|
|
2163
|
-
jsonSumOut,
|
|
2164
|
-
jsonSumView,
|
|
2165
|
-
dateScratch,
|
|
2166
|
-
dateScratchView,
|
|
2167
|
-
rateScratch,
|
|
2168
|
-
rateScratchView
|
|
2169
|
-
};
|
|
2170
|
-
return {
|
|
2171
|
-
...buildCodecs(sym, ctx),
|
|
2172
|
-
...buildCompress(sym, ctx),
|
|
2173
|
-
...buildParse(sym, ctx),
|
|
2174
|
-
...buildInstances(sym, ctx),
|
|
2175
|
-
...buildMetrics(sym, ctx),
|
|
2176
|
-
...buildTask(sym, ctx)
|
|
2177
|
-
};
|
|
2178
|
-
}
|
|
2179
|
-
var init_build = __esm(() => {
|
|
2180
|
-
init_codecs();
|
|
2181
|
-
init_compress();
|
|
2182
|
-
init_instances();
|
|
2183
|
-
init_metrics();
|
|
2184
|
-
init_parse();
|
|
2589
|
+
var init_parse_selftest = __esm(() => {
|
|
2590
|
+
init_json_packed();
|
|
2591
|
+
init_codec2();
|
|
2592
|
+
init_constants();
|
|
2185
2593
|
});
|
|
2186
2594
|
|
|
2187
2595
|
// src/native/ffi/selftest.ts
|
|
@@ -2189,11 +2597,11 @@ function selfTest(b) {
|
|
|
2189
2597
|
return selfTestCodecs(b) && selfTestCompress(b) && selfTestParse(b) && selfTestInstances(b) && selfTestMetrics(b) && selfTestTask(b);
|
|
2190
2598
|
}
|
|
2191
2599
|
var init_selftest = __esm(() => {
|
|
2192
|
-
|
|
2600
|
+
init_codecs_selftest();
|
|
2193
2601
|
init_compress();
|
|
2194
|
-
|
|
2602
|
+
init_instances_selftest();
|
|
2195
2603
|
init_metrics();
|
|
2196
|
-
|
|
2604
|
+
init_parse_selftest();
|
|
2197
2605
|
});
|
|
2198
2606
|
|
|
2199
2607
|
// src/native/loader.ts
|
|
@@ -2353,6 +2761,12 @@ function getBunFFI() {
|
|
|
2353
2761
|
cached = bind();
|
|
2354
2762
|
return cached;
|
|
2355
2763
|
}
|
|
2764
|
+
function ffiAddonPath() {
|
|
2765
|
+
if (cached === undefined) {
|
|
2766
|
+
getBunFFI();
|
|
2767
|
+
}
|
|
2768
|
+
return boundAddonPath;
|
|
2769
|
+
}
|
|
2356
2770
|
function probeBufferLength(dlopen, path) {
|
|
2357
2771
|
try {
|
|
2358
2772
|
const { symbols, close } = dlopen(path, {
|
|
@@ -2386,350 +2800,7 @@ function bind(forcedPath, tried = []) {
|
|
|
2386
2800
|
const { dlopen } = __require("bun:ffi");
|
|
2387
2801
|
const useBufferLength = probeBufferLength(dlopen, path);
|
|
2388
2802
|
bufferAbiMode = useBufferLength ? "buffer-pair" : "ptr-len";
|
|
2389
|
-
const
|
|
2390
|
-
const abi = (shape) => {
|
|
2391
|
-
if (!useBufferLength)
|
|
2392
|
-
return shape;
|
|
2393
|
-
const out = [];
|
|
2394
|
-
for (let i = 0;i < shape.length; i++) {
|
|
2395
|
-
const t = shape[i];
|
|
2396
|
-
if (t === "ptr") {
|
|
2397
|
-
out.push("buffer");
|
|
2398
|
-
out.push("buffer_length");
|
|
2399
|
-
i++;
|
|
2400
|
-
} else {
|
|
2401
|
-
out.push(t);
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
return out;
|
|
2405
|
-
};
|
|
2406
|
-
const U64_FAST = "u64_fast";
|
|
2407
|
-
const { symbols } = dlopen(path, {
|
|
2408
|
-
castrum_crc32: { args: inputAbi, returns: "u32" },
|
|
2409
|
-
castrum_fnv1a64: { args: inputAbi, returns: "u64" },
|
|
2410
|
-
castrum_xxh3: { args: inputAbi, returns: "u64" },
|
|
2411
|
-
castrum_json_valid: { args: inputAbi, returns: "u8" },
|
|
2412
|
-
castrum_utf8_valid: { args: inputAbi, returns: "u8" },
|
|
2413
|
-
castrum_hex_encode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2414
|
-
castrum_hex_decode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2415
|
-
castrum_url_encode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2416
|
-
castrum_url_decode: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2417
|
-
castrum_validate_email: { args: ["cstring"], returns: "u8" },
|
|
2418
|
-
castrum_validate_uuid: { args: ["cstring"], returns: "u8" },
|
|
2419
|
-
castrum_validate_ipv4: { args: ["cstring"], returns: "u8" },
|
|
2420
|
-
castrum_validate_ipv6: { args: ["cstring"], returns: "u8" },
|
|
2421
|
-
castrum_validate_email_bytes: { args: inputAbi, returns: "u8" },
|
|
2422
|
-
castrum_validate_uuid_bytes: { args: inputAbi, returns: "u8" },
|
|
2423
|
-
castrum_validate_ipv4_bytes: { args: inputAbi, returns: "u8" },
|
|
2424
|
-
castrum_validate_ipv6_bytes: { args: inputAbi, returns: "u8" },
|
|
2425
|
-
castrum_json_sum_ids: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2426
|
-
castrum_hmac_sha256_verify: {
|
|
2427
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2428
|
-
returns: "u8"
|
|
2429
|
-
},
|
|
2430
|
-
castrum_csrf_verify: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "u8" },
|
|
2431
|
-
castrum_password_verify: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "u8" },
|
|
2432
|
-
castrum_password_verify_bcrypt: {
|
|
2433
|
-
args: abi(["ptr", "usize", "cstring"]),
|
|
2434
|
-
returns: "u8"
|
|
2435
|
-
},
|
|
2436
|
-
castrum_ws_accept_key: { args: ["cstring"], returns: "cstring" },
|
|
2437
|
-
castrum_ws_accept_key_into: {
|
|
2438
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2439
|
-
returns: U64_FAST
|
|
2440
|
-
},
|
|
2441
|
-
castrum_etag: { args: abi(["ptr", "usize", "u8"]), returns: "cstring" },
|
|
2442
|
-
castrum_etag_into: { args: abi(["ptr", "usize", "u8", "ptr", "usize"]), returns: U64_FAST },
|
|
2443
|
-
castrum_conditional_is_not_modified: {
|
|
2444
|
-
args: abi(["usize", "ptr", "usize", "ptr", "usize", "u8"]),
|
|
2445
|
-
returns: "u8"
|
|
2446
|
-
},
|
|
2447
|
-
castrum_media_type_matcher_matches: {
|
|
2448
|
-
args: abi(["usize", "ptr", "usize"]),
|
|
2449
|
-
returns: "u8"
|
|
2450
|
-
},
|
|
2451
|
-
castrum_accept_negotiator_negotiate: {
|
|
2452
|
-
args: abi(["usize", "ptr", "usize"]),
|
|
2453
|
-
returns: "cstring"
|
|
2454
|
-
},
|
|
2455
|
-
castrum_accept_negotiator_negotiate_server: {
|
|
2456
|
-
args: ["usize", "cstring"],
|
|
2457
|
-
returns: "cstring"
|
|
2458
|
-
},
|
|
2459
|
-
castrum_jwt_signer_sign: {
|
|
2460
|
-
args: abi(["usize", "ptr", "usize", "i64", "ptr", "usize"]),
|
|
2461
|
-
returns: U64_FAST
|
|
2462
|
-
},
|
|
2463
|
-
castrum_jwt_signer_verify: {
|
|
2464
|
-
args: abi(["usize", "ptr", "usize", "i64", "ptr", "usize"]),
|
|
2465
|
-
returns: U64_FAST
|
|
2466
|
-
},
|
|
2467
|
-
castrum_template_render: {
|
|
2468
|
-
args: abi(["usize", "ptr", "usize", "ptr", "usize"]),
|
|
2469
|
-
returns: U64_FAST
|
|
2470
|
-
},
|
|
2471
|
-
castrum_schema_validator_validate: {
|
|
2472
|
-
args: abi(["usize", "ptr", "usize"]),
|
|
2473
|
-
returns: "u8"
|
|
2474
|
-
},
|
|
2475
|
-
castrum_rate_limiter_check: {
|
|
2476
|
-
args: abi(["usize", "cstring", "i64", "ptr", "usize"]),
|
|
2477
|
-
returns: U64_FAST
|
|
2478
|
-
},
|
|
2479
|
-
castrum_rate_limiter_check_key: {
|
|
2480
|
-
args: abi(["usize", "i64", "i64", "ptr", "usize"]),
|
|
2481
|
-
returns: U64_FAST
|
|
2482
|
-
},
|
|
2483
|
-
castrum_random_token: { args: ["u32"], returns: "cstring" },
|
|
2484
|
-
castrum_random_token_into: { args: ["u32", ...abi(["ptr", "usize"])], returns: U64_FAST },
|
|
2485
|
-
castrum_base64_encode: {
|
|
2486
|
-
args: abi(["ptr", "usize", "ptr", "usize", "u8", "u8"]),
|
|
2487
|
-
returns: U64_FAST
|
|
2488
|
-
},
|
|
2489
|
-
castrum_base64_decode: {
|
|
2490
|
-
args: abi(["ptr", "usize", "ptr", "usize", "u8", "u8"]),
|
|
2491
|
-
returns: U64_FAST
|
|
2492
|
-
},
|
|
2493
|
-
castrum_hmac_sha256: {
|
|
2494
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2495
|
-
returns: U64_FAST
|
|
2496
|
-
},
|
|
2497
|
-
castrum_sign_cookie: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "cstring" },
|
|
2498
|
-
castrum_sign_cookie_into: {
|
|
2499
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2500
|
-
returns: U64_FAST
|
|
2501
|
-
},
|
|
2502
|
-
castrum_verify_cookie: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: "cstring" },
|
|
2503
|
-
castrum_verify_cookie_into: {
|
|
2504
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2505
|
-
returns: U64_FAST
|
|
2506
|
-
},
|
|
2507
|
-
castrum_csrf_token: { args: abi(["ptr", "usize"]), returns: "cstring" },
|
|
2508
|
-
castrum_csrf_token_into: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2509
|
-
castrum_password_hash: {
|
|
2510
|
-
args: abi(["ptr", "usize", "ptr", "usize", "u32", "u32", "u32", "u32", "ptr", "usize"]),
|
|
2511
|
-
returns: U64_FAST
|
|
2512
|
-
},
|
|
2513
|
-
castrum_password_hash_bcrypt: {
|
|
2514
|
-
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
2515
|
-
returns: U64_FAST
|
|
2516
|
-
},
|
|
2517
|
-
castrum_pbkdf2_sha256: {
|
|
2518
|
-
args: abi(["ptr", "usize", "ptr", "usize", "u32", "u32", "ptr", "usize"]),
|
|
2519
|
-
returns: U64_FAST
|
|
2520
|
-
},
|
|
2521
|
-
castrum_aead_encrypt: {
|
|
2522
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize", "u8", "ptr", "usize"]),
|
|
2523
|
-
returns: U64_FAST
|
|
2524
|
-
},
|
|
2525
|
-
castrum_aead_decrypt: {
|
|
2526
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize", "u8", "ptr", "usize"]),
|
|
2527
|
-
returns: U64_FAST
|
|
2528
|
-
},
|
|
2529
|
-
castrum_ws_frame_encode: {
|
|
2530
|
-
args: abi(["u8", "ptr", "usize", "u8", "u8", "ptr", "usize"]),
|
|
2531
|
-
returns: U64_FAST
|
|
2532
|
-
},
|
|
2533
|
-
castrum_json_patch: {
|
|
2534
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2535
|
-
returns: U64_FAST
|
|
2536
|
-
},
|
|
2537
|
-
castrum_gzip_compress: {
|
|
2538
|
-
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
2539
|
-
returns: U64_FAST
|
|
2540
|
-
},
|
|
2541
|
-
castrum_gzip_decompress: {
|
|
2542
|
-
args: abi(["ptr", "usize", "usize", "ptr", "usize"]),
|
|
2543
|
-
returns: U64_FAST
|
|
2544
|
-
},
|
|
2545
|
-
castrum_brotli_compress: {
|
|
2546
|
-
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
2547
|
-
returns: U64_FAST
|
|
2548
|
-
},
|
|
2549
|
-
castrum_brotli_decompress: {
|
|
2550
|
-
args: abi(["ptr", "usize", "usize", "ptr", "usize"]),
|
|
2551
|
-
returns: U64_FAST
|
|
2552
|
-
},
|
|
2553
|
-
castrum_gzip_isize: { args: abi(["ptr", "usize"]), returns: "u32" },
|
|
2554
|
-
castrum_http_parse_request_packed: {
|
|
2555
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2556
|
-
returns: U64_FAST
|
|
2557
|
-
},
|
|
2558
|
-
castrum_query_parse_packed: {
|
|
2559
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2560
|
-
returns: U64_FAST
|
|
2561
|
-
},
|
|
2562
|
-
castrum_cookie_parse_packed: {
|
|
2563
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2564
|
-
returns: U64_FAST
|
|
2565
|
-
},
|
|
2566
|
-
castrum_http_date_into: { args: ["f64", ...abi(["ptr", "usize"])], returns: U64_FAST },
|
|
2567
|
-
castrum_sse_encode_into: {
|
|
2568
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize", "u8", "u32", "ptr", "usize"]),
|
|
2569
|
-
returns: U64_FAST
|
|
2570
|
-
},
|
|
2571
|
-
castrum_jwt_sign_bytes: {
|
|
2572
|
-
args: abi(["ptr", "usize", "ptr", "usize", "i64", "i64"]),
|
|
2573
|
-
returns: "cstring"
|
|
2574
|
-
},
|
|
2575
|
-
castrum_jwt_sign_bytes_into: {
|
|
2576
|
-
args: abi(["ptr", "usize", "ptr", "usize", "i64", "i64", "ptr", "usize"]),
|
|
2577
|
-
returns: U64_FAST
|
|
2578
|
-
},
|
|
2579
|
-
castrum_ed25519_generate_keypair: {
|
|
2580
|
-
args: abi(["ptr", "usize"]),
|
|
2581
|
-
returns: U64_FAST
|
|
2582
|
-
},
|
|
2583
|
-
castrum_ed25519_sign: {
|
|
2584
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2585
|
-
returns: U64_FAST
|
|
2586
|
-
},
|
|
2587
|
-
castrum_ed25519_verify: {
|
|
2588
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2589
|
-
returns: "u32"
|
|
2590
|
-
},
|
|
2591
|
-
castrum_jwt_eddsa_sign: {
|
|
2592
|
-
args: abi(["ptr", "usize", "ptr", "usize", "i64", "i64"]),
|
|
2593
|
-
returns: "cstring"
|
|
2594
|
-
},
|
|
2595
|
-
castrum_jwt_eddsa_verify: {
|
|
2596
|
-
args: abi(["ptr", "usize", "ptr", "usize", "i64"]),
|
|
2597
|
-
returns: "cstring"
|
|
2598
|
-
},
|
|
2599
|
-
castrum_ws_frame_decode_packed: {
|
|
2600
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2601
|
-
returns: U64_FAST
|
|
2602
|
-
},
|
|
2603
|
-
castrum_multipart_parse_packed: {
|
|
2604
|
-
args: abi(["ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2605
|
-
returns: U64_FAST
|
|
2606
|
-
},
|
|
2607
|
-
castrum_form_parse_packed: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2608
|
-
castrum_json_parse_packed: {
|
|
2609
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2610
|
-
returns: U64_FAST
|
|
2611
|
-
},
|
|
2612
|
-
castrum_parse_media_type: {
|
|
2613
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2614
|
-
returns: U64_FAST
|
|
2615
|
-
},
|
|
2616
|
-
castrum_parse_http_date: {
|
|
2617
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2618
|
-
returns: U64_FAST
|
|
2619
|
-
},
|
|
2620
|
-
castrum_parse_accept_encoding: {
|
|
2621
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2622
|
-
returns: U64_FAST
|
|
2623
|
-
},
|
|
2624
|
-
castrum_url_encode_query: { args: abi(["ptr", "usize"]), returns: "cstring" },
|
|
2625
|
-
castrum_url_resolve: {
|
|
2626
|
-
args: abi(["ptr", "usize", "ptr", "usize"]),
|
|
2627
|
-
returns: "cstring"
|
|
2628
|
-
},
|
|
2629
|
-
castrum_url_builder_resolve: {
|
|
2630
|
-
args: abi(["usize", "ptr", "usize", "ptr", "usize"]),
|
|
2631
|
-
returns: U64_FAST
|
|
2632
|
-
},
|
|
2633
|
-
castrum_mime_from_extension: { args: ["cstring"], returns: "cstring" },
|
|
2634
|
-
castrum_jwt_verify: {
|
|
2635
|
-
args: abi(["ptr", "usize", "ptr", "usize", "i64"]),
|
|
2636
|
-
returns: "cstring"
|
|
2637
|
-
},
|
|
2638
|
-
castrum_ingress_handle_packed: {
|
|
2639
|
-
args: abi(["usize", "ptr", "usize", "ptr", "usize", "ptr", "usize"]),
|
|
2640
|
-
returns: U64_FAST
|
|
2641
|
-
},
|
|
2642
|
-
castrum_ingress_handle_components: {
|
|
2643
|
-
args: abi([
|
|
2644
|
-
"usize",
|
|
2645
|
-
"u8",
|
|
2646
|
-
"cstring",
|
|
2647
|
-
"cstring",
|
|
2648
|
-
"ptr",
|
|
2649
|
-
"usize",
|
|
2650
|
-
"ptr",
|
|
2651
|
-
"usize",
|
|
2652
|
-
"ptr",
|
|
2653
|
-
"usize",
|
|
2654
|
-
"ptr",
|
|
2655
|
-
"usize"
|
|
2656
|
-
]),
|
|
2657
|
-
returns: U64_FAST
|
|
2658
|
-
},
|
|
2659
|
-
castrum_ingress_layout: { args: abi(["ptr", "usize"]), returns: U64_FAST },
|
|
2660
|
-
castrum_route_compile: { args: abi(["ptr", "usize"]), returns: U64_FAST },
|
|
2661
|
-
castrum_route_run: {
|
|
2662
|
-
args: abi(["usize", "ptr", "usize", "ptr", "usize"]),
|
|
2663
|
-
returns: U64_FAST
|
|
2664
|
-
},
|
|
2665
|
-
castrum_route_destroy: { args: ["usize"], returns: "void" },
|
|
2666
|
-
castrum_hex_validate_batch: {
|
|
2667
|
-
args: abi(["ptr", "usize", "u32", "ptr", "usize"]),
|
|
2668
|
-
returns: U64_FAST
|
|
2669
|
-
},
|
|
2670
|
-
castrum_regex_escape: { args: abi(["ptr", "usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2671
|
-
castrum_metrics_create: { args: [], returns: U64_FAST },
|
|
2672
|
-
castrum_metrics_counter: { args: ["usize", "cstring", "cstring"], returns: "u32" },
|
|
2673
|
-
castrum_metrics_gauge: { args: ["usize", "cstring", "cstring"], returns: "u32" },
|
|
2674
|
-
castrum_metrics_histogram: {
|
|
2675
|
-
args: ["usize", "cstring", "cstring", "cstring"],
|
|
2676
|
-
returns: "u32"
|
|
2677
|
-
},
|
|
2678
|
-
castrum_metrics_record: {
|
|
2679
|
-
args: abi(["usize", "u32", "ptr", "usize", "f64"]),
|
|
2680
|
-
returns: "u8"
|
|
2681
|
-
},
|
|
2682
|
-
castrum_metrics_gauge_set: {
|
|
2683
|
-
args: abi(["usize", "u32", "ptr", "usize", "f64"]),
|
|
2684
|
-
returns: "u8"
|
|
2685
|
-
},
|
|
2686
|
-
castrum_metrics_record_str: {
|
|
2687
|
-
args: ["usize", "u32", "cstring", "f64"],
|
|
2688
|
-
returns: "u8"
|
|
2689
|
-
},
|
|
2690
|
-
castrum_metrics_gauge_set_str: {
|
|
2691
|
-
args: ["usize", "u32", "cstring", "f64"],
|
|
2692
|
-
returns: "u8"
|
|
2693
|
-
},
|
|
2694
|
-
castrum_regex_escape_str: { args: ["cstring"], returns: "cstring" },
|
|
2695
|
-
castrum_hex_validate_batch_str: {
|
|
2696
|
-
args: abi(["cstring", "u32", "ptr", "usize"]),
|
|
2697
|
-
returns: U64_FAST
|
|
2698
|
-
},
|
|
2699
|
-
castrum_metrics_render: { args: abi(["usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2700
|
-
castrum_metrics_snapshot: { args: abi(["usize", "ptr", "usize"]), returns: U64_FAST },
|
|
2701
|
-
castrum_metrics_record_batch: {
|
|
2702
|
-
args: abi(["usize", "ptr", "usize"]),
|
|
2703
|
-
returns: "u8"
|
|
2704
|
-
},
|
|
2705
|
-
castrum_query_validate: { args: ["usize", "cstring"], returns: "u8" },
|
|
2706
|
-
castrum_cookie_validate: { args: ["usize", "cstring"], returns: "u8" },
|
|
2707
|
-
castrum_session_seal: {
|
|
2708
|
-
args: ["cstring", "cstring", "i64", "cstring"],
|
|
2709
|
-
returns: "cstring"
|
|
2710
|
-
},
|
|
2711
|
-
castrum_session_open: {
|
|
2712
|
-
args: abi(["cstring", "cstring", "ptr", "usize"]),
|
|
2713
|
-
returns: U64_FAST
|
|
2714
|
-
},
|
|
2715
|
-
castrum_task_init: { args: ["u32"], returns: "u32" },
|
|
2716
|
-
castrum_task_submit: { args: abi(["u32", "ptr", "usize", "usize"]), returns: "u32" },
|
|
2717
|
-
castrum_task_submit_slice: {
|
|
2718
|
-
args: abi(["u32", "ptr", "usize", "ptr", "usize", "usize"]),
|
|
2719
|
-
returns: "u32"
|
|
2720
|
-
},
|
|
2721
|
-
castrum_task_submit_out: {
|
|
2722
|
-
args: abi(["u32", "ptr", "usize", "usize", "ptr", "usize"]),
|
|
2723
|
-
returns: "u32"
|
|
2724
|
-
},
|
|
2725
|
-
castrum_task_drain: { args: abi(["ptr", "usize"]), returns: U64_FAST },
|
|
2726
|
-
castrum_task_pending: { args: [], returns: "u32" },
|
|
2727
|
-
castrum_task_cancel: { args: ["usize"], returns: "u32" },
|
|
2728
|
-
castrum_task_set_doorbell: { args: ["ptr"], returns: "u32" },
|
|
2729
|
-
castrum_task_shutdown: { args: [], returns: "u32" },
|
|
2730
|
-
castrum_task_threads: { args: [], returns: "u32" },
|
|
2731
|
-
castrum_metrics_destroy: { args: ["usize"], returns: "void" }
|
|
2732
|
-
});
|
|
2803
|
+
const { symbols } = dlopen(path, buildFFISymbolMap(useBufferLength));
|
|
2733
2804
|
const bindings = build(symbols, useBufferLength);
|
|
2734
2805
|
if (!selfTest(bindings)) {
|
|
2735
2806
|
throw new Error(`bun:ffi bind-time self-test failed on ${path}`);
|
|
@@ -3404,24 +3475,53 @@ var init_wire = __esm(() => {
|
|
|
3404
3475
|
});
|
|
3405
3476
|
|
|
3406
3477
|
// src/shared/packed/parsers.ts
|
|
3407
|
-
function
|
|
3408
|
-
const
|
|
3478
|
+
function scratchFor(kind, input) {
|
|
3479
|
+
const need = input.length * 9 + 16;
|
|
3480
|
+
let s = ffiCaches[kind];
|
|
3481
|
+
if (s === null || s.length < need) {
|
|
3482
|
+
s = new Uint8Array(need);
|
|
3483
|
+
ffiCaches[kind] = s;
|
|
3484
|
+
}
|
|
3485
|
+
return s;
|
|
3486
|
+
}
|
|
3487
|
+
function parsePackedInto(kind, input) {
|
|
3488
|
+
const f = getBunFFI();
|
|
3489
|
+
if (!f)
|
|
3490
|
+
return null;
|
|
3491
|
+
const out = scratchFor(kind, input);
|
|
3492
|
+
const w = kind === "query" ? f.queryParsePackedInto(input, out) : kind === "cookie" ? f.cookieParsePackedInto(input, out) : f.formParsePackedInto(input, out);
|
|
3493
|
+
if (w === 0 || w > out.length)
|
|
3494
|
+
return null;
|
|
3495
|
+
return pairsToObject(readPairsPacked(out.subarray(0, w)));
|
|
3496
|
+
}
|
|
3497
|
+
function parseVia(kind, input) {
|
|
3498
|
+
const fast = parsePackedInto(kind, input);
|
|
3499
|
+
if (fast)
|
|
3500
|
+
return fast;
|
|
3501
|
+
const packed = kind === "query" ? addon2.queryParsePacked(input) : kind === "cookie" ? addon2.cookieParsePacked(input) : addon2.formParsePacked(input);
|
|
3409
3502
|
return pairsToObject(readPairsPacked(packed));
|
|
3410
3503
|
}
|
|
3504
|
+
function parseQueryString(query) {
|
|
3505
|
+
return parseVia("query", encoder.encode(query));
|
|
3506
|
+
}
|
|
3411
3507
|
function parseCookieHeader(header) {
|
|
3412
|
-
|
|
3413
|
-
return pairsToObject(readPairsPacked(packed));
|
|
3508
|
+
return parseVia("cookie", encoder.encode(header));
|
|
3414
3509
|
}
|
|
3415
3510
|
function parseFormBody(body) {
|
|
3416
|
-
|
|
3417
|
-
return pairsToObject(readPairsPacked(packed));
|
|
3511
|
+
return parseVia("form", body);
|
|
3418
3512
|
}
|
|
3419
|
-
var addon2;
|
|
3513
|
+
var addon2, ffiCaches;
|
|
3420
3514
|
var init_parsers = __esm(() => {
|
|
3515
|
+
init_ffi();
|
|
3421
3516
|
init_native();
|
|
3422
3517
|
init_bytes();
|
|
3423
3518
|
init_wire();
|
|
3424
3519
|
addon2 = lazyAddon(getAddon);
|
|
3520
|
+
ffiCaches = {
|
|
3521
|
+
query: null,
|
|
3522
|
+
cookie: null,
|
|
3523
|
+
form: null
|
|
3524
|
+
};
|
|
3425
3525
|
});
|
|
3426
3526
|
|
|
3427
3527
|
// src/shared/packed/schema.ts
|
|
@@ -3486,6 +3586,11 @@ function asNumber(value) {
|
|
|
3486
3586
|
}
|
|
3487
3587
|
return 0;
|
|
3488
3588
|
}
|
|
3589
|
+
function assertNonEmptySecret(secret, op) {
|
|
3590
|
+
if (secret.byteLength === 0) {
|
|
3591
|
+
throw new Error(`${op}: secret must not be empty`);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3489
3594
|
function normalizeExt(ext) {
|
|
3490
3595
|
let s;
|
|
3491
3596
|
try {
|
|
@@ -3653,17 +3758,29 @@ function buildBatch(ctx) {
|
|
|
3653
3758
|
gzipDecompress: (items, maxDecompressed) => via(ctx, "gzipDecompressBatchPacked", unpackByteResults, [maxDecompressed ?? null])(items),
|
|
3654
3759
|
brotliCompress: (items, quality) => via(ctx, "brotliCompressBatchPacked", unpackByteResults, [quality ?? null])(items),
|
|
3655
3760
|
brotliDecompress: (items, maxDecompressed) => via(ctx, "brotliDecompressBatchPacked", unpackByteResults, [maxDecompressed ?? null])(items),
|
|
3656
|
-
jwtVerify: (tokens, secret, nowSeconds) =>
|
|
3657
|
-
|
|
3658
|
-
|
|
3761
|
+
jwtVerify: (tokens, secret, nowSeconds) => {
|
|
3762
|
+
assertNonEmptySecret(secret, "batch.jwtVerify");
|
|
3763
|
+
return via(ctx, "jwtVerifyBatchPacked", unpackBitset, [secret, nowSeconds])(tokens);
|
|
3764
|
+
},
|
|
3765
|
+
hmacSha256: (items, key) => {
|
|
3766
|
+
assertNonEmptySecret(key, "batch.hmacSha256");
|
|
3767
|
+
return via(ctx, "hmacSha256BatchPacked", unpackByteResults, [key])(items);
|
|
3768
|
+
},
|
|
3769
|
+
hmacSha256Verify: (items, sigs, key) => {
|
|
3770
|
+
assertNonEmptySecret(key, "batch.hmacSha256Verify");
|
|
3771
|
+
return via2(ctx, "hmacSha256VerifyBatchPacked", unpackBitset, [key])(items, sigs);
|
|
3772
|
+
},
|
|
3659
3773
|
passwordVerify: (items, phcs) => via2(ctx, "passwordVerifyBatchPacked", unpackBitset)(items, phcs),
|
|
3660
3774
|
urlResolve: (bases, references) => via2(ctx, "urlResolveBatchPacked", unpackByteResults)(bases, references),
|
|
3661
3775
|
templateRender: (source, contexts) => via(ctx, "templateRenderBatchPacked", unpackByteResults, [source])(contexts),
|
|
3662
|
-
jwtSign: (items, secret, ttlSeconds, nowSeconds) =>
|
|
3663
|
-
secret,
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3776
|
+
jwtSign: (items, secret, ttlSeconds, nowSeconds) => {
|
|
3777
|
+
assertNonEmptySecret(secret, "batch.jwtSign");
|
|
3778
|
+
return via(ctx, "jwtSignBatchPacked", unpackByteResults, [
|
|
3779
|
+
secret,
|
|
3780
|
+
ttlSeconds ?? null,
|
|
3781
|
+
nowSeconds ?? Math.floor(Date.now() / 1000)
|
|
3782
|
+
])(items);
|
|
3783
|
+
},
|
|
3667
3784
|
sseEncode: (items, event, id, retry) => via(ctx, "sseEncodeBatchPacked", unpackByteResults, [
|
|
3668
3785
|
event ?? null,
|
|
3669
3786
|
id ?? null,
|
|
@@ -3677,6 +3794,7 @@ function buildBatch(ctx) {
|
|
|
3677
3794
|
var init_build2 = __esm(() => {
|
|
3678
3795
|
init_packed();
|
|
3679
3796
|
init_context();
|
|
3797
|
+
init_options();
|
|
3680
3798
|
});
|
|
3681
3799
|
|
|
3682
3800
|
// src/rust-ffi/batch/index.ts
|
|
@@ -3938,18 +4056,22 @@ function buildCrypto(ctx) {
|
|
|
3938
4056
|
return writeInto("verify cookie", output, value);
|
|
3939
4057
|
},
|
|
3940
4058
|
csrfToken(secret) {
|
|
4059
|
+
assertNonEmptySecret(secret, "csrfToken");
|
|
3941
4060
|
return resolveNative(ctx, "csrfToken")(secret);
|
|
3942
4061
|
},
|
|
3943
4062
|
csrfTokenInto(secret, output) {
|
|
4063
|
+
assertNonEmptySecret(secret, "csrfTokenInto");
|
|
3944
4064
|
const f = ffi();
|
|
3945
4065
|
if (f)
|
|
3946
4066
|
return f.csrfTokenInto(secret, output);
|
|
3947
4067
|
return writeInto("csrf token", output, addon.csrfToken(secret));
|
|
3948
4068
|
},
|
|
3949
4069
|
csrfVerify(token, secret) {
|
|
4070
|
+
assertNonEmptySecret(secret, "csrfVerify");
|
|
3950
4071
|
return resolveNative(ctx, "csrfVerify")(token, secret);
|
|
3951
4072
|
},
|
|
3952
4073
|
jwtSign(claims, secret, ttlSeconds, nowSeconds) {
|
|
4074
|
+
assertNonEmptySecret(secret, "jwtSign");
|
|
3953
4075
|
const f = ffi();
|
|
3954
4076
|
if (f) {
|
|
3955
4077
|
const token = f.jwtSignBytes(encoder.encode(JSON.stringify(claims)), secret, ttlSeconds ?? 0, nowSeconds ?? Math.floor(Date.now() / 1000));
|
|
@@ -3958,6 +4080,7 @@ function buildCrypto(ctx) {
|
|
|
3958
4080
|
return addon.jwtSign(claims, secret, ttlSeconds ?? null, nowSeconds ?? Math.floor(Date.now() / 1000));
|
|
3959
4081
|
},
|
|
3960
4082
|
jwtSignBytes(claimsJson, secret, ttlSeconds, nowSeconds) {
|
|
4083
|
+
assertNonEmptySecret(secret, "jwtSignBytes");
|
|
3961
4084
|
const f = ffi();
|
|
3962
4085
|
if (f) {
|
|
3963
4086
|
return f.jwtSignBytes(claimsJson, secret, ttlSeconds ?? 0, nowSeconds ?? Math.floor(Date.now() / 1000));
|
|
@@ -3965,6 +4088,7 @@ function buildCrypto(ctx) {
|
|
|
3965
4088
|
return addon.jwtSignBytes(claimsJson, secret, ttlSeconds ?? null, nowSeconds ?? Math.floor(Date.now() / 1000));
|
|
3966
4089
|
},
|
|
3967
4090
|
jwtSignBytesInto(claimsJson, secret, output, ttlSeconds, nowSeconds) {
|
|
4091
|
+
assertNonEmptySecret(secret, "jwtSignBytesInto");
|
|
3968
4092
|
const f = ffi();
|
|
3969
4093
|
if (f) {
|
|
3970
4094
|
return f.jwtSignBytesInto(claimsJson, secret, ttlSeconds ?? 0, nowSeconds ?? Math.floor(Date.now() / 1000), output);
|
|
@@ -3972,6 +4096,7 @@ function buildCrypto(ctx) {
|
|
|
3972
4096
|
return writeInto("jwt sign", output, addon.jwtSignBytes(claimsJson, secret, ttlSeconds ?? null, nowSeconds ?? Math.floor(Date.now() / 1000)));
|
|
3973
4097
|
},
|
|
3974
4098
|
jwtVerify(token, secret, nowSeconds) {
|
|
4099
|
+
assertNonEmptySecret(secret, "jwtVerify");
|
|
3975
4100
|
const f = ffi();
|
|
3976
4101
|
if (f) {
|
|
3977
4102
|
const claims = f.jwtVerify(token, secret, nowSeconds ?? Math.floor(Date.now() / 1000));
|
|
@@ -4032,9 +4157,17 @@ function buildCrypto(ctx) {
|
|
|
4032
4157
|
var init_crypto = __esm(() => {
|
|
4033
4158
|
init_bytes();
|
|
4034
4159
|
init_context();
|
|
4160
|
+
init_options();
|
|
4035
4161
|
});
|
|
4036
4162
|
|
|
4037
4163
|
// src/rust-ffi/scalar/factories.ts
|
|
4164
|
+
function handlePtr(inner) {
|
|
4165
|
+
const v = inner ?? 0n;
|
|
4166
|
+
if (v < 0n || v > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
4167
|
+
throw new Error("castrum: native handle exceeds Number.MAX_SAFE_INTEGER");
|
|
4168
|
+
}
|
|
4169
|
+
return Number(v);
|
|
4170
|
+
}
|
|
4038
4171
|
function ffiHmacSigner(key, ffi) {
|
|
4039
4172
|
return {
|
|
4040
4173
|
sign(data) {
|
|
@@ -4215,47 +4348,55 @@ function ffiFormParser(ffi) {
|
|
|
4215
4348
|
};
|
|
4216
4349
|
}
|
|
4217
4350
|
function ffiMetricsRegistry(ffi) {
|
|
4218
|
-
|
|
4351
|
+
let handle = ffi.metricsCreate();
|
|
4352
|
+
const live = () => {
|
|
4353
|
+
if (handle === 0)
|
|
4354
|
+
throw new Error("metrics registry: already destroyed");
|
|
4355
|
+
return handle;
|
|
4356
|
+
};
|
|
4219
4357
|
return {
|
|
4220
4358
|
counter(name, labelKeys) {
|
|
4221
|
-
return ffi.metricsCounter(
|
|
4359
|
+
return ffi.metricsCounter(live(), name, (labelKeys ?? []).join("\x1F"));
|
|
4222
4360
|
},
|
|
4223
4361
|
gauge(name, labelKeys) {
|
|
4224
|
-
return ffi.metricsGauge(
|
|
4362
|
+
return ffi.metricsGauge(live(), name, (labelKeys ?? []).join("\x1F"));
|
|
4225
4363
|
},
|
|
4226
4364
|
histogram(name, labelKeys, buckets) {
|
|
4227
|
-
return ffi.metricsHistogram(
|
|
4365
|
+
return ffi.metricsHistogram(live(), name, (labelKeys ?? []).join("\x1F"), (buckets ?? []).join(","));
|
|
4228
4366
|
},
|
|
4229
4367
|
record(series, values, amount) {
|
|
4230
4368
|
const joined = (values ?? []).join("\x1F");
|
|
4231
|
-
if (!ffi.metricsRecordStr(
|
|
4369
|
+
if (!ffi.metricsRecordStr(live(), series, joined, amount ?? 1)) {
|
|
4232
4370
|
throw new Error("metrics record: unknown series / arity mismatch / invalid amount");
|
|
4233
4371
|
}
|
|
4234
4372
|
},
|
|
4235
4373
|
gaugeSet(series, values, value) {
|
|
4236
4374
|
const joined = (values ?? []).join("\x1F");
|
|
4237
|
-
if (!ffi.metricsGaugeSetStr(
|
|
4375
|
+
if (!ffi.metricsGaugeSetStr(live(), series, joined, value)) {
|
|
4238
4376
|
throw new Error("metrics gauge set: unknown series / arity mismatch");
|
|
4239
4377
|
}
|
|
4240
4378
|
},
|
|
4241
4379
|
render() {
|
|
4242
4380
|
let out = new Uint8Array(8192);
|
|
4243
|
-
let w = ffi.metricsRender(
|
|
4381
|
+
let w = ffi.metricsRender(live(), out);
|
|
4244
4382
|
if (w > out.length) {
|
|
4245
4383
|
out = new Uint8Array(w);
|
|
4246
|
-
w = ffi.metricsRender(
|
|
4384
|
+
w = ffi.metricsRender(live(), out);
|
|
4247
4385
|
}
|
|
4248
4386
|
return decodeUtf8(out.subarray(0, w));
|
|
4249
4387
|
},
|
|
4250
4388
|
destroy() {
|
|
4251
|
-
|
|
4389
|
+
if (handle !== 0) {
|
|
4390
|
+
ffi.metricsDestroy(handle);
|
|
4391
|
+
handle = 0;
|
|
4392
|
+
}
|
|
4252
4393
|
},
|
|
4253
4394
|
snapshot() {
|
|
4254
4395
|
let out = new Uint8Array(4096);
|
|
4255
|
-
let w = ffi.metricsSnapshot(
|
|
4396
|
+
let w = ffi.metricsSnapshot(live(), out);
|
|
4256
4397
|
if (w > out.length) {
|
|
4257
4398
|
out = new Uint8Array(w);
|
|
4258
|
-
w = ffi.metricsSnapshot(
|
|
4399
|
+
w = ffi.metricsSnapshot(live(), out);
|
|
4259
4400
|
}
|
|
4260
4401
|
return out.subarray(0, w);
|
|
4261
4402
|
}
|
|
@@ -4269,11 +4410,12 @@ function buildFactories(ctx) {
|
|
|
4269
4410
|
const ffi = transport.ffi;
|
|
4270
4411
|
if (ffi) {
|
|
4271
4412
|
const napi = new addon.SchemaValidator(schema);
|
|
4272
|
-
return ffiSchemaValidator(napi,
|
|
4413
|
+
return ffiSchemaValidator(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4273
4414
|
}
|
|
4274
4415
|
return new addon.SchemaValidator(schema);
|
|
4275
4416
|
},
|
|
4276
4417
|
createHmacSigner(key) {
|
|
4418
|
+
assertNonEmptySecret(key, "createHmacSigner");
|
|
4277
4419
|
const ffi = transport.ffi;
|
|
4278
4420
|
if (ffi)
|
|
4279
4421
|
return ffiHmacSigner(key, ffi);
|
|
@@ -4283,7 +4425,7 @@ function buildFactories(ctx) {
|
|
|
4283
4425
|
const ffi = transport.ffi;
|
|
4284
4426
|
if (ffi) {
|
|
4285
4427
|
const napi = new addon.TemplateRenderer(source);
|
|
4286
|
-
return ffiTemplateRenderer(napi,
|
|
4428
|
+
return ffiTemplateRenderer(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4287
4429
|
}
|
|
4288
4430
|
return new addon.TemplateRenderer(source);
|
|
4289
4431
|
},
|
|
@@ -4300,7 +4442,7 @@ function buildFactories(ctx) {
|
|
|
4300
4442
|
const ffi = transport.ffi;
|
|
4301
4443
|
if (ffi) {
|
|
4302
4444
|
const napi = new addon.ConditionalRequest(etagValue, lastModifiedSecs ?? undefined);
|
|
4303
|
-
return ffiConditionalRequest(napi,
|
|
4445
|
+
return ffiConditionalRequest(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4304
4446
|
}
|
|
4305
4447
|
return new addon.ConditionalRequest(etagValue, lastModifiedSecs ?? undefined);
|
|
4306
4448
|
},
|
|
@@ -4308,7 +4450,7 @@ function buildFactories(ctx) {
|
|
|
4308
4450
|
const ffi = transport.ffi;
|
|
4309
4451
|
if (ffi) {
|
|
4310
4452
|
const napi = new addon.AcceptNegotiator(supported);
|
|
4311
|
-
return ffiAcceptNegotiator(napi,
|
|
4453
|
+
return ffiAcceptNegotiator(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4312
4454
|
}
|
|
4313
4455
|
return new addon.AcceptNegotiator(supported);
|
|
4314
4456
|
},
|
|
@@ -4319,12 +4461,14 @@ function buildFactories(ctx) {
|
|
|
4319
4461
|
return new addon.Base64Codec(urlSafe ?? undefined, padding ?? undefined);
|
|
4320
4462
|
},
|
|
4321
4463
|
createCookieSigner(secret) {
|
|
4464
|
+
assertNonEmptySecret(secret, "createCookieSigner");
|
|
4322
4465
|
const ffi = transport.ffi;
|
|
4323
4466
|
if (ffi)
|
|
4324
4467
|
return ffiCookieSigner(secret, ffi);
|
|
4325
4468
|
return new addon.CookieSigner(secret);
|
|
4326
4469
|
},
|
|
4327
4470
|
createCsrfProtector(secret) {
|
|
4471
|
+
assertNonEmptySecret(secret, "createCsrfProtector");
|
|
4328
4472
|
const ffi = transport.ffi;
|
|
4329
4473
|
if (ffi)
|
|
4330
4474
|
return ffiCsrfProtector(secret, ffi);
|
|
@@ -4334,15 +4478,16 @@ function buildFactories(ctx) {
|
|
|
4334
4478
|
const ffi = transport.ffi;
|
|
4335
4479
|
if (ffi) {
|
|
4336
4480
|
const napi = new addon.UrlBuilder(base);
|
|
4337
|
-
return ffiUrlBuilder(napi,
|
|
4481
|
+
return ffiUrlBuilder(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4338
4482
|
}
|
|
4339
4483
|
return new addon.UrlBuilder(base);
|
|
4340
4484
|
},
|
|
4341
4485
|
createJwtSigner(secret, ttlSeconds) {
|
|
4486
|
+
assertNonEmptySecret(secret, "createJwtSigner");
|
|
4342
4487
|
const ffi = transport.ffi;
|
|
4343
4488
|
if (ffi) {
|
|
4344
4489
|
const napi = new addon.JwtSigner(secret, ttlSeconds ?? undefined);
|
|
4345
|
-
return ffiJwtSigner(napi,
|
|
4490
|
+
return ffiJwtSigner(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4346
4491
|
}
|
|
4347
4492
|
return new addon.JwtSigner(secret, ttlSeconds ?? undefined);
|
|
4348
4493
|
},
|
|
@@ -4362,7 +4507,7 @@ function buildFactories(ctx) {
|
|
|
4362
4507
|
const ffi = transport.ffi;
|
|
4363
4508
|
if (ffi) {
|
|
4364
4509
|
const napi = new addon.MediaTypeMatcher(expected);
|
|
4365
|
-
return ffiMediaTypeMatcher(napi,
|
|
4510
|
+
return ffiMediaTypeMatcher(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4366
4511
|
}
|
|
4367
4512
|
return new addon.MediaTypeMatcher(expected);
|
|
4368
4513
|
},
|
|
@@ -4370,7 +4515,7 @@ function buildFactories(ctx) {
|
|
|
4370
4515
|
const ffi = transport.ffi;
|
|
4371
4516
|
if (ffi) {
|
|
4372
4517
|
const napi = new addon.RateLimiter(limit, windowMs, maxEntries ?? undefined);
|
|
4373
|
-
return ffiRateLimiter(napi,
|
|
4518
|
+
return ffiRateLimiter(napi, handlePtr(napi.innerPtr?.()), ffi);
|
|
4374
4519
|
}
|
|
4375
4520
|
return new addon.RateLimiter(limit, windowMs, maxEntries ?? undefined);
|
|
4376
4521
|
},
|
|
@@ -4471,37 +4616,63 @@ function ensureValidUtf8(bytes, ffi) {
|
|
|
4471
4616
|
function u32LE(b, off) {
|
|
4472
4617
|
return ((b[off] ?? 0) | (b[off + 1] ?? 0) << 8 | (b[off + 2] ?? 0) << 16 | (b[off + 3] ?? 0) << 24) >>> 0;
|
|
4473
4618
|
}
|
|
4619
|
+
function keyBytesEqual(b, off, lit) {
|
|
4620
|
+
for (let i = 0;i < lit.length; i++) {
|
|
4621
|
+
if ((b[off + i] ?? 0) !== (lit[i] ?? 0))
|
|
4622
|
+
return false;
|
|
4623
|
+
}
|
|
4624
|
+
return true;
|
|
4625
|
+
}
|
|
4474
4626
|
function unpackMediaType(packed) {
|
|
4627
|
+
const view = Buffer.from(packed.buffer, packed.byteOffset, packed.byteLength);
|
|
4475
4628
|
let off = 0;
|
|
4476
4629
|
const mtLen = u32LE(packed, off);
|
|
4477
4630
|
off += 4;
|
|
4478
|
-
const mediaType =
|
|
4631
|
+
const mediaType = decodeUtf8RangeView(view, off, off + mtLen);
|
|
4479
4632
|
off += mtLen;
|
|
4480
4633
|
let charset = null;
|
|
4481
4634
|
const csLen = u32LE(packed, off);
|
|
4482
4635
|
off += 4;
|
|
4483
4636
|
if (csLen !== 4294967295) {
|
|
4484
|
-
charset =
|
|
4637
|
+
charset = decodeUtf8RangeView(view, off, off + csLen);
|
|
4485
4638
|
off += csLen;
|
|
4486
4639
|
}
|
|
4487
4640
|
let boundary = null;
|
|
4488
4641
|
const bLen = u32LE(packed, off);
|
|
4489
4642
|
off += 4;
|
|
4490
4643
|
if (bLen !== 4294967295) {
|
|
4491
|
-
boundary =
|
|
4644
|
+
boundary = decodeUtf8RangeView(view, off, off + bLen);
|
|
4492
4645
|
off += bLen;
|
|
4493
4646
|
}
|
|
4494
4647
|
const count = u32LE(packed, off);
|
|
4495
4648
|
off += 4;
|
|
4496
4649
|
const params = {};
|
|
4650
|
+
let charsetTaken = charset === null;
|
|
4651
|
+
let boundaryTaken = boundary === null;
|
|
4497
4652
|
for (let i = 0;i < count; i++) {
|
|
4498
4653
|
const kLen = u32LE(packed, off);
|
|
4499
4654
|
off += 4;
|
|
4500
|
-
|
|
4655
|
+
let key;
|
|
4656
|
+
if (kLen === 7 && keyBytesEqual(packed, off, CHARSET_KEY_BYTES)) {
|
|
4657
|
+
key = "charset";
|
|
4658
|
+
} else if (kLen === 8 && keyBytesEqual(packed, off, BOUNDARY_KEY_BYTES)) {
|
|
4659
|
+
key = "boundary";
|
|
4660
|
+
} else {
|
|
4661
|
+
key = decodeUtf8RangeView(view, off, off + kLen);
|
|
4662
|
+
}
|
|
4501
4663
|
off += kLen;
|
|
4502
4664
|
const vLen = u32LE(packed, off);
|
|
4503
4665
|
off += 4;
|
|
4504
|
-
|
|
4666
|
+
let val;
|
|
4667
|
+
if (!charsetTaken && key === "charset" && charset !== null) {
|
|
4668
|
+
val = charset;
|
|
4669
|
+
charsetTaken = true;
|
|
4670
|
+
} else if (!boundaryTaken && key === "boundary" && boundary !== null) {
|
|
4671
|
+
val = boundary;
|
|
4672
|
+
boundaryTaken = true;
|
|
4673
|
+
} else {
|
|
4674
|
+
val = decodeUtf8RangeView(view, off, off + vLen);
|
|
4675
|
+
}
|
|
4505
4676
|
off += vLen;
|
|
4506
4677
|
params[key] = val;
|
|
4507
4678
|
}
|
|
@@ -4632,7 +4803,7 @@ function buildHttp(ctx) {
|
|
|
4632
4803
|
queryParsePacked(input) {
|
|
4633
4804
|
const f = ffi();
|
|
4634
4805
|
if (f) {
|
|
4635
|
-
const out = new Uint8Array(input.length *
|
|
4806
|
+
const out = new Uint8Array(input.length * 5 + 8);
|
|
4636
4807
|
const w = f.queryParsePackedInto(input, out);
|
|
4637
4808
|
return out.subarray(0, w);
|
|
4638
4809
|
}
|
|
@@ -4644,7 +4815,7 @@ function buildHttp(ctx) {
|
|
|
4644
4815
|
cookieParsePacked(input) {
|
|
4645
4816
|
const f = ffi();
|
|
4646
4817
|
if (f) {
|
|
4647
|
-
const out = new Uint8Array(input.length *
|
|
4818
|
+
const out = new Uint8Array(input.length * 5 + 8);
|
|
4648
4819
|
const w = f.cookieParsePackedInto(input, out);
|
|
4649
4820
|
return out.subarray(0, w);
|
|
4650
4821
|
}
|
|
@@ -4656,7 +4827,7 @@ function buildHttp(ctx) {
|
|
|
4656
4827
|
formParsePacked(input) {
|
|
4657
4828
|
const f = ffi();
|
|
4658
4829
|
if (f) {
|
|
4659
|
-
const out = new Uint8Array(input.length *
|
|
4830
|
+
const out = new Uint8Array(input.length * 5 + 8);
|
|
4660
4831
|
const w = f.formParsePackedInto(input, out);
|
|
4661
4832
|
return out.subarray(0, w);
|
|
4662
4833
|
}
|
|
@@ -4728,13 +4899,15 @@ function buildHttp(ctx) {
|
|
|
4728
4899
|
}
|
|
4729
4900
|
};
|
|
4730
4901
|
}
|
|
4731
|
-
var WS_ACCEPT_SCRATCH, f32Bits, f32BitsU32;
|
|
4902
|
+
var WS_ACCEPT_SCRATCH, CHARSET_KEY_BYTES, BOUNDARY_KEY_BYTES, f32Bits, f32BitsU32;
|
|
4732
4903
|
var init_http = __esm(() => {
|
|
4733
4904
|
init_bytes();
|
|
4734
4905
|
init_codec2();
|
|
4735
4906
|
init_packed();
|
|
4736
4907
|
init_context();
|
|
4737
4908
|
WS_ACCEPT_SCRATCH = new Uint8Array(64);
|
|
4909
|
+
CHARSET_KEY_BYTES = new Uint8Array([99, 104, 97, 114, 115, 101, 116]);
|
|
4910
|
+
BOUNDARY_KEY_BYTES = new Uint8Array([98, 111, 117, 110, 100, 97, 114, 121]);
|
|
4738
4911
|
f32Bits = new Float32Array(1);
|
|
4739
4912
|
f32BitsU32 = new Uint32Array(f32Bits.buffer);
|
|
4740
4913
|
});
|
|
@@ -4814,6 +4987,7 @@ function buildPayload(ctx) {
|
|
|
4814
4987
|
const { builtins, transport } = ctx.runtime;
|
|
4815
4988
|
const HAS = { gzipCompress: builtins.has("gzipCompress") };
|
|
4816
4989
|
const ffi = memoizeFfi(transport);
|
|
4990
|
+
let SCRATCH = null;
|
|
4817
4991
|
return {
|
|
4818
4992
|
gzipCompress(data, level) {
|
|
4819
4993
|
if (HAS.gzipCompress)
|
|
@@ -4867,8 +5041,20 @@ function buildPayload(ctx) {
|
|
|
4867
5041
|
},
|
|
4868
5042
|
multipartParse(body, boundary) {
|
|
4869
5043
|
const f = ffi();
|
|
4870
|
-
if (f)
|
|
4871
|
-
|
|
5044
|
+
if (f) {
|
|
5045
|
+
const need = body.length + boundary.length + 64;
|
|
5046
|
+
if (need > 64 * 1024)
|
|
5047
|
+
return unpackMultipart(f.multipartParsePacked(body, boundary));
|
|
5048
|
+
if (!SCRATCH || SCRATCH.length < need)
|
|
5049
|
+
SCRATCH = new Uint8Array(need);
|
|
5050
|
+
let w;
|
|
5051
|
+
try {
|
|
5052
|
+
w = f.multipartParsePackedInto(body, boundary, SCRATCH);
|
|
5053
|
+
} catch {
|
|
5054
|
+
return unpackMultipart(f.multipartParsePacked(body, boundary));
|
|
5055
|
+
}
|
|
5056
|
+
return unpackMultipart(SCRATCH.subarray(0, w));
|
|
5057
|
+
}
|
|
4872
5058
|
return addon.multipartParse(body, boundary).map((p) => ({
|
|
4873
5059
|
name: p.name,
|
|
4874
5060
|
filename: p.filename ?? null,
|
|
@@ -5297,10 +5483,13 @@ function createNativeRoute(plan = {}) {
|
|
|
5297
5483
|
const parseQuery = plan.parseQuery === true;
|
|
5298
5484
|
const parseCookies = plan.parseCookies === true;
|
|
5299
5485
|
const bunFFI = getBunFFI();
|
|
5300
|
-
|
|
5301
|
-
|
|
5486
|
+
let ffiHandle = bunFFI !== null ? compileFfi(bunFFI, descriptor) : 0;
|
|
5487
|
+
let napiRoute = bunFFI === null || ffiHandle === 0 ? compileNapi(descriptor) : null;
|
|
5302
5488
|
let out = new Uint8Array(256);
|
|
5303
5489
|
const runFrame = (frame) => {
|
|
5490
|
+
if (ffiHandle === 0 && napiRoute === null) {
|
|
5491
|
+
throw new Error("native route: already destroyed");
|
|
5492
|
+
}
|
|
5304
5493
|
let written;
|
|
5305
5494
|
if (ffiHandle !== 0 && bunFFI !== null) {
|
|
5306
5495
|
written = bunFFI.routeRun(ffiHandle, frame, out);
|
|
@@ -5333,7 +5522,10 @@ function createNativeRoute(plan = {}) {
|
|
|
5333
5522
|
destroy: () => {
|
|
5334
5523
|
if (ffiHandle !== 0 && bunFFI !== null) {
|
|
5335
5524
|
bunFFI.routeDestroy(ffiHandle);
|
|
5525
|
+
ffiHandle = 0;
|
|
5336
5526
|
}
|
|
5527
|
+
napiRoute?.destroy?.();
|
|
5528
|
+
napiRoute = null;
|
|
5337
5529
|
}
|
|
5338
5530
|
};
|
|
5339
5531
|
}
|
|
@@ -5357,6 +5549,17 @@ function compileNapi(descriptor) {
|
|
|
5357
5549
|
}
|
|
5358
5550
|
}
|
|
5359
5551
|
|
|
5552
|
+
// src/ingress/headers/memoized-headers.ts
|
|
5553
|
+
var CACHE = new WeakMap;
|
|
5554
|
+
function memoizedHeaders(entries) {
|
|
5555
|
+
const existing = CACHE.get(entries);
|
|
5556
|
+
if (existing !== undefined)
|
|
5557
|
+
return existing;
|
|
5558
|
+
const built = new Headers(entries);
|
|
5559
|
+
CACHE.set(entries, built);
|
|
5560
|
+
return built;
|
|
5561
|
+
}
|
|
5562
|
+
|
|
5360
5563
|
// src/ingress/shared.ts
|
|
5361
5564
|
var DEFAULT_MAX_BODY_BYTES = 1048576;
|
|
5362
5565
|
var DEFAULT_FAST_OUTPUT_BUFFER_SIZE = 262144;
|
|
@@ -5399,10 +5602,9 @@ function resolveIp(req, srv, opts) {
|
|
|
5399
5602
|
return opts.getIp ? opts.getIp(req, srv) : undefined;
|
|
5400
5603
|
}
|
|
5401
5604
|
function buildSuccessInit(ingress, result, ctx, status = 200) {
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
};
|
|
5605
|
+
const rateResetSecs = result.rateResetMs > 0 ? secondsFromMs(result.rateResetMs) : undefined;
|
|
5606
|
+
const headers = ingress.successHeaders ? ingress.successHeaders(result.headerVariant, ctx.requestIdHeader, ctx.origin, result.rateRemaining, rateResetSecs) : memoizedHeaders(ingress.responseHeaders(result.headerVariant, ctx.requestIdHeader, ctx.origin, result.rateRemaining, rateResetSecs));
|
|
5607
|
+
return { status, headers };
|
|
5406
5608
|
}
|
|
5407
5609
|
function runBaked(ingress, opts, respond) {
|
|
5408
5610
|
return (req, srv) => ingress.run(req, resolveIp(req, srv, opts), null, respond);
|
|
@@ -5781,6 +5983,18 @@ function rateLimitedBody(retryAfterMs) {
|
|
|
5781
5983
|
}
|
|
5782
5984
|
|
|
5783
5985
|
// src/ingress/routes/echo.ts
|
|
5986
|
+
var SAFE_ECHO_CONTENT_TYPES = new Set([
|
|
5987
|
+
"application/json",
|
|
5988
|
+
"application/octet-stream",
|
|
5989
|
+
"text/plain"
|
|
5990
|
+
]);
|
|
5991
|
+
function echoMediaTypeOf(contentType) {
|
|
5992
|
+
const semi = contentType.indexOf(";");
|
|
5993
|
+
return (semi >= 0 ? contentType.slice(0, semi) : contentType).trim().toLowerCase();
|
|
5994
|
+
}
|
|
5995
|
+
function safeEchoContentType(contentType) {
|
|
5996
|
+
return SAFE_ECHO_CONTENT_TYPES.has(echoMediaTypeOf(contentType)) ? contentType : "application/octet-stream";
|
|
5997
|
+
}
|
|
5784
5998
|
function echoHandler(ingress, opts = {}) {
|
|
5785
5999
|
const maxBodyBytes = opts.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
5786
6000
|
const bodyTimeoutMs = opts.bodyTimeoutMs ?? DEFAULT_BODY_TIMEOUT_MS;
|
|
@@ -5801,32 +6015,39 @@ function echoHandler(ingress, opts = {}) {
|
|
|
5801
6015
|
if (prep.terminal)
|
|
5802
6016
|
return prep.terminal;
|
|
5803
6017
|
const baseHeaders = prep.headers ?? [];
|
|
5804
|
-
const requestedContentType = req.headers.get("content-type") ?? "application/octet-stream";
|
|
6018
|
+
const requestedContentType = safeEchoContentType(req.headers.get("content-type") ?? "application/octet-stream");
|
|
6019
|
+
const withEchoHeaders = (contentType) => {
|
|
6020
|
+
const headers = ingress.withContentType(baseHeaders, contentType);
|
|
6021
|
+
if (!headers.some(([name]) => name.toLowerCase() === "x-content-type-options")) {
|
|
6022
|
+
headers.push(["x-content-type-options", "nosniff"]);
|
|
6023
|
+
}
|
|
6024
|
+
return headers;
|
|
6025
|
+
};
|
|
5805
6026
|
const contentLengthHeader = req.headers.get("content-length");
|
|
5806
6027
|
const contentLength = contentLengthHeader === null ? NaN : Number(contentLengthHeader);
|
|
5807
6028
|
if (Number.isFinite(contentLength)) {
|
|
5808
6029
|
if (contentLength > maxBodyBytes) {
|
|
5809
6030
|
return new Response(ERROR_BODIES.body_too_large, {
|
|
5810
6031
|
status: 413,
|
|
5811
|
-
headers:
|
|
6032
|
+
headers: withEchoHeaders("application/json")
|
|
5812
6033
|
});
|
|
5813
6034
|
}
|
|
5814
6035
|
if (contentLength <= 0 || req.body === null) {
|
|
5815
6036
|
return new Response(null, {
|
|
5816
6037
|
status: 200,
|
|
5817
|
-
headers:
|
|
6038
|
+
headers: withEchoHeaders(requestedContentType)
|
|
5818
6039
|
});
|
|
5819
6040
|
}
|
|
5820
6041
|
return new Response(req.body, {
|
|
5821
6042
|
status: 200,
|
|
5822
|
-
headers:
|
|
6043
|
+
headers: withEchoHeaders(requestedContentType)
|
|
5823
6044
|
});
|
|
5824
6045
|
}
|
|
5825
6046
|
try {
|
|
5826
6047
|
const bodyBytes = await readBodyWithLimit(req, maxBodyBytes, true, bodyTimeoutMs);
|
|
5827
6048
|
return new Response(bodyBytes.byteLength > 0 ? bodyBytes : null, {
|
|
5828
6049
|
status: 200,
|
|
5829
|
-
headers:
|
|
6050
|
+
headers: withEchoHeaders(requestedContentType)
|
|
5830
6051
|
});
|
|
5831
6052
|
} catch (err) {
|
|
5832
6053
|
const code = err?.code;
|
|
@@ -5834,7 +6055,7 @@ function echoHandler(ingress, opts = {}) {
|
|
|
5834
6055
|
const isTimeout = code === "REQUEST_TIMEOUT";
|
|
5835
6056
|
return new Response(isTooLarge ? ERROR_BODIES.body_too_large : isTimeout ? ERROR_BODIES.request_timeout : ERROR_BODIES.bad_request, {
|
|
5836
6057
|
status: isTooLarge ? 413 : isTimeout ? 408 : 400,
|
|
5837
|
-
headers:
|
|
6058
|
+
headers: withEchoHeaders("application/json")
|
|
5838
6059
|
});
|
|
5839
6060
|
}
|
|
5840
6061
|
};
|
|
@@ -5872,6 +6093,11 @@ function defaultJsonValid(bytes) {
|
|
|
5872
6093
|
return false;
|
|
5873
6094
|
}
|
|
5874
6095
|
}
|
|
6096
|
+
function isJsonContentType(contentType) {
|
|
6097
|
+
const semi = contentType.indexOf(";");
|
|
6098
|
+
const mediaType = (semi >= 0 ? contentType.slice(0, semi) : contentType).trim().toLowerCase();
|
|
6099
|
+
return mediaType === "application/json" || mediaType.endsWith("+json");
|
|
6100
|
+
}
|
|
5875
6101
|
function jsonWriteHandler(ingress, opts = {}) {
|
|
5876
6102
|
const maxBodyBytes = opts.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
5877
6103
|
const fallback = opts.fallback ?? ingress;
|
|
@@ -5881,13 +6107,8 @@ function jsonWriteHandler(ingress, opts = {}) {
|
|
|
5881
6107
|
return async (req, srv) => {
|
|
5882
6108
|
const ip = resolveIp(req, srv, opts);
|
|
5883
6109
|
const contentType = req.headers.get("content-type") ?? "";
|
|
5884
|
-
if (!contentType
|
|
5885
|
-
return fallback.run(req, ip, null, (result, ctx) =>
|
|
5886
|
-
const terminal = fallback.terminalResponse(req, result, ctx);
|
|
5887
|
-
if (terminal)
|
|
5888
|
-
return terminal;
|
|
5889
|
-
return fallback.errorResponse(req, result, 415, "unsupported_media_type", "Content-Type must be application/json", ctx);
|
|
5890
|
-
});
|
|
6110
|
+
if (!isJsonContentType(contentType)) {
|
|
6111
|
+
return fallback.run(req, ip, null, (result, ctx) => fallback.errorResponse(req, result, 415, "unsupported_media_type", "Content-Type must be application/json", ctx));
|
|
5891
6112
|
}
|
|
5892
6113
|
let bodyBytes;
|
|
5893
6114
|
try {
|
|
@@ -6072,7 +6293,7 @@ function safeTerminalStatus(r) {
|
|
|
6072
6293
|
}
|
|
6073
6294
|
|
|
6074
6295
|
// src/ingress/routes/responder.ts
|
|
6075
|
-
var
|
|
6296
|
+
var IGNEX_SECURITY_HEADERS = [
|
|
6076
6297
|
["x-frame-options", "DENY"],
|
|
6077
6298
|
["x-content-type-options", "nosniff"],
|
|
6078
6299
|
["referrer-policy", "no-referrer"]
|
|
@@ -6080,7 +6301,7 @@ var IGNGEX_SECURITY_HEADERS = [
|
|
|
6080
6301
|
function buildIgngexTerminal(result, _ctx) {
|
|
6081
6302
|
const preflightAllowed = result.isPreflight && result.corsAllowed;
|
|
6082
6303
|
if (preflightAllowed) {
|
|
6083
|
-
return new Response(null, { status: 204, headers:
|
|
6304
|
+
return new Response(null, { status: 204, headers: IGNEX_SECURITY_HEADERS });
|
|
6084
6305
|
}
|
|
6085
6306
|
const status = safeTerminalStatus(result);
|
|
6086
6307
|
const body = JSON.stringify({
|
|
@@ -6088,7 +6309,7 @@ function buildIgngexTerminal(result, _ctx) {
|
|
|
6088
6309
|
status,
|
|
6089
6310
|
code: errorCodeName(result.errorCode)
|
|
6090
6311
|
});
|
|
6091
|
-
const headers = new Headers(
|
|
6312
|
+
const headers = new Headers(IGNEX_SECURITY_HEADERS);
|
|
6092
6313
|
headers.set("content-type", "application/json; charset=utf-8");
|
|
6093
6314
|
return new Response(body, { status, headers });
|
|
6094
6315
|
}
|
|
@@ -6116,7 +6337,7 @@ function nativeResponderRoute(ingress, responder, opts = {}) {
|
|
|
6116
6337
|
const code = err.code;
|
|
6117
6338
|
const status = code === "REQUEST_TIMEOUT" ? 408 : code === "BODY_TOO_LARGE" ? 413 : 400;
|
|
6118
6339
|
const payload = JSON.stringify({ error: "Bad Request", status, code: "BAD_REQUEST" });
|
|
6119
|
-
const headers = new Headers(
|
|
6340
|
+
const headers = new Headers(IGNEX_SECURITY_HEADERS);
|
|
6120
6341
|
headers.set("content-type", "application/json; charset=utf-8");
|
|
6121
6342
|
return new Response(payload, { status, headers });
|
|
6122
6343
|
}
|
|
@@ -6447,7 +6668,7 @@ function nodeRequestToWebRequest(req) {
|
|
|
6447
6668
|
}
|
|
6448
6669
|
const method = req.method ?? "GET";
|
|
6449
6670
|
const bodyForbidden = method === "GET" || method === "HEAD";
|
|
6450
|
-
const hasBody =
|
|
6671
|
+
const hasBody = requestHasBody(req);
|
|
6451
6672
|
const init = { method, headers };
|
|
6452
6673
|
if (hasBody && !bodyForbidden) {
|
|
6453
6674
|
init.body = Readable.toWeb(req);
|
|
@@ -6491,7 +6712,7 @@ async function writeResponse(res, response) {
|
|
|
6491
6712
|
}
|
|
6492
6713
|
function drainUnreadBody(res) {
|
|
6493
6714
|
const req = res.req;
|
|
6494
|
-
if (req !== undefined && req !== null && !req.complete &&
|
|
6715
|
+
if (req !== undefined && req !== null && !req.complete && requestHasBody(req)) {
|
|
6495
6716
|
req.resume();
|
|
6496
6717
|
res.shouldKeepAlive = false;
|
|
6497
6718
|
if (!res.headersSent) {
|
|
@@ -6499,7 +6720,7 @@ function drainUnreadBody(res) {
|
|
|
6499
6720
|
}
|
|
6500
6721
|
}
|
|
6501
6722
|
}
|
|
6502
|
-
function
|
|
6723
|
+
function requestHasBody(req) {
|
|
6503
6724
|
return Number(req.headers["content-length"] ?? 0) > 0 || req.headers["transfer-encoding"] !== undefined;
|
|
6504
6725
|
}
|
|
6505
6726
|
function makeRequestListener(options, routes, baseOpts) {
|
|
@@ -6555,6 +6776,23 @@ function makeRequestListener(options, routes, baseOpts) {
|
|
|
6555
6776
|
}
|
|
6556
6777
|
};
|
|
6557
6778
|
}
|
|
6779
|
+
var WS_PROTOCOL_TOKEN = /^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/;
|
|
6780
|
+
function formatWebSocketHandshake(accept, protocol) {
|
|
6781
|
+
if (/[\r\n]/.test(accept)) {
|
|
6782
|
+
throw new Error("invalid Sec-WebSocket-Accept value");
|
|
6783
|
+
}
|
|
6784
|
+
let raw = `HTTP/1.1 101 Switching Protocols\r
|
|
6785
|
+
` + `Upgrade: websocket\r
|
|
6786
|
+
` + `Connection: Upgrade\r
|
|
6787
|
+
` + `Sec-WebSocket-Accept: ${accept}\r
|
|
6788
|
+
`;
|
|
6789
|
+
if (protocol && WS_PROTOCOL_TOKEN.test(protocol)) {
|
|
6790
|
+
raw += `Sec-WebSocket-Protocol: ${protocol}\r
|
|
6791
|
+
`;
|
|
6792
|
+
}
|
|
6793
|
+
return `${raw}\r
|
|
6794
|
+
`;
|
|
6795
|
+
}
|
|
6558
6796
|
function makeUpgradeListener(options) {
|
|
6559
6797
|
return async (req, socket, head) => {
|
|
6560
6798
|
try {
|
|
@@ -6564,18 +6802,7 @@ function makeUpgradeListener(options) {
|
|
|
6564
6802
|
socket.destroy();
|
|
6565
6803
|
return;
|
|
6566
6804
|
}
|
|
6567
|
-
|
|
6568
|
-
` + `Upgrade: websocket\r
|
|
6569
|
-
` + `Connection: Upgrade\r
|
|
6570
|
-
` + `Sec-WebSocket-Accept: ${handshake.accept}\r
|
|
6571
|
-
`;
|
|
6572
|
-
if (handshake.protocol) {
|
|
6573
|
-
raw += `Sec-WebSocket-Protocol: ${handshake.protocol}\r
|
|
6574
|
-
`;
|
|
6575
|
-
}
|
|
6576
|
-
raw += `\r
|
|
6577
|
-
`;
|
|
6578
|
-
socket.write(raw);
|
|
6805
|
+
socket.write(formatWebSocketHandshake(handshake.accept, handshake.protocol));
|
|
6579
6806
|
options.onUpgrade?.(socket, req, head);
|
|
6580
6807
|
} catch {
|
|
6581
6808
|
socket.destroy();
|
|
@@ -6799,6 +7026,7 @@ class IngressResultBase {
|
|
|
6799
7026
|
bodyTruncated = false;
|
|
6800
7027
|
body = EMPTY_BODY2;
|
|
6801
7028
|
_buf = EMPTY_BODY2;
|
|
7029
|
+
_len = 0;
|
|
6802
7030
|
_bodyJsonStart = OUT_DATA_START;
|
|
6803
7031
|
_bodyJsonLen = 0;
|
|
6804
7032
|
_cookiesBuf = null;
|
|
@@ -6824,6 +7052,7 @@ class IngressResultBase {
|
|
|
6824
7052
|
this.bodyTruncated = false;
|
|
6825
7053
|
this.body = EMPTY_BODY2;
|
|
6826
7054
|
this._buf = EMPTY_BODY2;
|
|
7055
|
+
this._len = 0;
|
|
6827
7056
|
this._bodyJsonStart = OUT_DATA_START;
|
|
6828
7057
|
this._bodyJsonLen = 0;
|
|
6829
7058
|
this._cookiesBuf = null;
|
|
@@ -6848,8 +7077,9 @@ class IngressResultBase {
|
|
|
6848
7077
|
this.retryAfterMs = 0;
|
|
6849
7078
|
}
|
|
6850
7079
|
}
|
|
6851
|
-
setSections(buf, layout, bodyJsonLen) {
|
|
7080
|
+
setSections(buf, layout, bodyJsonLen, len = buf.byteLength) {
|
|
6852
7081
|
this._buf = buf;
|
|
7082
|
+
this._len = len;
|
|
6853
7083
|
this._bodyJsonStart = layout.bodyJsonStart;
|
|
6854
7084
|
this._bodyJsonLen = bodyJsonLen;
|
|
6855
7085
|
this._cookiesBuf = layout.safeCookiesLen > 0 ? buf.subarray(OUT_DATA_START, OUT_DATA_START + layout.safeCookiesLen) : null;
|
|
@@ -6879,7 +7109,7 @@ class IngressResultBase {
|
|
|
6879
7109
|
if (this._bodyJsonLen === 0)
|
|
6880
7110
|
return EMPTY_BODY2;
|
|
6881
7111
|
const end = this._bodyJsonStart + this._bodyJsonLen;
|
|
6882
|
-
if (end > this.
|
|
7112
|
+
if (end > this._len)
|
|
6883
7113
|
return EMPTY_BODY2;
|
|
6884
7114
|
return this._buf.subarray(this._bodyJsonStart, end);
|
|
6885
7115
|
}
|
|
@@ -6943,7 +7173,7 @@ class FastIngressResult extends IngressResultBase {
|
|
|
6943
7173
|
this.isPreflight = (flags & FLAG_IS_PREFLIGHT) !== 0;
|
|
6944
7174
|
this.rateLimited = (flags & FLAG_RATE_LIMITED) !== 0;
|
|
6945
7175
|
const layout = sectionLayout(buf.byteLength, cookiesJsonLen, queryJsonLen, bodyJsonLen);
|
|
6946
|
-
this.setSections(buf, layout, bodyJsonLen);
|
|
7176
|
+
this.setSections(buf, layout, bodyJsonLen, buf.byteLength);
|
|
6947
7177
|
this.bodyTruncated = (flags & FLAG_BODY_TRUNCATED) !== 0 || layout.truncated;
|
|
6948
7178
|
}
|
|
6949
7179
|
invalidate() {
|
|
@@ -7035,7 +7265,14 @@ function warnTrustProxyDeprecated() {
|
|
|
7035
7265
|
if (trustProxyWarned)
|
|
7036
7266
|
return;
|
|
7037
7267
|
trustProxyWarned = true;
|
|
7038
|
-
console.warn("[castrum] WARN: `trustProxy: true` is deprecated and trusts EVERY hop — " + "clients can spoof X-Forwarded-For / X-Real-IP to bypass IP-based rate " + "limiting. Use `trustedProxies: { enabled: true, networks: [...] }`
|
|
7268
|
+
console.warn("[castrum] WARN: `trustProxy: true` is deprecated and trusts EVERY hop — " + "clients can spoof X-Forwarded-For / X-Real-IP to bypass IP-based rate " + "limiting. Use `trustedProxies: { enabled: true, networks: [...] }` " + "(which takes precedence) and only enable proxy trust behind a trusted edge.");
|
|
7269
|
+
}
|
|
7270
|
+
var rateLimitNoIpWarned = false;
|
|
7271
|
+
function warnRateLimitWithoutGetIp() {
|
|
7272
|
+
if (rateLimitNoIpWarned)
|
|
7273
|
+
return;
|
|
7274
|
+
rateLimitNoIpWarned = true;
|
|
7275
|
+
console.warn("[castrum] WARN: rate limiting is enabled without `getIp` — every client " + "shares ONE bucket, so one client can exhaust it for everyone. Provide " + "`getIp` for per-client limits (or use a global limit deliberately).");
|
|
7039
7276
|
}
|
|
7040
7277
|
|
|
7041
7278
|
// src/ingress/packing/header-packing.ts
|
|
@@ -7601,6 +7838,11 @@ class BufferPool {
|
|
|
7601
7838
|
this.estimate = options.adaptive === true ? new AdaptiveEstimate({ alpha: 0.25, min: this.initialSize }) : null;
|
|
7602
7839
|
this.free.push(new Uint8Array(this.initialSize));
|
|
7603
7840
|
this.created = 1;
|
|
7841
|
+
const prefill = Math.min(Math.max(0, Math.floor(options.prefill ?? 0)), this.maxBuffers - 1, options.prefillBytes !== undefined ? Math.max(0, Math.floor(options.prefillBytes / this.initialSize)) : Number.POSITIVE_INFINITY);
|
|
7842
|
+
for (let i = 0;i < prefill; i++) {
|
|
7843
|
+
this.free.push(new Uint8Array(this.initialSize));
|
|
7844
|
+
this.created++;
|
|
7845
|
+
}
|
|
7604
7846
|
}
|
|
7605
7847
|
get freeCount() {
|
|
7606
7848
|
return this.free.length;
|
|
@@ -7634,7 +7876,9 @@ class BufferPool {
|
|
|
7634
7876
|
for (let i = 0;i < this.free.length; i++) {
|
|
7635
7877
|
const candidate = this.free[i];
|
|
7636
7878
|
if (candidate.byteLength >= minSize) {
|
|
7637
|
-
this.free.
|
|
7879
|
+
const last = this.free.pop();
|
|
7880
|
+
if (i < this.free.length)
|
|
7881
|
+
this.free[i] = last;
|
|
7638
7882
|
return candidate;
|
|
7639
7883
|
}
|
|
7640
7884
|
}
|
|
@@ -7649,7 +7893,9 @@ class BufferPool {
|
|
|
7649
7893
|
}
|
|
7650
7894
|
}
|
|
7651
7895
|
if (largestBuf !== null) {
|
|
7652
|
-
this.free.
|
|
7896
|
+
const last = this.free.pop();
|
|
7897
|
+
if (largest < this.free.length)
|
|
7898
|
+
this.free[largest] = last;
|
|
7653
7899
|
this.created++;
|
|
7654
7900
|
return new Uint8Array(target);
|
|
7655
7901
|
}
|
|
@@ -7658,16 +7904,15 @@ class BufferPool {
|
|
|
7658
7904
|
}
|
|
7659
7905
|
releaseBuffer(buffer) {
|
|
7660
7906
|
if (this.free.length >= this.maxBuffers) {
|
|
7661
|
-
let smallestBuf = null;
|
|
7662
7907
|
let smallest = 0;
|
|
7663
|
-
for (let i =
|
|
7664
|
-
|
|
7665
|
-
if (smallestBuf === null || candidate.byteLength < smallestBuf.byteLength) {
|
|
7666
|
-
smallestBuf = candidate;
|
|
7908
|
+
for (let i = 1;i < this.free.length; i++) {
|
|
7909
|
+
if (this.free[i].byteLength < this.free[smallest].byteLength) {
|
|
7667
7910
|
smallest = i;
|
|
7668
7911
|
}
|
|
7669
7912
|
}
|
|
7670
|
-
this.free.
|
|
7913
|
+
const last = this.free.pop();
|
|
7914
|
+
if (smallest < this.free.length)
|
|
7915
|
+
this.free[smallest] = last;
|
|
7671
7916
|
}
|
|
7672
7917
|
this.free.push(buffer);
|
|
7673
7918
|
}
|
|
@@ -7727,8 +7972,8 @@ function createStructuredLogger(stream = process.stderr, env = process.env) {
|
|
|
7727
7972
|
|
|
7728
7973
|
// src/ingress/decode/baked-result.ts
|
|
7729
7974
|
class BakedIngressResult extends IngressResultBase {
|
|
7730
|
-
refresh(buf, body, view) {
|
|
7731
|
-
if (
|
|
7975
|
+
refresh(buf, body, view, len = buf.byteLength) {
|
|
7976
|
+
if (len < OUT_DATA_START) {
|
|
7732
7977
|
this.setInternalError();
|
|
7733
7978
|
return;
|
|
7734
7979
|
}
|
|
@@ -7741,7 +7986,7 @@ class BakedIngressResult extends IngressResultBase {
|
|
|
7741
7986
|
const queryLenRaw = view.getUint32(OUT_QUERY_JSON_LEN, true);
|
|
7742
7987
|
const headerVariant = view.getUint8(OUT_HEADER_VARIANT);
|
|
7743
7988
|
const bodyJsonLenRaw = view.getUint32(OUT_BODY_JSON_LEN, true);
|
|
7744
|
-
const layout = sectionLayout(
|
|
7989
|
+
const layout = sectionLayout(len, cookiesLenRaw, queryLenRaw, bodyJsonLenRaw);
|
|
7745
7990
|
if (h0 === 0 && h1 === 0) {
|
|
7746
7991
|
this.verdict = 1;
|
|
7747
7992
|
this.errorCode = ERR_CODE_INTERNAL;
|
|
@@ -7756,7 +8001,7 @@ class BakedIngressResult extends IngressResultBase {
|
|
|
7756
8001
|
this.rateRemaining = h3;
|
|
7757
8002
|
this.setRateWindow(h2, (flags & FLAG_RATE_LIMITED) !== 0, view);
|
|
7758
8003
|
this.headerVariant = headerVariant;
|
|
7759
|
-
this.setSections(buf, layout, layout.safeBodyJsonLen);
|
|
8004
|
+
this.setSections(buf, layout, layout.safeBodyJsonLen, len);
|
|
7760
8005
|
this.updateOkTerminal();
|
|
7761
8006
|
this.isPreflight = (flags & FLAG_IS_PREFLIGHT) !== 0;
|
|
7762
8007
|
this.corsAllowed = (flags & FLAG_CORS_ALLOWED) !== 0;
|
|
@@ -7779,8 +8024,16 @@ class BakedIngressResult extends IngressResultBase {
|
|
|
7779
8024
|
this.ok = false;
|
|
7780
8025
|
}
|
|
7781
8026
|
bodyJson(copy) {
|
|
8027
|
+
if (copy) {
|
|
8028
|
+
if (this._bodyJsonLen === 0)
|
|
8029
|
+
return this._buf.slice(0, 0);
|
|
8030
|
+
const end = this._bodyJsonStart + this._bodyJsonLen;
|
|
8031
|
+
if (end > this._len)
|
|
8032
|
+
return this._buf.slice(0, 0);
|
|
8033
|
+
return this._buf.slice(this._bodyJsonStart, end);
|
|
8034
|
+
}
|
|
7782
8035
|
const slice = this.bodyJsonSlice();
|
|
7783
|
-
return
|
|
8036
|
+
return slice;
|
|
7784
8037
|
}
|
|
7785
8038
|
}
|
|
7786
8039
|
|
|
@@ -7904,6 +8157,13 @@ function cachedOriginBlock(origin) {
|
|
|
7904
8157
|
ORIGIN_BLOCK_CACHE.set(origin, block);
|
|
7905
8158
|
return block;
|
|
7906
8159
|
}
|
|
8160
|
+
function prewarmOriginBlocks(origins) {
|
|
8161
|
+
for (const origin of origins) {
|
|
8162
|
+
if (typeof origin === "string" && origin.length > 0 && origin.length <= MAX_SMALL_HEADER_BYTES && !origin.includes("*")) {
|
|
8163
|
+
cachedOriginBlock(origin);
|
|
8164
|
+
}
|
|
8165
|
+
}
|
|
8166
|
+
}
|
|
7907
8167
|
|
|
7908
8168
|
// src/ingress/response/baked-response.ts
|
|
7909
8169
|
init_bytes();
|
|
@@ -7956,6 +8216,12 @@ function pooledBodyResponse(handle, bytes, init = {}, timeoutMs = 0) {
|
|
|
7956
8216
|
|
|
7957
8217
|
// src/ingress/response/baked-response.ts
|
|
7958
8218
|
var EMPTY_BODY4 = new Uint8Array(0);
|
|
8219
|
+
var ORIGIN_HEADER_CACHE_MAX = 64;
|
|
8220
|
+
function prewarmOriginHeaders(responseHeaders, origins, variant) {
|
|
8221
|
+
for (const origin of origins) {
|
|
8222
|
+
memoizedHeaders(responseHeaders(variant, null, origin));
|
|
8223
|
+
}
|
|
8224
|
+
}
|
|
7959
8225
|
function buildBakedResponseBuilders(deps) {
|
|
7960
8226
|
const {
|
|
7961
8227
|
headerTemplates,
|
|
@@ -7965,6 +8231,45 @@ function buildBakedResponseBuilders(deps) {
|
|
|
7965
8231
|
originHeaderCache,
|
|
7966
8232
|
state
|
|
7967
8233
|
} = deps;
|
|
8234
|
+
function originBaseEntries(variant, origin) {
|
|
8235
|
+
const key = `${variant & 31}\x00${origin}`;
|
|
8236
|
+
const cached = originHeaderCache.get(key);
|
|
8237
|
+
if (cached !== undefined) {
|
|
8238
|
+
return cached;
|
|
8239
|
+
}
|
|
8240
|
+
const template = headerTemplates[variant & 31] ?? headerTemplates[0] ?? [];
|
|
8241
|
+
const entries = new Array(template.length + 1);
|
|
8242
|
+
let i = 0;
|
|
8243
|
+
for (const pair of template) {
|
|
8244
|
+
entries[i++] = pair;
|
|
8245
|
+
}
|
|
8246
|
+
entries[i] = ["access-control-allow-origin", origin];
|
|
8247
|
+
if (originHeaderCache.size >= ORIGIN_HEADER_CACHE_MAX) {
|
|
8248
|
+
const oldest = originHeaderCache.keys().next().value;
|
|
8249
|
+
if (oldest !== undefined)
|
|
8250
|
+
originHeaderCache.delete(oldest);
|
|
8251
|
+
}
|
|
8252
|
+
originHeaderCache.set(key, entries);
|
|
8253
|
+
return entries;
|
|
8254
|
+
}
|
|
8255
|
+
function successHeaders(variant, requestIdHeader, origin, rateRemaining, rateResetSecs) {
|
|
8256
|
+
const needsRequestId = emitRequestIdHeader && requestIdHeader !== null;
|
|
8257
|
+
const needsOrigin = ((variant & HV_CORS_SIMPLE) !== 0 || (variant & HV_CORS_PREFLIGHT) !== 0) && origin !== null;
|
|
8258
|
+
const needsRate = (variant & HV_RATE_ACTIVE) !== 0;
|
|
8259
|
+
const base = needsOrigin ? memoizedHeaders(originBaseEntries(variant, origin)) : memoizedHeaders(headerTemplates[variant & 31] ?? headerTemplates[0] ?? []);
|
|
8260
|
+
if (!needsRequestId && !needsRate) {
|
|
8261
|
+
return base;
|
|
8262
|
+
}
|
|
8263
|
+
const headers = new Headers(base);
|
|
8264
|
+
if (needsRequestId) {
|
|
8265
|
+
headers.set("x-request-id", requestIdHeader);
|
|
8266
|
+
}
|
|
8267
|
+
if (needsRate) {
|
|
8268
|
+
headers.set("ratelimit-remaining", String(rateRemaining ?? 0));
|
|
8269
|
+
headers.set("ratelimit-reset", String(rateResetSecs ?? 0));
|
|
8270
|
+
}
|
|
8271
|
+
return headers;
|
|
8272
|
+
}
|
|
7968
8273
|
function responseHeaders(variant, requestIdHeader, origin, rateRemaining, rateResetSecs, retryAfterSecs) {
|
|
7969
8274
|
const template = headerTemplates[variant & 31] ?? headerTemplates[0] ?? [];
|
|
7970
8275
|
const needsRequestId = emitRequestIdHeader && requestIdHeader !== null;
|
|
@@ -7975,19 +8280,7 @@ function buildBakedResponseBuilders(deps) {
|
|
|
7975
8280
|
return template;
|
|
7976
8281
|
}
|
|
7977
8282
|
if (!needsRequestId && !needsRate && !needsRetry && needsOrigin) {
|
|
7978
|
-
|
|
7979
|
-
const cached = originHeaderCache.get(key);
|
|
7980
|
-
if (cached !== undefined) {
|
|
7981
|
-
return cached;
|
|
7982
|
-
}
|
|
7983
|
-
const entries = new Array(template.length + 1);
|
|
7984
|
-
let i = 0;
|
|
7985
|
-
for (const pair of template) {
|
|
7986
|
-
entries[i++] = pair;
|
|
7987
|
-
}
|
|
7988
|
-
entries[i] = ["access-control-allow-origin", origin];
|
|
7989
|
-
originHeaderCache.set(key, entries);
|
|
7990
|
-
return entries;
|
|
8283
|
+
return originBaseEntries(variant, origin);
|
|
7991
8284
|
}
|
|
7992
8285
|
let extra = 0;
|
|
7993
8286
|
if (needsRequestId)
|
|
@@ -8048,27 +8341,27 @@ function buildBakedResponseBuilders(deps) {
|
|
|
8048
8341
|
if (preflightAllowed) {
|
|
8049
8342
|
return new Response(null, {
|
|
8050
8343
|
status: 204,
|
|
8051
|
-
headers: responseHeaders(result.headerVariant, ctx.requestIdHeader, ctx.origin, result.rateRemaining, result.rateResetMs > 0 ? secondsFromMs(result.rateResetMs) : undefined, result.retryAfterMs > 0 ? secondsFromMs(result.retryAfterMs) : undefined)
|
|
8344
|
+
headers: memoizedHeaders(responseHeaders(result.headerVariant, ctx.requestIdHeader, ctx.origin, result.rateRemaining, result.rateResetMs > 0 ? secondsFromMs(result.rateResetMs) : undefined, result.retryAfterMs > 0 ? secondsFromMs(result.retryAfterMs) : undefined))
|
|
8052
8345
|
});
|
|
8053
8346
|
}
|
|
8054
8347
|
const status = safeTerminalStatus(result);
|
|
8055
8348
|
const body = result.errorCode === ERR_CODE_RATE_LIMITED ? rateLimitedBody(result.retryAfterMs) : ERROR_CODE_BODIES[result.errorCode] ?? ERROR_BODIES.internal ?? EMPTY_BODY4;
|
|
8056
8349
|
return new Response(body, {
|
|
8057
8350
|
status,
|
|
8058
|
-
headers: terminalHeaders(result.headerVariant, ctx, result)
|
|
8351
|
+
headers: memoizedHeaders(terminalHeaders(result.headerVariant, ctx, result))
|
|
8059
8352
|
});
|
|
8060
8353
|
}
|
|
8061
8354
|
function errorResponse(_req, result, status, code, message, ctx) {
|
|
8062
8355
|
const body = ERROR_BODIES[code] ?? encoder.encode(JSON.stringify({ ok: false, error: { code, message } }));
|
|
8063
8356
|
return new Response(body, {
|
|
8064
8357
|
status,
|
|
8065
|
-
headers: terminalHeaders(result?.headerVariant ?? HV_JSON, ctx, result)
|
|
8358
|
+
headers: memoizedHeaders(terminalHeaders(result?.headerVariant ?? HV_JSON, ctx, result))
|
|
8066
8359
|
});
|
|
8067
8360
|
}
|
|
8068
8361
|
function internalErrorResponse(ctx, result) {
|
|
8069
8362
|
return new Response(ERROR_BODIES.internal, {
|
|
8070
8363
|
status: 500,
|
|
8071
|
-
headers: terminalHeaders(result?.headerVariant ?? HV_JSON, ctx, result ?? null)
|
|
8364
|
+
headers: memoizedHeaders(terminalHeaders(result?.headerVariant ?? HV_JSON, ctx, result ?? null))
|
|
8072
8365
|
});
|
|
8073
8366
|
}
|
|
8074
8367
|
function withContentType(headers, contentType) {
|
|
@@ -8089,6 +8382,7 @@ function buildBakedResponseBuilders(deps) {
|
|
|
8089
8382
|
}
|
|
8090
8383
|
return {
|
|
8091
8384
|
responseHeaders,
|
|
8385
|
+
successHeaders,
|
|
8092
8386
|
terminalHeaders,
|
|
8093
8387
|
terminalResponse,
|
|
8094
8388
|
errorResponse,
|
|
@@ -8103,6 +8397,8 @@ var EMPTY_BODY5 = new Uint8Array(0);
|
|
|
8103
8397
|
var EMPTY_IP = "0.0.0.0";
|
|
8104
8398
|
var RATE_LIMIT_U32_MAX = 4294967295;
|
|
8105
8399
|
var MAX_FFI_FAILURES = 3;
|
|
8400
|
+
var PREWARM_POOL_BUFFERS = 16;
|
|
8401
|
+
var PREWARM_POOL_BYTES = 4 * 1024 * 1024;
|
|
8106
8402
|
function pathForLog(req) {
|
|
8107
8403
|
try {
|
|
8108
8404
|
return new URL(req.url).pathname;
|
|
@@ -8139,14 +8435,24 @@ function createIngressHandler(options = {}, runtime = {}) {
|
|
|
8139
8435
|
const outputBufferSize = clampIngressBufferSize(runtime.outputBufferSize ?? DEFAULT_BAKED_OUTPUT_BUFFER_SIZE, OUT_DATA_START);
|
|
8140
8436
|
const logger = runtime.logger ?? (runtime.structuredLog === true ? createStructuredLogger() : null);
|
|
8141
8437
|
const needRequestIdString = !!(emitRequestIdHeader || runtime.onRequest || runtime.onError || runtime.onResponse || logger);
|
|
8438
|
+
const hasOnRequest = runtime.onRequest !== undefined;
|
|
8439
|
+
const hasOnResponse = runtime.onResponse !== undefined;
|
|
8142
8440
|
const outputPool = new BufferPool({
|
|
8143
8441
|
initialSize: outputBufferSize,
|
|
8144
|
-
maxInFlight: runtime.maxInFlight
|
|
8442
|
+
maxInFlight: runtime.maxInFlight,
|
|
8443
|
+
prefill: PREWARM_POOL_BUFFERS,
|
|
8444
|
+
prefillBytes: PREWARM_POOL_BYTES
|
|
8145
8445
|
});
|
|
8146
8446
|
const inputPacker = new IngressInputPacker;
|
|
8147
8447
|
const responseState = { currentHandle: null, responseBorrowsBuffer: false };
|
|
8148
8448
|
let ffiFailures = 0;
|
|
8149
8449
|
let ffiDisabled = false;
|
|
8450
|
+
const noteFfiFailure = () => {
|
|
8451
|
+
ffiFailures++;
|
|
8452
|
+
if (ffiFailures >= MAX_FFI_FAILURES) {
|
|
8453
|
+
ffiDisabled = true;
|
|
8454
|
+
}
|
|
8455
|
+
};
|
|
8150
8456
|
const originHeaderCache = new Map;
|
|
8151
8457
|
const cors = options.cors;
|
|
8152
8458
|
const securityEntries = runtime.enableSecurityHeaders === false ? Object.freeze([]) : Object.freeze(buildBakedSecurityEntries(options.security, options.https, runtime.securityHeaders));
|
|
@@ -8172,6 +8478,17 @@ function createIngressHandler(options = {}, runtime = {}) {
|
|
|
8172
8478
|
originHeaderCache,
|
|
8173
8479
|
state: responseState
|
|
8174
8480
|
});
|
|
8481
|
+
if (headerPlan.cors && typeof cors?.allowOrigin === "object") {
|
|
8482
|
+
const exactOrigins = [];
|
|
8483
|
+
for (const origin of cors.allowOrigin) {
|
|
8484
|
+
if (typeof origin === "string" && origin.length > 0 && origin.length <= 512 && !origin.includes("*")) {
|
|
8485
|
+
exactOrigins.push(origin);
|
|
8486
|
+
}
|
|
8487
|
+
}
|
|
8488
|
+
prewarmOriginBlocks(exactOrigins);
|
|
8489
|
+
const successVariant = HV_JSON | HV_CORS_SIMPLE | (rateEnabled ? HV_RATE_ACTIVE : 0);
|
|
8490
|
+
prewarmOriginHeaders(responseBuilders.responseHeaders, exactOrigins, successVariant);
|
|
8491
|
+
}
|
|
8175
8492
|
const result = new BakedIngressResult;
|
|
8176
8493
|
const ctx = {
|
|
8177
8494
|
requestIdHeader: null,
|
|
@@ -8184,7 +8501,8 @@ function createIngressHandler(options = {}, runtime = {}) {
|
|
|
8184
8501
|
const requestIdStr = needRequestIdString ? decoder.decode(ridBytes) : "";
|
|
8185
8502
|
ctx.requestIdHeader = emitRequestIdHeader ? requestIdStr : null;
|
|
8186
8503
|
ctx.origin = headerPlan.cors ? req.headers.get("origin") : null;
|
|
8187
|
-
|
|
8504
|
+
if (hasOnRequest)
|
|
8505
|
+
runtime.onRequest?.(req, requestIdStr, ip);
|
|
8188
8506
|
const ipStr = ip ?? EMPTY_IP;
|
|
8189
8507
|
let handle = null;
|
|
8190
8508
|
responseState.currentHandle = null;
|
|
@@ -8202,10 +8520,7 @@ function createIngressHandler(options = {}, runtime = {}) {
|
|
|
8202
8520
|
written = bunFFI.ingressHandleComponents(ingressPtr, methodKind, packUrl, packIp ?? "", ridBytes, packedHeaders, body, handle.buffer);
|
|
8203
8521
|
ffiFailures = 0;
|
|
8204
8522
|
} catch {
|
|
8205
|
-
|
|
8206
|
-
if (ffiFailures >= MAX_FFI_FAILURES) {
|
|
8207
|
-
ffiDisabled = true;
|
|
8208
|
-
}
|
|
8523
|
+
noteFfiFailure();
|
|
8209
8524
|
const input = inputPacker.packParts(methodKind, packUrl, packIp, ridBytes, packedHeaders);
|
|
8210
8525
|
written = handler.handleRequestPacked(input, body, handle.buffer);
|
|
8211
8526
|
}
|
|
@@ -8215,18 +8530,14 @@ function createIngressHandler(options = {}, runtime = {}) {
|
|
|
8215
8530
|
written = bunFFI.ingressHandlePacked(ingressPtr, input, body, handle.buffer);
|
|
8216
8531
|
ffiFailures = 0;
|
|
8217
8532
|
} catch {
|
|
8218
|
-
|
|
8219
|
-
if (ffiFailures >= MAX_FFI_FAILURES) {
|
|
8220
|
-
ffiDisabled = true;
|
|
8221
|
-
}
|
|
8533
|
+
noteFfiFailure();
|
|
8222
8534
|
written = handler.handleRequestPacked(input, body, handle.buffer);
|
|
8223
8535
|
}
|
|
8224
8536
|
} else {
|
|
8225
8537
|
const input = inputPacker.packParts(methodKind, packUrl, packIp, ridBytes, packedHeaders);
|
|
8226
8538
|
written = handler.handleRequestPacked(input, body, handle.buffer);
|
|
8227
8539
|
}
|
|
8228
|
-
|
|
8229
|
-
result.refresh(used, body ?? EMPTY_BODY5, viewForArrayBuffer(used.buffer, used.byteOffset));
|
|
8540
|
+
result.refresh(handle.buffer, body ?? EMPTY_BODY5, viewForArrayBuffer(handle.buffer.buffer, handle.buffer.byteOffset), written);
|
|
8230
8541
|
} catch (err) {
|
|
8231
8542
|
result.setInternalError();
|
|
8232
8543
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -8242,17 +8553,20 @@ function createIngressHandler(options = {}, runtime = {}) {
|
|
|
8242
8553
|
try {
|
|
8243
8554
|
const out = fn(result, ctx);
|
|
8244
8555
|
assertSyncCallback(out, "createIngressHandler().run()");
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8556
|
+
if (hasOnResponse || logger !== null) {
|
|
8557
|
+
const status = out instanceof Response ? out.status : safeTerminalStatus(result);
|
|
8558
|
+
if (hasOnResponse)
|
|
8559
|
+
runtime.onResponse?.(req, result, status, requestIdStr);
|
|
8560
|
+
if (logger !== null) {
|
|
8561
|
+
logger.request({
|
|
8562
|
+
requestId: requestIdStr,
|
|
8563
|
+
method: req.method,
|
|
8564
|
+
path: pathForLog(req),
|
|
8565
|
+
status,
|
|
8566
|
+
durationMs: Math.round((performance.now() - startedAt) * 1000) / 1000,
|
|
8567
|
+
ip: ipStr
|
|
8568
|
+
});
|
|
8569
|
+
}
|
|
8256
8570
|
}
|
|
8257
8571
|
return out;
|
|
8258
8572
|
} finally {
|
|
@@ -8566,6 +8880,10 @@ function createIngressRouter(options) {
|
|
|
8566
8880
|
}
|
|
8567
8881
|
const handler = createIngressHandler(spec.options ?? {}, runtime);
|
|
8568
8882
|
compiled[path] = handler;
|
|
8883
|
+
const routeLimit = spec.options?.rateLimit?.limit;
|
|
8884
|
+
if (typeof routeLimit === "number" && routeLimit > 0 && routeLimit !== RATE_LIMIT_U32_MAX && options.getIp === undefined) {
|
|
8885
|
+
warnRateLimitWithoutGetIp();
|
|
8886
|
+
}
|
|
8569
8887
|
if (spec.responder) {
|
|
8570
8888
|
bakedRoutes[path] = {
|
|
8571
8889
|
responder: {
|
|
@@ -8790,6 +9108,22 @@ function createIngress(options = {}) {
|
|
|
8790
9108
|
}
|
|
8791
9109
|
};
|
|
8792
9110
|
}
|
|
9111
|
+
// src/native/ingress-binding.ts
|
|
9112
|
+
init_ffi();
|
|
9113
|
+
var cached2;
|
|
9114
|
+
function getIngressBinding() {
|
|
9115
|
+
if (cached2 !== undefined)
|
|
9116
|
+
return cached2;
|
|
9117
|
+
const ffi = getBunFFI();
|
|
9118
|
+
const addonPath = ffiAddonPath();
|
|
9119
|
+
cached2 = ffi && addonPath ? {
|
|
9120
|
+
addonPath,
|
|
9121
|
+
ingressHandleComponents: ffi.ingressHandleComponents,
|
|
9122
|
+
ingressHandlePacked: ffi.ingressHandlePacked,
|
|
9123
|
+
ingressLayout: ffi.ingressLayout
|
|
9124
|
+
} : null;
|
|
9125
|
+
return cached2;
|
|
9126
|
+
}
|
|
8793
9127
|
// src/loader/index.ts
|
|
8794
9128
|
init_rust_ffi();
|
|
8795
9129
|
init_bytes();
|
|
@@ -10207,6 +10541,7 @@ function createTaskRuntime(options = {}) {
|
|
|
10207
10541
|
queueMicrotask(drainNow);
|
|
10208
10542
|
}
|
|
10209
10543
|
function submit(op, args, runOptions) {
|
|
10544
|
+
ensureDoorbell();
|
|
10210
10545
|
const id = nextId++;
|
|
10211
10546
|
return new Promise((resolve, reject) => {
|
|
10212
10547
|
inflight.set(id, { out: null, keep: null, resolve, reject });
|
|
@@ -10228,6 +10563,7 @@ function createTaskRuntime(options = {}) {
|
|
|
10228
10563
|
});
|
|
10229
10564
|
}
|
|
10230
10565
|
function submitInto(op, args, output, runOptions) {
|
|
10566
|
+
ensureDoorbell();
|
|
10231
10567
|
const id = nextId++;
|
|
10232
10568
|
return new Promise((resolve, reject) => {
|
|
10233
10569
|
inflight.set(id, { out: output, keep: null, resolve, reject });
|
|
@@ -10249,6 +10585,7 @@ function createTaskRuntime(options = {}) {
|
|
|
10249
10585
|
});
|
|
10250
10586
|
}
|
|
10251
10587
|
function submitSlice(op, hdr, data, runOptions) {
|
|
10588
|
+
ensureDoorbell();
|
|
10252
10589
|
const id = nextId++;
|
|
10253
10590
|
return new Promise((resolve, reject) => {
|
|
10254
10591
|
inflight.set(id, { out: null, keep: data, resolve, reject });
|
|
@@ -10270,11 +10607,16 @@ function createTaskRuntime(options = {}) {
|
|
|
10270
10607
|
});
|
|
10271
10608
|
}
|
|
10272
10609
|
const { JSCallback } = __require("bun:ffi");
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10610
|
+
function ensureDoorbell() {
|
|
10611
|
+
if (doorbell !== null)
|
|
10612
|
+
return;
|
|
10613
|
+
const cb = new JSCallback(() => {
|
|
10614
|
+
drainNow();
|
|
10615
|
+
}, { args: [], returns: "void", threadsafe: true });
|
|
10616
|
+
doorbell = cb;
|
|
10617
|
+
ffi.taskSetDoorbell(Number(cb.ptr));
|
|
10618
|
+
}
|
|
10619
|
+
ensureDoorbell();
|
|
10278
10620
|
schedule();
|
|
10279
10621
|
const runtime = {
|
|
10280
10622
|
gzipDecompress(data, runOptions) {
|
|
@@ -10318,6 +10660,7 @@ function createTaskRuntime(options = {}) {
|
|
|
10318
10660
|
deferred.reject(cancelledError());
|
|
10319
10661
|
}
|
|
10320
10662
|
inflight.clear();
|
|
10663
|
+
ffi.taskSetDoorbell(0);
|
|
10321
10664
|
ffi.taskShutdown();
|
|
10322
10665
|
release();
|
|
10323
10666
|
doorbell?.close();
|
|
@@ -10454,6 +10797,7 @@ export {
|
|
|
10454
10797
|
errorMessage,
|
|
10455
10798
|
fallbackHandler,
|
|
10456
10799
|
generateRequestId,
|
|
10800
|
+
getIngressBinding,
|
|
10457
10801
|
gracefulShutdown,
|
|
10458
10802
|
gzipCompressUpperBound,
|
|
10459
10803
|
headHandler,
|