mirror of
https://github.com/oven-sh/bun
synced 2026-02-21 16:21:51 +00:00
Compare commits
22 Commits
claude/str
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e60329f6a7 | ||
|
|
6d14b90e5e | ||
|
|
7848648e09 | ||
|
|
379daff22d | ||
|
|
5b0db0191e | ||
|
|
9ef9ac1db1 | ||
|
|
f5d98191b7 | ||
|
|
83bca9bea8 | ||
|
|
7794cc866e | ||
|
|
70b354aa04 | ||
|
|
9d5a800c3d | ||
|
|
77ca318336 | ||
|
|
337a9f7f2b | ||
|
|
38f41dccdf | ||
|
|
883e43c371 | ||
|
|
cd17934207 | ||
|
|
f6d4ff6779 | ||
|
|
2c173529fa | ||
|
|
243fa45bec | ||
|
|
c19dcb3181 | ||
|
|
c57af9df38 | ||
|
|
3debd0a2d2 |
@@ -1,68 +0,0 @@
|
||||
import { bench, group, run, summary } from "../runner.mjs";
|
||||
|
||||
// === TypedArray structuredClone benchmarks ===
|
||||
|
||||
// Uint8Array at various sizes
|
||||
var uint8_64 = new Uint8Array(64);
|
||||
var uint8_1K = new Uint8Array(1024);
|
||||
var uint8_64K = new Uint8Array(64 * 1024);
|
||||
var uint8_1M = new Uint8Array(1024 * 1024);
|
||||
|
||||
// Fill with non-zero data to be realistic
|
||||
for (var i = 0; i < uint8_64.length; i++) uint8_64[i] = i & 0xff;
|
||||
for (var i = 0; i < uint8_1K.length; i++) uint8_1K[i] = i & 0xff;
|
||||
for (var i = 0; i < uint8_64K.length; i++) uint8_64K[i] = i & 0xff;
|
||||
for (var i = 0; i < uint8_1M.length; i++) uint8_1M[i] = i & 0xff;
|
||||
|
||||
// Other typed array types (1KB each)
|
||||
var int8_1K = new Int8Array(1024);
|
||||
var uint16_1K = new Uint16Array(512); // 1KB
|
||||
var int32_1K = new Int32Array(256); // 1KB
|
||||
var float32_1K = new Float32Array(256); // 1KB
|
||||
var float64_1K = new Float64Array(128); // 1KB
|
||||
var bigint64_1K = new BigInt64Array(128); // 1KB
|
||||
|
||||
for (var i = 0; i < int8_1K.length; i++) int8_1K[i] = (i % 256) - 128;
|
||||
for (var i = 0; i < uint16_1K.length; i++) uint16_1K[i] = i;
|
||||
for (var i = 0; i < int32_1K.length; i++) int32_1K[i] = i * 1000;
|
||||
for (var i = 0; i < float32_1K.length; i++) float32_1K[i] = i * 0.1;
|
||||
for (var i = 0; i < float64_1K.length; i++) float64_1K[i] = i * 0.1;
|
||||
for (var i = 0; i < bigint64_1K.length; i++) bigint64_1K[i] = BigInt(i);
|
||||
|
||||
// Slice view (byteOffset != 0) — should fall back to slow path
|
||||
var sliceBuf = new ArrayBuffer(2048);
|
||||
var uint8_slice = new Uint8Array(sliceBuf, 512, 512);
|
||||
|
||||
summary(() => {
|
||||
group("Uint8Array by size", () => {
|
||||
bench("Uint8Array 64B", () => structuredClone(uint8_64));
|
||||
bench("Uint8Array 1KB", () => structuredClone(uint8_1K));
|
||||
bench("Uint8Array 64KB", () => structuredClone(uint8_64K));
|
||||
bench("Uint8Array 1MB", () => structuredClone(uint8_1M));
|
||||
});
|
||||
});
|
||||
|
||||
summary(() => {
|
||||
group("TypedArray types (1KB each)", () => {
|
||||
bench("Int8Array", () => structuredClone(int8_1K));
|
||||
bench("Uint8Array", () => structuredClone(uint8_1K));
|
||||
bench("Uint16Array", () => structuredClone(uint16_1K));
|
||||
bench("Int32Array", () => structuredClone(int32_1K));
|
||||
bench("Float32Array", () => structuredClone(float32_1K));
|
||||
bench("Float64Array", () => structuredClone(float64_1K));
|
||||
bench("BigInt64Array", () => structuredClone(bigint64_1K));
|
||||
});
|
||||
});
|
||||
|
||||
// Pre-create for fair comparison
|
||||
var uint8_whole = new Uint8Array(512);
|
||||
for (var i = 0; i < 512; i++) uint8_whole[i] = i & 0xff;
|
||||
|
||||
summary(() => {
|
||||
group("fast path vs slow path (512B)", () => {
|
||||
bench("Uint8Array whole (fast path)", () => structuredClone(uint8_whole));
|
||||
bench("Uint8Array slice (slow path)", () => structuredClone(uint8_slice));
|
||||
});
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -59,15 +59,4 @@ var objectsMedium = Array.from({ length: 100 }, (_, i) => ({ id: i, name: `item-
|
||||
bench("structuredClone([10 objects])", () => structuredClone(objectsSmall));
|
||||
bench("structuredClone([100 objects])", () => structuredClone(objectsMedium));
|
||||
|
||||
// TypedArray fast path targets
|
||||
var uint8Small = new Uint8Array(64);
|
||||
var uint8Medium = new Uint8Array(1024);
|
||||
var uint8Large = new Uint8Array(1024 * 1024);
|
||||
var float64Medium = new Float64Array(128);
|
||||
|
||||
bench("structuredClone(Uint8Array 64B)", () => structuredClone(uint8Small));
|
||||
bench("structuredClone(Uint8Array 1KB)", () => structuredClone(uint8Medium));
|
||||
bench("structuredClone(Uint8Array 1MB)", () => structuredClone(uint8Large));
|
||||
bench("structuredClone(Float64Array 1KB)", () => structuredClone(float64Medium));
|
||||
|
||||
await run();
|
||||
|
||||
@@ -198,13 +198,16 @@ const myPlugin: BunPlugin = {
|
||||
};
|
||||
```
|
||||
|
||||
The builder object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use `config` (same thing but with Bun's `BuildConfig` format) instead.
|
||||
The builder object provides some methods for hooking into parts of the bundling process. Bun implements `onStart`, `onEnd`, `onResolve`, and `onLoad`. It does not yet implement the esbuild hooks `onDispose` and `resolve`. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use `config` (same thing but with Bun's `BuildConfig` format) instead.
|
||||
|
||||
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
|
||||
import type { BunPlugin } from "bun";
|
||||
const myPlugin: BunPlugin = {
|
||||
name: "my-plugin",
|
||||
setup(builder) {
|
||||
builder.onStart(() => {
|
||||
/* called when the bundle starts */
|
||||
});
|
||||
builder.onResolve(
|
||||
{
|
||||
/* onResolve.options */
|
||||
@@ -225,6 +228,9 @@ const myPlugin: BunPlugin = {
|
||||
};
|
||||
},
|
||||
);
|
||||
builder.onEnd(result => {
|
||||
/* called when the bundle is complete */
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
@@ -15,6 +15,7 @@ Plugins can register callbacks to be run at various points in the lifecycle of a
|
||||
- `onResolve()`: Run before a module is resolved
|
||||
- `onLoad()`: Run before a module is loaded
|
||||
- `onBeforeParse()`: Run zero-copy native addons in the parser thread before a file is parsed
|
||||
- `onEnd()`: Run after the bundle is complete
|
||||
|
||||
## Reference
|
||||
|
||||
@@ -39,6 +40,7 @@ type PluginBuilder = {
|
||||
exports?: Record<string, any>;
|
||||
},
|
||||
) => void;
|
||||
onEnd(callback: (result: BuildOutput) => void | Promise<void>): void;
|
||||
config: BuildConfig;
|
||||
};
|
||||
|
||||
@@ -423,3 +425,53 @@ This lifecycle callback is run immediately before a file is parsed by Bun's bund
|
||||
As input, it receives the file's contents and can optionally return new source code.
|
||||
|
||||
<Info>This callback can be called from any thread and so the napi module implementation must be thread-safe.</Info>
|
||||
|
||||
### onEnd
|
||||
|
||||
```ts
|
||||
onEnd(callback: (result: BuildOutput) => void | Promise<void>): void;
|
||||
```
|
||||
|
||||
Registers a callback to be run after the bundle is complete. The callback receives the [`BuildOutput`](/docs/bundler#outputs) object containing the build results, including output files and any build messages.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
plugins: [
|
||||
{
|
||||
name: "onEnd example",
|
||||
setup(build) {
|
||||
build.onEnd(result => {
|
||||
console.log(`Build completed with ${result.outputs.length} files`);
|
||||
for (const log of result.logs) {
|
||||
console.log(log);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
The callback can return a `Promise`. The build output promise from `Bun.build()` will not resolve until all `onEnd()` callbacks have completed.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
plugins: [
|
||||
{
|
||||
name: "Upload to S3",
|
||||
setup(build) {
|
||||
build.onEnd(async result => {
|
||||
if (!result.success) return;
|
||||
for (const output of result.outputs) {
|
||||
await uploadToS3(output);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
@@ -23196,557 +23196,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust ECC Root-01"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Serial Number:43:70:82:77:cf:4d:5d:34:f1:ca:ae:32:2f:37:f7:f4:7f:75:a0:9e
|
||||
# Subject: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:35:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:35:42 2046
|
||||
# Fingerprint (SHA-256): 11:43:7C:DA:7B:B4:5E:41:36:5F:45:B3:9A:38:98:6B:0D:E0:0D:EF:34:8E:0C:7B:B0:87:36:33:80:0B:C3:8B
|
||||
# Fingerprint (SHA1): 07:86:C0:D8:DD:8E:C0:80:98:06:98:D0:58:7A:EF:DE:A6:CC:A2:5D
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-01"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\103\160\202\167\317\115\135\064\361\312\256\062\057\067
|
||||
\367\364\177\165\240\236
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\002\035\060\202\001\243\240\003\002\001\002\002\024\103
|
||||
\160\202\167\317\115\135\064\361\312\256\062\057\067\367\364\177
|
||||
\165\240\236\060\012\006\010\052\206\110\316\075\004\003\003\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061\060
|
||||
\036\027\015\062\061\060\064\062\070\061\067\063\065\064\063\132
|
||||
\027\015\064\066\060\064\062\070\061\067\063\065\064\062\132\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061\060
|
||||
\166\060\020\006\007\052\206\110\316\075\002\001\006\005\053\201
|
||||
\004\000\042\003\142\000\004\113\066\351\256\127\136\250\160\327
|
||||
\320\217\164\142\167\303\136\172\252\345\266\242\361\170\375\002
|
||||
\176\127\335\221\171\234\154\271\122\210\124\274\057\004\276\270
|
||||
\315\366\020\321\051\354\265\320\240\303\360\211\160\031\273\121
|
||||
\145\305\103\234\303\233\143\235\040\203\076\006\013\246\102\104
|
||||
\205\021\247\112\072\055\351\326\150\057\110\116\123\053\007\077
|
||||
\115\275\271\254\167\071\127\243\102\060\100\060\017\006\003\125
|
||||
\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006\003
|
||||
\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006\003
|
||||
\125\035\016\004\026\004\024\216\007\142\300\120\335\306\031\006
|
||||
\000\106\164\004\367\363\256\175\165\115\060\060\012\006\010\052
|
||||
\206\110\316\075\004\003\003\003\150\000\060\145\002\061\000\234
|
||||
\063\337\101\343\043\250\102\066\046\227\065\134\173\353\333\113
|
||||
\370\252\213\163\125\025\134\254\170\051\017\272\041\330\304\240
|
||||
\330\321\003\335\155\321\071\075\304\223\140\322\343\162\262\002
|
||||
\060\174\305\176\210\323\120\365\036\045\350\372\116\165\346\130
|
||||
\226\244\065\137\033\145\352\141\232\160\043\265\015\243\233\222
|
||||
\122\157\151\240\214\215\112\320\356\213\016\313\107\216\320\215
|
||||
\021
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust ECC Root-01"
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Serial Number:43:70:82:77:cf:4d:5d:34:f1:ca:ae:32:2f:37:f7:f4:7f:75:a0:9e
|
||||
# Subject: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:35:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:35:42 2046
|
||||
# Fingerprint (SHA-256): 11:43:7C:DA:7B:B4:5E:41:36:5F:45:B3:9A:38:98:6B:0D:E0:0D:EF:34:8E:0C:7B:B0:87:36:33:80:0B:C3:8B
|
||||
# Fingerprint (SHA1): 07:86:C0:D8:DD:8E:C0:80:98:06:98:D0:58:7A:EF:DE:A6:CC:A2:5D
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-01"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\007\206\300\330\335\216\300\200\230\006\230\320\130\172\357\336
|
||||
\246\314\242\135
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\072\100\247\374\003\214\234\070\171\057\072\242\154\266\012\026
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\103\160\202\167\317\115\135\064\361\312\256\062\057\067
|
||||
\367\364\177\165\240\236
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust ECC Root-02"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Serial Number:28:fd:99:60:41:47:a6:01:3a:ca:14:7b:1f:ef:f9:68:08:83:5d:7d
|
||||
# Subject: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:44:54 2021
|
||||
# Not Valid After : Sat Apr 28 17:44:53 2046
|
||||
# Fingerprint (SHA-256): 2F:FB:7F:81:3B:BB:B3:C8:9A:B4:E8:16:2D:0F:16:D7:15:09:A8:30:CC:9D:73:C2:62:E5:14:08:75:D1:AD:4A
|
||||
# Fingerprint (SHA1): 3C:3F:EF:57:0F:FE:65:93:86:9E:A0:FE:B0:F6:ED:8E:D1:13:C7:E5
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-02"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\050\375\231\140\101\107\246\001\072\312\024\173\037\357
|
||||
\371\150\010\203\135\175
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\002\034\060\202\001\243\240\003\002\001\002\002\024\050
|
||||
\375\231\140\101\107\246\001\072\312\024\173\037\357\371\150\010
|
||||
\203\135\175\060\012\006\010\052\206\110\316\075\004\003\003\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062\060
|
||||
\036\027\015\062\061\060\064\062\070\061\067\064\064\065\064\132
|
||||
\027\015\064\066\060\064\062\070\061\067\064\064\065\063\132\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062\060
|
||||
\166\060\020\006\007\052\206\110\316\075\002\001\006\005\053\201
|
||||
\004\000\042\003\142\000\004\170\060\201\350\143\036\345\353\161
|
||||
\121\017\367\007\007\312\071\231\174\116\325\017\314\060\060\013
|
||||
\217\146\223\076\317\275\305\206\275\371\261\267\264\076\264\007
|
||||
\310\363\226\061\363\355\244\117\370\243\116\215\051\025\130\270
|
||||
\325\157\177\356\154\042\265\260\257\110\105\012\275\250\111\224
|
||||
\277\204\103\260\333\204\112\003\043\031\147\152\157\301\156\274
|
||||
\006\071\067\321\210\042\367\243\102\060\100\060\017\006\003\125
|
||||
\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006\003
|
||||
\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006\003
|
||||
\125\035\016\004\026\004\024\346\030\165\377\357\140\336\204\244
|
||||
\365\106\307\336\112\125\343\062\066\171\365\060\012\006\010\052
|
||||
\206\110\316\075\004\003\003\003\147\000\060\144\002\060\046\163
|
||||
\111\172\266\253\346\111\364\175\122\077\324\101\004\256\200\103
|
||||
\203\145\165\271\205\200\070\073\326\157\344\223\206\253\217\347
|
||||
\211\310\177\233\176\153\012\022\125\141\252\021\340\171\002\060
|
||||
\167\350\061\161\254\074\161\003\326\204\046\036\024\270\363\073
|
||||
\073\336\355\131\374\153\114\060\177\131\316\105\351\163\140\025
|
||||
\232\114\360\346\136\045\042\025\155\302\207\131\320\262\216\152
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust ECC Root-02"
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Serial Number:28:fd:99:60:41:47:a6:01:3a:ca:14:7b:1f:ef:f9:68:08:83:5d:7d
|
||||
# Subject: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:44:54 2021
|
||||
# Not Valid After : Sat Apr 28 17:44:53 2046
|
||||
# Fingerprint (SHA-256): 2F:FB:7F:81:3B:BB:B3:C8:9A:B4:E8:16:2D:0F:16:D7:15:09:A8:30:CC:9D:73:C2:62:E5:14:08:75:D1:AD:4A
|
||||
# Fingerprint (SHA1): 3C:3F:EF:57:0F:FE:65:93:86:9E:A0:FE:B0:F6:ED:8E:D1:13:C7:E5
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-02"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\074\077\357\127\017\376\145\223\206\236\240\376\260\366\355\216
|
||||
\321\023\307\345
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\131\260\104\325\145\115\270\134\125\031\222\002\266\321\224\262
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\050\375\231\140\101\107\246\001\072\312\024\173\037\357
|
||||
\371\150\010\203\135\175
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust RSA Root-01"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Serial Number:3e:03:49:81:75:16:74:31:8e:4c:ab:d5:c5:90:29:96:c5:39:10:dd
|
||||
# Subject: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 16:45:54 2021
|
||||
# Not Valid After : Sat Apr 28 16:45:53 2046
|
||||
# Fingerprint (SHA-256): 02:BD:F9:6E:2A:45:DD:9B:F1:8F:C7:E1:DB:DF:21:A0:37:9B:A3:C9:C2:61:03:44:CF:D8:D6:06:FE:C1:ED:81
|
||||
# Fingerprint (SHA1): 6D:0A:5F:F7:B4:23:06:B4:85:B3:B7:97:64:FC:AC:75:F5:33:F2:93
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-01"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\076\003\111\201\165\026\164\061\216\114\253\325\305\220
|
||||
\051\226\305\071\020\335
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\005\154\060\202\003\124\240\003\002\001\002\002\024\076
|
||||
\003\111\201\165\026\164\061\216\114\253\325\305\220\051\226\305
|
||||
\071\020\335\060\015\006\011\052\206\110\206\367\015\001\001\013
|
||||
\005\000\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\061\060\036\027\015\062\061\060\064\062\070\061\066\064\065
|
||||
\065\064\132\027\015\064\066\060\064\062\070\061\066\064\065\065
|
||||
\063\132\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\061\060\202\002\042\060\015\006\011\052\206\110\206\367\015
|
||||
\001\001\001\005\000\003\202\002\017\000\060\202\002\012\002\202
|
||||
\002\001\000\260\110\145\243\015\035\102\343\221\155\235\204\244
|
||||
\141\226\022\302\355\303\332\043\064\031\166\366\352\375\125\132
|
||||
\366\125\001\123\017\362\314\214\227\117\271\120\313\263\001\104
|
||||
\126\226\375\233\050\354\173\164\013\347\102\153\125\316\311\141
|
||||
\262\350\255\100\074\272\271\101\012\005\117\033\046\205\217\103
|
||||
\265\100\265\205\321\324\161\334\203\101\363\366\105\307\200\242
|
||||
\204\120\227\106\316\240\014\304\140\126\004\035\007\133\106\245
|
||||
\016\262\113\244\016\245\174\356\370\324\142\003\271\223\152\212
|
||||
\024\270\160\370\056\202\106\070\043\016\164\307\153\101\267\320
|
||||
\051\243\235\200\260\176\167\223\143\102\373\064\203\073\163\243
|
||||
\132\041\066\353\107\372\030\027\331\272\146\302\223\244\217\374
|
||||
\135\244\255\374\120\152\225\254\274\044\063\321\275\210\177\206
|
||||
\365\365\262\163\052\217\174\257\010\362\032\230\077\251\201\145
|
||||
\077\301\214\211\305\226\060\232\012\317\364\324\310\064\355\235
|
||||
\057\274\215\070\206\123\356\227\237\251\262\143\224\027\215\017
|
||||
\334\146\052\174\122\121\165\313\231\216\350\075\134\277\236\073
|
||||
\050\215\203\002\017\251\237\162\342\054\053\263\334\146\227\000
|
||||
\100\320\244\124\216\233\135\173\105\066\046\326\162\103\353\317
|
||||
\300\352\015\334\316\022\346\175\070\237\005\047\250\227\076\351
|
||||
\121\306\154\005\050\301\002\017\351\030\155\354\275\234\006\324
|
||||
\247\111\364\124\005\153\154\060\361\353\003\325\352\075\152\166
|
||||
\302\313\032\050\111\115\177\144\340\372\053\332\163\203\201\377
|
||||
\221\003\275\224\273\344\270\216\234\062\143\315\237\273\150\201
|
||||
\261\204\133\257\066\277\167\356\035\177\367\111\233\122\354\322
|
||||
\167\132\175\221\235\115\302\071\055\344\272\202\370\157\362\116
|
||||
\036\017\116\346\077\131\245\043\334\075\207\250\050\130\050\321
|
||||
\361\033\066\333\117\304\377\341\214\133\162\214\307\046\003\047
|
||||
\243\071\012\001\252\300\262\061\140\203\042\241\117\022\011\001
|
||||
\021\257\064\324\317\327\256\142\323\005\007\264\061\165\340\015
|
||||
\155\127\117\151\207\371\127\251\272\025\366\310\122\155\241\313
|
||||
\234\037\345\374\170\250\065\232\237\101\024\316\245\264\316\224
|
||||
\010\034\011\255\126\345\332\266\111\232\112\352\143\030\123\234
|
||||
\054\056\303\002\003\001\000\001\243\102\060\100\060\017\006\003
|
||||
\125\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006
|
||||
\003\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006
|
||||
\003\125\035\016\004\026\004\024\067\135\246\232\164\062\302\302
|
||||
\371\307\246\025\020\131\270\344\375\345\270\155\060\015\006\011
|
||||
\052\206\110\206\367\015\001\001\013\005\000\003\202\002\001\000
|
||||
\257\247\317\336\377\340\275\102\215\115\345\042\226\337\150\352
|
||||
\175\115\052\175\320\255\075\026\134\103\347\175\300\206\350\172
|
||||
\065\143\361\314\201\310\306\013\350\056\122\065\244\246\111\220
|
||||
\143\121\254\064\254\005\073\127\000\351\323\142\323\331\051\325
|
||||
\124\276\034\020\221\234\262\155\376\131\375\171\367\352\126\320
|
||||
\236\150\124\102\217\046\122\342\114\337\057\227\246\057\322\007
|
||||
\230\250\363\140\135\113\232\130\127\210\357\202\345\372\257\154
|
||||
\201\113\222\217\100\232\223\106\131\313\137\170\026\261\147\076
|
||||
\102\013\337\050\331\260\255\230\040\276\103\174\321\136\032\011
|
||||
\027\044\215\173\135\225\351\253\301\140\253\133\030\144\200\373
|
||||
\255\340\006\175\035\312\131\270\363\170\051\147\306\126\035\257
|
||||
\266\265\164\052\166\241\077\373\165\060\237\224\136\073\245\140
|
||||
\363\313\134\014\342\016\311\140\370\311\037\026\212\046\335\347
|
||||
\047\177\353\045\246\212\275\270\055\066\020\232\261\130\115\232
|
||||
\150\117\140\124\345\366\106\023\216\210\254\274\041\102\022\255
|
||||
\306\112\211\175\233\301\330\055\351\226\003\364\242\164\014\274
|
||||
\000\035\277\326\067\045\147\264\162\213\257\205\275\352\052\003
|
||||
\217\314\373\074\104\044\202\342\001\245\013\131\266\064\215\062
|
||||
\013\022\015\353\047\302\375\101\327\100\074\162\106\051\300\214
|
||||
\352\272\017\361\006\223\056\367\234\250\364\140\076\243\361\070
|
||||
\136\216\023\301\263\072\227\207\077\222\312\170\251\034\257\320
|
||||
\260\033\046\036\276\160\354\172\365\063\230\352\134\377\053\013
|
||||
\004\116\103\335\143\176\016\247\116\170\003\225\076\324\055\060
|
||||
\225\021\020\050\056\277\240\002\076\377\136\131\323\005\016\225
|
||||
\137\123\105\357\153\207\325\110\315\026\246\226\203\341\337\263
|
||||
\006\363\301\024\333\247\354\034\213\135\220\220\015\162\121\347
|
||||
\141\371\024\312\257\203\217\277\257\261\012\131\135\334\134\327
|
||||
\344\226\255\133\140\035\332\256\227\262\071\331\006\365\166\000
|
||||
\023\370\150\114\041\260\065\304\334\125\262\311\301\101\132\034
|
||||
\211\300\214\157\164\240\153\063\115\265\001\050\375\255\255\211
|
||||
\027\073\246\232\204\274\353\214\352\304\161\044\250\272\051\371
|
||||
\010\262\047\126\065\062\137\352\071\373\061\232\325\031\314\360
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust RSA Root-01"
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Serial Number:3e:03:49:81:75:16:74:31:8e:4c:ab:d5:c5:90:29:96:c5:39:10:dd
|
||||
# Subject: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 16:45:54 2021
|
||||
# Not Valid After : Sat Apr 28 16:45:53 2046
|
||||
# Fingerprint (SHA-256): 02:BD:F9:6E:2A:45:DD:9B:F1:8F:C7:E1:DB:DF:21:A0:37:9B:A3:C9:C2:61:03:44:CF:D8:D6:06:FE:C1:ED:81
|
||||
# Fingerprint (SHA1): 6D:0A:5F:F7:B4:23:06:B4:85:B3:B7:97:64:FC:AC:75:F5:33:F2:93
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-01"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\155\012\137\367\264\043\006\264\205\263\267\227\144\374\254\165
|
||||
\365\063\362\223
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\016\264\025\274\207\143\135\135\002\163\324\046\070\150\163\330
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\076\003\111\201\165\026\164\061\216\114\253\325\305\220
|
||||
\051\226\305\071\020\335
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust RSA Root-02"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Serial Number:54:16:bf:3b:7e:39:95:71:8d:d1:aa:00:a5:86:0d:2b:8f:7a:05:4e
|
||||
# Subject: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:16:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:16:42 2046
|
||||
# Fingerprint (SHA-256): FF:E9:43:D7:93:42:4B:4F:7C:44:0C:1C:3D:64:8D:53:63:F3:4B:82:DC:87:AA:7A:9F:11:8F:C5:DE:E1:01:F1
|
||||
# Fingerprint (SHA1): EA:B0:E2:52:1B:89:93:4C:11:68:F2:D8:9A:AC:22:4C:A3:8A:57:AE
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-02"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\124\026\277\073\176\071\225\161\215\321\252\000\245\206
|
||||
\015\053\217\172\005\116
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\005\154\060\202\003\124\240\003\002\001\002\002\024\124
|
||||
\026\277\073\176\071\225\161\215\321\252\000\245\206\015\053\217
|
||||
\172\005\116\060\015\006\011\052\206\110\206\367\015\001\001\013
|
||||
\005\000\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\062\060\036\027\015\062\061\060\064\062\070\061\067\061\066
|
||||
\064\063\132\027\015\064\066\060\064\062\070\061\067\061\066\064
|
||||
\062\132\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\062\060\202\002\042\060\015\006\011\052\206\110\206\367\015
|
||||
\001\001\001\005\000\003\202\002\017\000\060\202\002\012\002\202
|
||||
\002\001\000\341\372\016\373\150\000\022\310\115\325\254\042\304
|
||||
\065\001\073\305\124\345\131\166\143\245\177\353\301\304\152\230
|
||||
\275\062\215\027\200\353\135\272\321\142\075\045\043\031\065\024
|
||||
\351\177\211\247\033\142\074\326\120\347\064\225\003\062\261\264
|
||||
\223\042\075\247\342\261\355\346\173\116\056\207\233\015\063\165
|
||||
\012\336\252\065\347\176\345\066\230\242\256\045\236\225\263\062
|
||||
\226\244\053\130\036\357\077\376\142\064\110\121\321\264\215\102
|
||||
\255\140\332\111\152\225\160\335\322\000\342\314\127\143\002\173
|
||||
\226\335\111\227\133\222\116\225\323\371\313\051\037\030\112\370
|
||||
\001\052\322\143\011\156\044\351\211\322\345\307\042\114\334\163
|
||||
\206\107\000\252\015\210\216\256\205\175\112\351\273\063\117\016
|
||||
\122\160\235\225\343\174\155\226\133\055\075\137\241\203\106\135
|
||||
\266\343\045\270\174\247\031\200\034\352\145\103\334\221\171\066
|
||||
\054\164\174\362\147\006\311\211\311\333\277\332\150\277\043\355
|
||||
\334\153\255\050\203\171\057\354\070\245\015\067\001\147\047\232
|
||||
\351\063\331\063\137\067\241\305\360\253\075\372\170\260\347\054
|
||||
\237\366\076\237\140\340\357\110\351\220\105\036\005\121\170\032
|
||||
\054\022\054\134\050\254\015\242\043\236\064\217\005\346\242\063
|
||||
\316\021\167\023\324\016\244\036\102\037\206\315\160\376\331\056
|
||||
\025\075\035\273\270\362\123\127\333\314\306\164\051\234\030\263
|
||||
\066\165\070\056\017\124\241\370\222\037\211\226\117\273\324\356
|
||||
\235\351\073\066\102\265\012\073\052\324\144\171\066\020\341\371
|
||||
\221\003\053\173\040\124\315\015\031\032\310\101\062\064\321\260
|
||||
\231\341\220\036\001\100\066\265\267\372\251\345\167\165\244\042
|
||||
\201\135\260\213\344\047\022\017\124\210\306\333\205\164\346\267
|
||||
\300\327\246\051\372\333\336\363\223\227\047\004\125\057\012\157
|
||||
\067\305\075\023\257\012\000\251\054\213\034\201\050\327\357\206
|
||||
\061\251\256\362\156\270\312\152\054\124\107\330\052\210\056\257
|
||||
\301\007\020\170\254\021\242\057\102\360\067\305\362\270\126\335
|
||||
\016\142\055\316\055\126\176\125\362\247\104\366\053\062\364\043
|
||||
\250\107\350\324\052\001\170\317\152\303\067\250\236\145\322\054
|
||||
\345\372\272\063\301\006\104\366\346\317\245\015\247\146\010\064
|
||||
\212\054\363\002\003\001\000\001\243\102\060\100\060\017\006\003
|
||||
\125\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006
|
||||
\003\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006
|
||||
\003\125\035\016\004\026\004\024\107\320\347\261\042\377\235\054
|
||||
\365\331\127\140\263\261\261\160\225\357\141\172\060\015\006\011
|
||||
\052\206\110\206\367\015\001\001\013\005\000\003\202\002\001\000
|
||||
\206\151\261\115\057\351\237\117\042\223\150\216\344\041\231\243
|
||||
\316\105\123\033\163\104\123\000\201\141\315\061\343\010\272\201
|
||||
\050\050\172\222\271\266\250\310\103\236\307\023\046\115\302\330
|
||||
\345\125\234\222\135\120\330\302\053\333\376\346\250\227\317\122
|
||||
\072\044\303\145\144\134\107\061\243\145\065\023\303\223\271\367
|
||||
\371\121\227\273\244\360\142\207\305\326\006\323\227\203\040\251
|
||||
\176\273\266\041\302\245\015\204\000\341\362\047\020\203\272\335
|
||||
\003\201\325\335\150\303\146\020\310\321\166\264\263\157\051\236
|
||||
\000\371\302\051\365\261\223\031\122\151\032\054\114\240\213\340
|
||||
\025\232\061\057\323\210\225\131\156\345\304\263\120\310\024\010
|
||||
\112\233\213\023\203\261\244\162\262\073\166\063\101\334\334\252
|
||||
\246\007\157\035\044\022\237\310\166\275\057\331\216\364\054\356
|
||||
\267\322\070\020\044\066\121\057\343\134\135\201\041\247\332\273
|
||||
\116\377\346\007\250\376\271\015\047\154\273\160\132\125\172\023
|
||||
\351\361\052\111\151\307\137\207\127\114\103\171\155\072\145\351
|
||||
\060\134\101\356\353\167\245\163\022\210\350\277\175\256\345\304
|
||||
\250\037\015\216\034\155\120\002\117\046\030\103\336\217\125\205
|
||||
\261\013\067\005\140\311\125\071\022\004\241\052\317\161\026\237
|
||||
\066\121\111\277\160\073\236\147\234\373\173\171\311\071\034\170
|
||||
\254\167\221\124\232\270\165\012\201\122\227\343\146\141\153\355
|
||||
\076\070\036\226\141\125\341\221\124\214\355\214\044\037\201\311
|
||||
\020\232\163\231\053\026\116\162\000\077\124\033\370\215\272\213
|
||||
\347\024\326\266\105\117\140\354\226\256\303\057\002\116\135\235
|
||||
\226\111\162\000\262\253\165\134\017\150\133\035\145\302\137\063
|
||||
\017\036\017\360\073\206\365\260\116\273\234\367\352\045\005\334
|
||||
\255\242\233\113\027\001\276\102\337\065\041\035\255\253\256\364
|
||||
\277\256\037\033\323\342\073\374\263\162\163\034\233\050\220\211
|
||||
\023\075\035\301\000\107\011\226\232\070\033\335\261\317\015\302
|
||||
\264\104\363\226\225\316\062\072\217\064\234\340\027\307\136\316
|
||||
\256\015\333\207\070\345\077\133\375\233\031\341\061\101\172\160
|
||||
\252\043\153\001\341\105\114\315\224\316\073\236\055\347\210\002
|
||||
\042\364\156\350\310\354\326\074\363\271\262\327\167\172\254\173
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust RSA Root-02"
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Serial Number:54:16:bf:3b:7e:39:95:71:8d:d1:aa:00:a5:86:0d:2b:8f:7a:05:4e
|
||||
# Subject: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:16:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:16:42 2046
|
||||
# Fingerprint (SHA-256): FF:E9:43:D7:93:42:4B:4F:7C:44:0C:1C:3D:64:8D:53:63:F3:4B:82:DC:87:AA:7A:9F:11:8F:C5:DE:E1:01:F1
|
||||
# Fingerprint (SHA1): EA:B0:E2:52:1B:89:93:4C:11:68:F2:D8:9A:AC:22:4C:A3:8A:57:AE
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-02"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\352\260\342\122\033\211\223\114\021\150\362\330\232\254\042\114
|
||||
\243\212\127\256
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\341\051\371\142\173\166\342\226\155\363\324\327\017\256\037\252
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\124\026\277\073\176\071\225\161\215\321\252\000\245\206
|
||||
\015\053\217\172\005\116
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "D-Trust SBR Root CA 1 2022"
|
||||
#
|
||||
|
||||
@@ -3182,96 +3182,6 @@ static struct us_cert_string_t root_certs[] = {
|
||||
"MvHVI5TWWA==\n"
|
||||
"-----END CERTIFICATE-----",.len=869},
|
||||
|
||||
/* CommScope Public Trust ECC Root-01 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMwTjELMAkG\n"
|
||||
"A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1Ymxp\n"
|
||||
"YyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNaFw00NjA0MjgxNzM1NDJaME4x\n"
|
||||
"CzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQ\n"
|
||||
"dWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16o\n"
|
||||
"cNfQj3Rid8NeeqrltqLxeP0CflfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOc\n"
|
||||
"w5tjnSCDPgYLpkJEhRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMB\n"
|
||||
"Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq\n"
|
||||
"hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg2NED3W3R\n"
|
||||
"OT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uSUm9poIyNStDuiw7L\n"
|
||||
"R47QjRE=\n"
|
||||
"-----END CERTIFICATE-----",.len=792},
|
||||
|
||||
/* CommScope Public Trust ECC Root-02 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMwTjELMAkG\n"
|
||||
"A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1Ymxp\n"
|
||||
"YyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRaFw00NjA0MjgxNzQ0NTNaME4x\n"
|
||||
"CzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQ\n"
|
||||
"dWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l\n"
|
||||
"63FRD/cHB8o5mXxO1Q/MMDALj2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/u\n"
|
||||
"bCK1sK9IRQq9qEmUv4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMB\n"
|
||||
"Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq\n"
|
||||
"hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/nich/m35r\n"
|
||||
"ChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AVmkzw5l4lIhVtwodZ\n"
|
||||
"0LKOag==\n"
|
||||
"-----END CERTIFICATE-----",.len=792},
|
||||
|
||||
/* CommScope Public Trust RSA Root-01 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQELBQAwTjEL\n"
|
||||
"MAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1\n"
|
||||
"YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1NTRaFw00NjA0MjgxNjQ1NTNa\n"
|
||||
"ME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29w\n"
|
||||
"ZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3QtMDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
|
||||
"AoICAQCwSGWjDR1C45FtnYSkYZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2b\n"
|
||||
"KOx7dAvnQmtVzslhsuitQDy6uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBW\n"
|
||||
"BB0HW0alDrJLpA6lfO741GIDuZNqihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3Oj\n"
|
||||
"WiE260f6GBfZumbCk6SP/F2krfxQapWsvCQz0b2If4b19bJzKo98rwjyGpg/qYFlP8GMicWW\n"
|
||||
"MJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/cZip8UlF1y5mO6D1cv547KI2DAg+pn3LiLCuz\n"
|
||||
"3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTifBSeolz7pUcZsBSjBAg/pGG3svZwG1KdJ\n"
|
||||
"9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/kQO9lLvkuI6cMmPNn7togbGEW682v3fu\n"
|
||||
"HX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JOHg9O5j9ZpSPcPYeoKFgo0fEbNttPxP/hjFtyjMcm\n"
|
||||
"AyejOQoBqsCyMWCDIqFPEgkBEa801M/XrmLTBQe0MXXgDW1XT2mH+VepuhX2yFJtocucH+X8\n"
|
||||
"eKg1mp9BFM6ltM6UCBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\n"
|
||||
"AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm45P3luG0wDQYJ\n"
|
||||
"KoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6NWPxzIHI\n"
|
||||
"xgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQnmhUQo8mUuJM3y+X\n"
|
||||
"pi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+QgvfKNmwrZggvkN80V4aCRck\n"
|
||||
"jXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2vtrV0KnahP/t1MJ+UXjulYPPLXAziDslg\n"
|
||||
"+MkfFoom3ecnf+slpoq9uC02EJqxWE2aaE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0\n"
|
||||
"DLwAHb/WNyVntHKLr4W96ioDj8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/x\n"
|
||||
"BpMu95yo9GA+o/E4Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054\n"
|
||||
"A5U+1C0wlREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn\n"
|
||||
"YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVocicCMb3Sg\n"
|
||||
"azNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw\n"
|
||||
"-----END CERTIFICATE-----",.len=1935},
|
||||
|
||||
/* CommScope Public Trust RSA Root-02 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQELBQAwTjEL\n"
|
||||
"MAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1\n"
|
||||
"YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2NDNaFw00NjA0MjgxNzE2NDJa\n"
|
||||
"ME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29w\n"
|
||||
"ZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3QtMDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
|
||||
"AoICAQDh+g77aAASyE3VrCLENQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mn\n"
|
||||
"G2I81lDnNJUDMrG0kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0\n"
|
||||
"SFHRtI1CrWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz\n"
|
||||
"hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2LHR88mcG\n"
|
||||
"yYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcsn/Y+n2Dg70jpkEUe\n"
|
||||
"BVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tkuFT0du7jyU1fbzMZ0KZwYszZ1\n"
|
||||
"OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1\n"
|
||||
"t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX\n"
|
||||
"74Yxqa7ybrjKaixUR9gqiC6vwQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jU\n"
|
||||
"KgF4z2rDN6ieZdIs5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\n"
|
||||
"AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ\n"
|
||||
"KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqBKCh6krm2\n"
|
||||
"qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3+VGXu6TwYofF1gbT\n"
|
||||
"l4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbymeAPnCKfWxkxlSaRosTKCL4BWa\n"
|
||||
"MS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3NyqpgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv\n"
|
||||
"41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u\n"
|
||||
"5cSoHw2OHG1QAk8mGEPej1WFsQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FU\n"
|
||||
"mrh1CoFSl+NmYWvtPjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jau\n"
|
||||
"wy8CTl2dlklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670\n"
|
||||
"v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17Org3bhzjl\n"
|
||||
"P1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7\n"
|
||||
"-----END CERTIFICATE-----",.len=1935},
|
||||
|
||||
/* Telekom Security TLS ECC Root 2020 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQswCQYDVQQG\n"
|
||||
|
||||
@@ -1140,14 +1140,14 @@ export fn Bun__runVirtualModule(globalObject: *JSGlobalObject, specifier_ptr: *c
|
||||
fn getHardcodedModule(jsc_vm: *VirtualMachine, specifier: bun.String, hardcoded: HardcodedModule) ?ResolvedSource {
|
||||
analytics.Features.builtin_modules.insert(hardcoded);
|
||||
return switch (hardcoded) {
|
||||
.@"bun:main" => .{
|
||||
.@"bun:main" => if (jsc_vm.entry_point.generated) .{
|
||||
.allocator = null,
|
||||
.source_code = bun.String.cloneUTF8(jsc_vm.entry_point.source.contents),
|
||||
.specifier = specifier,
|
||||
.source_url = specifier,
|
||||
.tag = .esm,
|
||||
.source_code_needs_deref = true,
|
||||
},
|
||||
} else null,
|
||||
.@"bun:internal-for-testing" => {
|
||||
if (!Environment.isDebug) {
|
||||
if (!is_allowed_to_use_internal_testing_apis)
|
||||
|
||||
@@ -1616,7 +1616,7 @@ fn _resolve(
|
||||
if (strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) {
|
||||
ret.path = Runtime.Runtime.Imports.Name;
|
||||
return;
|
||||
} else if (strings.eqlComptime(specifier, main_file_name)) {
|
||||
} else if (strings.eqlComptime(specifier, main_file_name) and jsc_vm.entry_point.generated) {
|
||||
ret.result = null;
|
||||
ret.path = jsc_vm.entry_point.source.path.text;
|
||||
return;
|
||||
|
||||
@@ -4,7 +4,7 @@ const TimerObjectInternals = @This();
|
||||
/// Identifier for this timer that is exposed to JavaScript (by `+timer`)
|
||||
id: i32 = -1,
|
||||
interval: u31 = 0,
|
||||
strong_this: jsc.Strong.Optional = .empty,
|
||||
this_value: jsc.JSRef = .empty(),
|
||||
flags: Flags = .{},
|
||||
|
||||
/// Used by:
|
||||
@@ -76,31 +76,41 @@ pub fn runImmediateTask(this: *TimerObjectInternals, vm: *VirtualMachine) bool {
|
||||
// loop alive other than setImmediates
|
||||
(!this.flags.is_keeping_event_loop_alive and !vm.isEventLoopAliveExcludingImmediates()))
|
||||
{
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
return false;
|
||||
}
|
||||
|
||||
const timer = this.strong_this.get() orelse {
|
||||
const timer = this.this_value.tryGet() orelse {
|
||||
if (Environment.isDebug) {
|
||||
@panic("TimerObjectInternals.runImmediateTask: this_object is null");
|
||||
}
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.deref();
|
||||
return false;
|
||||
};
|
||||
const globalThis = vm.global;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
this.eventLoopTimer().state = .FIRED;
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
timer.ensureStillAlive();
|
||||
|
||||
vm.eventLoop().enter();
|
||||
const callback = ImmediateObject.js.callbackGetCached(timer).?;
|
||||
const arguments = ImmediateObject.js.argumentsGetCached(timer).?;
|
||||
this.ref();
|
||||
const exception_thrown = this.run(globalThis, timer, callback, arguments, this.asyncID(), vm);
|
||||
this.deref();
|
||||
|
||||
if (this.eventLoopTimer().state == .FIRED) {
|
||||
this.deref();
|
||||
}
|
||||
const exception_thrown = brk: {
|
||||
this.ref();
|
||||
defer {
|
||||
if (this.eventLoopTimer().state == .FIRED) {
|
||||
this.deref();
|
||||
}
|
||||
this.deref();
|
||||
}
|
||||
break :brk this.run(globalThis, timer, callback, arguments, this.asyncID(), vm);
|
||||
};
|
||||
// --- after this point, the timer is no longer guaranteed to be alive ---
|
||||
|
||||
vm.eventLoop().exitMaybeDrainMicrotasks(!exception_thrown) catch return true;
|
||||
|
||||
@@ -120,7 +130,13 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
this.eventLoopTimer().state = .FIRED;
|
||||
|
||||
const globalThis = vm.global;
|
||||
const this_object = this.strong_this.get().?;
|
||||
const this_object = this.this_value.tryGet() orelse {
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
return;
|
||||
};
|
||||
|
||||
const callback: JSValue, const arguments: JSValue, var idle_timeout: JSValue, var repeat: JSValue = switch (kind) {
|
||||
.setImmediate => .{
|
||||
@@ -143,7 +159,7 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
}
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
|
||||
return;
|
||||
@@ -152,7 +168,7 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
var time_before_call: timespec = undefined;
|
||||
|
||||
if (kind != .setInterval) {
|
||||
this.strong_this.clearWithoutDeallocation();
|
||||
this.this_value.downgrade();
|
||||
} else {
|
||||
time_before_call = timespec.msFromNow(.allow_mocked_time, this.interval);
|
||||
}
|
||||
@@ -239,7 +255,7 @@ fn convertToInterval(this: *TimerObjectInternals, global: *JSGlobalObject, timer
|
||||
|
||||
// https://github.com/nodejs/node/blob/a7cbb904745591c9a9d047a364c2c188e5470047/lib/internal/timers.js#L613
|
||||
TimeoutObject.js.idleTimeoutSetCached(timer, global, repeat);
|
||||
this.strong_this.set(global, timer);
|
||||
this.this_value.setStrong(timer, global);
|
||||
this.flags.kind = .setInterval;
|
||||
this.interval = new_interval;
|
||||
this.reschedule(timer, vm, global);
|
||||
@@ -297,7 +313,7 @@ pub fn init(
|
||||
this.reschedule(timer, vm, global);
|
||||
}
|
||||
|
||||
this.strong_this.set(global, timer);
|
||||
this.this_value.setStrong(timer, global);
|
||||
}
|
||||
|
||||
pub fn doRef(this: *TimerObjectInternals, _: *jsc.JSGlobalObject, this_value: JSValue) JSValue {
|
||||
@@ -327,7 +343,7 @@ pub fn doRefresh(this: *TimerObjectInternals, globalObject: *jsc.JSGlobalObject,
|
||||
return this_value;
|
||||
}
|
||||
|
||||
this.strong_this.set(globalObject, this_value);
|
||||
this.this_value.setStrong(this_value, globalObject);
|
||||
this.reschedule(this_value, VirtualMachine.get(), globalObject);
|
||||
|
||||
return this_value;
|
||||
@@ -350,12 +366,18 @@ pub fn cancel(this: *TimerObjectInternals, vm: *VirtualMachine) void {
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
|
||||
if (this.flags.kind == .setImmediate) return;
|
||||
if (this.flags.kind == .setImmediate) {
|
||||
// Release the strong reference so the GC can collect the JS object.
|
||||
// The immediate task is still in the event loop queue and will be skipped
|
||||
// by runImmediateTask when it sees has_cleared_timer == true.
|
||||
this.this_value.downgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
const was_active = this.eventLoopTimer().state == .ACTIVE;
|
||||
|
||||
this.eventLoopTimer().state = .CANCELLED;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
|
||||
if (was_active) {
|
||||
vm.timer.remove(this.eventLoopTimer());
|
||||
@@ -442,12 +464,12 @@ pub fn getDestroyed(this: *TimerObjectInternals) bool {
|
||||
}
|
||||
|
||||
pub fn finalize(this: *TimerObjectInternals) void {
|
||||
this.strong_this.deinit();
|
||||
this.this_value.finalize();
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn deinit(this: *TimerObjectInternals) void {
|
||||
this.strong_this.deinit();
|
||||
this.this_value.deinit();
|
||||
const vm = VirtualMachine.get();
|
||||
const kind = this.flags.kind;
|
||||
|
||||
|
||||
@@ -1707,6 +1707,15 @@ pub fn NewWrappedHandler(comptime tls: bool) type {
|
||||
|
||||
pub fn onClose(this: WrappedSocket, socket: Socket, err: c_int, data: ?*anyopaque) bun.JSError!void {
|
||||
if (comptime tls) {
|
||||
// Clean up the raw TCP socket from upgradeTLS() — its onClose
|
||||
// never fires because uws closes through the TLS context only.
|
||||
defer {
|
||||
if (!this.tcp.socket.isDetached()) {
|
||||
this.tcp.socket.detach();
|
||||
this.tcp.has_pending_activity.store(false, .release);
|
||||
this.tcp.deref();
|
||||
}
|
||||
}
|
||||
try TLSSocket.onClose(this.tls, socket, err, data);
|
||||
} else {
|
||||
try TLSSocket.onClose(this.tcp, socket, err, data);
|
||||
|
||||
@@ -468,6 +468,17 @@ pub fn writeHead(this: *NodeHTTPResponse, globalObject: *jsc.JSGlobalObject, cal
|
||||
return globalObject.ERR(.HTTP_HEADERS_SENT, "Stream already started", .{}).throw();
|
||||
}
|
||||
|
||||
// Validate status message does not contain invalid characters (defense-in-depth
|
||||
// against HTTP response splitting). Matches Node.js checkInvalidHeaderChar:
|
||||
// rejects any char not in [\t\x20-\x7e\x80-\xff].
|
||||
if (status_message_slice.len > 0) {
|
||||
for (status_message_slice.slice()) |c| {
|
||||
if (c != '\t' and (c < 0x20 or c == 0x7f)) {
|
||||
return globalObject.ERR(.INVALID_CHAR, "Invalid character in statusMessage", .{}).throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_it: {
|
||||
if (status_message_slice.len == 0) {
|
||||
if (HTTPStatusText.get(@intCast(status_code))) |status_message| {
|
||||
|
||||
@@ -560,6 +560,8 @@ JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_infoAccess, (JSGlobalObject * g
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
BUF_MEM* bptr = bio;
|
||||
if (!bptr)
|
||||
return JSValue::encode(jsUndefined());
|
||||
return JSValue::encode(undefinedIfEmpty(jsString(vm, String::fromUTF8(std::span(bptr->data, bptr->length)))));
|
||||
}
|
||||
|
||||
@@ -602,20 +604,10 @@ JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_issuerCertificate, (JSGlobalObj
|
||||
return {};
|
||||
}
|
||||
|
||||
auto issuerCert = thisObject->view().getIssuer();
|
||||
if (!issuerCert)
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
auto bio = issuerCert.get();
|
||||
|
||||
BUF_MEM* bptr = nullptr;
|
||||
BIO_get_mem_ptr(bio, &bptr);
|
||||
std::span<const uint8_t> span(reinterpret_cast<const uint8_t*>(bptr->data), bptr->length);
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
auto* structure = zigGlobalObject->m_JSX509CertificateClassStructure.get(zigGlobalObject);
|
||||
auto jsIssuerCert = JSX509Certificate::create(vm, structure, globalObject, span);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
return JSValue::encode(jsIssuerCert);
|
||||
// issuerCertificate is only available when the certificate was obtained from
|
||||
// a TLS connection with a peer certificate chain. For certificates parsed
|
||||
// directly from PEM/DER data, it is always undefined (matching Node.js behavior).
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_publicKey, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
#include <JavaScriptCore/ExceptionScope.h>
|
||||
#include <JavaScriptCore/FunctionConstructor.h>
|
||||
#include <JavaScriptCore/Heap.h>
|
||||
#include <JavaScriptCore/Integrity.h>
|
||||
#include <JavaScriptCore/MarkedBlock.h>
|
||||
#include <JavaScriptCore/PreciseAllocation.h>
|
||||
#include <JavaScriptCore/Identifier.h>
|
||||
#include <JavaScriptCore/InitializeThreading.h>
|
||||
#include <JavaScriptCore/IteratorOperations.h>
|
||||
@@ -2415,6 +2418,18 @@ extern "C" napi_status napi_typeof(napi_env env, napi_value val,
|
||||
if (value.isCell()) {
|
||||
JSCell* cell = value.asCell();
|
||||
|
||||
// Validate that the cell pointer is a real GC-managed object.
|
||||
// Native modules may accidentally pass garbage (e.g. a C string pointer)
|
||||
// as napi_value, which would crash when we dereference the cell.
|
||||
// isSanePointer rejects obviously invalid addresses (null-near, non-canonical).
|
||||
// The bloom filter provides fast rejection of pointers not in any known
|
||||
// MarkedBlock, using only pointer arithmetic (no dereference).
|
||||
if (!JSC::Integrity::isSanePointer(cell)
|
||||
|| (!JSC::PreciseAllocation::isPreciseAllocation(cell)
|
||||
&& toJS(env)->vm().heap.objectSpace().blocks().filter().ruleOut(
|
||||
std::bit_cast<uintptr_t>(JSC::MarkedBlock::blockFor(cell))))) [[unlikely]]
|
||||
return napi_set_last_error(env, napi_invalid_arg);
|
||||
|
||||
switch (cell->type()) {
|
||||
case JSC::JSFunctionType:
|
||||
case JSC::InternalFunctionType:
|
||||
|
||||
@@ -42,6 +42,13 @@ static std::optional<WTF::String> stripANSI(const std::span<const Char> input)
|
||||
// Append everything before the escape sequence
|
||||
result.append(std::span { start, escPos });
|
||||
const auto newPos = ANSI::consumeANSI(escPos, end);
|
||||
if (newPos == escPos) {
|
||||
// No ANSI found
|
||||
result.append(std::span { escPos, escPos + 1 });
|
||||
start = escPos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ASSERT(newPos > start);
|
||||
ASSERT(newPos <= end);
|
||||
foundANSI = true;
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
#include <JavaScriptCore/RegExp.h>
|
||||
#include <JavaScriptCore/RegExpObject.h>
|
||||
#include <JavaScriptCore/TypedArrayInlines.h>
|
||||
#include <JavaScriptCore/TypedArrayType.h>
|
||||
#include <JavaScriptCore/TypedArrays.h>
|
||||
#include <JavaScriptCore/WasmModule.h>
|
||||
#include <JavaScriptCore/YarrFlags.h>
|
||||
@@ -386,38 +385,6 @@ static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag)
|
||||
}
|
||||
}
|
||||
|
||||
static ArrayBufferViewSubtag subtagForTypedArrayType(TypedArrayType type)
|
||||
{
|
||||
switch (type) {
|
||||
case TypeInt8:
|
||||
return Int8ArrayTag;
|
||||
case TypeUint8:
|
||||
return Uint8ArrayTag;
|
||||
case TypeUint8Clamped:
|
||||
return Uint8ClampedArrayTag;
|
||||
case TypeInt16:
|
||||
return Int16ArrayTag;
|
||||
case TypeUint16:
|
||||
return Uint16ArrayTag;
|
||||
case TypeInt32:
|
||||
return Int32ArrayTag;
|
||||
case TypeUint32:
|
||||
return Uint32ArrayTag;
|
||||
case TypeFloat16:
|
||||
return Float16ArrayTag;
|
||||
case TypeFloat32:
|
||||
return Float32ArrayTag;
|
||||
case TypeFloat64:
|
||||
return Float64ArrayTag;
|
||||
case TypeBigInt64:
|
||||
return BigInt64ArrayTag;
|
||||
case TypeBigUint64:
|
||||
return BigUint64ArrayTag;
|
||||
default:
|
||||
return DataViewTag;
|
||||
}
|
||||
}
|
||||
|
||||
enum class SerializableErrorType : uint8_t {
|
||||
Error,
|
||||
EvalError,
|
||||
@@ -5639,14 +5606,6 @@ SerializedScriptValue::SerializedScriptValue(WTF::FixedVector<DenseArrayElement>
|
||||
m_memoryCost = computeMemoryCost();
|
||||
}
|
||||
|
||||
SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& data, uint8_t subtag)
|
||||
: m_arrayButterflyData(WTF::move(data))
|
||||
, m_fastPath(FastPath::TypedArray)
|
||||
, m_typedArraySubtag(subtag)
|
||||
{
|
||||
m_memoryCost = computeMemoryCost();
|
||||
}
|
||||
|
||||
Ref<SerializedScriptValue> SerializedScriptValue::createDenseArrayFastPath(
|
||||
WTF::FixedVector<DenseArrayElement>&& elements)
|
||||
{
|
||||
@@ -5736,7 +5695,6 @@ size_t SerializedScriptValue::computeMemoryCost() const
|
||||
break;
|
||||
case FastPath::Int32Array:
|
||||
case FastPath::DoubleArray:
|
||||
case FastPath::TypedArray:
|
||||
cost += m_arrayButterflyData.size();
|
||||
break;
|
||||
case FastPath::DenseArray:
|
||||
@@ -5958,35 +5916,7 @@ ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalOb
|
||||
object = cell->getObject();
|
||||
structure = object->structure();
|
||||
|
||||
// TypedArray fast path: check before JSArray since TypedArray is not a JSArray
|
||||
auto jsType = structure->typeInfo().type();
|
||||
if (isTypedView(jsType)) {
|
||||
auto* view = jsCast<JSArrayBufferView*>(object);
|
||||
size_t byteLength = view->byteLength();
|
||||
if (!view->isDetached()
|
||||
&& !view->isOutOfBounds()
|
||||
&& !view->isShared()
|
||||
&& !view->isResizableOrGrowableShared()
|
||||
&& view->byteOffset() == 0
|
||||
&& structure->maxOffset() == invalidOffset
|
||||
// For WastefulTypedArray (hasArrayBuffer()==true), verify the view
|
||||
// covers the full ArrayBuffer; partial views (e.g. new Uint8Array(buf, 0, 8)
|
||||
// over a 16-byte buffer) must fall through to the slow path.
|
||||
// possiblySharedBuffer() is safe after isDetached()/isShared() checks:
|
||||
// WastefulTypedArray just returns existingBufferInButterfly() without
|
||||
// triggering slowDownAndWasteMemory(). Must be evaluated AFTER isDetached()
|
||||
// to avoid null deref on detached buffers.
|
||||
&& (!view->hasArrayBuffer()
|
||||
|| view->byteLength() == view->possiblySharedBuffer()->byteLength())) {
|
||||
auto taType = typedArrayType(jsType);
|
||||
auto subtag = subtagForTypedArrayType(taType);
|
||||
auto* data = static_cast<const uint8_t*>(view->vector());
|
||||
// Use span constructor: single allocation + memcpy, no zero-fill
|
||||
Vector<uint8_t> buffer(std::span<const uint8_t> { data, byteLength });
|
||||
return SerializedScriptValue::createTypedArrayFastPath(WTF::move(buffer), static_cast<uint8_t>(subtag));
|
||||
}
|
||||
// Conditions not met → fall through to slow path
|
||||
} else if (auto* jsArray = jsDynamicCast<JSArray*>(object)) {
|
||||
if (auto* jsArray = jsDynamicCast<JSArray*>(object)) {
|
||||
canUseArrayFastPath = true;
|
||||
array = jsArray;
|
||||
} else if (isObjectFastPathCandidate(structure)) {
|
||||
@@ -6024,16 +5954,14 @@ ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalOb
|
||||
auto* data = array->butterfly()->contiguous().data();
|
||||
if (!containsHole(data, length)) {
|
||||
size_t byteSize = sizeof(JSValue) * length;
|
||||
Vector<uint8_t> buffer(byteSize, 0);
|
||||
memcpy(buffer.mutableSpan().data(), data, byteSize);
|
||||
Vector<uint8_t> buffer(std::span<const uint8_t> { reinterpret_cast<const uint8_t*>(data), byteSize });
|
||||
return SerializedScriptValue::createInt32ArrayFastPath(WTF::move(buffer), length);
|
||||
}
|
||||
} else {
|
||||
auto* data = array->butterfly()->contiguousDouble().data();
|
||||
if (!containsHole(data, length)) {
|
||||
size_t byteSize = sizeof(double) * length;
|
||||
Vector<uint8_t> buffer(byteSize, 0);
|
||||
memcpy(buffer.mutableSpan().data(), data, byteSize);
|
||||
Vector<uint8_t> buffer(std::span<const uint8_t> { reinterpret_cast<const uint8_t*>(data), byteSize });
|
||||
return SerializedScriptValue::createDoubleArrayFastPath(WTF::move(buffer), length);
|
||||
}
|
||||
}
|
||||
@@ -6436,11 +6364,6 @@ Ref<SerializedScriptValue> SerializedScriptValue::createDoubleArrayFastPath(Vect
|
||||
return adoptRef(*new SerializedScriptValue(WTF::move(data), length, FastPath::DoubleArray));
|
||||
}
|
||||
|
||||
Ref<SerializedScriptValue> SerializedScriptValue::createTypedArrayFastPath(Vector<uint8_t>&& data, uint8_t subtag)
|
||||
{
|
||||
return adoptRef(*new SerializedScriptValue(WTF::move(data), subtag));
|
||||
}
|
||||
|
||||
RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception)
|
||||
{
|
||||
JSGlobalObject* lexicalGlobalObject = toJS(originContext);
|
||||
@@ -6751,71 +6674,6 @@ JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject,
|
||||
*didFail = false;
|
||||
return resultArray;
|
||||
}
|
||||
case FastPath::TypedArray: {
|
||||
size_t byteLength = m_arrayButterflyData.size();
|
||||
auto subtag = static_cast<ArrayBufferViewSubtag>(m_typedArraySubtag);
|
||||
unsigned elemSize = typedArrayElementSize(subtag);
|
||||
if (!elemSize) [[unlikely]]
|
||||
break;
|
||||
|
||||
auto arrayBuffer = ArrayBuffer::tryCreate(m_arrayButterflyData.span());
|
||||
if (!arrayBuffer) [[unlikely]] {
|
||||
if (didFail)
|
||||
*didFail = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<size_t> length = byteLength / elemSize;
|
||||
JSValue typedArrayValue;
|
||||
|
||||
switch (subtag) {
|
||||
case Int8ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Int8Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Uint8ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Uint8Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Uint8ClampedArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Uint8ClampedArray::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Int16ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Int16Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Uint16ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Uint16Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Int32ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Int32Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Uint32ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Uint32Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Float16ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Float16Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Float32ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Float32Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case Float64ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, Float64Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case BigInt64ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, BigInt64Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
case BigUint64ArrayTag:
|
||||
typedArrayValue = toJS(&lexicalGlobalObject, globalObject, BigUint64Array::wrappedAs(arrayBuffer.releaseNonNull(), 0, length).get());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (typedArrayValue) {
|
||||
if (didFail)
|
||||
*didFail = false;
|
||||
return typedArrayValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FastPath::None: {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ enum class FastPath : uint8_t {
|
||||
Int32Array,
|
||||
DoubleArray,
|
||||
DenseArray,
|
||||
TypedArray,
|
||||
};
|
||||
|
||||
#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
|
||||
@@ -149,9 +148,6 @@ public:
|
||||
// Fast path for postMessage with dense arrays containing simple objects
|
||||
static Ref<SerializedScriptValue> createDenseArrayFastPath(WTF::FixedVector<DenseArrayElement>&& elements);
|
||||
|
||||
// Fast path for postMessage with TypedArray (Uint8Array, Float64Array, etc.)
|
||||
static Ref<SerializedScriptValue> createTypedArrayFastPath(Vector<uint8_t>&& data, uint8_t subtag);
|
||||
|
||||
static Ref<SerializedScriptValue> nullValue();
|
||||
|
||||
WEBCORE_EXPORT JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, SerializationErrorMode = SerializationErrorMode::Throwing, bool* didFail = nullptr);
|
||||
@@ -259,8 +255,6 @@ private:
|
||||
SerializedScriptValue(Vector<uint8_t>&& butterflyData, uint32_t length, FastPath fastPath);
|
||||
// Constructor for DenseArray fast path
|
||||
explicit SerializedScriptValue(WTF::FixedVector<DenseArrayElement>&& denseElements);
|
||||
// Constructor for TypedArray fast path
|
||||
SerializedScriptValue(Vector<uint8_t>&& data, uint8_t subtag);
|
||||
|
||||
size_t computeMemoryCost() const;
|
||||
|
||||
@@ -300,9 +294,6 @@ private:
|
||||
|
||||
// DenseArray fast path: array of primitives/strings/simple objects
|
||||
FixedVector<DenseArrayElement> m_denseArrayElements {};
|
||||
|
||||
// TypedArray fast path: subtag identifying the TypedArray type (ArrayBufferViewSubtag)
|
||||
uint8_t m_typedArraySubtag { 0 };
|
||||
};
|
||||
|
||||
template<class Encoder>
|
||||
|
||||
@@ -1154,6 +1154,14 @@ pub const FetchTasklet = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the request body should skip chunked transfer encoding framing.
|
||||
/// True for upgraded connections (e.g. WebSocket) or when the user explicitly
|
||||
/// set Content-Length without setting Transfer-Encoding.
|
||||
fn skipChunkedFraming(this: *const FetchTasklet) bool {
|
||||
return this.upgraded_connection or
|
||||
(this.request_headers.get("content-length") != null and this.request_headers.get("transfer-encoding") == null);
|
||||
}
|
||||
|
||||
pub fn writeRequestData(this: *FetchTasklet, data: []const u8) ResumableSinkBackpressure {
|
||||
log("writeRequestData {}", .{data.len});
|
||||
if (this.signal) |signal| {
|
||||
@@ -1175,7 +1183,7 @@ pub const FetchTasklet = struct {
|
||||
// dont have backpressure so we will schedule the data to be written
|
||||
// if we have backpressure the onWritable will drain the buffer
|
||||
needs_schedule = stream_buffer.isEmpty();
|
||||
if (this.upgraded_connection) {
|
||||
if (this.skipChunkedFraming()) {
|
||||
bun.handleOom(stream_buffer.write(data));
|
||||
} else {
|
||||
//16 is the max size of a hex number size that represents 64 bits + 2 for the \r\n
|
||||
@@ -1209,15 +1217,14 @@ pub const FetchTasklet = struct {
|
||||
}
|
||||
this.abortTask();
|
||||
} else {
|
||||
if (!this.upgraded_connection) {
|
||||
// If is not upgraded we need to send the terminating chunk
|
||||
if (!this.skipChunkedFraming()) {
|
||||
// Using chunked transfer encoding, send the terminating chunk
|
||||
const thread_safe_stream_buffer = this.request_body_streaming_buffer orelse return;
|
||||
const stream_buffer = thread_safe_stream_buffer.acquire();
|
||||
defer thread_safe_stream_buffer.release();
|
||||
bun.handleOom(stream_buffer.write(http.end_of_chunked_http1_1_encoding_response_body));
|
||||
}
|
||||
if (this.http) |http_| {
|
||||
// just tell to write the end of the chunked encoding aka 0\r\n\r\n
|
||||
http.http_thread.scheduleRequestWrite(http_, .end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,16 @@ pub const Chunk = struct {
|
||||
}
|
||||
|
||||
pub fn getCSSChunkForHTML(this: *const Chunk, chunks: []Chunk) ?*Chunk {
|
||||
// Look up the CSS chunk via the JS chunk's css_chunks indices.
|
||||
// This correctly handles deduplicated CSS chunks that are shared
|
||||
// across multiple HTML entry points (see issue #23668).
|
||||
if (this.getJSChunkForHTML(chunks)) |js_chunk| {
|
||||
const css_chunk_indices = js_chunk.content.javascript.css_chunks;
|
||||
if (css_chunk_indices.len > 0) {
|
||||
return &chunks[css_chunk_indices[0]];
|
||||
}
|
||||
}
|
||||
// Fallback: match by entry_point_id for cases without a JS chunk.
|
||||
const entry_point_id = this.entry_point.entry_point_id;
|
||||
for (chunks) |*other| {
|
||||
if (other.content == .css) {
|
||||
|
||||
@@ -3683,7 +3683,20 @@ pub const BundleV2 = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const import_record_loader = import_record.loader orelse path.loader(&transpiler.options.loaders) orelse .file;
|
||||
const import_record_loader = brk: {
|
||||
const resolved_loader = import_record.loader orelse path.loader(&transpiler.options.loaders) orelse .file;
|
||||
// When an HTML file references a URL asset (e.g. <link rel="manifest" href="./manifest.json" />),
|
||||
// the file must be copied to the output directory as-is. If the resolved loader would
|
||||
// parse/transform the file (e.g. .json, .toml) rather than copy it, force the .file loader
|
||||
// so that `shouldCopyForBundling()` returns true and the asset is emitted.
|
||||
// Only do this for HTML sources — CSS url() imports should retain their original behavior.
|
||||
if (loader == .html and import_record.kind == .url and !resolved_loader.shouldCopyForBundling() and
|
||||
!resolved_loader.isJavaScriptLike() and !resolved_loader.isCSS() and resolved_loader != .html)
|
||||
{
|
||||
break :brk Loader.file;
|
||||
}
|
||||
break :brk resolved_loader;
|
||||
};
|
||||
import_record.loader = import_record_loader;
|
||||
|
||||
const is_html_entrypoint = import_record_loader == .html and target.isServerSide() and this.transpiler.options.dev_server == null;
|
||||
|
||||
@@ -150,6 +150,7 @@ pub const ClientEntryPoint = struct {
|
||||
|
||||
pub const ServerEntryPoint = struct {
|
||||
source: logger.Source = undefined,
|
||||
generated: bool = false,
|
||||
|
||||
pub fn generate(
|
||||
entry: *ServerEntryPoint,
|
||||
@@ -230,6 +231,7 @@ pub const ServerEntryPoint = struct {
|
||||
entry.source = logger.Source.initPathString(name, code);
|
||||
entry.source.path.text = name;
|
||||
entry.source.path.namespace = "server-entry";
|
||||
entry.generated = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ pub noinline fn computeChunks(
|
||||
|
||||
const entry_source_indices = this.graph.entry_points.items(.source_index);
|
||||
const css_asts = this.graph.ast.items(.css);
|
||||
const css_chunking = this.options.css_chunking;
|
||||
var html_chunks = bun.StringArrayHashMap(Chunk).init(temp_allocator);
|
||||
const loaders = this.parse_graph.input_files.items(.loader);
|
||||
const ast_targets = this.graph.ast.items(.target);
|
||||
@@ -148,10 +147,11 @@ pub noinline fn computeChunks(
|
||||
if (css_source_indices.len > 0) {
|
||||
const order = this.findImportedFilesInCSSOrder(temp_allocator, css_source_indices.slice());
|
||||
|
||||
const use_content_based_key = css_chunking or has_server_html_imports;
|
||||
const hash_to_use = if (!use_content_based_key)
|
||||
bun.hash(try temp_allocator.dupe(u8, entry_bits.bytes(this.graph.entry_points.len)))
|
||||
else brk: {
|
||||
// Always use content-based hashing for CSS chunk deduplication.
|
||||
// This ensures that when multiple JS entry points import the
|
||||
// same CSS files, they share a single CSS output chunk rather
|
||||
// than producing duplicates that collide on hash-based naming.
|
||||
const hash_to_use = brk: {
|
||||
var hasher = std.hash.Wyhash.init(5);
|
||||
bun.writeAnyToHasher(&hasher, order.len);
|
||||
for (order.slice()) |x| x.hash(&hasher);
|
||||
@@ -322,7 +322,10 @@ pub noinline fn computeChunks(
|
||||
const remapped_css_indexes = try temp_allocator.alloc(u32, css_chunks.count());
|
||||
|
||||
const css_chunk_values = css_chunks.values();
|
||||
for (sorted_css_keys, js_chunks.count()..) |key, sorted_index| {
|
||||
// Use sorted_chunks.len as the starting index because HTML chunks
|
||||
// may be interleaved with JS chunks, so js_chunks.count() would be
|
||||
// incorrect when HTML entry points are present.
|
||||
for (sorted_css_keys, sorted_chunks.len..) |key, sorted_index| {
|
||||
const index = css_chunks.getIndex(key) orelse unreachable;
|
||||
sorted_chunks.appendAssumeCapacity(css_chunk_values[index]);
|
||||
remapped_css_indexes[index] = @intCast(sorted_index);
|
||||
|
||||
16
src/http.zig
16
src/http.zig
@@ -719,7 +719,21 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
|
||||
|
||||
if (body_len > 0 or this.method.hasRequestBody()) {
|
||||
if (this.flags.is_streaming_request_body) {
|
||||
if (add_transfer_encoding and this.flags.upgrade_state == .none) {
|
||||
if (original_content_length) |content_length| {
|
||||
if (add_transfer_encoding) {
|
||||
// User explicitly set Content-Length and did not set Transfer-Encoding;
|
||||
// preserve Content-Length instead of using chunked encoding.
|
||||
// This matches Node.js behavior where an explicit Content-Length is always honored.
|
||||
request_headers_buf[header_count] = .{
|
||||
.name = content_length_header_name,
|
||||
.value = content_length,
|
||||
};
|
||||
header_count += 1;
|
||||
}
|
||||
// If !add_transfer_encoding, the user explicitly set Transfer-Encoding,
|
||||
// which was already added to request_headers_buf. We respect that and
|
||||
// do not add Content-Length (they are mutually exclusive per HTTP/1.1).
|
||||
} else if (add_transfer_encoding and this.flags.upgrade_state == .none) {
|
||||
request_headers_buf[header_count] = chunked_encoded_header;
|
||||
header_count += 1;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,10 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
|
||||
// Set to null FIRST to prevent re-entrancy (shutdown can trigger callbacks)
|
||||
if (this.proxy_tunnel) |tunnel| {
|
||||
this.proxy_tunnel = null;
|
||||
// Detach the websocket from the tunnel before shutdown so the
|
||||
// tunnel's onClose callback doesn't dispatch a spurious 1006
|
||||
// after we've already handled a clean close.
|
||||
tunnel.clearConnectedWebSocket();
|
||||
tunnel.shutdown();
|
||||
tunnel.deref();
|
||||
}
|
||||
@@ -910,7 +914,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
|
||||
}
|
||||
|
||||
fn sendPong(this: *WebSocket, socket: Socket) bool {
|
||||
if (socket.isClosed() or socket.isShutdown()) {
|
||||
if (!this.hasTCP()) {
|
||||
this.dispatchAbruptClose(ErrorCode.ended);
|
||||
return false;
|
||||
}
|
||||
@@ -942,14 +946,17 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
|
||||
body_len: usize,
|
||||
) void {
|
||||
log("Sending close with code {d}", .{code});
|
||||
if (socket.isClosed() or socket.isShutdown()) {
|
||||
if (!this.hasTCP()) {
|
||||
this.dispatchAbruptClose(ErrorCode.ended);
|
||||
this.clearData();
|
||||
return;
|
||||
}
|
||||
// we dont wanna shutdownRead when SSL, because SSL handshake can happen when writting
|
||||
// For tunnel mode, shutdownRead on the detached socket is a no-op; skip it.
|
||||
if (comptime !ssl) {
|
||||
socket.shutdownRead();
|
||||
if (this.proxy_tunnel == null) {
|
||||
socket.shutdownRead();
|
||||
}
|
||||
}
|
||||
var final_body_bytes: [128 + 8]u8 = undefined;
|
||||
var header = @as(WebsocketHeader, @bitCast(@as(u16, 0)));
|
||||
|
||||
@@ -253,6 +253,13 @@ pub fn setConnectedWebSocket(this: *WebSocketProxyTunnel, ws: *WebSocketClient)
|
||||
this.#upgrade_client = .{ .none = {} };
|
||||
}
|
||||
|
||||
/// Clear the connected WebSocket reference. Called before tunnel shutdown during
|
||||
/// a clean close so the tunnel's onClose callback doesn't dispatch a spurious
|
||||
/// abrupt close (1006) after the WebSocket has already sent a clean close frame.
|
||||
pub fn clearConnectedWebSocket(this: *WebSocketProxyTunnel) void {
|
||||
this.#connected_websocket = null;
|
||||
}
|
||||
|
||||
/// SSLWrapper callback: Called with encrypted data to send to network
|
||||
fn writeEncrypted(this: *WebSocketProxyTunnel, encrypted_data: []const u8) void {
|
||||
log("writeEncrypted: {} bytes", .{encrypted_data.len});
|
||||
|
||||
@@ -623,6 +623,17 @@ pub const PackageInstaller = struct {
|
||||
// else => unreachable,
|
||||
// };
|
||||
|
||||
// If a newly computed integrity hash is available (e.g. for a GitHub
|
||||
// tarball) and the lockfile doesn't already have one, persist it so
|
||||
// the lockfile gets re-saved with the hash.
|
||||
if (data.integrity.tag.isSupported()) {
|
||||
var pkg_metas = this.lockfile.packages.items(.meta);
|
||||
if (!pkg_metas[package_id].integrity.tag.isSupported()) {
|
||||
pkg_metas[package_id].integrity = data.integrity;
|
||||
this.manager.options.enable.force_save_lockfile = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.manager.task_queue.fetchRemove(task_id)) |removed| {
|
||||
var callbacks = removed.value;
|
||||
defer callbacks.deinit(this.manager.allocator);
|
||||
|
||||
@@ -133,6 +133,12 @@ pub fn processExtractedTarballPackage(
|
||||
break :package pkg;
|
||||
};
|
||||
|
||||
// Store the tarball integrity hash so the lockfile can pin the
|
||||
// exact content downloaded from the remote (GitHub) server.
|
||||
if (data.integrity.tag.isSupported()) {
|
||||
package.meta.integrity = data.integrity;
|
||||
}
|
||||
|
||||
package = manager.lockfile.appendPackage(package) catch unreachable;
|
||||
package_id.* = package.meta.id;
|
||||
|
||||
|
||||
@@ -23,7 +23,26 @@ pub inline fn run(this: *const ExtractTarball, log: *logger.Log, bytes: []const
|
||||
return error.IntegrityCheckFailed;
|
||||
}
|
||||
}
|
||||
return this.extract(log, bytes);
|
||||
var result = try this.extract(log, bytes);
|
||||
|
||||
// Compute and store SHA-512 integrity hash for GitHub tarballs so the
|
||||
// lockfile can pin the exact tarball content. On subsequent installs the
|
||||
// hash stored in the lockfile is forwarded via this.integrity and verified
|
||||
// above, preventing a compromised server from silently swapping the tarball.
|
||||
if (this.resolution.tag == .github) {
|
||||
if (this.integrity.tag.isSupported()) {
|
||||
// Re-installing with an existing lockfile: integrity was already
|
||||
// verified above, propagate the known value to ExtractData so that
|
||||
// the lockfile keeps it on re-serialisation.
|
||||
result.integrity = this.integrity;
|
||||
} else {
|
||||
// First install (no integrity in the lockfile yet): compute it.
|
||||
result.integrity = .{ .tag = .sha512 };
|
||||
Crypto.SHA512.hash(bytes, result.integrity.value[0..Crypto.SHA512.digest]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn buildURL(
|
||||
@@ -547,6 +566,7 @@ const string = []const u8;
|
||||
|
||||
const Npm = @import("./npm.zig");
|
||||
const std = @import("std");
|
||||
const Crypto = @import("../sha.zig").Hashers;
|
||||
const FileSystem = @import("../fs.zig").FileSystem;
|
||||
const Integrity = @import("./integrity.zig").Integrity;
|
||||
const Resolution = @import("./resolution.zig").Resolution;
|
||||
|
||||
@@ -209,6 +209,7 @@ pub const ExtractData = struct {
|
||||
path: string = "",
|
||||
buf: []u8 = "",
|
||||
} = null,
|
||||
integrity: Integrity = .{},
|
||||
};
|
||||
|
||||
pub const DependencyInstallContext = struct {
|
||||
@@ -271,6 +272,7 @@ pub const VersionSlice = external.VersionSlice;
|
||||
|
||||
pub const Dependency = @import("./dependency.zig");
|
||||
pub const Behavior = @import("./dependency.zig").Behavior;
|
||||
pub const Integrity = @import("./integrity.zig").Integrity;
|
||||
|
||||
pub const Lockfile = @import("./lockfile.zig");
|
||||
pub const PatchedDep = Lockfile.PatchedDep;
|
||||
|
||||
@@ -644,9 +644,16 @@ pub const Stringifier = struct {
|
||||
&path_buf,
|
||||
);
|
||||
|
||||
try writer.print(", {f}]", .{
|
||||
repo.resolved.fmtJson(buf, .{}),
|
||||
});
|
||||
if (pkg_meta.integrity.tag.isSupported()) {
|
||||
try writer.print(", {f}, \"{f}\"]", .{
|
||||
repo.resolved.fmtJson(buf, .{}),
|
||||
pkg_meta.integrity,
|
||||
});
|
||||
} else {
|
||||
try writer.print(", {f}]", .{
|
||||
repo.resolved.fmtJson(buf, .{}),
|
||||
});
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@@ -1885,6 +1892,15 @@ pub fn parseIntoBinaryLockfile(
|
||||
};
|
||||
|
||||
@field(res.value, @tagName(tag)).resolved = try string_buf.append(bun_tag_str);
|
||||
|
||||
// Optional integrity hash (added to pin tarball content)
|
||||
if (i < pkg_info.len) {
|
||||
const integrity_expr = pkg_info.at(i);
|
||||
if (integrity_expr.asString(allocator)) |integrity_str| {
|
||||
pkg.meta.integrity = Integrity.parse(integrity_str);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
@@ -51,6 +51,15 @@ function onError(msg, err, callback) {
|
||||
process.nextTick(emitErrorNt, msg, err, callback);
|
||||
}
|
||||
|
||||
function isHTTPHeaderStateSentOrAssigned(state) {
|
||||
return state === NodeHTTPHeaderState.sent || state === NodeHTTPHeaderState.assigned;
|
||||
}
|
||||
function throwHeadersSentIfNecessary(self, action) {
|
||||
if (self._header != null || isHTTPHeaderStateSentOrAssigned(self[headerStateSymbol])) {
|
||||
throw $ERR_HTTP_HEADERS_SENT(action);
|
||||
}
|
||||
}
|
||||
|
||||
function write_(msg, chunk, encoding, callback, fromEnd) {
|
||||
if (typeof callback !== "function") callback = nop;
|
||||
|
||||
@@ -252,18 +261,14 @@ const OutgoingMessagePrototype = {
|
||||
|
||||
removeHeader(name) {
|
||||
validateString(name, "name");
|
||||
if ((this._header !== undefined && this._header !== null) || this[headerStateSymbol] === NodeHTTPHeaderState.sent) {
|
||||
throw $ERR_HTTP_HEADERS_SENT("remove");
|
||||
}
|
||||
throwHeadersSentIfNecessary(this, "remove");
|
||||
const headers = this[headersSymbol];
|
||||
if (!headers) return;
|
||||
headers.delete(name);
|
||||
},
|
||||
|
||||
setHeader(name, value) {
|
||||
if ((this._header !== undefined && this._header !== null) || this[headerStateSymbol] == NodeHTTPHeaderState.sent) {
|
||||
throw $ERR_HTTP_HEADERS_SENT("set");
|
||||
}
|
||||
throwHeadersSentIfNecessary(this, "set");
|
||||
validateHeaderName(name);
|
||||
validateHeaderValue(name, value);
|
||||
const headers = (this[headersSymbol] ??= new Headers());
|
||||
@@ -271,9 +276,7 @@ const OutgoingMessagePrototype = {
|
||||
return this;
|
||||
},
|
||||
setHeaders(headers) {
|
||||
if (this._header || this[headerStateSymbol] !== NodeHTTPHeaderState.none) {
|
||||
throw $ERR_HTTP_HEADERS_SENT("set");
|
||||
}
|
||||
throwHeadersSentIfNecessary(this, "set");
|
||||
|
||||
if (!headers || $isArray(headers) || typeof headers.keys !== "function" || typeof headers.get !== "function") {
|
||||
throw $ERR_INVALID_ARG_TYPE("headers", ["Headers", "Map"], headers);
|
||||
|
||||
@@ -766,19 +766,13 @@ pub extern fn napi_type_tag_object(env: napi_env, _: napi_value, _: [*c]const na
|
||||
pub extern fn napi_check_object_type_tag(env: napi_env, _: napi_value, _: [*c]const napi_type_tag, _: *bool) napi_status;
|
||||
|
||||
// do nothing for both of these
|
||||
pub export fn napi_open_callback_scope(env_: napi_env, _: napi_value, _: *anyopaque, _: *anyopaque) napi_status {
|
||||
pub export fn napi_open_callback_scope(_: napi_env, _: napi_value, _: *anyopaque, _: *anyopaque) napi_status {
|
||||
log("napi_open_callback_scope", .{});
|
||||
const env = env_ orelse {
|
||||
return envIsNull();
|
||||
};
|
||||
return env.ok();
|
||||
return @intFromEnum(NapiStatus.ok);
|
||||
}
|
||||
pub export fn napi_close_callback_scope(env_: napi_env, _: *anyopaque) napi_status {
|
||||
pub export fn napi_close_callback_scope(_: napi_env, _: *anyopaque) napi_status {
|
||||
log("napi_close_callback_scope", .{});
|
||||
const env = env_ orelse {
|
||||
return envIsNull();
|
||||
};
|
||||
return env.ok();
|
||||
return @intFromEnum(NapiStatus.ok);
|
||||
}
|
||||
pub extern fn napi_throw(env: napi_env, @"error": napi_value) napi_status;
|
||||
pub extern fn napi_throw_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status;
|
||||
|
||||
@@ -12,24 +12,32 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
// Shared getter/setter functions: .bind(obj, key) avoids creating a closure
|
||||
// and JSLexicalEnvironment per property. BoundFunction is much cheaper.
|
||||
// Must be regular functions (not arrows) so .bind() can set `this`.
|
||||
function __accessProp(key) {
|
||||
return this[key];
|
||||
}
|
||||
|
||||
// This is used to implement "export * from" statements. It copies properties
|
||||
// from the imported module to the current module's ESM export object. If the
|
||||
// current module is an entry point and the target format is CommonJS, we
|
||||
// also copy the properties to "module.exports" in addition to our module's
|
||||
// internal ESM export object.
|
||||
export var __reExport = (target, mod, secondTarget) => {
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
var keys = __getOwnPropNames(mod);
|
||||
for (let key of keys)
|
||||
if (!__hasOwnProp.call(target, key) && key !== "default")
|
||||
__defProp(target, key, {
|
||||
get: () => mod[key],
|
||||
get: __accessProp.bind(mod, key),
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
if (secondTarget) {
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
for (let key of keys)
|
||||
if (!__hasOwnProp.call(secondTarget, key) && key !== "default")
|
||||
__defProp(secondTarget, key, {
|
||||
get: () => mod[key],
|
||||
get: __accessProp.bind(mod, key),
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
@@ -37,11 +45,22 @@ export var __reExport = (target, mod, secondTarget) => {
|
||||
}
|
||||
};
|
||||
|
||||
/*__PURE__*/
|
||||
var __toESMCache_node;
|
||||
/*__PURE__*/
|
||||
var __toESMCache_esm;
|
||||
|
||||
// Converts the module from CommonJS to ESM. When in node mode (i.e. in an
|
||||
// ".mjs" file, package.json has "type: module", or the "__esModule" export
|
||||
// in the CommonJS file is falsy or missing), the "default" property is
|
||||
// overridden to point to the original CommonJS exports object instead.
|
||||
export var __toESM = (mod, isNodeMode, target) => {
|
||||
var canCache = mod != null && typeof mod === "object";
|
||||
if (canCache) {
|
||||
var cache = isNodeMode ? (__toESMCache_node ??= new WeakMap()) : (__toESMCache_esm ??= new WeakMap());
|
||||
var cached = cache.get(mod);
|
||||
if (cached) return cached;
|
||||
}
|
||||
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
||||
const to =
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
||||
@@ -53,34 +72,34 @@ export var __toESM = (mod, isNodeMode, target) => {
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
if (!__hasOwnProp.call(to, key))
|
||||
__defProp(to, key, {
|
||||
get: () => mod[key],
|
||||
get: __accessProp.bind(mod, key),
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
if (canCache) cache.set(mod, to);
|
||||
return to;
|
||||
};
|
||||
|
||||
// Converts the module from ESM to CommonJS. This clones the input module
|
||||
// object with the addition of a non-enumerable "__esModule" property set
|
||||
// to "true", which overwrites any existing export named "__esModule".
|
||||
var __moduleCache = /* @__PURE__ */ new WeakMap();
|
||||
export var __toCommonJS = /* @__PURE__ */ from => {
|
||||
var entry = __moduleCache.get(from),
|
||||
export var __toCommonJS = from => {
|
||||
var entry = (__moduleCache ??= new WeakMap()).get(from),
|
||||
desc;
|
||||
if (entry) return entry;
|
||||
entry = __defProp({}, "__esModule", { value: true });
|
||||
if ((from && typeof from === "object") || typeof from === "function")
|
||||
__getOwnPropNames(from).map(
|
||||
key =>
|
||||
!__hasOwnProp.call(entry, key) &&
|
||||
for (var key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(entry, key))
|
||||
__defProp(entry, key, {
|
||||
get: () => from[key],
|
||||
get: __accessProp.bind(from, key),
|
||||
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
|
||||
}),
|
||||
);
|
||||
});
|
||||
__moduleCache.set(from, entry);
|
||||
return entry;
|
||||
};
|
||||
/*__PURE__*/
|
||||
var __moduleCache;
|
||||
|
||||
// When you do know the module is CJS
|
||||
export var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
@@ -97,6 +116,10 @@ export var __name = (target, name) => {
|
||||
|
||||
// ESM export -> CJS export
|
||||
// except, writable incase something re-exports
|
||||
var __returnValue = v => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
|
||||
export var __export = /* @__PURE__ */ (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -104,15 +127,19 @@ export var __export = /* @__PURE__ */ (target, all) => {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: newValue => (all[name] = () => newValue),
|
||||
set: __exportSetter.bind(all, name),
|
||||
});
|
||||
};
|
||||
|
||||
function __exportValueSetter(name, newValue) {
|
||||
this[name] = newValue;
|
||||
}
|
||||
|
||||
export var __exportValue = (target, all) => {
|
||||
for (var name in all) {
|
||||
__defProp(target, name, {
|
||||
get: () => all[name],
|
||||
set: newValue => (all[name] = newValue),
|
||||
get: __accessProp.bind(all, name),
|
||||
set: __exportValueSetter.bind(all, name),
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
@@ -266,8 +266,8 @@ pub const ShellCpOutputTask = OutputTask(Cp, .{
|
||||
|
||||
const ShellCpOutputTaskVTable = struct {
|
||||
pub fn writeErr(this: *Cp, childptr: anytype, errbuf: []const u8) ?Yield {
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stderr.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
return this.bltn().stderr.enqueue(childptr, errbuf, safeguard);
|
||||
}
|
||||
_ = this.bltn().writeNoIO(.stderr, errbuf);
|
||||
@@ -279,8 +279,8 @@ const ShellCpOutputTaskVTable = struct {
|
||||
}
|
||||
|
||||
pub fn writeOut(this: *Cp, childptr: anytype, output: *OutputSrc) ?Yield {
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stdout.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
return this.bltn().stdout.enqueue(childptr, output.slice(), safeguard);
|
||||
}
|
||||
_ = this.bltn().writeNoIO(.stdout, output.slice());
|
||||
|
||||
@@ -175,8 +175,8 @@ pub const ShellLsOutputTask = OutputTask(Ls, .{
|
||||
const ShellLsOutputTaskVTable = struct {
|
||||
pub fn writeErr(this: *Ls, childptr: anytype, errbuf: []const u8) ?Yield {
|
||||
log("ShellLsOutputTaskVTable.writeErr(0x{x}, {s})", .{ @intFromPtr(this), errbuf });
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stderr.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
return this.bltn().stderr.enqueue(childptr, errbuf, safeguard);
|
||||
}
|
||||
_ = this.bltn().writeNoIO(.stderr, errbuf);
|
||||
@@ -190,8 +190,8 @@ const ShellLsOutputTaskVTable = struct {
|
||||
|
||||
pub fn writeOut(this: *Ls, childptr: anytype, output: *OutputSrc) ?Yield {
|
||||
log("ShellLsOutputTaskVTable.writeOut(0x{x}, {s})", .{ @intFromPtr(this), output.slice() });
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stdout.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
return this.bltn().stdout.enqueue(childptr, output.slice(), safeguard);
|
||||
}
|
||||
log("ShellLsOutputTaskVTable.writeOut(0x{x}, {s}) no IO", .{ @intFromPtr(this), output.slice() });
|
||||
|
||||
@@ -129,8 +129,8 @@ pub const ShellMkdirOutputTask = OutputTask(Mkdir, .{
|
||||
|
||||
const ShellMkdirOutputTaskVTable = struct {
|
||||
pub fn writeErr(this: *Mkdir, childptr: anytype, errbuf: []const u8) ?Yield {
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stderr.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
return this.bltn().stderr.enqueue(childptr, errbuf, safeguard);
|
||||
}
|
||||
_ = this.bltn().writeNoIO(.stderr, errbuf);
|
||||
@@ -142,8 +142,8 @@ const ShellMkdirOutputTaskVTable = struct {
|
||||
}
|
||||
|
||||
pub fn writeOut(this: *Mkdir, childptr: anytype, output: *OutputSrc) ?Yield {
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stdout.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
const slice = output.slice();
|
||||
log("THE SLICE: {d} {s}", .{ slice.len, slice });
|
||||
return this.bltn().stdout.enqueue(childptr, slice, safeguard);
|
||||
|
||||
@@ -46,12 +46,14 @@ pub fn start(this: *@This()) Yield {
|
||||
|
||||
const maybe1 = iter.next().?;
|
||||
const int1 = std.fmt.parseFloat(f32, bun.sliceTo(maybe1, 0)) catch return this.fail("seq: invalid argument\n");
|
||||
if (!std.math.isFinite(int1)) return this.fail("seq: invalid argument\n");
|
||||
this._end = int1;
|
||||
if (this._start > this._end) this.increment = -1;
|
||||
|
||||
const maybe2 = iter.next();
|
||||
if (maybe2 == null) return this.do();
|
||||
const int2 = std.fmt.parseFloat(f32, bun.sliceTo(maybe2.?, 0)) catch return this.fail("seq: invalid argument\n");
|
||||
if (!std.math.isFinite(int2)) return this.fail("seq: invalid argument\n");
|
||||
this._start = int1;
|
||||
this._end = int2;
|
||||
if (this._start < this._end) this.increment = 1;
|
||||
@@ -60,6 +62,7 @@ pub fn start(this: *@This()) Yield {
|
||||
const maybe3 = iter.next();
|
||||
if (maybe3 == null) return this.do();
|
||||
const int3 = std.fmt.parseFloat(f32, bun.sliceTo(maybe3.?, 0)) catch return this.fail("seq: invalid argument\n");
|
||||
if (!std.math.isFinite(int3)) return this.fail("seq: invalid argument\n");
|
||||
this._start = int1;
|
||||
this.increment = int2;
|
||||
this._end = int3;
|
||||
|
||||
@@ -132,8 +132,8 @@ pub const ShellTouchOutputTask = OutputTask(Touch, .{
|
||||
|
||||
const ShellTouchOutputTaskVTable = struct {
|
||||
pub fn writeErr(this: *Touch, childptr: anytype, errbuf: []const u8) ?Yield {
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stderr.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
return this.bltn().stderr.enqueue(childptr, errbuf, safeguard);
|
||||
}
|
||||
_ = this.bltn().writeNoIO(.stderr, errbuf);
|
||||
@@ -145,8 +145,8 @@ const ShellTouchOutputTaskVTable = struct {
|
||||
}
|
||||
|
||||
pub fn writeOut(this: *Touch, childptr: anytype, output: *OutputSrc) ?Yield {
|
||||
this.state.exec.output_waiting += 1;
|
||||
if (this.bltn().stdout.needsIO()) |safeguard| {
|
||||
this.state.exec.output_waiting += 1;
|
||||
const slice = output.slice();
|
||||
log("THE SLICE: {d} {s}", .{ slice.len, slice });
|
||||
return this.bltn().stdout.enqueue(childptr, slice, safeguard);
|
||||
|
||||
@@ -168,6 +168,8 @@ fn commandImplStart(this: *CondExpr) Yield {
|
||||
.@"-d",
|
||||
.@"-f",
|
||||
=> {
|
||||
// Empty string expansion produces no args; the path doesn't exist.
|
||||
if (this.args.items.len == 0) return this.parent.childDone(this, 1);
|
||||
this.state = .waiting_stat;
|
||||
return this.doStat();
|
||||
},
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
exports[`Bun.build Bun.write(BuildArtifact) 1`] = `
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -31,13 +35,17 @@ NS.then(({ fn: fn2 }) => {
|
||||
|
||||
exports[`Bun.build outdir + reading out blobs works 1`] = `
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -58,23 +66,27 @@ NS.then(({ fn: fn2 }) => {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties: hash 1`] = `"d1c7nm6t"`;
|
||||
exports[`Bun.build BuildArtifact properties: hash 1`] = `"est79qzq"`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"rm7e36cf"`;
|
||||
exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"7gfnt0h6"`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"d1c7nm6t"`;
|
||||
exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"est79qzq"`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`;
|
||||
|
||||
exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = `
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -416,7 +416,8 @@ describe("bundler", () => {
|
||||
const db = new Database("test.db");
|
||||
const query = db.query(\`select "Hello world" as message\`);
|
||||
if (query.get().message !== "Hello world") throw "fail from sqlite";
|
||||
const icon = await fetch("https://bun.sh/favicon.ico").then(x=>x.arrayBuffer())
|
||||
const icon = new Uint8Array(256);
|
||||
for (let i = 0; i < 256; i++) icon[i] = i;
|
||||
if(icon.byteLength < 100) throw "fail from icon";
|
||||
if (typeof getRandomSeed() !== 'number') throw "fail from bun:jsc";
|
||||
const server = serve({
|
||||
|
||||
@@ -1113,7 +1113,7 @@ describe("bundler", () => {
|
||||
snapshotSourceMap: {
|
||||
"entry.js.map": {
|
||||
files: ["../node_modules/react/index.js", "../entry.js"],
|
||||
mappingsExactMatch: "qYACA,WAAW,IAAQ,EAAE,ICDrB,eACA,QAAQ,IAAI,CAAK",
|
||||
mappingsExactMatch: "miBACA,WAAW,IAAQ,EAAE,ICDrB,eACA,QAAQ,IAAI,CAAK",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -843,4 +843,131 @@ body {
|
||||
api.expectFile("out/" + jsFile).toContain("sourceMappingURL");
|
||||
},
|
||||
});
|
||||
|
||||
// Test that multiple HTML entrypoints sharing the same CSS file both get
|
||||
// the CSS link tag in production mode (css_chunking deduplication).
|
||||
// Regression test for https://github.com/oven-sh/bun/issues/23668
|
||||
itBundled("html/SharedCSSProductionMultipleEntries", {
|
||||
outdir: "out/",
|
||||
production: true,
|
||||
files: {
|
||||
"/entry1.html": `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./main1.tsx"></script>
|
||||
</body>
|
||||
</html>`,
|
||||
"/entry2.html": `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./main2.tsx"></script>
|
||||
</body>
|
||||
</html>`,
|
||||
"/global.css": `h1 { font-size: 24px; }`,
|
||||
"/main1.tsx": `console.log("entry1");`,
|
||||
"/main2.tsx": `console.log("entry2");`,
|
||||
},
|
||||
entryPoints: ["/entry1.html", "/entry2.html"],
|
||||
onAfterBundle(api) {
|
||||
const entry1Html = api.readFile("out/entry1.html");
|
||||
const entry2Html = api.readFile("out/entry2.html");
|
||||
|
||||
// Both HTML files must contain a CSS link tag
|
||||
const cssMatch1 = entry1Html.match(/href="(.*\.css)"/);
|
||||
const cssMatch2 = entry2Html.match(/href="(.*\.css)"/);
|
||||
|
||||
expect(cssMatch1).not.toBeNull();
|
||||
expect(cssMatch2).not.toBeNull();
|
||||
|
||||
// Both should reference the same deduplicated CSS chunk
|
||||
expect(cssMatch1![1]).toBe(cssMatch2![1]);
|
||||
|
||||
// The CSS file should contain the shared styles
|
||||
const cssContent = api.readFile("out/" + cssMatch1![1]);
|
||||
expect(cssContent).toContain("font-size");
|
||||
|
||||
// Both HTML files should also have their respective JS bundles
|
||||
expect(entry1Html).toMatch(/src=".*\.js"/);
|
||||
expect(entry2Html).toMatch(/src=".*\.js"/);
|
||||
},
|
||||
});
|
||||
|
||||
// Test manifest.json is copied as an asset and link href is rewritten
|
||||
itBundled("html/manifest-json", {
|
||||
outdir: "out/",
|
||||
files: {
|
||||
"/index.html": `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="manifest" href="./manifest.json" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>App</h1>
|
||||
<script src="./app.js"></script>
|
||||
</body>
|
||||
</html>`,
|
||||
"/manifest.json": JSON.stringify({
|
||||
name: "My App",
|
||||
short_name: "App",
|
||||
start_url: "/",
|
||||
display: "standalone",
|
||||
background_color: "#ffffff",
|
||||
theme_color: "#000000",
|
||||
}),
|
||||
"/app.js": "console.log('hello')",
|
||||
},
|
||||
entryPoints: ["/index.html"],
|
||||
onAfterBundle(api) {
|
||||
const htmlContent = api.readFile("out/index.html");
|
||||
|
||||
// The original manifest.json reference should be rewritten to a hashed filename
|
||||
expect(htmlContent).not.toContain('manifest.json"');
|
||||
expect(htmlContent).toMatch(/href="(?:\.\/|\/)?manifest-[a-zA-Z0-9]+\.json"/);
|
||||
|
||||
// Extract the hashed manifest filename and verify its content
|
||||
const manifestMatch = htmlContent.match(/href="(?:\.\/|\/)?(manifest-[a-zA-Z0-9]+\.json)"/);
|
||||
expect(manifestMatch).not.toBeNull();
|
||||
const manifestContent = api.readFile("out/" + manifestMatch![1]);
|
||||
expect(manifestContent).toContain('"name"');
|
||||
expect(manifestContent).toContain('"My App"');
|
||||
},
|
||||
});
|
||||
|
||||
// Test that other non-JS/CSS file types referenced via URL imports are copied as assets
|
||||
itBundled("html/xml-asset", {
|
||||
outdir: "out/",
|
||||
files: {
|
||||
"/index.html": `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="manifest" href="./site.webmanifest" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>App</h1>
|
||||
</body>
|
||||
</html>`,
|
||||
"/site.webmanifest": JSON.stringify({
|
||||
name: "My App",
|
||||
icons: [{ src: "/icon.png", sizes: "192x192" }],
|
||||
}),
|
||||
},
|
||||
entryPoints: ["/index.html"],
|
||||
onAfterBundle(api) {
|
||||
const htmlContent = api.readFile("out/index.html");
|
||||
|
||||
// The webmanifest reference should be rewritten to a hashed filename
|
||||
expect(htmlContent).not.toContain("site.webmanifest");
|
||||
expect(htmlContent).toMatch(/href=".*\.webmanifest"/);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,17 +57,17 @@ describe("bundler", () => {
|
||||
"../entry.tsx",
|
||||
],
|
||||
mappings: [
|
||||
["react.development.js:524:'getContextName'", "1:5412:Y1"],
|
||||
["react.development.js:524:'getContextName'", "1:5567:Y1"],
|
||||
["react.development.js:2495:'actScopeDepth'", "23:4082:GJ++"],
|
||||
["react.development.js:696:''Component'", '1:7474:\'Component "%s"'],
|
||||
["entry.tsx:6:'\"Content-Type\"'", '100:18809:"Content-Type"'],
|
||||
["entry.tsx:11:'<html>'", "100:19063:void"],
|
||||
["entry.tsx:23:'await'", "100:19163:await"],
|
||||
["react.development.js:696:''Component'", '1:7629:\'Component "%s"'],
|
||||
["entry.tsx:6:'\"Content-Type\"'", '100:18808:"Content-Type"'],
|
||||
["entry.tsx:11:'<html>'", "100:19062:void"],
|
||||
["entry.tsx:23:'await'", "100:19161:await"],
|
||||
],
|
||||
},
|
||||
},
|
||||
expectExactFilesize: {
|
||||
"out/entry.js": 221720,
|
||||
"out/entry.js": 221895,
|
||||
},
|
||||
run: {
|
||||
stdout: "<!DOCTYPE html><html><body><h1>Hello World</h1><p>This is an example.</p></body></html>",
|
||||
|
||||
@@ -76,13 +76,17 @@ describe("bundler", () => {
|
||||
|
||||
expect(bundled).toMatchInlineSnapshot(`
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
||||
@@ -160,7 +164,7 @@ describe("bundler", () => {
|
||||
var { AsyncEntryPoint: AsyncEntryPoint2 } = await Promise.resolve().then(() => exports_AsyncEntryPoint);
|
||||
AsyncEntryPoint2();
|
||||
|
||||
//# debugId=5E85CC0956C6307964756E2164756E21
|
||||
//# debugId=42062903F19477CF64756E2164756E21
|
||||
//# sourceMappingURL=out.js.map
|
||||
"
|
||||
`);
|
||||
@@ -337,13 +341,17 @@ describe("bundler", () => {
|
||||
|
||||
expect(bundled).toMatchInlineSnapshot(`
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
||||
@@ -402,7 +410,7 @@ describe("bundler", () => {
|
||||
var { AsyncEntryPoint: AsyncEntryPoint2 } = await Promise.resolve().then(() => exports_AsyncEntryPoint);
|
||||
AsyncEntryPoint2();
|
||||
|
||||
//# debugId=C92CBF0103732ECC64756E2164756E21
|
||||
//# debugId=BF876FBF618133C264756E2164756E21
|
||||
//# sourceMappingURL=out.js.map
|
||||
"
|
||||
`);
|
||||
|
||||
@@ -2150,10 +2150,7 @@ c {
|
||||
toplevel-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
|
||||
`, */
|
||||
});
|
||||
// TODO: Bun's bundler doesn't support multiple entry points generating CSS outputs
|
||||
// with identical content hashes to the same output path. This test exposes that
|
||||
// limitation. Skip until the bundler can deduplicate or handle this case.
|
||||
itBundled.skip("css/MetafileCSSBundleTwoToOne", {
|
||||
itBundled("css/MetafileCSSBundleTwoToOne", {
|
||||
files: {
|
||||
"/foo/entry.js": /* js */ `
|
||||
import '../common.css'
|
||||
|
||||
@@ -103,11 +103,11 @@ console.log(favicon);
|
||||
"files": [
|
||||
{
|
||||
"input": "client.html",
|
||||
"path": "./client-s249t5qg.js",
|
||||
"path": "./client-b5m4ng86.js",
|
||||
"loader": "js",
|
||||
"isEntry": true,
|
||||
"headers": {
|
||||
"etag": "fxoJ6L-0X3o",
|
||||
"etag": "Ax71YVYyZQc",
|
||||
"content-type": "text/javascript;charset=utf-8"
|
||||
}
|
||||
},
|
||||
|
||||
255
test/cli/install/GHSA-pfwx-36v6-832x.test.ts
Normal file
255
test/cli/install/GHSA-pfwx-36v6-832x.test.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import { file } from "bun";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { rm } from "fs/promises";
|
||||
import { bunEnv, bunExe, tempDir } from "harness";
|
||||
import { join } from "path";
|
||||
|
||||
// Each test uses its own BUN_INSTALL_CACHE_DIR inside the temp dir for full
|
||||
// isolation. This avoids interfering with the global cache or other tests.
|
||||
function envWithCache(dir: string) {
|
||||
return { ...bunEnv, BUN_INSTALL_CACHE_DIR: join(String(dir), ".bun-cache") };
|
||||
}
|
||||
|
||||
describe.concurrent("GitHub tarball integrity", () => {
|
||||
test("should store integrity hash in lockfile for GitHub dependencies", async () => {
|
||||
using dir = tempDir("github-integrity", {
|
||||
"package.json": JSON.stringify({
|
||||
name: "test-github-integrity",
|
||||
dependencies: {
|
||||
"is-number": "jonschlinkert/is-number#98e8ff1",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const env = envWithCache(dir);
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stderr).toContain("Saved lockfile");
|
||||
expect(exitCode).toBe(0);
|
||||
|
||||
const lockfileContent = await file(join(String(dir), "bun.lock")).text();
|
||||
|
||||
// The lockfile should contain a sha512 integrity hash for the GitHub dependency
|
||||
expect(lockfileContent).toContain("sha512-");
|
||||
// The resolved commit hash should be present
|
||||
expect(lockfileContent).toContain("jonschlinkert-is-number-98e8ff1");
|
||||
// Verify the format: the integrity appears after the resolved commit hash
|
||||
expect(lockfileContent).toMatch(/"jonschlinkert-is-number-98e8ff1",\s*"sha512-/);
|
||||
});
|
||||
|
||||
test("should verify integrity passes on re-install with matching hash", async () => {
|
||||
using dir = tempDir("github-integrity-match", {
|
||||
"package.json": JSON.stringify({
|
||||
name: "test-github-integrity-match",
|
||||
dependencies: {
|
||||
"is-number": "jonschlinkert/is-number#98e8ff1",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const env = envWithCache(dir);
|
||||
|
||||
// First install to generate lockfile with correct integrity
|
||||
await using proc1 = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout1, stderr1, exitCode1] = await Promise.all([proc1.stdout.text(), proc1.stderr.text(), proc1.exited]);
|
||||
expect(stderr1).not.toContain("error:");
|
||||
expect(exitCode1).toBe(0);
|
||||
|
||||
// Read the generated lockfile and extract the integrity hash adjacent to
|
||||
// the GitHub resolved entry to avoid accidentally matching an npm hash.
|
||||
const lockfileContent = await file(join(String(dir), "bun.lock")).text();
|
||||
const integrityMatch = lockfileContent.match(/"jonschlinkert-is-number-98e8ff1",\s*"(sha512-[A-Za-z0-9+/]+=*)"/);
|
||||
expect(integrityMatch).not.toBeNull();
|
||||
const integrityHash = integrityMatch![1];
|
||||
|
||||
// Clear cache and node_modules, then re-install with the same lockfile
|
||||
await rm(join(String(dir), ".bun-cache"), { recursive: true, force: true });
|
||||
await rm(join(String(dir), "node_modules"), { recursive: true, force: true });
|
||||
|
||||
await using proc2 = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout2, stderr2, exitCode2] = await Promise.all([proc2.stdout.text(), proc2.stderr.text(), proc2.exited]);
|
||||
|
||||
// Should succeed because the integrity matches
|
||||
expect(stderr2).not.toContain("Integrity check failed");
|
||||
expect(exitCode2).toBe(0);
|
||||
|
||||
// Lockfile should still contain the same integrity hash
|
||||
const lockfileContent2 = await file(join(String(dir), "bun.lock")).text();
|
||||
expect(lockfileContent2).toContain(integrityHash);
|
||||
});
|
||||
|
||||
test("should reject GitHub tarball when integrity check fails", async () => {
|
||||
using dir = tempDir("github-integrity-reject", {
|
||||
"package.json": JSON.stringify({
|
||||
name: "test-github-integrity-reject",
|
||||
dependencies: {
|
||||
"is-number": "jonschlinkert/is-number#98e8ff1",
|
||||
},
|
||||
}),
|
||||
// Pre-create a lockfile with an invalid integrity hash (valid base64, 64 zero bytes)
|
||||
"bun.lock": JSON.stringify({
|
||||
lockfileVersion: 1,
|
||||
configVersion: 1,
|
||||
workspaces: {
|
||||
"": {
|
||||
name: "test-github-integrity-reject",
|
||||
dependencies: {
|
||||
"is-number": "jonschlinkert/is-number#98e8ff1",
|
||||
},
|
||||
},
|
||||
},
|
||||
packages: {
|
||||
"is-number": [
|
||||
"is-number@github:jonschlinkert/is-number#98e8ff1",
|
||||
{},
|
||||
"jonschlinkert-is-number-98e8ff1",
|
||||
"sha512-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||
],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Fresh per-test cache ensures the tarball must be downloaded from the network
|
||||
const env = envWithCache(dir);
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stderr).toContain("Integrity check failed");
|
||||
expect(exitCode).not.toBe(0);
|
||||
});
|
||||
|
||||
test("should update lockfile with integrity when old format has none", async () => {
|
||||
using dir = tempDir("github-integrity-upgrade", {
|
||||
"package.json": JSON.stringify({
|
||||
name: "test-github-integrity-upgrade",
|
||||
dependencies: {
|
||||
"is-number": "jonschlinkert/is-number#98e8ff1",
|
||||
},
|
||||
}),
|
||||
// Pre-create a lockfile in the old format (no integrity hash)
|
||||
"bun.lock": JSON.stringify({
|
||||
lockfileVersion: 1,
|
||||
configVersion: 1,
|
||||
workspaces: {
|
||||
"": {
|
||||
name: "test-github-integrity-upgrade",
|
||||
dependencies: {
|
||||
"is-number": "jonschlinkert/is-number#98e8ff1",
|
||||
},
|
||||
},
|
||||
},
|
||||
packages: {
|
||||
"is-number": ["is-number@github:jonschlinkert/is-number#98e8ff1", {}, "jonschlinkert-is-number-98e8ff1"],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Fresh per-test cache ensures the tarball must be downloaded
|
||||
const env = envWithCache(dir);
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
// Should succeed without errors
|
||||
expect(stderr).not.toContain("Integrity check failed");
|
||||
expect(stderr).not.toContain("error:");
|
||||
// The lockfile should be re-saved with the new integrity hash
|
||||
expect(stderr).toContain("Saved lockfile");
|
||||
expect(exitCode).toBe(0);
|
||||
|
||||
// Verify the lockfile now contains the integrity hash
|
||||
const lockfileContent = await file(join(String(dir), "bun.lock")).text();
|
||||
expect(lockfileContent).toContain("sha512-");
|
||||
expect(lockfileContent).toMatch(/"jonschlinkert-is-number-98e8ff1",\s*"sha512-/);
|
||||
});
|
||||
|
||||
test("should accept GitHub dependency from cache without re-downloading", async () => {
|
||||
// Use a shared cache dir for both installs so the second is a true cache hit
|
||||
using dir = tempDir("github-integrity-cached", {
|
||||
"package.json": JSON.stringify({
|
||||
name: "test-github-integrity-cached",
|
||||
dependencies: {
|
||||
"is-number": "jonschlinkert/is-number#98e8ff1",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const env = envWithCache(dir);
|
||||
|
||||
// First install warms the per-test cache
|
||||
await using proc1 = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout1, stderr1, exitCode1] = await Promise.all([proc1.stdout.text(), proc1.stderr.text(), proc1.exited]);
|
||||
expect(stderr1).not.toContain("error:");
|
||||
expect(exitCode1).toBe(0);
|
||||
|
||||
// Remove node_modules but keep the cache
|
||||
await rm(join(String(dir), "node_modules"), { recursive: true, force: true });
|
||||
|
||||
// Strip the integrity from the lockfile to simulate an old-format lockfile
|
||||
// that should still work when the cache already has the package
|
||||
const lockfileContent = await file(join(String(dir), "bun.lock")).text();
|
||||
const stripped = lockfileContent.replace(/,\s*"sha512-[^"]*"/, "");
|
||||
await Bun.write(join(String(dir), "bun.lock"), stripped);
|
||||
|
||||
// Second install should hit the cache and succeed without re-downloading
|
||||
await using proc2 = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout2, stderr2, exitCode2] = await Promise.all([proc2.stdout.text(), proc2.stderr.text(), proc2.exited]);
|
||||
|
||||
// Should succeed without integrity errors (package served from cache)
|
||||
expect(stderr2).not.toContain("Integrity check failed");
|
||||
expect(stderr2).not.toContain("error:");
|
||||
expect(exitCode2).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -634,3 +634,42 @@ test.concurrent("bun serve files with correct Content-Type headers", async () =>
|
||||
// The process will be automatically cleaned up by 'await using'
|
||||
}
|
||||
});
|
||||
|
||||
test("importing bun:main from HTML entry preload does not crash", async () => {
|
||||
const dir = tempDirWithFiles("html-entry-bun-main", {
|
||||
"index.html": /*html*/ `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Test</title></head>
|
||||
<body><h1>Hello</h1></body>
|
||||
</html>
|
||||
`,
|
||||
"preload.mjs": /*js*/ `
|
||||
try {
|
||||
await import("bun:main");
|
||||
} catch {}
|
||||
// Signal that preload ran successfully without crashing
|
||||
console.log("PRELOAD_OK");
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--preload", "./preload.mjs", "index.html", "--port=0"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let text = "";
|
||||
for await (const chunk of proc.stdout) {
|
||||
text += decoder.decode(chunk, { stream: true });
|
||||
if (text.includes("http://")) break;
|
||||
}
|
||||
|
||||
expect(text).toContain("PRELOAD_OK");
|
||||
|
||||
proc.kill();
|
||||
await proc.exited;
|
||||
});
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCIzOJskt6VkEJY
|
||||
XKSJv/Gdil3XYkjk3NVc/+m+kzqnkTRbPtT9w+IGWgmJhuf9DJPLCwHFAEFarVwV
|
||||
x16Q0PbU4ajXaLRHEYGhrH10oTMjQnJ24xVm26mxRXPQa5vaLpWJqNyIdNLIQLe+
|
||||
UXUOzSGGsFTRMAjvYrkzjBe4ZUnaZV+aFY/ug0jfzeA1dJjzKZs6+yTJRbsuWUEb
|
||||
8MsDmT4v+kBZDKdaDn7AFDWRVqx/38BnqsRzkM0CxpnyT2kRzw5zQajIE13gdTJo
|
||||
1EHvYSUkkxrY5m30Rl9BuBBZBjhMzOHq0fYVVooHO+sf4XHPgvFTTxJum85u7J1J
|
||||
oEUjrLKtAgMBAAECggEACInVNhaiqu4infZGVMy0rXMV8VwSlapM7O2SLtFsr0nK
|
||||
XUmaLK6dvGzBPKK9dxdiYCFzPlMKQTkhzsAvYFWSmm3tRmikG+11TFyCRhXLpc8/
|
||||
ark4vD9Io6ZkmKUmyKLwtXNjNGcqQtJ7RXc7Ga3nAkueN6JKZHqieZusXVeBGQ70
|
||||
YH1LKyVNBeJggbj+g9rqaksPyNJQ8EWiNTJkTRQPazZ0o1VX/fzDFyr/a5npFtHl
|
||||
4BHfafv9o1Xyr70Kie8CYYRJNViOCN+ylFs7Gd3XRaAkSkgMT/7DzrHdEM2zrrHK
|
||||
yNg2gyDVX9UeEJG2X5UtU0o9BVW7WBshz/2hqIUHoQKBgQC8zsRFvC7u/rGr5vRR
|
||||
mhZZG+Wvg03/xBSuIgOrzm+Qie6mAzOdVmfSL/pNV9EFitXt1yd2ROo31AbS7Evy
|
||||
Bm/QVKr2mBlmLgov3B7O/e6ABteooOL7769qV/v+yo8VdEg0biHmsfGIIXDe3Lwl
|
||||
OT0XwF9r/SeZLbw1zfkSsUVG/QKBgQC5fANM3Dc9LEek+6PHv5+eC1cKkyioEjUl
|
||||
/y1VUD00aABI1TUcdLF3BtFN2t/S6HW0hrP3KwbcUfqC25k+GDLh1nM6ZK/gI3Yn
|
||||
IGtCHxtE3S6jKhE9QcK/H+PzGVKWge9SezeYRP0GHJYDrTVTA8Kt9HgoZPPeReJl
|
||||
+Ss9c8ThcQKBgECX6HQHFnNzNSufXtSQB7dCoQizvjqTRZPxVRoxDOABIGExVTYt
|
||||
umUhPtu5AGyJ+/hblEeU+iBRbGg6qRzK8PPwE3E7xey8MYYAI5YjL7YjISKysBUL
|
||||
AhM6uJ6Jg/wOBSnSx8xZ8kzlS+0izUda1rjKeprCSArSp8IsjlrDxPStAoGAEcPr
|
||||
+P+altRX5Fhpvmb/Hb8OTif8G+TqjEIdkG9H/W38oP0ywg/3M2RGxcMx7txu8aR5
|
||||
NjI7zPxZFxF7YvQkY3cLwEsGgVxEI8k6HLIoBXd90Qjlb82NnoqqZY1GWL4HMwo0
|
||||
L/Rjm6M/Rwje852Hluu0WoIYzXA6F/Q+jPs6nzECgYAxx4IbDiGXuenkwSF1SUyj
|
||||
NwJXhx4HDh7U6EO/FiPZE5BHE3BoTrFu3o1lzverNk7G3m+j+m1IguEAalHlukYl
|
||||
rip9iUISlKYqbYZdLBoLwHAfHhszdrjqn8/v6oqbB5yR3HXjPFUWJo0WJ2pqJp56
|
||||
ZshgmQQ/5Khoj6x0/dMPSg==
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCt7iqkEIco372h
|
||||
v19q0zjaYbm6gzxEnR45UjpQYqgztq4QHicD80mqIkCBCYknFxhwxhNn+Y3g5RWQ
|
||||
dReplpQbkneqRVp+qixMvu2FmOA4zRRoqObP7FyF1Yusvmroe0Y9SP2xTTmA9Zo7
|
||||
3paywPUIuZ9eKGwIiFTtj1yQ1FdghLhzZgxcf3LHEHRkGnxgxxNITFxh4nd6fGIj
|
||||
NqM5fQAY8z35lMXdeWjrhtaqgFYB+Z20YY0X7LJx39vYao0wqW8sZjX88TqHI1zX
|
||||
WLpUk6UK9RqaNza5xc80wV+9/zjhr3dc1FRjBxI1DS/ufo33dUfvilxv9/LtWwUn
|
||||
KfKLns9LAgMBAAECggEAAacPHM2G7GBIm/9rCr6tvihNgD8M685zOOZAqGYn9CqY
|
||||
cYHC4gtF/L2U6CBj2pNAoCwo3LXUkD+6r7MYKXAgqQg3HTCM4rwFbhD1rU8FVHfh
|
||||
OL0QwwZ2ut95DVdjoxTAlEN9ZcdSFc//llMJ1cF8lxoVvKFc4cv3uCI2mcaJk858
|
||||
iABfJLl3yfdv1xtpAuOfXf66sXbAmn5NQfN0qTEg2iOdgb4BUee5Wb35MakDQb6+
|
||||
/s7/bWB+ublZzYt12ChIh1jkBBHaGyQ8mFnPj99ZAJdFjAzi6ydoJ0a2rCVY7Ugs
|
||||
bkhnzDUtAaHKxo9JXaqIwbUaVFkX8dDhbg82dJrWUQKBgQDb7hNR0bJFW845N19M
|
||||
74p2PM+0dIiVzwxAg4E2dXDVe39awO/tw8Vu1o1+NPFhWAzGcidP7pAHmPEgRTVO
|
||||
7LA2P3CDXpkAEx5E0QW6QWZGqHfSa3+P1AvetvAV+OxtlDphcNeLApY16TUVOKZg
|
||||
SZlxW2e0dZylbHewgLBTIV9wUQKBgQDKdML+JD18WfenPeowsw8HzKdaw01iGiV1
|
||||
fvTjEXu6YxPPynWFMuj5gjBQodXM2vv0EsQBAPKYfe0nzRFL2kNuYs7TLoaNxqkp
|
||||
DNfJ2Ww5OSg7Mp76XgppeKKlsXLyUMYHHrDh6MRi5jvWtiHRpaNmV3cHMRs22c+B
|
||||
cqKP5Zma2wKBgCPNnS2Lsrbh3C+qWQRgVq0q9zFMa1PgEgGKpwVjlwvaAACZOjX9
|
||||
0e1aVkx+d/E98U55FPdJQf9Koa58NdJ0a7dZGor4YnYFpr7TPFh2/xxvnpoN0AVt
|
||||
IsWOCIW7MVohcGOeiChkMmnyXibnQwaX1LgEhlx1bRvtDYsZWBsgarYRAoGAARvo
|
||||
oYnDSHYZtDHToZapg2pslEOzndD02ZLrdn73BYtbZWz/fc5MlmlPKHHqgOfGL40W
|
||||
w8akjY9LCEfIS3kTm3wxE9kSZZ5r+MyYNgPZ4upcPQ7G7iortm4xveSd85PbsdhK
|
||||
McKbqMsIEuIGh2Z34ayi+0galQ9WYqglGdKxJ7cCgYEAuSPBHa+en0xaraZNRvMk
|
||||
OfV9Su/wrpR3TXSeo0E1mZHLwq1JwulpfO1SjxTH5uOJtG0tusl122wfm0KjrXUO
|
||||
vG5/It+X4u1Nv9oWj+z1+EV4fQrQ/Coqcc1r+5w1yzfURkKlHh74jbK5Yy/KfXrE
|
||||
eqbbJD40tKhY8ho15D3iCSo=
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID5jCCAs6gAwIBAgIUN7coIsdMcLo9amZfkwogu0YkeLEwDQYJKoZIhvcNAQEL
|
||||
MIIEDDCCAvSgAwIBAgIUbddWE2woW5e96uC4S2fd2M0AsFAwDQYJKoZIhvcNAQEL
|
||||
BQAwfjELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVN0YXRlMREwDwYDVQQHDAhMb2Nh
|
||||
dGlvbjEaMBgGA1UECgwRT3JnYW5pemF0aW9uIE5hbWUxHDAaBgNVBAsME09yZ2Fu
|
||||
aXphdGlvbmFsIFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzA5MjExNDE2
|
||||
MjNaFw0yNDA5MjAxNDE2MjNaMH4xCzAJBgNVBAYTAlNFMQ4wDAYDVQQIDAVTdGF0
|
||||
aXphdGlvbmFsIFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNjAyMTMyMzEx
|
||||
MjlaFw0zNjAyMTEyMzExMjlaMH4xCzAJBgNVBAYTAlNFMQ4wDAYDVQQIDAVTdGF0
|
||||
ZTERMA8GA1UEBwwITG9jYXRpb24xGjAYBgNVBAoMEU9yZ2FuaXphdGlvbiBOYW1l
|
||||
MRwwGgYDVQQLDBNPcmdhbml6YXRpb25hbCBVbml0MRIwEAYDVQQDDAlsb2NhbGhv
|
||||
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCIzOJskt6VkEJYXKSJ
|
||||
v/Gdil3XYkjk3NVc/+m+kzqnkTRbPtT9w+IGWgmJhuf9DJPLCwHFAEFarVwVx16Q
|
||||
0PbU4ajXaLRHEYGhrH10oTMjQnJ24xVm26mxRXPQa5vaLpWJqNyIdNLIQLe+UXUO
|
||||
zSGGsFTRMAjvYrkzjBe4ZUnaZV+aFY/ug0jfzeA1dJjzKZs6+yTJRbsuWUEb8MsD
|
||||
mT4v+kBZDKdaDn7AFDWRVqx/38BnqsRzkM0CxpnyT2kRzw5zQajIE13gdTJo1EHv
|
||||
YSUkkxrY5m30Rl9BuBBZBjhMzOHq0fYVVooHO+sf4XHPgvFTTxJum85u7J1JoEUj
|
||||
rLKtAgMBAAGjXDBaMA4GA1UdDwEB/wQEAwIDiDATBgNVHSUEDDAKBggrBgEFBQcD
|
||||
ATAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFNzx4Rfs9m8XR5ML0WsI
|
||||
sorKmB4PMA0GCSqGSIb3DQEBCwUAA4IBAQB87iQy8R0fiOky9WTcyzVeMaavS3MX
|
||||
iTe1BRn1OCyDq+UiwwoNz7zdzZJFEmRtFBwPNFOe4HzLu6E+7yLFR552eYRHlqIi
|
||||
/fiLb5JiZfPtokUHeqwELWBsoXtU8vKxViPiLZ09jkWOPZWo7b/xXd6QYykBfV91
|
||||
usUXLzyTD2orMagpqNksLDGS3p3ggHEJBZtRZA8R7kPEw98xZHznOQpr26iv8kYz
|
||||
ZWdLFoFdwgFBSfxePKax5rfo+FbwdrcTX0MhbORyiu2XsBAghf8s2vKDkHg2UQE8
|
||||
haonxFYMFaASfaZ/5vWKYDTCJkJ67m/BtkpRafFEO+ad1i1S61OjfxH4
|
||||
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt7iqkEIco372hv19q
|
||||
0zjaYbm6gzxEnR45UjpQYqgztq4QHicD80mqIkCBCYknFxhwxhNn+Y3g5RWQdRep
|
||||
lpQbkneqRVp+qixMvu2FmOA4zRRoqObP7FyF1Yusvmroe0Y9SP2xTTmA9Zo73pay
|
||||
wPUIuZ9eKGwIiFTtj1yQ1FdghLhzZgxcf3LHEHRkGnxgxxNITFxh4nd6fGIjNqM5
|
||||
fQAY8z35lMXdeWjrhtaqgFYB+Z20YY0X7LJx39vYao0wqW8sZjX88TqHI1zXWLpU
|
||||
k6UK9RqaNza5xc80wV+9/zjhr3dc1FRjBxI1DS/ufo33dUfvilxv9/LtWwUnKfKL
|
||||
ns9LAgMBAAGjgYEwfzAdBgNVHQ4EFgQUQCpSY7ODhdyD6pdZHvfHoWRXWsIwHwYD
|
||||
VR0jBBgwFoAUQCpSY7ODhdyD6pdZHvfHoWRXWsIwDwYDVR0TAQH/BAUwAwEB/zAs
|
||||
BgNVHREEJTAjgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJ
|
||||
KoZIhvcNAQELBQADggEBAGKTIzGQsOqfD0+x15F2cu7FKjIo1ua0OiILAhPqGX65
|
||||
kGcetjC/dJip2bGnw1NjG9WxEJNZ4YcsGrwh9egfnXXmfHNL0wzx/LTo2oysbXsN
|
||||
nEj+cmzw3Lwjn/ywJc+AC221/xrmDfm3m/hMzLqncnj23ZAHqkXTSp5UtSMs+UDQ
|
||||
my0AJOvsDGPVKHQsAX3JDjKHaoVJn4YqpHcIGmpjrNcQSvwUocDHPcC0ywco6SgF
|
||||
Ylzy2bwWWdPd9Cz9JkAMb95nWc7Rwf/nxAqCjJFzKEisvrx7VZ+QSVI0nqJzt8V1
|
||||
pbtWYH5gMFVstU3ghWdSLbAk4XufGYrIWAlA5mqjQ4o=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -221,8 +221,14 @@ describe.concurrent(() => {
|
||||
["''", "''"],
|
||||
['""', '""'],
|
||||
])("test proxy env, http_proxy=%s https_proxy=%s", async (http_proxy, https_proxy) => {
|
||||
using localServer = Bun.serve({
|
||||
port: 0,
|
||||
fetch() {
|
||||
return new Response("OK");
|
||||
},
|
||||
});
|
||||
const { exited, stderr: stream } = Bun.spawn({
|
||||
cmd: [bunExe(), "-e", 'await fetch("https://example.com")'],
|
||||
cmd: [bunExe(), "-e", `await fetch("${localServer.url}")`],
|
||||
env: {
|
||||
...bunEnv,
|
||||
http_proxy: http_proxy,
|
||||
|
||||
48
test/js/bun/shell/shell-cmdsub-crash.test.ts
Normal file
48
test/js/bun/shell/shell-cmdsub-crash.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDir } from "harness";
|
||||
|
||||
// Regression test for use-after-poison in builtin OutputTask callbacks
|
||||
// inside command substitution $().
|
||||
//
|
||||
// The bug: output_waiting was only incremented for async writes but
|
||||
// output_done was always incremented, so when stdout is sync (.pipe
|
||||
// in cmdsub) the counter check `output_done >= output_waiting` fires
|
||||
// prematurely, calling done() and freeing the builtin while IOWriter
|
||||
// callbacks are still pending.
|
||||
//
|
||||
// Repro requires many ls tasks with errors — listing many entries
|
||||
// alongside non-existent paths reliably triggers the ASAN
|
||||
// use-after-poison.
|
||||
|
||||
describe("builtins in command substitution with errors should not crash", () => {
|
||||
test("ls with errors in command substitution", async () => {
|
||||
// Create a temp directory with many files to produce output,
|
||||
// and include non-existent paths to produce errors.
|
||||
const files: Record<string, string> = {};
|
||||
for (let i = 0; i < 50; i++) {
|
||||
files[`file${i}.txt`] = `content${i}`;
|
||||
}
|
||||
using dir = tempDir("shell-cmdsub", files);
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`
|
||||
import { $ } from "bun";
|
||||
$.throws(false);
|
||||
await $\`echo $(ls $TEST_DIR/* /nonexistent_path_1 /nonexistent_path_2)\`;
|
||||
console.log("done");
|
||||
`,
|
||||
],
|
||||
env: { ...bunEnv, TEST_DIR: String(dir) },
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stdout).toContain("done");
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
});
|
||||
85
test/js/bun/shell/shell-seq-condexpr.test.ts
Normal file
85
test/js/bun/shell/shell-seq-condexpr.test.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
test("seq inf does not hang", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`import { $ } from "bun"; $.throws(false); const r = await $\`seq inf\`; process.exit(r.exitCode)`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stderr).toContain("invalid argument");
|
||||
expect(exitCode).toBe(1);
|
||||
}, 10_000);
|
||||
|
||||
test("seq nan does not hang", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`import { $ } from "bun"; $.throws(false); const r = await $\`seq nan\`; process.exit(r.exitCode)`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stderr).toContain("invalid argument");
|
||||
expect(exitCode).toBe(1);
|
||||
}, 10_000);
|
||||
|
||||
test("seq -inf does not hang", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`import { $ } from "bun"; $.throws(false); const r = await $\`seq -- -inf\`; process.exit(r.exitCode)`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stderr).toContain("invalid argument");
|
||||
expect(exitCode).toBe(1);
|
||||
}, 10_000);
|
||||
|
||||
test('[[ -d "" ]] does not crash', async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`import { $ } from "bun"; $.throws(false); const r = await $\`[[ -d "" ]]\`; process.exit(r.exitCode)`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
}, 10_000);
|
||||
|
||||
test('[[ -f "" ]] does not crash', async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`import { $ } from "bun"; $.throws(false); const r = await $\`[[ -f "" ]]\`; process.exit(r.exitCode)`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
}, 10_000);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { spawnSync } from "bun";
|
||||
import { constants, Database, SQLiteError } from "bun:sqlite";
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { existsSync, readdirSync, realpathSync, writeFileSync } from "fs";
|
||||
import { readdirSync, realpathSync } from "fs";
|
||||
import { bunEnv, bunExe, isMacOS, isMacOSVersionAtLeast, isWindows, tempDirWithFiles } from "harness";
|
||||
import { tmpdir } from "os";
|
||||
import path from "path";
|
||||
@@ -846,13 +846,15 @@ it("db.transaction()", () => {
|
||||
|
||||
// this bug was fixed by ensuring FinalObject has no more than 64 properties
|
||||
it("inlineCapacity #987", async () => {
|
||||
const path = tmpbase + "bun-987.db";
|
||||
if (!existsSync(path)) {
|
||||
const arrayBuffer = await (await fetch("https://github.com/oven-sh/bun/files/9265429/logs.log")).arrayBuffer();
|
||||
writeFileSync(path, arrayBuffer);
|
||||
}
|
||||
|
||||
const db = new Database(path);
|
||||
const db = new Database(":memory:");
|
||||
// Create schema matching the original regression test (media + logs tables)
|
||||
db.exec(`
|
||||
CREATE TABLE media (id INTEGER PRIMARY KEY, mid TEXT, name TEXT, url TEXT, duration INTEGER);
|
||||
CREATE TABLE logs (mid INTEGER, duration INTEGER, start INTEGER, did TEXT, vid TEXT);
|
||||
INSERT INTO media VALUES (1, 'm1', 'Test Media', 'http://test', 120);
|
||||
INSERT INTO logs VALUES (1, 60, 1654100000, 'd1', 'v1');
|
||||
INSERT INTO logs VALUES (1, 45, 1654200000, 'd2', 'v2');
|
||||
`);
|
||||
|
||||
const query = `SELECT
|
||||
media.mid,
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
import { tls } from "harness";
|
||||
import https from "node:https";
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls,
|
||||
fetch() {
|
||||
return new Response("OK");
|
||||
},
|
||||
});
|
||||
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const client = https.request("https://example.com/", { agent: false });
|
||||
const client = https.request(`https://localhost:${server.port}/`, {
|
||||
agent: false,
|
||||
ca: tls.cert,
|
||||
rejectUnauthorized: true,
|
||||
});
|
||||
client.on("error", reject);
|
||||
client.on("close", resolve);
|
||||
client.end();
|
||||
|
||||
@@ -212,10 +212,16 @@ describe("async context passes through", () => {
|
||||
expect(s.getStore()).toBe(undefined);
|
||||
});
|
||||
test("fetch", async () => {
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch() {
|
||||
return new Response("OK");
|
||||
},
|
||||
});
|
||||
const s = new AsyncLocalStorage<string>();
|
||||
await s.run("value", async () => {
|
||||
expect(s.getStore()).toBe("value");
|
||||
const response = await fetch("https://bun.sh") //
|
||||
const response = await fetch(server.url) //
|
||||
.then(r => {
|
||||
expect(s.getStore()).toBe("value");
|
||||
return true;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* A handful of older tests do not run in Node in this file. These tests should be updated to run in Node, or deleted.
|
||||
*/
|
||||
import { bunEnv, bunExe, exampleSite, randomPort } from "harness";
|
||||
import { bunEnv, bunExe, exampleSite, randomPort, tls as tlsCert } from "harness";
|
||||
import { createTest } from "node-harness";
|
||||
import { EventEmitter, once } from "node:events";
|
||||
import nodefs, { unlinkSync } from "node:fs";
|
||||
@@ -1081,9 +1081,19 @@ describe("node:http", () => {
|
||||
});
|
||||
|
||||
test("should not decompress gzip, issue#4397", async () => {
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls: tlsCert,
|
||||
fetch() {
|
||||
const body = Bun.gzipSync(Buffer.from("<html>Hello</html>"));
|
||||
return new Response(body, {
|
||||
headers: { "content-encoding": "gzip" },
|
||||
});
|
||||
},
|
||||
});
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
https
|
||||
.request("https://bun.sh/", { headers: { "accept-encoding": "gzip" } }, res => {
|
||||
.request(server.url, { ca: tlsCert.cert, headers: { "accept-encoding": "gzip" } }, res => {
|
||||
res.on("data", function cb(chunk) {
|
||||
resolve(chunk);
|
||||
res.off("data", cb);
|
||||
@@ -1632,6 +1642,75 @@ describe("HTTP Server Security Tests - Advanced", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Response Splitting Protection", () => {
|
||||
test("rejects CRLF in statusMessage set via property assignment followed by res.end()", async () => {
|
||||
const { promise: errorPromise, resolve: resolveError } = Promise.withResolvers<Error>();
|
||||
server.on("request", (req, res) => {
|
||||
res.statusCode = 200;
|
||||
res.statusMessage = "OK\r\nSet-Cookie: admin=true";
|
||||
try {
|
||||
res.end("body");
|
||||
} catch (e: any) {
|
||||
resolveError(e);
|
||||
res.statusMessage = "OK";
|
||||
res.end("safe");
|
||||
}
|
||||
});
|
||||
|
||||
const response = (await sendRequest("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")) as string;
|
||||
const err = await errorPromise;
|
||||
expect((err as any).code).toBe("ERR_INVALID_CHAR");
|
||||
// The injected Set-Cookie header must NOT appear in the response
|
||||
expect(response).not.toInclude("Set-Cookie: admin=true");
|
||||
});
|
||||
|
||||
test("rejects CRLF in statusMessage set via property assignment followed by res.write()", async () => {
|
||||
const { promise: errorPromise, resolve: resolveError } = Promise.withResolvers<Error>();
|
||||
server.on("request", (req, res) => {
|
||||
res.statusCode = 200;
|
||||
res.statusMessage = "OK\r\nX-Injected: evil";
|
||||
try {
|
||||
res.write("chunk");
|
||||
} catch (e: any) {
|
||||
resolveError(e);
|
||||
res.statusMessage = "OK";
|
||||
res.end("safe");
|
||||
}
|
||||
});
|
||||
|
||||
const response = (await sendRequest("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")) as string;
|
||||
const err = await errorPromise;
|
||||
expect((err as any).code).toBe("ERR_INVALID_CHAR");
|
||||
expect(response).not.toInclude("X-Injected: evil");
|
||||
});
|
||||
|
||||
test("rejects CRLF in statusMessage passed to writeHead()", async () => {
|
||||
server.on("request", (req, res) => {
|
||||
expect(() => {
|
||||
res.writeHead(200, "OK\r\nX-Injected: evil");
|
||||
}).toThrow(/Invalid character in statusMessage/);
|
||||
res.writeHead(200, "OK");
|
||||
res.end("safe");
|
||||
});
|
||||
|
||||
const response = (await sendRequest("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")) as string;
|
||||
expect(response).not.toInclude("X-Injected");
|
||||
expect(response).toInclude("safe");
|
||||
});
|
||||
|
||||
test("allows valid statusMessage without control characters", async () => {
|
||||
server.on("request", (req, res) => {
|
||||
res.statusCode = 200;
|
||||
res.statusMessage = "Everything Is Fine";
|
||||
res.end("ok");
|
||||
});
|
||||
|
||||
const response = (await sendRequest("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")) as string;
|
||||
expect(response).toInclude("200 Everything Is Fine");
|
||||
expect(response).toInclude("ok");
|
||||
});
|
||||
});
|
||||
|
||||
test("Server should not crash in clientError is emitted when calling destroy", async () => {
|
||||
await using server = http.createServer(async (req, res) => {
|
||||
res.end("Hello World");
|
||||
|
||||
@@ -821,60 +821,74 @@ for (const nodeExecutable of [nodeExe(), bunExe()]) {
|
||||
expect(typeof settings.maxHeaderListSize).toBe("number");
|
||||
expect(typeof settings.maxHeaderSize).toBe("number");
|
||||
};
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const client = http2.connect("https://www.example.com");
|
||||
client.on("error", reject);
|
||||
expect(client.connecting).toBeTrue();
|
||||
expect(client.alpnProtocol).toBeUndefined();
|
||||
expect(client.encrypted).toBeTrue();
|
||||
expect(client.closed).toBeFalse();
|
||||
expect(client.destroyed).toBeFalse();
|
||||
expect(client.originSet.length).toBe(1);
|
||||
expect(client.pendingSettingsAck).toBeTrue();
|
||||
assertSettings(client.localSettings);
|
||||
expect(client.remoteSettings).toBeNull();
|
||||
const headers = { ":path": "/" };
|
||||
const req = client.request(headers);
|
||||
expect(req.closed).toBeFalse();
|
||||
expect(req.destroyed).toBeFalse();
|
||||
// we always asign a stream id to the request
|
||||
expect(req.pending).toBeFalse();
|
||||
expect(typeof req.id).toBe("number");
|
||||
expect(req.session).toBeDefined();
|
||||
expect(req.sentHeaders).toEqual({
|
||||
":authority": "www.example.com",
|
||||
":method": "GET",
|
||||
":path": "/",
|
||||
":scheme": "https",
|
||||
const h2Server = http2.createSecureServer({ ...TLS_CERT, allowHTTP1: false });
|
||||
h2Server.on("stream", (stream, headers) => {
|
||||
stream.respond({ ":status": 200 });
|
||||
stream.end("OK");
|
||||
});
|
||||
expect(req.sentTrailers).toBeUndefined();
|
||||
expect(req.sentInfoHeaders.length).toBe(0);
|
||||
expect(req.scheme).toBe("https");
|
||||
let response_headers = null;
|
||||
req.on("response", (headers, flags) => {
|
||||
response_headers = headers;
|
||||
});
|
||||
req.resume();
|
||||
req.on("end", () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
expect(response_headers[":status"]).toBe(200);
|
||||
const settings = client.remoteSettings;
|
||||
const localSettings = client.localSettings;
|
||||
assertSettings(settings);
|
||||
assertSettings(localSettings);
|
||||
expect(settings).toEqual(client.remoteSettings);
|
||||
expect(localSettings).toEqual(client.localSettings);
|
||||
client.destroy();
|
||||
expect(client.connecting).toBeFalse();
|
||||
expect(client.alpnProtocol).toBe("h2");
|
||||
expect(client.pendingSettingsAck).toBeFalse();
|
||||
expect(client.destroyed).toBeTrue();
|
||||
expect(client.closed).toBeTrue();
|
||||
expect(req.closed).toBeTrue();
|
||||
expect(req.destroyed).toBeTrue();
|
||||
expect(req.rstCode).toBe(http2.constants.NGHTTP2_NO_ERROR);
|
||||
const { promise: listenPromise, resolve: listenResolve } = Promise.withResolvers();
|
||||
h2Server.listen(0, () => listenResolve());
|
||||
await listenPromise;
|
||||
const serverAddress = h2Server.address();
|
||||
const serverUrl = `https://localhost:${serverAddress.port}`;
|
||||
try {
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const client = http2.connect(serverUrl, TLS_OPTIONS);
|
||||
client.on("error", reject);
|
||||
expect(client.connecting).toBeTrue();
|
||||
expect(client.alpnProtocol).toBeUndefined();
|
||||
expect(client.encrypted).toBeTrue();
|
||||
expect(client.closed).toBeFalse();
|
||||
expect(client.destroyed).toBeFalse();
|
||||
expect(client.originSet.length).toBe(1);
|
||||
expect(client.pendingSettingsAck).toBeTrue();
|
||||
assertSettings(client.localSettings);
|
||||
expect(client.remoteSettings).toBeNull();
|
||||
const headers = { ":path": "/" };
|
||||
const req = client.request(headers);
|
||||
expect(req.closed).toBeFalse();
|
||||
expect(req.destroyed).toBeFalse();
|
||||
// we always asign a stream id to the request
|
||||
expect(req.pending).toBeFalse();
|
||||
expect(typeof req.id).toBe("number");
|
||||
expect(req.session).toBeDefined();
|
||||
expect(req.sentHeaders).toEqual({
|
||||
":authority": `localhost:${serverAddress.port}`,
|
||||
":method": "GET",
|
||||
":path": "/",
|
||||
":scheme": "https",
|
||||
});
|
||||
expect(req.sentTrailers).toBeUndefined();
|
||||
expect(req.sentInfoHeaders.length).toBe(0);
|
||||
expect(req.scheme).toBe("https");
|
||||
let response_headers = null;
|
||||
req.on("response", (headers, flags) => {
|
||||
response_headers = headers;
|
||||
});
|
||||
req.resume();
|
||||
req.on("end", () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
expect(response_headers[":status"]).toBe(200);
|
||||
const settings = client.remoteSettings;
|
||||
const localSettings = client.localSettings;
|
||||
assertSettings(settings);
|
||||
assertSettings(localSettings);
|
||||
expect(settings).toEqual(client.remoteSettings);
|
||||
expect(localSettings).toEqual(client.localSettings);
|
||||
client.destroy();
|
||||
expect(client.connecting).toBeFalse();
|
||||
expect(client.alpnProtocol).toBe("h2");
|
||||
expect(client.pendingSettingsAck).toBeFalse();
|
||||
expect(client.destroyed).toBeTrue();
|
||||
expect(client.closed).toBeTrue();
|
||||
expect(req.closed).toBeTrue();
|
||||
expect(req.destroyed).toBeTrue();
|
||||
expect(req.rstCode).toBe(http2.constants.NGHTTP2_NO_ERROR);
|
||||
} finally {
|
||||
h2Server.close();
|
||||
}
|
||||
});
|
||||
it("ping events should work", async () => {
|
||||
await using server = await nodeEchoServer(paddingStrategy);
|
||||
|
||||
@@ -577,19 +577,27 @@ describe("fetch", () => {
|
||||
});
|
||||
|
||||
it.concurrent('redirect: "follow"', async () => {
|
||||
using target = Bun.serve({
|
||||
port: 0,
|
||||
tls,
|
||||
fetch() {
|
||||
return new Response("redirected!");
|
||||
},
|
||||
});
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(req) {
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: "https://example.com",
|
||||
Location: target.url.href,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
const response = await fetch(`http://${server.hostname}:${server.port}`, {
|
||||
redirect: "follow",
|
||||
tls: { ca: tls.cert },
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("location")).toBe(null);
|
||||
@@ -734,8 +742,15 @@ it.concurrent("simultaneous HTTPS fetch", async () => {
|
||||
});
|
||||
|
||||
it.concurrent("website with tlsextname", async () => {
|
||||
// irony
|
||||
await fetch("https://bun.sh", { method: "HEAD" });
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls,
|
||||
fetch() {
|
||||
return new Response("OK");
|
||||
},
|
||||
});
|
||||
const resp = await fetch(server.url, { method: "HEAD", tls: { ca: tls.cert } });
|
||||
expect(resp.status).toBe(200);
|
||||
});
|
||||
|
||||
function testBlobInterface(blobbyConstructor: { (..._: any[]): any }, hasBlobFn?: boolean) {
|
||||
|
||||
@@ -30,94 +30,110 @@ async function createServer(cert: TLSOptions, callback: (port: number) => Promis
|
||||
|
||||
describe.concurrent("fetch-tls", () => {
|
||||
it("can handle multiple requests with non native checkServerIdentity", async () => {
|
||||
async function request() {
|
||||
let called = false;
|
||||
const result = await fetch("https://www.example.com", {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
checkServerIdentity(hostname: string, cert: tls.PeerCertificate) {
|
||||
called = true;
|
||||
return tls.checkServerIdentity(hostname, cert);
|
||||
},
|
||||
},
|
||||
}).then((res: Response) => res.blob());
|
||||
expect(result?.size).toBeGreaterThan(0);
|
||||
expect(called).toBe(true);
|
||||
}
|
||||
const promises = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
promises.push(request());
|
||||
}
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
it("fetch with valid tls should not throw", async () => {
|
||||
const promises = [`https://example.com`, `https://www.example.com`].map(async url => {
|
||||
const result = await fetch(url, { keepalive: false }).then((res: Response) => res.blob());
|
||||
expect(result?.size).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
it("fetch with valid tls and non-native checkServerIdentity should work", async () => {
|
||||
for (const isBusy of [true, false]) {
|
||||
let count = 0;
|
||||
const promises = [`https://example.com`, `https://www.example.com`].map(async url => {
|
||||
await fetch(url, {
|
||||
await createServer(CERT_LOCALHOST_IP, async port => {
|
||||
async function request() {
|
||||
let called = false;
|
||||
const result = await fetch(`https://localhost:${port}`, {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
ca: validTls.cert,
|
||||
checkServerIdentity(hostname: string, cert: tls.PeerCertificate) {
|
||||
count++;
|
||||
expect(url).toContain(hostname);
|
||||
called = true;
|
||||
return tls.checkServerIdentity(hostname, cert);
|
||||
},
|
||||
},
|
||||
}).then((res: Response) => res.blob());
|
||||
});
|
||||
if (isBusy) {
|
||||
const start = performance.now();
|
||||
while (performance.now() - start < 500) {}
|
||||
expect(result?.size).toBeGreaterThan(0);
|
||||
expect(called).toBe(true);
|
||||
}
|
||||
const promises = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
promises.push(request());
|
||||
}
|
||||
await Promise.all(promises);
|
||||
expect(count).toBe(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("fetch with valid tls should not throw", async () => {
|
||||
await createServer(CERT_LOCALHOST_IP, async port => {
|
||||
const urls = [`https://localhost:${port}`, `https://127.0.0.1:${port}`];
|
||||
const promises = urls.map(async url => {
|
||||
const result = await fetch(url, { keepalive: false, tls: { ca: validTls.cert } }).then((res: Response) =>
|
||||
res.blob(),
|
||||
);
|
||||
expect(result?.size).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
});
|
||||
});
|
||||
|
||||
it("fetch with valid tls and non-native checkServerIdentity should work", async () => {
|
||||
let count = 0;
|
||||
const promises = [`https://example.com`, `https://www.example.com`].map(async url => {
|
||||
await fetch(url, {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
checkServerIdentity(hostname: string, cert: tls.PeerCertificate) {
|
||||
count++;
|
||||
expect(url).toContain(hostname);
|
||||
throw new Error("CustomError");
|
||||
},
|
||||
},
|
||||
});
|
||||
await createServer(CERT_LOCALHOST_IP, async port => {
|
||||
for (const isBusy of [true, false]) {
|
||||
let count = 0;
|
||||
const urls = [`https://localhost:${port}`, `https://127.0.0.1:${port}`];
|
||||
const promises = urls.map(async url => {
|
||||
await fetch(url, {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
ca: validTls.cert,
|
||||
checkServerIdentity(hostname: string, cert: tls.PeerCertificate) {
|
||||
count++;
|
||||
return tls.checkServerIdentity(hostname, cert);
|
||||
},
|
||||
},
|
||||
}).then((res: Response) => res.blob());
|
||||
});
|
||||
if (isBusy) {
|
||||
const start = performance.now();
|
||||
while (performance.now() - start < 500) {}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
expect(count).toBe(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("fetch with valid tls and non-native checkServerIdentity that throws should reject", async () => {
|
||||
await createServer(CERT_LOCALHOST_IP, async port => {
|
||||
let count = 0;
|
||||
const urls = [`https://localhost:${port}`, `https://127.0.0.1:${port}`];
|
||||
const promises = urls.map(async url => {
|
||||
await fetch(url, {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
ca: validTls.cert,
|
||||
checkServerIdentity(hostname: string, cert: tls.PeerCertificate) {
|
||||
count++;
|
||||
throw new Error("CustomError");
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
const start = performance.now();
|
||||
while (performance.now() - start < 1000) {}
|
||||
expect((await Promise.allSettled(promises)).every(p => p.status === "rejected")).toBe(true);
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
const start = performance.now();
|
||||
while (performance.now() - start < 1000) {}
|
||||
expect((await Promise.allSettled(promises)).every(p => p.status === "rejected")).toBe(true);
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
|
||||
it("fetch with rejectUnauthorized: false should not call checkServerIdentity", async () => {
|
||||
let count = 0;
|
||||
await createServer(CERT_LOCALHOST_IP, async port => {
|
||||
let count = 0;
|
||||
|
||||
await fetch("https://example.com", {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
rejectUnauthorized: false,
|
||||
checkServerIdentity(hostname: string, cert: tls.PeerCertificate) {
|
||||
count++;
|
||||
return tls.checkServerIdentity(hostname, cert);
|
||||
await fetch(`https://localhost:${port}`, {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
rejectUnauthorized: false,
|
||||
checkServerIdentity(hostname: string, cert: tls.PeerCertificate) {
|
||||
count++;
|
||||
return tls.checkServerIdentity(hostname, cert);
|
||||
},
|
||||
},
|
||||
},
|
||||
}).then((res: Response) => res.blob());
|
||||
expect(count).toBe(0);
|
||||
}).then((res: Response) => res.blob());
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("fetch with self-sign tls should throw", async () => {
|
||||
@@ -152,20 +168,23 @@ describe.concurrent("fetch-tls", () => {
|
||||
});
|
||||
|
||||
it("fetch with checkServerIdentity failing should throw", async () => {
|
||||
try {
|
||||
await fetch(`https://example.com`, {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
checkServerIdentity() {
|
||||
return new Error("CustomError");
|
||||
await createServer(CERT_LOCALHOST_IP, async port => {
|
||||
try {
|
||||
await fetch(`https://localhost:${port}`, {
|
||||
keepalive: false,
|
||||
tls: {
|
||||
ca: validTls.cert,
|
||||
checkServerIdentity() {
|
||||
return new Error("CustomError");
|
||||
},
|
||||
},
|
||||
},
|
||||
}).then((res: Response) => res.blob());
|
||||
}).then((res: Response) => res.blob());
|
||||
|
||||
expect.unreachable();
|
||||
} catch (e: any) {
|
||||
expect(e.message).toBe("CustomError");
|
||||
}
|
||||
expect.unreachable();
|
||||
} catch (e: any) {
|
||||
expect(e.message).toBe("CustomError");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("fetch with self-sign certificate tls + rejectUnauthorized: false should not throw", async () => {
|
||||
|
||||
@@ -883,253 +883,4 @@ describe("Structured Clone Fast Path", () => {
|
||||
port1.close();
|
||||
port2.close();
|
||||
});
|
||||
|
||||
// === TypedArray fast path tests ===
|
||||
|
||||
const typedArrayCtors = [
|
||||
{ name: "Uint8Array", ctor: Uint8Array, values: [0, 1, 127, 255] },
|
||||
{ name: "Int8Array", ctor: Int8Array, values: [-128, -1, 0, 1, 127] },
|
||||
{ name: "Uint8ClampedArray", ctor: Uint8ClampedArray, values: [0, 1, 127, 255] },
|
||||
{ name: "Uint16Array", ctor: Uint16Array, values: [0, 1, 256, 65535] },
|
||||
{ name: "Int16Array", ctor: Int16Array, values: [-32768, -1, 0, 1, 32767] },
|
||||
{ name: "Uint32Array", ctor: Uint32Array, values: [0, 1, 65536, 4294967295] },
|
||||
{ name: "Int32Array", ctor: Int32Array, values: [-2147483648, -1, 0, 1, 2147483647] },
|
||||
{ name: "Float32Array", ctor: Float32Array, values: [0, 1.5, -1.5, 3.4028234663852886e38] },
|
||||
{ name: "Float64Array", ctor: Float64Array, values: [0, 1.5, -1.5, Number.MAX_VALUE, Number.MIN_VALUE] },
|
||||
] as const;
|
||||
|
||||
for (const { name, ctor, values } of typedArrayCtors) {
|
||||
test(`structuredClone(${name}) basic values`, () => {
|
||||
const input = new ctor(values as any);
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(ctor);
|
||||
expect(cloned).toEqual(input);
|
||||
expect(cloned.buffer).not.toBe(input.buffer);
|
||||
});
|
||||
}
|
||||
|
||||
test("structuredClone(BigInt64Array) basic values", () => {
|
||||
const input = new BigInt64Array([-9223372036854775808n, -1n, 0n, 1n, 9223372036854775807n]);
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(BigInt64Array);
|
||||
expect(cloned).toEqual(input);
|
||||
expect(cloned.buffer).not.toBe(input.buffer);
|
||||
});
|
||||
|
||||
test("structuredClone(BigUint64Array) basic values", () => {
|
||||
const input = new BigUint64Array([0n, 1n, 18446744073709551615n]);
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(BigUint64Array);
|
||||
expect(cloned).toEqual(input);
|
||||
expect(cloned.buffer).not.toBe(input.buffer);
|
||||
});
|
||||
|
||||
test("structuredClone(Float16Array) basic values", () => {
|
||||
const input = new Float16Array([0, 1.5, -1.5, 65504]);
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(Float16Array);
|
||||
expect(cloned).toEqual(input);
|
||||
expect(cloned.buffer).not.toBe(input.buffer);
|
||||
});
|
||||
|
||||
test("structuredClone empty TypedArray", () => {
|
||||
const input = new Uint8Array(0);
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(Uint8Array);
|
||||
expect(cloned.length).toBe(0);
|
||||
expect(cloned.byteLength).toBe(0);
|
||||
});
|
||||
|
||||
test("structuredClone large TypedArray (1MB)", () => {
|
||||
const input = new Uint8Array(1024 * 1024);
|
||||
for (let i = 0; i < input.length; i++) input[i] = i & 0xff;
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(Uint8Array);
|
||||
expect(cloned.length).toBe(input.length);
|
||||
expect(cloned).toEqual(input);
|
||||
expect(cloned.buffer).not.toBe(input.buffer);
|
||||
});
|
||||
|
||||
test("structuredClone Float64Array with special values", () => {
|
||||
const input = new Float64Array([NaN, Infinity, -Infinity, -0, 0]);
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(Float64Array);
|
||||
expect(cloned[0]).toBeNaN();
|
||||
expect(cloned[1]).toBe(Infinity);
|
||||
expect(cloned[2]).toBe(-Infinity);
|
||||
expect(Object.is(cloned[3], -0)).toBe(true);
|
||||
expect(cloned[4]).toBe(0);
|
||||
});
|
||||
|
||||
test("structuredClone Float32Array with special values", () => {
|
||||
const input = new Float32Array([NaN, Infinity, -Infinity, -0]);
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(Float32Array);
|
||||
expect(cloned[0]).toBeNaN();
|
||||
expect(cloned[1]).toBe(Infinity);
|
||||
expect(cloned[2]).toBe(-Infinity);
|
||||
expect(Object.is(cloned[3], -0)).toBe(true);
|
||||
});
|
||||
|
||||
test("structuredClone TypedArray creates independent copy", () => {
|
||||
const input = new Uint8Array([1, 2, 3, 4, 5]);
|
||||
const cloned = structuredClone(input);
|
||||
cloned[0] = 255;
|
||||
expect(input[0]).toBe(1);
|
||||
input[1] = 200;
|
||||
expect(cloned[1]).toBe(2);
|
||||
});
|
||||
|
||||
test("structuredClone DataView falls back to slow path but works correctly", () => {
|
||||
const buf = new ArrayBuffer(8);
|
||||
const view = new DataView(buf);
|
||||
view.setFloat64(0, 3.14);
|
||||
const cloned = structuredClone(view);
|
||||
expect(cloned).toBeInstanceOf(DataView);
|
||||
expect(cloned.getFloat64(0)).toBe(3.14);
|
||||
expect(cloned.buffer).not.toBe(buf);
|
||||
});
|
||||
|
||||
test("structuredClone TypedArray slice view falls back to slow path", () => {
|
||||
const buf = new ArrayBuffer(16);
|
||||
new Uint8Array(buf).set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
|
||||
const sliceView = new Uint8Array(buf, 4, 4);
|
||||
const cloned = structuredClone(sliceView);
|
||||
expect(cloned).toBeInstanceOf(Uint8Array);
|
||||
expect(cloned).toEqual(new Uint8Array([4, 5, 6, 7]));
|
||||
// structuredClone clones the full backing ArrayBuffer, preserving byteOffset
|
||||
expect(cloned.byteOffset).toBe(4);
|
||||
expect(cloned.buffer.byteLength).toBe(16);
|
||||
});
|
||||
|
||||
test("structuredClone TypedArray with named properties falls back to slow path", () => {
|
||||
const input = new Uint8Array([1, 2, 3]) as any;
|
||||
input.customProp = "hello";
|
||||
// Named properties on TypedArray are not cloneable via structuredClone,
|
||||
// the slow path handles this correctly (ignores them)
|
||||
const cloned = structuredClone(input);
|
||||
expect(cloned).toBeInstanceOf(Uint8Array);
|
||||
expect(cloned).toEqual(new Uint8Array([1, 2, 3]));
|
||||
});
|
||||
|
||||
test("structuredClone detached TypedArray throws DataCloneError", () => {
|
||||
const buf = new ArrayBuffer(8);
|
||||
const input = new Uint8Array(buf);
|
||||
// Detach the buffer by transferring it
|
||||
structuredClone(buf, { transfer: [buf] });
|
||||
expect(() => structuredClone(input)).toThrow();
|
||||
});
|
||||
|
||||
test("postMessage TypedArray via MessageChannel", async () => {
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
const input = new Uint8Array([10, 20, 30, 40, 50]);
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
port2.onmessage = (e: MessageEvent) => resolve(e.data);
|
||||
port1.postMessage(input);
|
||||
const result = await promise;
|
||||
expect(result).toBeInstanceOf(Uint8Array);
|
||||
expect(result).toEqual(input);
|
||||
port1.close();
|
||||
port2.close();
|
||||
});
|
||||
|
||||
test("postMessage Float64Array via MessageChannel", async () => {
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
const input = new Float64Array([1.1, 2.2, 3.3, NaN, Infinity]);
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
port2.onmessage = (e: MessageEvent) => resolve(e.data);
|
||||
port1.postMessage(input);
|
||||
const result = await promise;
|
||||
expect(result).toBeInstanceOf(Float64Array);
|
||||
expect(result[0]).toBe(1.1);
|
||||
expect(result[1]).toBe(2.2);
|
||||
expect(result[2]).toBe(3.3);
|
||||
expect(result[3]).toBeNaN();
|
||||
expect(result[4]).toBe(Infinity);
|
||||
port1.close();
|
||||
port2.close();
|
||||
});
|
||||
|
||||
test("postMessage BigInt64Array via MessageChannel", async () => {
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
const input = new BigInt64Array([0n, -1n, 9223372036854775807n]);
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
port2.onmessage = (e: MessageEvent) => resolve(e.data);
|
||||
port1.postMessage(input);
|
||||
const result = await promise;
|
||||
expect(result).toBeInstanceOf(BigInt64Array);
|
||||
expect(result).toEqual(input);
|
||||
port1.close();
|
||||
port2.close();
|
||||
});
|
||||
|
||||
test("structuredClone TypedArray backed by SharedArrayBuffer falls back to slow path", () => {
|
||||
const sab = new SharedArrayBuffer(16);
|
||||
const view = new Uint8Array(sab);
|
||||
view.set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||
const cloned = structuredClone(view);
|
||||
expect(cloned).toBeInstanceOf(Uint8Array);
|
||||
expect(cloned).toEqual(view);
|
||||
// The cloned view should NOT share memory with the original
|
||||
expect(cloned.buffer).not.toBe(sab);
|
||||
// Verify independence: modifying original doesn't affect clone
|
||||
view[0] = 255;
|
||||
expect(cloned[0]).toBe(1);
|
||||
});
|
||||
|
||||
test("structuredClone Int32Array backed by SharedArrayBuffer preserves values", () => {
|
||||
const sab = new SharedArrayBuffer(16);
|
||||
const view = new Int32Array(sab);
|
||||
view.set([100, 200, 300, 400]);
|
||||
const cloned = structuredClone(view);
|
||||
expect(cloned).toBeInstanceOf(Int32Array);
|
||||
expect(cloned).toEqual(new Int32Array([100, 200, 300, 400]));
|
||||
expect(cloned.buffer).not.toBe(sab);
|
||||
});
|
||||
|
||||
test("postMessage TypedArray backed by SharedArrayBuffer via MessageChannel", async () => {
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
const sab = new SharedArrayBuffer(8);
|
||||
const input = new Uint8Array(sab);
|
||||
input.set([10, 20, 30, 40, 50, 60, 70, 80]);
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
port2.onmessage = (e: MessageEvent) => resolve(e.data);
|
||||
port1.postMessage(input);
|
||||
const result = await promise;
|
||||
expect(result).toBeInstanceOf(Uint8Array);
|
||||
expect(result).toEqual(input);
|
||||
port1.close();
|
||||
port2.close();
|
||||
});
|
||||
|
||||
test("structuredClone partial-buffer TypedArray with byteOffset==0 preserves full buffer", () => {
|
||||
// new Uint8Array(buf, 0, 8) over a 16-byte buffer: byteOffset is 0 but
|
||||
// the view only covers the first half. The slow path clones the entire
|
||||
// backing ArrayBuffer and preserves byteOffset/byteLength.
|
||||
const buf = new ArrayBuffer(16);
|
||||
const full = new Uint8Array(buf);
|
||||
full.set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
|
||||
const partial = new Uint8Array(buf, 0, 8);
|
||||
|
||||
const cloned = structuredClone(partial);
|
||||
expect(cloned).toBeInstanceOf(Uint8Array);
|
||||
expect(cloned).toEqual(new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]));
|
||||
expect(cloned.byteOffset).toBe(0);
|
||||
expect(cloned.byteLength).toBe(8);
|
||||
// The cloned buffer must preserve the full backing ArrayBuffer size
|
||||
expect(cloned.buffer.byteLength).toBe(16);
|
||||
});
|
||||
|
||||
test("structuredClone partial-buffer Int32Array with byteOffset==0 preserves full buffer", () => {
|
||||
const buf = new ArrayBuffer(32);
|
||||
const partial = new Int32Array(buf, 0, 4); // 16 bytes out of 32
|
||||
partial.set([100, 200, 300, 400]);
|
||||
|
||||
const cloned = structuredClone(partial);
|
||||
expect(cloned).toBeInstanceOf(Int32Array);
|
||||
expect(cloned).toEqual(new Int32Array([100, 200, 300, 400]));
|
||||
expect(cloned.byteOffset).toBe(0);
|
||||
expect(cloned.byteLength).toBe(16);
|
||||
expect(cloned.buffer.byteLength).toBe(32);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -398,6 +398,71 @@ describe("WebSocket wss:// through HTTP proxy (TLS tunnel)", () => {
|
||||
expect(messages).toContain("hello via tls tunnel");
|
||||
gc();
|
||||
});
|
||||
|
||||
test("server-initiated ping survives through TLS tunnel proxy", async () => {
|
||||
// Regression test: sendPong checked socket.isClosed() on the detached tcp
|
||||
// field instead of using hasTCP(). For wss:// through HTTP proxy, the
|
||||
// WebSocket uses initWithTunnel which sets tcp = detached (all I/O goes
|
||||
// through proxy_tunnel). Detached sockets return true for isClosed(), so
|
||||
// sendPong would immediately dispatch a 1006 close instead of sending the
|
||||
// pong through the tunnel.
|
||||
using pingServer = Bun.serve({
|
||||
port: 0,
|
||||
tls: {
|
||||
key: tlsCerts.key,
|
||||
cert: tlsCerts.cert,
|
||||
},
|
||||
fetch(req, server) {
|
||||
if (server.upgrade(req)) return;
|
||||
return new Response("Expected WebSocket", { status: 400 });
|
||||
},
|
||||
websocket: {
|
||||
message(ws, message) {
|
||||
if (String(message) === "ready") {
|
||||
// Send a ping after the client confirms it's connected.
|
||||
// On the buggy code path, this triggers sendPong on the detached
|
||||
// socket → dispatchAbruptClose → 1006.
|
||||
ws.ping();
|
||||
// Follow up with a text message. If the client receives this,
|
||||
// the connection survived the ping/pong exchange.
|
||||
ws.send("after-ping");
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
||||
|
||||
const ws = new WebSocket(`wss://127.0.0.1:${pingServer.port}`, {
|
||||
proxy: `http://127.0.0.1:${proxyPort}`,
|
||||
tls: { rejectUnauthorized: false },
|
||||
});
|
||||
|
||||
ws.onopen = () => {
|
||||
ws.send("ready");
|
||||
};
|
||||
|
||||
ws.onmessage = event => {
|
||||
if (String(event.data) === "after-ping") {
|
||||
ws.close(1000);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = event => {
|
||||
if (event.code === 1000) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Unexpected close code: ${event.code}`));
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = event => {
|
||||
reject(event);
|
||||
};
|
||||
|
||||
await promise;
|
||||
gc();
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket through HTTPS proxy (TLS proxy)", () => {
|
||||
|
||||
@@ -109,13 +109,22 @@ describe("HTMLRewriter", () => {
|
||||
await gcTick();
|
||||
let content;
|
||||
{
|
||||
using contentServer = Bun.serve({
|
||||
port: 0,
|
||||
fetch(req) {
|
||||
return new Response("<h1>Hello from content server</h1>", {
|
||||
headers: { "Content-Type": "text/html" },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(req) {
|
||||
return new HTMLRewriter()
|
||||
.on("div", {
|
||||
async element(element) {
|
||||
content = await fetch("https://www.example.com/").then(res => res.text());
|
||||
content = await fetch(`http://localhost:${contentServer.port}/`).then(res => res.text());
|
||||
element.setInnerContent(content, { html: true });
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2119,6 +2119,35 @@ static napi_value test_napi_create_tsfn_async_context_frame(const Napi::Callback
|
||||
return env.Undefined();
|
||||
}
|
||||
|
||||
// Test for BUN-1PYR: napi_typeof should not crash when given an invalid
|
||||
// napi_value that is actually a raw C string pointer. This simulates the
|
||||
// scenario where a native module passes garbage data (e.g., a string pointer
|
||||
// like "Tensor ...") as a napi_value to napi_typeof.
|
||||
static napi_value test_napi_typeof_invalid_pointer(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
// Simulate the exact crash scenario: a C string pointer reinterpreted as napi_value.
|
||||
// The crash address 0x6F20726F736E6554 decoded to ASCII is "Tensor o",
|
||||
// meaning a string pointer was being used as a JSValue.
|
||||
// Use aligned_alloc to ensure 16-byte alignment (bit 3 = 0), so the pointer
|
||||
// goes through the MarkedBlock validation path (not the PreciseAllocation path).
|
||||
char *fake_string = static_cast<char *>(aligned_alloc(16, 64));
|
||||
memcpy(fake_string, "Tensor operation test string", 29);
|
||||
napi_value bad_value = reinterpret_cast<napi_value>(fake_string);
|
||||
|
||||
napi_valuetype type;
|
||||
napi_status status = napi_typeof(env, bad_value, &type);
|
||||
|
||||
if (status != napi_ok) {
|
||||
printf("PASS: napi_typeof returned error status %d for invalid pointer\n", status);
|
||||
} else {
|
||||
printf("PASS: napi_typeof did not crash for invalid pointer (returned type %d)\n", type);
|
||||
}
|
||||
|
||||
free(fake_string);
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
void register_standalone_tests(Napi::Env env, Napi::Object exports) {
|
||||
REGISTER_FUNCTION(env, exports, test_issue_7685);
|
||||
REGISTER_FUNCTION(env, exports, test_issue_11949);
|
||||
@@ -2157,6 +2186,7 @@ void register_standalone_tests(Napi::Env env, Napi::Object exports) {
|
||||
REGISTER_FUNCTION(env, exports, test_issue_25933);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_make_callback_async_context_frame);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_create_tsfn_async_context_frame);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_typeof_invalid_pointer);
|
||||
}
|
||||
|
||||
} // namespace napitests
|
||||
|
||||
@@ -822,6 +822,24 @@ describe("cleanup hooks", () => {
|
||||
expect(output).toContain("PASS: napi_create_threadsafe_function accepted AsyncContextFrame");
|
||||
});
|
||||
|
||||
it("should not crash when given an invalid pointer as napi_value", async () => {
|
||||
// Regression test for BUN-1PYR: napi_typeof segfaults when a native
|
||||
// module passes a raw C string pointer as napi_value. The crash address
|
||||
// 0x6F20726F736E6554 decoded to "Tensor o", indicating string data was
|
||||
// being dereferenced as a JSValue.
|
||||
const { BUN_INSPECT_CONNECT_TO: _, ...rest } = bunEnv;
|
||||
await using exec = spawn({
|
||||
cmd: [bunExe(), "--expose-gc", join(__dirname, "napi-app/main.js"), "test_napi_typeof_invalid_pointer", "[]"],
|
||||
env: rest,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout, exitCode] = await Promise.all([new Response(exec.stdout).text(), exec.exited]);
|
||||
// napi_typeof should return an error status instead of crashing
|
||||
expect(stdout).toContain("PASS");
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
it("should return napi_object for boxed primitives (String, Number, Boolean)", async () => {
|
||||
// Regression test for https://github.com/oven-sh/bun/issues/25351
|
||||
// napi_typeof was incorrectly returning napi_string for String objects (new String("hello"))
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCIzOJskt6VkEJY
|
||||
XKSJv/Gdil3XYkjk3NVc/+m+kzqnkTRbPtT9w+IGWgmJhuf9DJPLCwHFAEFarVwV
|
||||
x16Q0PbU4ajXaLRHEYGhrH10oTMjQnJ24xVm26mxRXPQa5vaLpWJqNyIdNLIQLe+
|
||||
UXUOzSGGsFTRMAjvYrkzjBe4ZUnaZV+aFY/ug0jfzeA1dJjzKZs6+yTJRbsuWUEb
|
||||
8MsDmT4v+kBZDKdaDn7AFDWRVqx/38BnqsRzkM0CxpnyT2kRzw5zQajIE13gdTJo
|
||||
1EHvYSUkkxrY5m30Rl9BuBBZBjhMzOHq0fYVVooHO+sf4XHPgvFTTxJum85u7J1J
|
||||
oEUjrLKtAgMBAAECggEACInVNhaiqu4infZGVMy0rXMV8VwSlapM7O2SLtFsr0nK
|
||||
XUmaLK6dvGzBPKK9dxdiYCFzPlMKQTkhzsAvYFWSmm3tRmikG+11TFyCRhXLpc8/
|
||||
ark4vD9Io6ZkmKUmyKLwtXNjNGcqQtJ7RXc7Ga3nAkueN6JKZHqieZusXVeBGQ70
|
||||
YH1LKyVNBeJggbj+g9rqaksPyNJQ8EWiNTJkTRQPazZ0o1VX/fzDFyr/a5npFtHl
|
||||
4BHfafv9o1Xyr70Kie8CYYRJNViOCN+ylFs7Gd3XRaAkSkgMT/7DzrHdEM2zrrHK
|
||||
yNg2gyDVX9UeEJG2X5UtU0o9BVW7WBshz/2hqIUHoQKBgQC8zsRFvC7u/rGr5vRR
|
||||
mhZZG+Wvg03/xBSuIgOrzm+Qie6mAzOdVmfSL/pNV9EFitXt1yd2ROo31AbS7Evy
|
||||
Bm/QVKr2mBlmLgov3B7O/e6ABteooOL7769qV/v+yo8VdEg0biHmsfGIIXDe3Lwl
|
||||
OT0XwF9r/SeZLbw1zfkSsUVG/QKBgQC5fANM3Dc9LEek+6PHv5+eC1cKkyioEjUl
|
||||
/y1VUD00aABI1TUcdLF3BtFN2t/S6HW0hrP3KwbcUfqC25k+GDLh1nM6ZK/gI3Yn
|
||||
IGtCHxtE3S6jKhE9QcK/H+PzGVKWge9SezeYRP0GHJYDrTVTA8Kt9HgoZPPeReJl
|
||||
+Ss9c8ThcQKBgECX6HQHFnNzNSufXtSQB7dCoQizvjqTRZPxVRoxDOABIGExVTYt
|
||||
umUhPtu5AGyJ+/hblEeU+iBRbGg6qRzK8PPwE3E7xey8MYYAI5YjL7YjISKysBUL
|
||||
AhM6uJ6Jg/wOBSnSx8xZ8kzlS+0izUda1rjKeprCSArSp8IsjlrDxPStAoGAEcPr
|
||||
+P+altRX5Fhpvmb/Hb8OTif8G+TqjEIdkG9H/W38oP0ywg/3M2RGxcMx7txu8aR5
|
||||
NjI7zPxZFxF7YvQkY3cLwEsGgVxEI8k6HLIoBXd90Qjlb82NnoqqZY1GWL4HMwo0
|
||||
L/Rjm6M/Rwje852Hluu0WoIYzXA6F/Q+jPs6nzECgYAxx4IbDiGXuenkwSF1SUyj
|
||||
NwJXhx4HDh7U6EO/FiPZE5BHE3BoTrFu3o1lzverNk7G3m+j+m1IguEAalHlukYl
|
||||
rip9iUISlKYqbYZdLBoLwHAfHhszdrjqn8/v6oqbB5yR3HXjPFUWJo0WJ2pqJp56
|
||||
ZshgmQQ/5Khoj6x0/dMPSg==
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCt7iqkEIco372h
|
||||
v19q0zjaYbm6gzxEnR45UjpQYqgztq4QHicD80mqIkCBCYknFxhwxhNn+Y3g5RWQ
|
||||
dReplpQbkneqRVp+qixMvu2FmOA4zRRoqObP7FyF1Yusvmroe0Y9SP2xTTmA9Zo7
|
||||
3paywPUIuZ9eKGwIiFTtj1yQ1FdghLhzZgxcf3LHEHRkGnxgxxNITFxh4nd6fGIj
|
||||
NqM5fQAY8z35lMXdeWjrhtaqgFYB+Z20YY0X7LJx39vYao0wqW8sZjX88TqHI1zX
|
||||
WLpUk6UK9RqaNza5xc80wV+9/zjhr3dc1FRjBxI1DS/ufo33dUfvilxv9/LtWwUn
|
||||
KfKLns9LAgMBAAECggEAAacPHM2G7GBIm/9rCr6tvihNgD8M685zOOZAqGYn9CqY
|
||||
cYHC4gtF/L2U6CBj2pNAoCwo3LXUkD+6r7MYKXAgqQg3HTCM4rwFbhD1rU8FVHfh
|
||||
OL0QwwZ2ut95DVdjoxTAlEN9ZcdSFc//llMJ1cF8lxoVvKFc4cv3uCI2mcaJk858
|
||||
iABfJLl3yfdv1xtpAuOfXf66sXbAmn5NQfN0qTEg2iOdgb4BUee5Wb35MakDQb6+
|
||||
/s7/bWB+ublZzYt12ChIh1jkBBHaGyQ8mFnPj99ZAJdFjAzi6ydoJ0a2rCVY7Ugs
|
||||
bkhnzDUtAaHKxo9JXaqIwbUaVFkX8dDhbg82dJrWUQKBgQDb7hNR0bJFW845N19M
|
||||
74p2PM+0dIiVzwxAg4E2dXDVe39awO/tw8Vu1o1+NPFhWAzGcidP7pAHmPEgRTVO
|
||||
7LA2P3CDXpkAEx5E0QW6QWZGqHfSa3+P1AvetvAV+OxtlDphcNeLApY16TUVOKZg
|
||||
SZlxW2e0dZylbHewgLBTIV9wUQKBgQDKdML+JD18WfenPeowsw8HzKdaw01iGiV1
|
||||
fvTjEXu6YxPPynWFMuj5gjBQodXM2vv0EsQBAPKYfe0nzRFL2kNuYs7TLoaNxqkp
|
||||
DNfJ2Ww5OSg7Mp76XgppeKKlsXLyUMYHHrDh6MRi5jvWtiHRpaNmV3cHMRs22c+B
|
||||
cqKP5Zma2wKBgCPNnS2Lsrbh3C+qWQRgVq0q9zFMa1PgEgGKpwVjlwvaAACZOjX9
|
||||
0e1aVkx+d/E98U55FPdJQf9Koa58NdJ0a7dZGor4YnYFpr7TPFh2/xxvnpoN0AVt
|
||||
IsWOCIW7MVohcGOeiChkMmnyXibnQwaX1LgEhlx1bRvtDYsZWBsgarYRAoGAARvo
|
||||
oYnDSHYZtDHToZapg2pslEOzndD02ZLrdn73BYtbZWz/fc5MlmlPKHHqgOfGL40W
|
||||
w8akjY9LCEfIS3kTm3wxE9kSZZ5r+MyYNgPZ4upcPQ7G7iortm4xveSd85PbsdhK
|
||||
McKbqMsIEuIGh2Z34ayi+0galQ9WYqglGdKxJ7cCgYEAuSPBHa+en0xaraZNRvMk
|
||||
OfV9Su/wrpR3TXSeo0E1mZHLwq1JwulpfO1SjxTH5uOJtG0tusl122wfm0KjrXUO
|
||||
vG5/It+X4u1Nv9oWj+z1+EV4fQrQ/Coqcc1r+5w1yzfURkKlHh74jbK5Yy/KfXrE
|
||||
eqbbJD40tKhY8ho15D3iCSo=
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID5jCCAs6gAwIBAgIUN7coIsdMcLo9amZfkwogu0YkeLEwDQYJKoZIhvcNAQEL
|
||||
MIIEDDCCAvSgAwIBAgIUbddWE2woW5e96uC4S2fd2M0AsFAwDQYJKoZIhvcNAQEL
|
||||
BQAwfjELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVN0YXRlMREwDwYDVQQHDAhMb2Nh
|
||||
dGlvbjEaMBgGA1UECgwRT3JnYW5pemF0aW9uIE5hbWUxHDAaBgNVBAsME09yZ2Fu
|
||||
aXphdGlvbmFsIFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzA5MjExNDE2
|
||||
MjNaFw0yNDA5MjAxNDE2MjNaMH4xCzAJBgNVBAYTAlNFMQ4wDAYDVQQIDAVTdGF0
|
||||
aXphdGlvbmFsIFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNjAyMTMyMzEx
|
||||
MjlaFw0zNjAyMTEyMzExMjlaMH4xCzAJBgNVBAYTAlNFMQ4wDAYDVQQIDAVTdGF0
|
||||
ZTERMA8GA1UEBwwITG9jYXRpb24xGjAYBgNVBAoMEU9yZ2FuaXphdGlvbiBOYW1l
|
||||
MRwwGgYDVQQLDBNPcmdhbml6YXRpb25hbCBVbml0MRIwEAYDVQQDDAlsb2NhbGhv
|
||||
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCIzOJskt6VkEJYXKSJ
|
||||
v/Gdil3XYkjk3NVc/+m+kzqnkTRbPtT9w+IGWgmJhuf9DJPLCwHFAEFarVwVx16Q
|
||||
0PbU4ajXaLRHEYGhrH10oTMjQnJ24xVm26mxRXPQa5vaLpWJqNyIdNLIQLe+UXUO
|
||||
zSGGsFTRMAjvYrkzjBe4ZUnaZV+aFY/ug0jfzeA1dJjzKZs6+yTJRbsuWUEb8MsD
|
||||
mT4v+kBZDKdaDn7AFDWRVqx/38BnqsRzkM0CxpnyT2kRzw5zQajIE13gdTJo1EHv
|
||||
YSUkkxrY5m30Rl9BuBBZBjhMzOHq0fYVVooHO+sf4XHPgvFTTxJum85u7J1JoEUj
|
||||
rLKtAgMBAAGjXDBaMA4GA1UdDwEB/wQEAwIDiDATBgNVHSUEDDAKBggrBgEFBQcD
|
||||
ATAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFNzx4Rfs9m8XR5ML0WsI
|
||||
sorKmB4PMA0GCSqGSIb3DQEBCwUAA4IBAQB87iQy8R0fiOky9WTcyzVeMaavS3MX
|
||||
iTe1BRn1OCyDq+UiwwoNz7zdzZJFEmRtFBwPNFOe4HzLu6E+7yLFR552eYRHlqIi
|
||||
/fiLb5JiZfPtokUHeqwELWBsoXtU8vKxViPiLZ09jkWOPZWo7b/xXd6QYykBfV91
|
||||
usUXLzyTD2orMagpqNksLDGS3p3ggHEJBZtRZA8R7kPEw98xZHznOQpr26iv8kYz
|
||||
ZWdLFoFdwgFBSfxePKax5rfo+FbwdrcTX0MhbORyiu2XsBAghf8s2vKDkHg2UQE8
|
||||
haonxFYMFaASfaZ/5vWKYDTCJkJ67m/BtkpRafFEO+ad1i1S61OjfxH4
|
||||
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt7iqkEIco372hv19q
|
||||
0zjaYbm6gzxEnR45UjpQYqgztq4QHicD80mqIkCBCYknFxhwxhNn+Y3g5RWQdRep
|
||||
lpQbkneqRVp+qixMvu2FmOA4zRRoqObP7FyF1Yusvmroe0Y9SP2xTTmA9Zo73pay
|
||||
wPUIuZ9eKGwIiFTtj1yQ1FdghLhzZgxcf3LHEHRkGnxgxxNITFxh4nd6fGIjNqM5
|
||||
fQAY8z35lMXdeWjrhtaqgFYB+Z20YY0X7LJx39vYao0wqW8sZjX88TqHI1zXWLpU
|
||||
k6UK9RqaNza5xc80wV+9/zjhr3dc1FRjBxI1DS/ufo33dUfvilxv9/LtWwUnKfKL
|
||||
ns9LAgMBAAGjgYEwfzAdBgNVHQ4EFgQUQCpSY7ODhdyD6pdZHvfHoWRXWsIwHwYD
|
||||
VR0jBBgwFoAUQCpSY7ODhdyD6pdZHvfHoWRXWsIwDwYDVR0TAQH/BAUwAwEB/zAs
|
||||
BgNVHREEJTAjgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJ
|
||||
KoZIhvcNAQELBQADggEBAGKTIzGQsOqfD0+x15F2cu7FKjIo1ua0OiILAhPqGX65
|
||||
kGcetjC/dJip2bGnw1NjG9WxEJNZ4YcsGrwh9egfnXXmfHNL0wzx/LTo2oysbXsN
|
||||
nEj+cmzw3Lwjn/ywJc+AC221/xrmDfm3m/hMzLqncnj23ZAHqkXTSp5UtSMs+UDQ
|
||||
my0AJOvsDGPVKHQsAX3JDjKHaoVJn4YqpHcIGmpjrNcQSvwUocDHPcC0ywco6SgF
|
||||
Ylzy2bwWWdPd9Cz9JkAMb95nWc7Rwf/nxAqCjJFzKEisvrx7VZ+QSVI0nqJzt8V1
|
||||
pbtWYH5gMFVstU3ghWdSLbAk4XufGYrIWAlA5mqjQ4o=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
63
test/regression/issue/12117.test.ts
Normal file
63
test/regression/issue/12117.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
// Regression test for TLS upgrade raw socket leak (#12117, #24118, #25948)
|
||||
// When a TCP socket is upgraded to TLS via tls.connect({ socket }),
|
||||
// both a TLS wrapper and a raw TCP wrapper are created in Zig.
|
||||
// Previously, the raw socket's has_pending_activity was never set to
|
||||
// false on close, causing it (and all its retained objects) to leak.
|
||||
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { tls as COMMON_CERT, expectMaxObjectTypeCount } from "harness";
|
||||
import { once } from "node:events";
|
||||
import net from "node:net";
|
||||
import tls from "node:tls";
|
||||
|
||||
describe("TLS upgrade", () => {
|
||||
it("should not leak TLSSocket objects after close", async () => {
|
||||
// Create a TLS server that echoes data and closes
|
||||
const server = tls.createServer(
|
||||
{
|
||||
key: COMMON_CERT.key,
|
||||
cert: COMMON_CERT.cert,
|
||||
},
|
||||
socket => {
|
||||
socket.end("hello");
|
||||
},
|
||||
);
|
||||
|
||||
await once(server.listen(0, "127.0.0.1"), "listening");
|
||||
const port = (server.address() as net.AddressInfo).port;
|
||||
|
||||
// Simulate the MongoDB driver pattern: create a plain TCP socket,
|
||||
// then upgrade it to TLS via tls.connect({ socket }).
|
||||
// Do this multiple times to accumulate leaked objects.
|
||||
const iterations = 50;
|
||||
|
||||
try {
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const tcpSocket = net.createConnection({ host: "127.0.0.1", port });
|
||||
await once(tcpSocket, "connect");
|
||||
|
||||
const tlsSocket = tls.connect({
|
||||
socket: tcpSocket,
|
||||
ca: COMMON_CERT.cert,
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
await once(tlsSocket, "secureConnect");
|
||||
|
||||
// Read any data and destroy the TLS socket (simulates SDAM close)
|
||||
tlsSocket.on("data", () => {});
|
||||
tlsSocket.destroy();
|
||||
|
||||
await once(tlsSocket, "close");
|
||||
}
|
||||
} finally {
|
||||
server.close();
|
||||
await once(server, "close");
|
||||
}
|
||||
|
||||
// After all connections are closed and GC runs, the TLSSocket count
|
||||
// should be low. Before the fix, each iteration would leak 1 raw
|
||||
// TLSSocket (the TCP wrapper from upgradeTLS), accumulating over time.
|
||||
// Allow some slack for prototypes/structures (typically 2-3 baseline).
|
||||
await expectMaxObjectTypeCount(expect, "TLSSocket", 10, 1000);
|
||||
});
|
||||
});
|
||||
8
test/regression/issue/27014.test.ts
Normal file
8
test/regression/issue/27014.test.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/27014
|
||||
test("Bun.stripANSI does not hang on non-ANSI control characters", () => {
|
||||
const s = "\u0016zo\u00BAd\u0019\u00E8\u00E0\u0013?\u00C1+\u0014d\u00D3\u00E9";
|
||||
const result = Bun.stripANSI(s);
|
||||
expect(result).toBe(s);
|
||||
});
|
||||
31
test/regression/issue/27025.test.ts
Normal file
31
test/regression/issue/27025.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { X509Certificate } from "crypto";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
const certPem = readFileSync(join(import.meta.dir, "../../js/node/test/fixtures/keys/agent1-cert.pem"));
|
||||
|
||||
test("issuerCertificate should return undefined for directly-parsed certificates without crashing", () => {
|
||||
const cert = new X509Certificate(certPem);
|
||||
|
||||
// issuerCertificate is only populated for certificates obtained from TLS
|
||||
// connections with a peer certificate chain. For directly parsed certs,
|
||||
// it should be undefined (matching Node.js behavior).
|
||||
expect(cert.issuerCertificate).toBeUndefined();
|
||||
});
|
||||
|
||||
test("X509Certificate properties should not crash on valid certificates", () => {
|
||||
const cert = new X509Certificate(certPem);
|
||||
|
||||
// These should all work without segfaulting
|
||||
expect(cert.subject).toBeDefined();
|
||||
expect(cert.issuer).toBeDefined();
|
||||
expect(cert.validFrom).toBeDefined();
|
||||
expect(cert.validTo).toBeDefined();
|
||||
expect(cert.fingerprint).toBeDefined();
|
||||
expect(cert.fingerprint256).toBeDefined();
|
||||
expect(cert.fingerprint512).toBeDefined();
|
||||
expect(cert.serialNumber).toBeDefined();
|
||||
expect(cert.raw).toBeInstanceOf(Uint8Array);
|
||||
expect(cert.ca).toBe(false);
|
||||
});
|
||||
89
test/regression/issue/27049.test.ts
Normal file
89
test/regression/issue/27049.test.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import http from "node:http";
|
||||
|
||||
test("ClientRequest.setHeaders should not throw ERR_HTTP_HEADERS_SENT on new request", async () => {
|
||||
await using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(req) {
|
||||
return new Response(req.headers.get("x-test") ?? "missing");
|
||||
},
|
||||
});
|
||||
|
||||
const { resolve, reject, promise } = Promise.withResolvers<string>();
|
||||
|
||||
const req = http.request(`http://localhost:${server.port}/test`, { method: "GET" }, res => {
|
||||
let data = "";
|
||||
res.on("data", (chunk: Buffer) => {
|
||||
data += chunk.toString();
|
||||
});
|
||||
res.on("end", () => resolve(data));
|
||||
});
|
||||
|
||||
req.on("error", reject);
|
||||
|
||||
// This should not throw - headers haven't been sent yet
|
||||
req.setHeaders(new Headers({ "x-test": "value" }));
|
||||
|
||||
req.end();
|
||||
|
||||
const body = await promise;
|
||||
expect(body).toBe("value");
|
||||
});
|
||||
|
||||
test("ClientRequest.setHeaders works with Map", async () => {
|
||||
await using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(req) {
|
||||
return new Response(req.headers.get("x-map-test") ?? "missing");
|
||||
},
|
||||
});
|
||||
|
||||
const { resolve, reject, promise } = Promise.withResolvers<string>();
|
||||
|
||||
const req = http.request(`http://localhost:${server.port}/test`, { method: "GET" }, res => {
|
||||
let data = "";
|
||||
res.on("data", (chunk: Buffer) => {
|
||||
data += chunk.toString();
|
||||
});
|
||||
res.on("end", () => resolve(data));
|
||||
});
|
||||
|
||||
req.on("error", reject);
|
||||
|
||||
req.setHeaders(new Map([["x-map-test", "map-value"]]));
|
||||
|
||||
req.end();
|
||||
|
||||
const body = await promise;
|
||||
expect(body).toBe("map-value");
|
||||
});
|
||||
|
||||
test("ServerResponse.setHeaders should not throw before headers are sent", async () => {
|
||||
const { resolve, reject, promise } = Promise.withResolvers<string>();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
// This should not throw - headers haven't been sent yet
|
||||
res.setHeaders(new Headers({ "x-custom": "server-value" }));
|
||||
res.writeHead(200);
|
||||
res.end("ok");
|
||||
});
|
||||
|
||||
try {
|
||||
server.listen(0, () => {
|
||||
const port = (server.address() as any).port;
|
||||
try {
|
||||
const req = http.request(`http://localhost:${port}/test`, res => {
|
||||
resolve(res.headers["x-custom"] as string);
|
||||
});
|
||||
req.on("error", reject);
|
||||
req.end();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
expect(await promise).toBe("server-value");
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
336
test/regression/issue/27061.test.ts
Normal file
336
test/regression/issue/27061.test.ts
Normal file
@@ -0,0 +1,336 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import http from "node:http";
|
||||
|
||||
// Regression test for https://github.com/oven-sh/bun/issues/27061
|
||||
// When http.ClientRequest.write() is called more than once (streaming data in chunks),
|
||||
// Bun was stripping the explicitly-set Content-Length header and switching to
|
||||
// Transfer-Encoding: chunked. Node.js preserves Content-Length in all cases.
|
||||
|
||||
describe("node:http ClientRequest preserves explicit Content-Length", () => {
|
||||
test("with multiple req.write() calls", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers<{
|
||||
contentLength: string | undefined;
|
||||
transferEncoding: string | undefined;
|
||||
bodyLength: number;
|
||||
}>();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
||||
req.on("end", () => {
|
||||
resolve({
|
||||
contentLength: req.headers["content-length"],
|
||||
transferEncoding: req.headers["transfer-encoding"],
|
||||
bodyLength: Buffer.concat(chunks).length,
|
||||
});
|
||||
res.writeHead(200);
|
||||
res.end("ok");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>(res => server.listen(0, "127.0.0.1", res));
|
||||
const port = (server.address() as any).port;
|
||||
|
||||
try {
|
||||
const chunk1 = Buffer.alloc(100, "a");
|
||||
const chunk2 = Buffer.alloc(100, "b");
|
||||
const totalLength = chunk1.length + chunk2.length;
|
||||
|
||||
const req = http.request({
|
||||
hostname: "127.0.0.1",
|
||||
port,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Length": totalLength.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
req.on("error", rej);
|
||||
req.on("response", () => res());
|
||||
req.write(chunk1);
|
||||
req.write(chunk2);
|
||||
req.end();
|
||||
});
|
||||
|
||||
const result = await promise;
|
||||
expect(result.contentLength).toBe("200");
|
||||
expect(result.transferEncoding).toBeUndefined();
|
||||
expect(result.bodyLength).toBe(200);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("with req.write() + req.end(data)", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers<{
|
||||
contentLength: string | undefined;
|
||||
transferEncoding: string | undefined;
|
||||
bodyLength: number;
|
||||
}>();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
||||
req.on("end", () => {
|
||||
resolve({
|
||||
contentLength: req.headers["content-length"],
|
||||
transferEncoding: req.headers["transfer-encoding"],
|
||||
bodyLength: Buffer.concat(chunks).length,
|
||||
});
|
||||
res.writeHead(200);
|
||||
res.end("ok");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>(res => server.listen(0, "127.0.0.1", res));
|
||||
const port = (server.address() as any).port;
|
||||
|
||||
try {
|
||||
const chunk1 = Buffer.alloc(100, "a");
|
||||
const chunk2 = Buffer.alloc(100, "b");
|
||||
const totalLength = chunk1.length + chunk2.length;
|
||||
|
||||
const req = http.request({
|
||||
hostname: "127.0.0.1",
|
||||
port,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Length": totalLength.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
req.on("error", rej);
|
||||
req.on("response", () => res());
|
||||
req.write(chunk1);
|
||||
req.end(chunk2);
|
||||
});
|
||||
|
||||
const result = await promise;
|
||||
expect(result.contentLength).toBe("200");
|
||||
expect(result.transferEncoding).toBeUndefined();
|
||||
expect(result.bodyLength).toBe(200);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("with three req.write() calls", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers<{
|
||||
contentLength: string | undefined;
|
||||
transferEncoding: string | undefined;
|
||||
bodyLength: number;
|
||||
}>();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
||||
req.on("end", () => {
|
||||
resolve({
|
||||
contentLength: req.headers["content-length"],
|
||||
transferEncoding: req.headers["transfer-encoding"],
|
||||
bodyLength: Buffer.concat(chunks).length,
|
||||
});
|
||||
res.writeHead(200);
|
||||
res.end("ok");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>(res => server.listen(0, "127.0.0.1", res));
|
||||
const port = (server.address() as any).port;
|
||||
|
||||
try {
|
||||
const chunk1 = Buffer.alloc(100, "a");
|
||||
const chunk2 = Buffer.alloc(100, "b");
|
||||
const chunk3 = Buffer.alloc(100, "c");
|
||||
const totalLength = chunk1.length + chunk2.length + chunk3.length;
|
||||
|
||||
const req = http.request({
|
||||
hostname: "127.0.0.1",
|
||||
port,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Length": totalLength.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
req.on("error", rej);
|
||||
req.on("response", () => res());
|
||||
req.write(chunk1);
|
||||
req.write(chunk2);
|
||||
req.write(chunk3);
|
||||
req.end();
|
||||
});
|
||||
|
||||
const result = await promise;
|
||||
expect(result.contentLength).toBe("300");
|
||||
expect(result.transferEncoding).toBeUndefined();
|
||||
expect(result.bodyLength).toBe(300);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("single req.write() still works", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers<{
|
||||
contentLength: string | undefined;
|
||||
transferEncoding: string | undefined;
|
||||
bodyLength: number;
|
||||
}>();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
||||
req.on("end", () => {
|
||||
resolve({
|
||||
contentLength: req.headers["content-length"],
|
||||
transferEncoding: req.headers["transfer-encoding"],
|
||||
bodyLength: Buffer.concat(chunks).length,
|
||||
});
|
||||
res.writeHead(200);
|
||||
res.end("ok");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>(res => server.listen(0, "127.0.0.1", res));
|
||||
const port = (server.address() as any).port;
|
||||
|
||||
try {
|
||||
const data = Buffer.alloc(200, "x");
|
||||
|
||||
const req = http.request({
|
||||
hostname: "127.0.0.1",
|
||||
port,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Length": data.length.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
req.on("error", rej);
|
||||
req.on("response", () => res());
|
||||
req.write(data);
|
||||
req.end();
|
||||
});
|
||||
|
||||
const result = await promise;
|
||||
expect(result.contentLength).toBe("200");
|
||||
expect(result.transferEncoding).toBeUndefined();
|
||||
expect(result.bodyLength).toBe(200);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("without explicit Content-Length still uses chunked encoding", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers<{
|
||||
contentLength: string | undefined;
|
||||
transferEncoding: string | undefined;
|
||||
bodyLength: number;
|
||||
}>();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
||||
req.on("end", () => {
|
||||
resolve({
|
||||
contentLength: req.headers["content-length"],
|
||||
transferEncoding: req.headers["transfer-encoding"],
|
||||
bodyLength: Buffer.concat(chunks).length,
|
||||
});
|
||||
res.writeHead(200);
|
||||
res.end("ok");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>(res => server.listen(0, "127.0.0.1", res));
|
||||
const port = (server.address() as any).port;
|
||||
|
||||
try {
|
||||
const chunk1 = Buffer.alloc(100, "a");
|
||||
const chunk2 = Buffer.alloc(100, "b");
|
||||
|
||||
const req = http.request({
|
||||
hostname: "127.0.0.1",
|
||||
port,
|
||||
method: "POST",
|
||||
// No Content-Length header
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
req.on("error", rej);
|
||||
req.on("response", () => res());
|
||||
req.write(chunk1);
|
||||
req.write(chunk2);
|
||||
req.end();
|
||||
});
|
||||
|
||||
const result = await promise;
|
||||
// Without explicit Content-Length, chunked encoding should be used
|
||||
expect(result.transferEncoding).toBe("chunked");
|
||||
expect(result.bodyLength).toBe(200);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("explicit Transfer-Encoding takes precedence over Content-Length", async () => {
|
||||
const { promise, resolve } = Promise.withResolvers<{
|
||||
contentLength: string | undefined;
|
||||
transferEncoding: string | undefined;
|
||||
bodyLength: number;
|
||||
}>();
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const chunks: Buffer[] = [];
|
||||
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
||||
req.on("end", () => {
|
||||
resolve({
|
||||
contentLength: req.headers["content-length"],
|
||||
transferEncoding: req.headers["transfer-encoding"],
|
||||
bodyLength: Buffer.concat(chunks).length,
|
||||
});
|
||||
res.writeHead(200);
|
||||
res.end("ok");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>(res => server.listen(0, "127.0.0.1", res));
|
||||
const port = (server.address() as any).port;
|
||||
|
||||
try {
|
||||
const chunk1 = Buffer.alloc(100, "a");
|
||||
const chunk2 = Buffer.alloc(100, "b");
|
||||
|
||||
const req = http.request({
|
||||
hostname: "127.0.0.1",
|
||||
port,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Length": "200",
|
||||
"Transfer-Encoding": "chunked",
|
||||
},
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
req.on("error", rej);
|
||||
req.on("response", () => res());
|
||||
req.write(chunk1);
|
||||
req.write(chunk2);
|
||||
req.end();
|
||||
});
|
||||
|
||||
const result = await promise;
|
||||
// When user explicitly sets Transfer-Encoding, it should be used
|
||||
// and Content-Length should not be added
|
||||
expect(result.transferEncoding).toBe("chunked");
|
||||
expect(result.contentLength).toBeUndefined();
|
||||
expect(result.bodyLength).toBe(200);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -92,13 +92,17 @@ test("cyclic imports with async dependencies should generate async wrappers", as
|
||||
|
||||
expect(bundled).toMatchInlineSnapshot(`
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
||||
@@ -176,7 +180,7 @@ test("cyclic imports with async dependencies should generate async wrappers", as
|
||||
var { AsyncEntryPoint: AsyncEntryPoint2 } = await Promise.resolve().then(() => exports_AsyncEntryPoint);
|
||||
AsyncEntryPoint2();
|
||||
|
||||
//# debugId=986E7BD819E590FD64756E2164756E21
|
||||
//# debugId=2020261114B67BB564756E2164756E21
|
||||
//# sourceMappingURL=entryBuild.js.map
|
||||
"
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user