@secure-exec/core 0.1.1-rc.3 → 0.2.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm-compiler.d.ts +5 -1
- package/dist/esm-compiler.js +5 -1
- package/dist/fs-helpers.d.ts +1 -1
- package/dist/generated/isolate-runtime.d.ts +15 -15
- package/dist/generated/isolate-runtime.js +15 -15
- package/dist/index.d.ts +24 -5
- package/dist/index.js +23 -3
- package/dist/isolate-runtime/apply-custom-global-policy.js +3 -3
- package/dist/isolate-runtime/apply-timing-mitigation-freeze.js +2 -2
- package/dist/isolate-runtime/apply-timing-mitigation-off.js +2 -2
- package/dist/isolate-runtime/bridge-attach.js +2 -2
- package/dist/isolate-runtime/bridge-initial-globals.js +145 -6
- package/dist/isolate-runtime/eval-script-result.js +1 -1
- package/dist/isolate-runtime/global-exposure-helpers.js +2 -2
- package/dist/isolate-runtime/init-commonjs-module-globals.js +2 -2
- package/dist/isolate-runtime/override-process-cwd.js +1 -1
- package/dist/isolate-runtime/override-process-env.js +1 -1
- package/dist/isolate-runtime/require-setup.js +2868 -494
- package/dist/isolate-runtime/set-commonjs-file-globals.js +2 -2
- package/dist/isolate-runtime/set-stdin-data.js +1 -1
- package/dist/isolate-runtime/setup-dynamic-import.js +78 -19
- package/dist/isolate-runtime/setup-fs-facade.js +62 -23
- package/dist/kernel/command-registry.d.ts +44 -0
- package/dist/kernel/command-registry.js +114 -0
- package/dist/kernel/device-layer.d.ts +12 -0
- package/dist/kernel/device-layer.js +262 -0
- package/dist/kernel/dns-cache.d.ts +29 -0
- package/dist/kernel/dns-cache.js +52 -0
- package/dist/kernel/fd-table.d.ts +84 -0
- package/dist/kernel/fd-table.js +278 -0
- package/dist/kernel/file-lock.d.ts +34 -0
- package/dist/kernel/file-lock.js +122 -0
- package/dist/kernel/host-adapter.d.ts +50 -0
- package/dist/kernel/host-adapter.js +8 -0
- package/dist/kernel/index.d.ts +36 -0
- package/dist/kernel/index.js +34 -0
- package/dist/kernel/inode-table.d.ts +43 -0
- package/dist/kernel/inode-table.js +85 -0
- package/dist/kernel/kernel.d.ts +9 -0
- package/dist/kernel/kernel.js +1393 -0
- package/dist/kernel/permissions.d.ts +27 -0
- package/dist/kernel/permissions.js +118 -0
- package/dist/kernel/pipe-manager.d.ts +64 -0
- package/dist/kernel/pipe-manager.js +267 -0
- package/dist/kernel/proc-layer.d.ts +11 -0
- package/dist/kernel/proc-layer.js +501 -0
- package/dist/kernel/process-table.d.ts +124 -0
- package/dist/kernel/process-table.js +631 -0
- package/dist/kernel/pty.d.ts +108 -0
- package/dist/kernel/pty.js +541 -0
- package/dist/kernel/socket-table.d.ts +312 -0
- package/dist/kernel/socket-table.js +1188 -0
- package/dist/kernel/timer-table.d.ts +54 -0
- package/dist/kernel/timer-table.js +108 -0
- package/dist/kernel/types.d.ts +500 -0
- package/dist/kernel/types.js +89 -0
- package/dist/kernel/user.d.ts +29 -0
- package/dist/kernel/user.js +35 -0
- package/dist/kernel/vfs.d.ts +54 -0
- package/dist/kernel/vfs.js +8 -0
- package/dist/kernel/wait.d.ts +45 -0
- package/dist/kernel/wait.js +112 -0
- package/dist/kernel/wstatus.d.ts +21 -0
- package/dist/kernel/wstatus.js +33 -0
- package/dist/module-resolver.d.ts +4 -0
- package/dist/module-resolver.js +4 -0
- package/dist/package-bundler.d.ts +6 -1
- package/dist/runtime-driver.d.ts +3 -1
- package/dist/shared/bridge-contract.d.ts +349 -22
- package/dist/shared/bridge-contract.js +62 -5
- package/dist/shared/console-formatter.js +8 -4
- package/dist/shared/global-exposure.js +364 -19
- package/dist/shared/in-memory-fs.d.ts +33 -11
- package/dist/shared/in-memory-fs.js +439 -130
- package/dist/shared/permissions.d.ts +4 -6
- package/dist/shared/permissions.js +19 -39
- package/dist/types.d.ts +8 -159
- package/dist/types.js +5 -0
- package/package.json +12 -22
- package/dist/bridge/active-handles.d.ts +0 -22
- package/dist/bridge/active-handles.js +0 -55
- package/dist/bridge/child-process.d.ts +0 -99
- package/dist/bridge/child-process.js +0 -670
- package/dist/bridge/fs.d.ts +0 -281
- package/dist/bridge/fs.js +0 -2235
- package/dist/bridge/index.d.ts +0 -10
- package/dist/bridge/index.js +0 -41
- package/dist/bridge/module.d.ts +0 -75
- package/dist/bridge/module.js +0 -308
- package/dist/bridge/network.d.ts +0 -350
- package/dist/bridge/network.js +0 -2050
- package/dist/bridge/os.d.ts +0 -13
- package/dist/bridge/os.js +0 -256
- package/dist/bridge/polyfills.d.ts +0 -2
- package/dist/bridge/polyfills.js +0 -11
- package/dist/bridge/process.d.ts +0 -89
- package/dist/bridge/process.js +0 -1015
- package/dist/bridge.js +0 -12496
- package/dist/python-runtime.d.ts +0 -16
- package/dist/python-runtime.js +0 -45
- package/dist/runtime.d.ts +0 -31
- package/dist/runtime.js +0 -69
package/dist/bridge/network.js
DELETED
|
@@ -1,2050 +0,0 @@
|
|
|
1
|
-
// Network module polyfill for isolated-vm
|
|
2
|
-
// Provides fetch, http, https, and dns module emulation that bridges to host
|
|
3
|
-
// Cap in-sandbox request/response buffering to prevent host memory exhaustion
|
|
4
|
-
const MAX_HTTP_BODY_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
5
|
-
import { exposeCustomGlobal } from "../shared/global-exposure.js";
|
|
6
|
-
// Fetch polyfill
|
|
7
|
-
export async function fetch(input, options = {}) {
|
|
8
|
-
if (typeof _networkFetchRaw === 'undefined') {
|
|
9
|
-
console.error('fetch requires NetworkAdapter to be configured');
|
|
10
|
-
throw new Error('fetch requires NetworkAdapter to be configured');
|
|
11
|
-
}
|
|
12
|
-
// Extract URL and options from Request object (used by axios fetch adapter)
|
|
13
|
-
let resolvedUrl;
|
|
14
|
-
if (input instanceof Request) {
|
|
15
|
-
resolvedUrl = input.url;
|
|
16
|
-
options = {
|
|
17
|
-
method: input.method,
|
|
18
|
-
headers: Object.fromEntries(input.headers.entries()),
|
|
19
|
-
body: input.body,
|
|
20
|
-
...options,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
resolvedUrl = String(input);
|
|
25
|
-
}
|
|
26
|
-
const optionsJson = JSON.stringify({
|
|
27
|
-
method: options.method || "GET",
|
|
28
|
-
headers: options.headers || {},
|
|
29
|
-
body: options.body || null,
|
|
30
|
-
});
|
|
31
|
-
const responseJson = await _networkFetchRaw.apply(undefined, [resolvedUrl, optionsJson], {
|
|
32
|
-
result: { promise: true },
|
|
33
|
-
});
|
|
34
|
-
const response = JSON.parse(responseJson);
|
|
35
|
-
// Create Response-like object
|
|
36
|
-
return {
|
|
37
|
-
ok: response.ok,
|
|
38
|
-
status: response.status,
|
|
39
|
-
statusText: response.statusText,
|
|
40
|
-
headers: new Map(Object.entries(response.headers || {})),
|
|
41
|
-
url: response.url || resolvedUrl,
|
|
42
|
-
redirected: response.redirected || false,
|
|
43
|
-
type: "basic",
|
|
44
|
-
async text() {
|
|
45
|
-
return response.body || "";
|
|
46
|
-
},
|
|
47
|
-
async json() {
|
|
48
|
-
return JSON.parse(response.body || "{}");
|
|
49
|
-
},
|
|
50
|
-
async arrayBuffer() {
|
|
51
|
-
// Not fully supported - return empty buffer
|
|
52
|
-
return new ArrayBuffer(0);
|
|
53
|
-
},
|
|
54
|
-
async blob() {
|
|
55
|
-
throw new Error("Blob not supported in sandbox");
|
|
56
|
-
},
|
|
57
|
-
clone() {
|
|
58
|
-
return { ...this };
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
// Headers class
|
|
63
|
-
export class Headers {
|
|
64
|
-
_headers = {};
|
|
65
|
-
constructor(init) {
|
|
66
|
-
if (init && init !== null) {
|
|
67
|
-
if (init instanceof Headers) {
|
|
68
|
-
this._headers = { ...init._headers };
|
|
69
|
-
}
|
|
70
|
-
else if (Array.isArray(init)) {
|
|
71
|
-
init.forEach(([key, value]) => {
|
|
72
|
-
this._headers[key.toLowerCase()] = value;
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
else if (typeof init === "object") {
|
|
76
|
-
Object.entries(init).forEach(([key, value]) => {
|
|
77
|
-
this._headers[key.toLowerCase()] = value;
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
get(name) {
|
|
83
|
-
return this._headers[name.toLowerCase()] || null;
|
|
84
|
-
}
|
|
85
|
-
set(name, value) {
|
|
86
|
-
this._headers[name.toLowerCase()] = value;
|
|
87
|
-
}
|
|
88
|
-
has(name) {
|
|
89
|
-
return name.toLowerCase() in this._headers;
|
|
90
|
-
}
|
|
91
|
-
delete(name) {
|
|
92
|
-
delete this._headers[name.toLowerCase()];
|
|
93
|
-
}
|
|
94
|
-
entries() {
|
|
95
|
-
return Object.entries(this._headers)[Symbol.iterator]();
|
|
96
|
-
}
|
|
97
|
-
[Symbol.iterator]() {
|
|
98
|
-
return this.entries();
|
|
99
|
-
}
|
|
100
|
-
keys() {
|
|
101
|
-
return Object.keys(this._headers)[Symbol.iterator]();
|
|
102
|
-
}
|
|
103
|
-
values() {
|
|
104
|
-
return Object.values(this._headers)[Symbol.iterator]();
|
|
105
|
-
}
|
|
106
|
-
forEach(callback) {
|
|
107
|
-
Object.entries(this._headers).forEach(([k, v]) => callback(v, k, this));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
// Request class
|
|
111
|
-
export class Request {
|
|
112
|
-
url;
|
|
113
|
-
method;
|
|
114
|
-
headers;
|
|
115
|
-
body;
|
|
116
|
-
mode;
|
|
117
|
-
credentials;
|
|
118
|
-
cache;
|
|
119
|
-
redirect;
|
|
120
|
-
referrer;
|
|
121
|
-
integrity;
|
|
122
|
-
constructor(input, init = {}) {
|
|
123
|
-
this.url = typeof input === "string" ? input : input.url;
|
|
124
|
-
this.method = init.method || (typeof input !== "string" ? input.method : undefined) || "GET";
|
|
125
|
-
this.headers = new Headers(init.headers || (typeof input !== "string" ? input.headers : undefined));
|
|
126
|
-
this.body = init.body || null;
|
|
127
|
-
this.mode = init.mode || "cors";
|
|
128
|
-
this.credentials = init.credentials || "same-origin";
|
|
129
|
-
this.cache = init.cache || "default";
|
|
130
|
-
this.redirect = init.redirect || "follow";
|
|
131
|
-
this.referrer = init.referrer || "about:client";
|
|
132
|
-
this.integrity = init.integrity || "";
|
|
133
|
-
}
|
|
134
|
-
clone() {
|
|
135
|
-
return new Request(this.url, this);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// Response class
|
|
139
|
-
export class Response {
|
|
140
|
-
_body;
|
|
141
|
-
status;
|
|
142
|
-
statusText;
|
|
143
|
-
headers;
|
|
144
|
-
ok;
|
|
145
|
-
type;
|
|
146
|
-
url;
|
|
147
|
-
redirected;
|
|
148
|
-
constructor(body, init = {}) {
|
|
149
|
-
this._body = body || null;
|
|
150
|
-
this.status = init.status || 200;
|
|
151
|
-
this.statusText = init.statusText || "OK";
|
|
152
|
-
this.headers = new Headers(init.headers);
|
|
153
|
-
this.ok = this.status >= 200 && this.status < 300;
|
|
154
|
-
this.type = "default";
|
|
155
|
-
this.url = "";
|
|
156
|
-
this.redirected = false;
|
|
157
|
-
}
|
|
158
|
-
async text() {
|
|
159
|
-
return String(this._body || "");
|
|
160
|
-
}
|
|
161
|
-
async json() {
|
|
162
|
-
return JSON.parse(this._body || "{}");
|
|
163
|
-
}
|
|
164
|
-
clone() {
|
|
165
|
-
return new Response(this._body, { status: this.status, statusText: this.statusText });
|
|
166
|
-
}
|
|
167
|
-
static error() {
|
|
168
|
-
return new Response(null, { status: 0, statusText: "" });
|
|
169
|
-
}
|
|
170
|
-
static redirect(url, status = 302) {
|
|
171
|
-
return new Response(null, { status, headers: { Location: url } });
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
// DNS module polyfill
|
|
175
|
-
export const dns = {
|
|
176
|
-
lookup(hostname, options, callback) {
|
|
177
|
-
let cb = callback;
|
|
178
|
-
if (typeof options === "function") {
|
|
179
|
-
cb = options;
|
|
180
|
-
}
|
|
181
|
-
_networkDnsLookupRaw
|
|
182
|
-
.apply(undefined, [hostname], { result: { promise: true } })
|
|
183
|
-
.then((resultJson) => {
|
|
184
|
-
const result = JSON.parse(resultJson);
|
|
185
|
-
if (result.error) {
|
|
186
|
-
const err = new Error(result.error);
|
|
187
|
-
err.code = result.code || "ENOTFOUND";
|
|
188
|
-
cb?.(err);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
cb?.(null, result.address, result.family);
|
|
192
|
-
}
|
|
193
|
-
})
|
|
194
|
-
.catch((err) => {
|
|
195
|
-
cb?.(err);
|
|
196
|
-
});
|
|
197
|
-
},
|
|
198
|
-
resolve(hostname, rrtype, callback) {
|
|
199
|
-
let cb = callback;
|
|
200
|
-
if (typeof rrtype === "function") {
|
|
201
|
-
cb = rrtype;
|
|
202
|
-
}
|
|
203
|
-
// Simplified - just do lookup for A records
|
|
204
|
-
dns.lookup(hostname, (err, address) => {
|
|
205
|
-
if (err) {
|
|
206
|
-
cb?.(err);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
cb?.(null, address ? [address] : []);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
},
|
|
213
|
-
resolve4(hostname, callback) {
|
|
214
|
-
dns.resolve(hostname, "A", callback);
|
|
215
|
-
},
|
|
216
|
-
resolve6(hostname, callback) {
|
|
217
|
-
dns.resolve(hostname, "AAAA", callback);
|
|
218
|
-
},
|
|
219
|
-
promises: {
|
|
220
|
-
lookup(hostname, _options) {
|
|
221
|
-
return new Promise((resolve, reject) => {
|
|
222
|
-
dns.lookup(hostname, _options, (err, address, family) => {
|
|
223
|
-
if (err)
|
|
224
|
-
reject(err);
|
|
225
|
-
else
|
|
226
|
-
resolve({ address: address || "", family: family || 4 });
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
},
|
|
230
|
-
resolve(hostname, rrtype) {
|
|
231
|
-
return new Promise((resolve, reject) => {
|
|
232
|
-
dns.resolve(hostname, rrtype || "A", (err, addresses) => {
|
|
233
|
-
if (err)
|
|
234
|
-
reject(err);
|
|
235
|
-
else
|
|
236
|
-
resolve(addresses || []);
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
};
|
|
242
|
-
// Module-level globalAgent used by ClientRequest when no agent option is provided.
|
|
243
|
-
// Initialized lazily after Agent class is defined; set by createHttpModule().
|
|
244
|
-
let _moduleGlobalAgent = null;
|
|
245
|
-
/**
|
|
246
|
-
* Polyfill of Node.js `http.IncomingMessage` (client-side response). Buffers
|
|
247
|
-
* the response body eagerly and emits `data`/`end` events on listener
|
|
248
|
-
* registration (flowing mode). Supports base64 binary decoding via
|
|
249
|
-
* `x-body-encoding` header.
|
|
250
|
-
*/
|
|
251
|
-
export class IncomingMessage {
|
|
252
|
-
headers;
|
|
253
|
-
rawHeaders;
|
|
254
|
-
trailers;
|
|
255
|
-
rawTrailers;
|
|
256
|
-
httpVersion;
|
|
257
|
-
httpVersionMajor;
|
|
258
|
-
httpVersionMinor;
|
|
259
|
-
method;
|
|
260
|
-
url;
|
|
261
|
-
statusCode;
|
|
262
|
-
statusMessage;
|
|
263
|
-
_body;
|
|
264
|
-
_isBinary;
|
|
265
|
-
_listeners;
|
|
266
|
-
complete;
|
|
267
|
-
aborted;
|
|
268
|
-
socket;
|
|
269
|
-
_bodyConsumed;
|
|
270
|
-
_ended;
|
|
271
|
-
_flowing;
|
|
272
|
-
readable;
|
|
273
|
-
readableEnded;
|
|
274
|
-
readableFlowing;
|
|
275
|
-
destroyed;
|
|
276
|
-
_encoding;
|
|
277
|
-
constructor(response) {
|
|
278
|
-
this.headers = response?.headers || {};
|
|
279
|
-
this.rawHeaders = [];
|
|
280
|
-
if (this.headers && typeof this.headers === "object") {
|
|
281
|
-
Object.entries(this.headers).forEach(([k, v]) => {
|
|
282
|
-
this.rawHeaders.push(k, v);
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
// Populate trailers if provided
|
|
286
|
-
if (response?.trailers && typeof response.trailers === "object") {
|
|
287
|
-
this.trailers = response.trailers;
|
|
288
|
-
this.rawTrailers = [];
|
|
289
|
-
Object.entries(response.trailers).forEach(([k, v]) => {
|
|
290
|
-
this.rawTrailers.push(k, v);
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
else {
|
|
294
|
-
this.trailers = {};
|
|
295
|
-
this.rawTrailers = [];
|
|
296
|
-
}
|
|
297
|
-
this.httpVersion = "1.1";
|
|
298
|
-
this.httpVersionMajor = 1;
|
|
299
|
-
this.httpVersionMinor = 1;
|
|
300
|
-
this.method = null;
|
|
301
|
-
this.url = response?.url || "";
|
|
302
|
-
this.statusCode = response?.status;
|
|
303
|
-
this.statusMessage = response?.statusText;
|
|
304
|
-
// Decode base64 body if x-body-encoding header is set
|
|
305
|
-
const bodyEncoding = this.headers['x-body-encoding'];
|
|
306
|
-
if (bodyEncoding === 'base64' && response?.body && typeof Buffer !== 'undefined') {
|
|
307
|
-
this._body = Buffer.from(response.body, 'base64').toString('binary');
|
|
308
|
-
this._isBinary = true;
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
this._body = response?.body || "";
|
|
312
|
-
this._isBinary = false;
|
|
313
|
-
}
|
|
314
|
-
this._listeners = {};
|
|
315
|
-
this.complete = false;
|
|
316
|
-
this.aborted = false;
|
|
317
|
-
this.socket = null;
|
|
318
|
-
this._bodyConsumed = false;
|
|
319
|
-
this._ended = false;
|
|
320
|
-
this._flowing = false;
|
|
321
|
-
this.readable = true;
|
|
322
|
-
this.readableEnded = false;
|
|
323
|
-
this.readableFlowing = null;
|
|
324
|
-
this.destroyed = false;
|
|
325
|
-
}
|
|
326
|
-
on(event, listener) {
|
|
327
|
-
if (!this._listeners[event])
|
|
328
|
-
this._listeners[event] = [];
|
|
329
|
-
this._listeners[event].push(listener);
|
|
330
|
-
// When 'data' listener is added, start flowing mode
|
|
331
|
-
// Note: We check for non-empty body (this._body.length > 0) because we need to
|
|
332
|
-
// emit 'end' even for empty responses, but only emit 'data' if there's actual data
|
|
333
|
-
if (event === "data" && !this._bodyConsumed) {
|
|
334
|
-
this._flowing = true;
|
|
335
|
-
this.readableFlowing = true;
|
|
336
|
-
// Emit data in next microtask
|
|
337
|
-
Promise.resolve().then(() => {
|
|
338
|
-
if (!this._bodyConsumed) {
|
|
339
|
-
this._bodyConsumed = true;
|
|
340
|
-
// Only emit data if there's actual content
|
|
341
|
-
if (this._body && this._body.length > 0) {
|
|
342
|
-
let buf;
|
|
343
|
-
if (typeof Buffer !== "undefined") {
|
|
344
|
-
// For binary data, use 'binary' encoding to preserve bytes
|
|
345
|
-
buf = this._isBinary ? Buffer.from(this._body, 'binary') : Buffer.from(this._body);
|
|
346
|
-
}
|
|
347
|
-
else {
|
|
348
|
-
buf = this._body;
|
|
349
|
-
}
|
|
350
|
-
this.emit("data", buf);
|
|
351
|
-
}
|
|
352
|
-
// Always emit end after data (even if no data was emitted)
|
|
353
|
-
Promise.resolve().then(() => {
|
|
354
|
-
if (!this._ended) {
|
|
355
|
-
this._ended = true;
|
|
356
|
-
this.complete = true;
|
|
357
|
-
this.readable = false;
|
|
358
|
-
this.readableEnded = true;
|
|
359
|
-
this.emit("end");
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
// If 'end' listener is added after data was already consumed, emit end
|
|
366
|
-
if (event === "end" && this._bodyConsumed && !this._ended) {
|
|
367
|
-
Promise.resolve().then(() => {
|
|
368
|
-
if (!this._ended) {
|
|
369
|
-
this._ended = true;
|
|
370
|
-
this.complete = true;
|
|
371
|
-
this.readable = false;
|
|
372
|
-
this.readableEnded = true;
|
|
373
|
-
listener();
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
return this;
|
|
378
|
-
}
|
|
379
|
-
once(event, listener) {
|
|
380
|
-
const wrapper = (...args) => {
|
|
381
|
-
this.off(event, wrapper);
|
|
382
|
-
listener(...args);
|
|
383
|
-
};
|
|
384
|
-
wrapper._originalListener = listener;
|
|
385
|
-
return this.on(event, wrapper);
|
|
386
|
-
}
|
|
387
|
-
off(event, listener) {
|
|
388
|
-
if (this._listeners[event]) {
|
|
389
|
-
const idx = this._listeners[event].findIndex((fn) => fn === listener || fn._originalListener === listener);
|
|
390
|
-
if (idx !== -1)
|
|
391
|
-
this._listeners[event].splice(idx, 1);
|
|
392
|
-
}
|
|
393
|
-
return this;
|
|
394
|
-
}
|
|
395
|
-
removeListener(event, listener) {
|
|
396
|
-
return this.off(event, listener);
|
|
397
|
-
}
|
|
398
|
-
removeAllListeners(event) {
|
|
399
|
-
if (event) {
|
|
400
|
-
delete this._listeners[event];
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
this._listeners = {};
|
|
404
|
-
}
|
|
405
|
-
return this;
|
|
406
|
-
}
|
|
407
|
-
emit(event, ...args) {
|
|
408
|
-
const handlers = this._listeners[event];
|
|
409
|
-
if (handlers) {
|
|
410
|
-
handlers.slice().forEach((fn) => fn(...args));
|
|
411
|
-
}
|
|
412
|
-
return handlers !== undefined && handlers.length > 0;
|
|
413
|
-
}
|
|
414
|
-
setEncoding(encoding) {
|
|
415
|
-
this._encoding = encoding;
|
|
416
|
-
return this;
|
|
417
|
-
}
|
|
418
|
-
read(_size) {
|
|
419
|
-
if (this._bodyConsumed)
|
|
420
|
-
return null;
|
|
421
|
-
this._bodyConsumed = true;
|
|
422
|
-
let buf;
|
|
423
|
-
if (typeof Buffer !== "undefined") {
|
|
424
|
-
buf = this._isBinary ? Buffer.from(this._body, 'binary') : Buffer.from(this._body);
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
buf = this._body;
|
|
428
|
-
}
|
|
429
|
-
// Schedule end event
|
|
430
|
-
Promise.resolve().then(() => {
|
|
431
|
-
if (!this._ended) {
|
|
432
|
-
this._ended = true;
|
|
433
|
-
this.complete = true;
|
|
434
|
-
this.readable = false;
|
|
435
|
-
this.readableEnded = true;
|
|
436
|
-
this.emit("end");
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
return buf;
|
|
440
|
-
}
|
|
441
|
-
pipe(dest) {
|
|
442
|
-
let buf;
|
|
443
|
-
if (typeof Buffer !== "undefined") {
|
|
444
|
-
buf = this._isBinary ? Buffer.from(this._body || "", 'binary') : Buffer.from(this._body || "");
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
buf = this._body || "";
|
|
448
|
-
}
|
|
449
|
-
if (typeof dest.write === "function" && (typeof buf === "string" ? buf.length : buf.length) > 0) {
|
|
450
|
-
dest.write(buf);
|
|
451
|
-
}
|
|
452
|
-
if (typeof dest.end === "function") {
|
|
453
|
-
Promise.resolve().then(() => dest.end());
|
|
454
|
-
}
|
|
455
|
-
this._bodyConsumed = true;
|
|
456
|
-
this._ended = true;
|
|
457
|
-
this.complete = true;
|
|
458
|
-
this.readable = false;
|
|
459
|
-
this.readableEnded = true;
|
|
460
|
-
return dest;
|
|
461
|
-
}
|
|
462
|
-
pause() {
|
|
463
|
-
this._flowing = false;
|
|
464
|
-
this.readableFlowing = false;
|
|
465
|
-
return this;
|
|
466
|
-
}
|
|
467
|
-
resume() {
|
|
468
|
-
this._flowing = true;
|
|
469
|
-
this.readableFlowing = true;
|
|
470
|
-
if (!this._bodyConsumed && this._body) {
|
|
471
|
-
Promise.resolve().then(() => {
|
|
472
|
-
if (!this._bodyConsumed) {
|
|
473
|
-
this._bodyConsumed = true;
|
|
474
|
-
let buf;
|
|
475
|
-
if (typeof Buffer !== "undefined") {
|
|
476
|
-
buf = this._isBinary ? Buffer.from(this._body, 'binary') : Buffer.from(this._body);
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
buf = this._body;
|
|
480
|
-
}
|
|
481
|
-
this.emit("data", buf);
|
|
482
|
-
Promise.resolve().then(() => {
|
|
483
|
-
if (!this._ended) {
|
|
484
|
-
this._ended = true;
|
|
485
|
-
this.complete = true;
|
|
486
|
-
this.readable = false;
|
|
487
|
-
this.readableEnded = true;
|
|
488
|
-
this.emit("end");
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
return this;
|
|
495
|
-
}
|
|
496
|
-
unpipe(_dest) {
|
|
497
|
-
return this;
|
|
498
|
-
}
|
|
499
|
-
destroy(err) {
|
|
500
|
-
this.destroyed = true;
|
|
501
|
-
this.readable = false;
|
|
502
|
-
if (err)
|
|
503
|
-
this.emit("error", err);
|
|
504
|
-
this.emit("close");
|
|
505
|
-
return this;
|
|
506
|
-
}
|
|
507
|
-
[Symbol.asyncIterator]() {
|
|
508
|
-
const self = this;
|
|
509
|
-
let dataEmitted = false;
|
|
510
|
-
let ended = false;
|
|
511
|
-
return {
|
|
512
|
-
async next() {
|
|
513
|
-
if (ended || self._ended) {
|
|
514
|
-
return { done: true, value: undefined };
|
|
515
|
-
}
|
|
516
|
-
if (!dataEmitted && !self._bodyConsumed) {
|
|
517
|
-
dataEmitted = true;
|
|
518
|
-
self._bodyConsumed = true;
|
|
519
|
-
let buf;
|
|
520
|
-
if (typeof Buffer !== "undefined") {
|
|
521
|
-
buf = self._isBinary ? Buffer.from(self._body || "", 'binary') : Buffer.from(self._body || "");
|
|
522
|
-
}
|
|
523
|
-
else {
|
|
524
|
-
buf = self._body || "";
|
|
525
|
-
}
|
|
526
|
-
return { done: false, value: buf };
|
|
527
|
-
}
|
|
528
|
-
ended = true;
|
|
529
|
-
self._ended = true;
|
|
530
|
-
self.complete = true;
|
|
531
|
-
self.readable = false;
|
|
532
|
-
self.readableEnded = true;
|
|
533
|
-
return { done: true, value: undefined };
|
|
534
|
-
},
|
|
535
|
-
return() {
|
|
536
|
-
ended = true;
|
|
537
|
-
return Promise.resolve({ done: true, value: undefined });
|
|
538
|
-
},
|
|
539
|
-
throw(err) {
|
|
540
|
-
ended = true;
|
|
541
|
-
self.emit("error", err);
|
|
542
|
-
return Promise.resolve({ done: true, value: undefined });
|
|
543
|
-
},
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Polyfill of Node.js `http.ClientRequest`. Executes the request asynchronously
|
|
549
|
-
* via the `_networkHttpRequestRaw` bridge and emits a `response` event with
|
|
550
|
-
* an IncomingMessage. Supports Agent-based connection pooling, socket events,
|
|
551
|
-
* HTTP upgrade (101), and trailer headers.
|
|
552
|
-
*/
|
|
553
|
-
export class ClientRequest {
|
|
554
|
-
_options;
|
|
555
|
-
_callback;
|
|
556
|
-
_listeners = {};
|
|
557
|
-
_body = "";
|
|
558
|
-
_bodyBytes = 0;
|
|
559
|
-
_ended = false;
|
|
560
|
-
_agent;
|
|
561
|
-
_hostKey;
|
|
562
|
-
socket;
|
|
563
|
-
finished = false;
|
|
564
|
-
aborted = false;
|
|
565
|
-
constructor(options, callback) {
|
|
566
|
-
this._options = options;
|
|
567
|
-
this._callback = callback;
|
|
568
|
-
// Resolve agent: false = no agent, undefined = globalAgent, or explicit Agent
|
|
569
|
-
const agentOpt = options.agent;
|
|
570
|
-
if (agentOpt === false) {
|
|
571
|
-
this._agent = null;
|
|
572
|
-
}
|
|
573
|
-
else if (agentOpt instanceof Agent) {
|
|
574
|
-
this._agent = agentOpt;
|
|
575
|
-
}
|
|
576
|
-
else {
|
|
577
|
-
this._agent = _moduleGlobalAgent;
|
|
578
|
-
}
|
|
579
|
-
this._hostKey = this._agent ? this._agent._getHostKey(options) : "";
|
|
580
|
-
// Create socket-like object and emit 'socket' event
|
|
581
|
-
this.socket = new FakeSocket({
|
|
582
|
-
host: (options.hostname || options.host || "localhost"),
|
|
583
|
-
port: Number(options.port) || 80,
|
|
584
|
-
});
|
|
585
|
-
Promise.resolve().then(() => this._emit("socket", this.socket));
|
|
586
|
-
// Execute request asynchronously
|
|
587
|
-
Promise.resolve().then(() => this._execute());
|
|
588
|
-
}
|
|
589
|
-
async _execute() {
|
|
590
|
-
// Acquire agent slot before executing
|
|
591
|
-
if (this._agent) {
|
|
592
|
-
await this._agent._acquireSlot(this._hostKey);
|
|
593
|
-
}
|
|
594
|
-
try {
|
|
595
|
-
if (typeof _networkHttpRequestRaw === 'undefined') {
|
|
596
|
-
console.error('http/https request requires NetworkAdapter to be configured');
|
|
597
|
-
throw new Error('http/https request requires NetworkAdapter to be configured');
|
|
598
|
-
}
|
|
599
|
-
const url = this._buildUrl();
|
|
600
|
-
const tls = {};
|
|
601
|
-
if (this._options.rejectUnauthorized !== undefined) {
|
|
602
|
-
tls.rejectUnauthorized = this._options.rejectUnauthorized;
|
|
603
|
-
}
|
|
604
|
-
const optionsJson = JSON.stringify({
|
|
605
|
-
method: this._options.method || "GET",
|
|
606
|
-
headers: this._options.headers || {},
|
|
607
|
-
body: this._body || null,
|
|
608
|
-
...tls,
|
|
609
|
-
});
|
|
610
|
-
const responseJson = await _networkHttpRequestRaw.apply(undefined, [url, optionsJson], {
|
|
611
|
-
result: { promise: true },
|
|
612
|
-
});
|
|
613
|
-
const response = JSON.parse(responseJson);
|
|
614
|
-
this.finished = true;
|
|
615
|
-
// 101 Switching Protocols → fire 'upgrade' event
|
|
616
|
-
if (response.status === 101) {
|
|
617
|
-
const res = new IncomingMessage(response);
|
|
618
|
-
// Use UpgradeSocket for bidirectional data relay when socketId is available
|
|
619
|
-
let socket = this.socket;
|
|
620
|
-
if (response.upgradeSocketId != null) {
|
|
621
|
-
socket = new UpgradeSocket(response.upgradeSocketId, {
|
|
622
|
-
host: this._options.hostname,
|
|
623
|
-
port: Number(this._options.port) || 80,
|
|
624
|
-
});
|
|
625
|
-
upgradeSocketInstances.set(response.upgradeSocketId, socket);
|
|
626
|
-
}
|
|
627
|
-
const head = typeof Buffer !== "undefined"
|
|
628
|
-
? (response.body ? Buffer.from(response.body, "base64") : Buffer.alloc(0))
|
|
629
|
-
: new Uint8Array(0);
|
|
630
|
-
this._emit("upgrade", res, socket, head);
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
const res = new IncomingMessage(response);
|
|
634
|
-
if (this._callback) {
|
|
635
|
-
this._callback(res);
|
|
636
|
-
}
|
|
637
|
-
this._emit("response", res);
|
|
638
|
-
}
|
|
639
|
-
catch (err) {
|
|
640
|
-
this._emit("error", err);
|
|
641
|
-
}
|
|
642
|
-
finally {
|
|
643
|
-
// Release agent slot
|
|
644
|
-
if (this._agent) {
|
|
645
|
-
this._agent._releaseSlot(this._hostKey);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
_buildUrl() {
|
|
650
|
-
const opts = this._options;
|
|
651
|
-
const protocol = opts.protocol || (opts.port === 443 ? "https:" : "http:");
|
|
652
|
-
const host = opts.hostname || opts.host || "localhost";
|
|
653
|
-
const port = opts.port ? ":" + opts.port : "";
|
|
654
|
-
const path = opts.path || "/";
|
|
655
|
-
return protocol + "//" + host + port + path;
|
|
656
|
-
}
|
|
657
|
-
on(event, listener) {
|
|
658
|
-
if (!this._listeners[event])
|
|
659
|
-
this._listeners[event] = [];
|
|
660
|
-
this._listeners[event].push(listener);
|
|
661
|
-
return this;
|
|
662
|
-
}
|
|
663
|
-
once(event, listener) {
|
|
664
|
-
const wrapper = (...args) => {
|
|
665
|
-
this.off(event, wrapper);
|
|
666
|
-
listener(...args);
|
|
667
|
-
};
|
|
668
|
-
return this.on(event, wrapper);
|
|
669
|
-
}
|
|
670
|
-
off(event, listener) {
|
|
671
|
-
if (this._listeners[event]) {
|
|
672
|
-
const idx = this._listeners[event].indexOf(listener);
|
|
673
|
-
if (idx !== -1)
|
|
674
|
-
this._listeners[event].splice(idx, 1);
|
|
675
|
-
}
|
|
676
|
-
return this;
|
|
677
|
-
}
|
|
678
|
-
_emit(event, ...args) {
|
|
679
|
-
if (this._listeners[event]) {
|
|
680
|
-
this._listeners[event].forEach((fn) => fn(...args));
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
write(data) {
|
|
684
|
-
const addedBytes = typeof Buffer !== "undefined" ? Buffer.byteLength(data) : data.length;
|
|
685
|
-
if (this._bodyBytes + addedBytes > MAX_HTTP_BODY_BYTES) {
|
|
686
|
-
throw new Error("ERR_HTTP_BODY_TOO_LARGE: request body exceeds " + MAX_HTTP_BODY_BYTES + " byte limit");
|
|
687
|
-
}
|
|
688
|
-
this._body += data;
|
|
689
|
-
this._bodyBytes += addedBytes;
|
|
690
|
-
return true;
|
|
691
|
-
}
|
|
692
|
-
end(data) {
|
|
693
|
-
if (data)
|
|
694
|
-
this.write(data);
|
|
695
|
-
this._ended = true;
|
|
696
|
-
return this;
|
|
697
|
-
}
|
|
698
|
-
abort() {
|
|
699
|
-
this.aborted = true;
|
|
700
|
-
}
|
|
701
|
-
setTimeout(_timeout) {
|
|
702
|
-
return this;
|
|
703
|
-
}
|
|
704
|
-
setNoDelay() {
|
|
705
|
-
return this;
|
|
706
|
-
}
|
|
707
|
-
setSocketKeepAlive() {
|
|
708
|
-
return this;
|
|
709
|
-
}
|
|
710
|
-
flushHeaders() {
|
|
711
|
-
// no-op
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
// Minimal socket-like object emitted by ClientRequest 'socket' event
|
|
715
|
-
class FakeSocket {
|
|
716
|
-
remoteAddress;
|
|
717
|
-
remotePort;
|
|
718
|
-
localAddress = "127.0.0.1";
|
|
719
|
-
localPort = 0;
|
|
720
|
-
connecting = false;
|
|
721
|
-
destroyed = false;
|
|
722
|
-
writable = true;
|
|
723
|
-
readable = true;
|
|
724
|
-
_listeners = {};
|
|
725
|
-
constructor(options) {
|
|
726
|
-
this.remoteAddress = options?.host || "127.0.0.1";
|
|
727
|
-
this.remotePort = options?.port || 80;
|
|
728
|
-
}
|
|
729
|
-
setTimeout(_ms, _cb) { return this; }
|
|
730
|
-
setNoDelay(_noDelay) { return this; }
|
|
731
|
-
setKeepAlive(_enable, _delay) { return this; }
|
|
732
|
-
on(event, listener) {
|
|
733
|
-
if (!this._listeners[event])
|
|
734
|
-
this._listeners[event] = [];
|
|
735
|
-
this._listeners[event].push(listener);
|
|
736
|
-
return this;
|
|
737
|
-
}
|
|
738
|
-
once(event, listener) {
|
|
739
|
-
const wrapper = (...args) => {
|
|
740
|
-
this.off(event, wrapper);
|
|
741
|
-
listener(...args);
|
|
742
|
-
};
|
|
743
|
-
return this.on(event, wrapper);
|
|
744
|
-
}
|
|
745
|
-
off(event, listener) {
|
|
746
|
-
if (this._listeners[event]) {
|
|
747
|
-
const idx = this._listeners[event].indexOf(listener);
|
|
748
|
-
if (idx !== -1)
|
|
749
|
-
this._listeners[event].splice(idx, 1);
|
|
750
|
-
}
|
|
751
|
-
return this;
|
|
752
|
-
}
|
|
753
|
-
removeListener(event, listener) {
|
|
754
|
-
return this.off(event, listener);
|
|
755
|
-
}
|
|
756
|
-
emit(event, ...args) {
|
|
757
|
-
const handlers = this._listeners[event];
|
|
758
|
-
if (handlers)
|
|
759
|
-
handlers.slice().forEach((fn) => fn(...args));
|
|
760
|
-
return handlers !== undefined && handlers.length > 0;
|
|
761
|
-
}
|
|
762
|
-
write(_data) { return true; }
|
|
763
|
-
end() { return this; }
|
|
764
|
-
destroy() {
|
|
765
|
-
this.destroyed = true;
|
|
766
|
-
this.writable = false;
|
|
767
|
-
this.readable = false;
|
|
768
|
-
return this;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
// HTTP Agent with connection pooling via maxSockets
|
|
772
|
-
class Agent {
|
|
773
|
-
maxSockets;
|
|
774
|
-
maxFreeSockets;
|
|
775
|
-
keepAlive;
|
|
776
|
-
keepAliveMsecs;
|
|
777
|
-
timeout;
|
|
778
|
-
requests;
|
|
779
|
-
sockets;
|
|
780
|
-
freeSockets;
|
|
781
|
-
// Per-host active count and pending queue
|
|
782
|
-
_activeCounts = new Map();
|
|
783
|
-
_queues = new Map();
|
|
784
|
-
constructor(options) {
|
|
785
|
-
this.keepAlive = options?.keepAlive ?? false;
|
|
786
|
-
this.keepAliveMsecs = options?.keepAliveMsecs ?? 1000;
|
|
787
|
-
this.maxSockets = options?.maxSockets ?? Infinity;
|
|
788
|
-
this.maxFreeSockets = options?.maxFreeSockets ?? 256;
|
|
789
|
-
this.timeout = options?.timeout ?? -1;
|
|
790
|
-
this.requests = {};
|
|
791
|
-
this.sockets = {};
|
|
792
|
-
this.freeSockets = {};
|
|
793
|
-
}
|
|
794
|
-
_getHostKey(options) {
|
|
795
|
-
const host = options.hostname || options.host || "localhost";
|
|
796
|
-
const port = options.port || 80;
|
|
797
|
-
return `${host}:${port}`;
|
|
798
|
-
}
|
|
799
|
-
// Wait for an available slot; resolves immediately if under maxSockets
|
|
800
|
-
_acquireSlot(hostKey) {
|
|
801
|
-
const active = this._activeCounts.get(hostKey) || 0;
|
|
802
|
-
if (active < this.maxSockets) {
|
|
803
|
-
this._activeCounts.set(hostKey, active + 1);
|
|
804
|
-
return Promise.resolve();
|
|
805
|
-
}
|
|
806
|
-
return new Promise((resolve) => {
|
|
807
|
-
let queue = this._queues.get(hostKey);
|
|
808
|
-
if (!queue) {
|
|
809
|
-
queue = [];
|
|
810
|
-
this._queues.set(hostKey, queue);
|
|
811
|
-
}
|
|
812
|
-
queue.push(resolve);
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
// Release a slot; dequeues next pending request if any
|
|
816
|
-
_releaseSlot(hostKey) {
|
|
817
|
-
const queue = this._queues.get(hostKey);
|
|
818
|
-
if (queue && queue.length > 0) {
|
|
819
|
-
const next = queue.shift();
|
|
820
|
-
if (queue.length === 0)
|
|
821
|
-
this._queues.delete(hostKey);
|
|
822
|
-
next();
|
|
823
|
-
}
|
|
824
|
-
else {
|
|
825
|
-
const active = this._activeCounts.get(hostKey) || 1;
|
|
826
|
-
const next = active - 1;
|
|
827
|
-
if (next <= 0)
|
|
828
|
-
this._activeCounts.delete(hostKey);
|
|
829
|
-
else
|
|
830
|
-
this._activeCounts.set(hostKey, next);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
destroy() {
|
|
834
|
-
this._activeCounts.clear();
|
|
835
|
-
for (const [, queue] of this._queues) {
|
|
836
|
-
queue.length = 0;
|
|
837
|
-
}
|
|
838
|
-
this._queues.clear();
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
let nextServerId = 1;
|
|
842
|
-
const serverRequestListeners = new Map();
|
|
843
|
-
// Server instances indexed by serverId — used by upgrade dispatch to emit 'upgrade' events
|
|
844
|
-
const serverInstances = new Map();
|
|
845
|
-
class ServerIncomingMessage {
|
|
846
|
-
headers;
|
|
847
|
-
rawHeaders;
|
|
848
|
-
method;
|
|
849
|
-
url;
|
|
850
|
-
socket;
|
|
851
|
-
connection;
|
|
852
|
-
rawBody;
|
|
853
|
-
destroyed = false;
|
|
854
|
-
errored;
|
|
855
|
-
readable = true;
|
|
856
|
-
httpVersion = "1.1";
|
|
857
|
-
httpVersionMajor = 1;
|
|
858
|
-
httpVersionMinor = 1;
|
|
859
|
-
complete = true;
|
|
860
|
-
// Readable stream state stub for frameworks that inspect internal state
|
|
861
|
-
_readableState = { flowing: null, length: 0, ended: false, objectMode: false };
|
|
862
|
-
_listeners = {};
|
|
863
|
-
constructor(request) {
|
|
864
|
-
this.headers = request.headers || {};
|
|
865
|
-
this.rawHeaders = request.rawHeaders || [];
|
|
866
|
-
if (!Array.isArray(this.rawHeaders) || this.rawHeaders.length % 2 !== 0) {
|
|
867
|
-
this.rawHeaders = [];
|
|
868
|
-
}
|
|
869
|
-
this.method = request.method || "GET";
|
|
870
|
-
this.url = request.url || "/";
|
|
871
|
-
const fakeSocket = {
|
|
872
|
-
encrypted: false,
|
|
873
|
-
remoteAddress: "127.0.0.1",
|
|
874
|
-
remotePort: 0,
|
|
875
|
-
writable: true,
|
|
876
|
-
on() { return fakeSocket; },
|
|
877
|
-
once() { return fakeSocket; },
|
|
878
|
-
removeListener() { return fakeSocket; },
|
|
879
|
-
destroy() { },
|
|
880
|
-
end() { },
|
|
881
|
-
};
|
|
882
|
-
this.socket = fakeSocket;
|
|
883
|
-
this.connection = fakeSocket;
|
|
884
|
-
const rawHost = this.headers.host;
|
|
885
|
-
if (typeof rawHost === "string" && rawHost.includes(",")) {
|
|
886
|
-
this.headers.host = rawHost.split(",")[0].trim();
|
|
887
|
-
}
|
|
888
|
-
if (!this.headers.host) {
|
|
889
|
-
this.headers.host = "127.0.0.1";
|
|
890
|
-
}
|
|
891
|
-
if (this.rawHeaders.length === 0) {
|
|
892
|
-
Object.entries(this.headers).forEach(([key, value]) => {
|
|
893
|
-
this.rawHeaders.push(key, value);
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
if (request.bodyBase64 && typeof Buffer !== "undefined") {
|
|
897
|
-
this.rawBody = Buffer.from(request.bodyBase64, "base64");
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
on(event, listener) {
|
|
901
|
-
if (!this._listeners[event])
|
|
902
|
-
this._listeners[event] = [];
|
|
903
|
-
this._listeners[event].push(listener);
|
|
904
|
-
return this;
|
|
905
|
-
}
|
|
906
|
-
once(event, listener) {
|
|
907
|
-
const wrapped = (...args) => {
|
|
908
|
-
this.off(event, wrapped);
|
|
909
|
-
listener(...args);
|
|
910
|
-
};
|
|
911
|
-
return this.on(event, wrapped);
|
|
912
|
-
}
|
|
913
|
-
off(event, listener) {
|
|
914
|
-
const listeners = this._listeners[event];
|
|
915
|
-
if (!listeners)
|
|
916
|
-
return this;
|
|
917
|
-
const index = listeners.indexOf(listener);
|
|
918
|
-
if (index !== -1)
|
|
919
|
-
listeners.splice(index, 1);
|
|
920
|
-
return this;
|
|
921
|
-
}
|
|
922
|
-
removeListener(event, listener) {
|
|
923
|
-
return this.off(event, listener);
|
|
924
|
-
}
|
|
925
|
-
emit(event, ...args) {
|
|
926
|
-
const listeners = this._listeners[event];
|
|
927
|
-
if (!listeners || listeners.length === 0)
|
|
928
|
-
return false;
|
|
929
|
-
listeners.slice().forEach((fn) => fn(...args));
|
|
930
|
-
return true;
|
|
931
|
-
}
|
|
932
|
-
// Readable stream stubs for framework compatibility
|
|
933
|
-
unpipe() { return this; }
|
|
934
|
-
pause() { return this; }
|
|
935
|
-
resume() { return this; }
|
|
936
|
-
read() { return null; }
|
|
937
|
-
pipe(dest) { return dest; }
|
|
938
|
-
isPaused() { return false; }
|
|
939
|
-
setEncoding() { return this; }
|
|
940
|
-
destroy(err) {
|
|
941
|
-
this.destroyed = true;
|
|
942
|
-
this.errored = err;
|
|
943
|
-
if (err) {
|
|
944
|
-
this.emit("error", err);
|
|
945
|
-
}
|
|
946
|
-
this.emit("close");
|
|
947
|
-
return this;
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
/**
|
|
951
|
-
* Sandbox-side response writer for HTTP server requests. Collects headers and
|
|
952
|
-
* body chunks, then serializes to JSON for transfer back to the host.
|
|
953
|
-
*/
|
|
954
|
-
class ServerResponseBridge {
|
|
955
|
-
statusCode = 200;
|
|
956
|
-
statusMessage = "OK";
|
|
957
|
-
headersSent = false;
|
|
958
|
-
writable = true;
|
|
959
|
-
writableFinished = false;
|
|
960
|
-
_headers = new Map();
|
|
961
|
-
_chunks = [];
|
|
962
|
-
_chunksBytes = 0;
|
|
963
|
-
_listeners = {};
|
|
964
|
-
_closedPromise;
|
|
965
|
-
_resolveClosed = null;
|
|
966
|
-
constructor() {
|
|
967
|
-
this._closedPromise = new Promise((resolve) => {
|
|
968
|
-
this._resolveClosed = resolve;
|
|
969
|
-
});
|
|
970
|
-
}
|
|
971
|
-
on(event, listener) {
|
|
972
|
-
if (!this._listeners[event])
|
|
973
|
-
this._listeners[event] = [];
|
|
974
|
-
this._listeners[event].push(listener);
|
|
975
|
-
return this;
|
|
976
|
-
}
|
|
977
|
-
once(event, listener) {
|
|
978
|
-
const wrapped = (...args) => {
|
|
979
|
-
this.off(event, wrapped);
|
|
980
|
-
listener(...args);
|
|
981
|
-
};
|
|
982
|
-
return this.on(event, wrapped);
|
|
983
|
-
}
|
|
984
|
-
off(event, listener) {
|
|
985
|
-
const listeners = this._listeners[event];
|
|
986
|
-
if (!listeners)
|
|
987
|
-
return this;
|
|
988
|
-
const index = listeners.indexOf(listener);
|
|
989
|
-
if (index !== -1)
|
|
990
|
-
listeners.splice(index, 1);
|
|
991
|
-
return this;
|
|
992
|
-
}
|
|
993
|
-
removeListener(event, listener) {
|
|
994
|
-
return this.off(event, listener);
|
|
995
|
-
}
|
|
996
|
-
emit(event, ...args) {
|
|
997
|
-
const listeners = this._listeners[event];
|
|
998
|
-
if (!listeners || listeners.length === 0)
|
|
999
|
-
return false;
|
|
1000
|
-
listeners.slice().forEach((fn) => fn(...args));
|
|
1001
|
-
return true;
|
|
1002
|
-
}
|
|
1003
|
-
_emit(event, ...args) {
|
|
1004
|
-
this.emit(event, ...args);
|
|
1005
|
-
}
|
|
1006
|
-
writeHead(statusCode, headers) {
|
|
1007
|
-
this.statusCode = statusCode;
|
|
1008
|
-
if (headers) {
|
|
1009
|
-
if (Array.isArray(headers)) {
|
|
1010
|
-
headers.forEach(([key, value]) => this.setHeader(key, value));
|
|
1011
|
-
}
|
|
1012
|
-
else {
|
|
1013
|
-
Object.entries(headers).forEach(([key, value]) => this.setHeader(key, value));
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
this.headersSent = true;
|
|
1017
|
-
return this;
|
|
1018
|
-
}
|
|
1019
|
-
setHeader(name, value) {
|
|
1020
|
-
const normalized = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1021
|
-
this._headers.set(name.toLowerCase(), normalized);
|
|
1022
|
-
return this;
|
|
1023
|
-
}
|
|
1024
|
-
getHeader(name) {
|
|
1025
|
-
return this._headers.get(name.toLowerCase());
|
|
1026
|
-
}
|
|
1027
|
-
hasHeader(name) {
|
|
1028
|
-
return this._headers.has(name.toLowerCase());
|
|
1029
|
-
}
|
|
1030
|
-
removeHeader(name) {
|
|
1031
|
-
this._headers.delete(name.toLowerCase());
|
|
1032
|
-
}
|
|
1033
|
-
write(chunk) {
|
|
1034
|
-
if (chunk == null)
|
|
1035
|
-
return true;
|
|
1036
|
-
this.headersSent = true;
|
|
1037
|
-
const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
1038
|
-
if (this._chunksBytes + buf.byteLength > MAX_HTTP_BODY_BYTES) {
|
|
1039
|
-
throw new Error("ERR_HTTP_BODY_TOO_LARGE: response body exceeds " + MAX_HTTP_BODY_BYTES + " byte limit");
|
|
1040
|
-
}
|
|
1041
|
-
this._chunks.push(buf);
|
|
1042
|
-
this._chunksBytes += buf.byteLength;
|
|
1043
|
-
return true;
|
|
1044
|
-
}
|
|
1045
|
-
end(chunk) {
|
|
1046
|
-
if (chunk != null) {
|
|
1047
|
-
this.write(chunk);
|
|
1048
|
-
}
|
|
1049
|
-
this._finalize();
|
|
1050
|
-
return this;
|
|
1051
|
-
}
|
|
1052
|
-
getHeaderNames() {
|
|
1053
|
-
return Array.from(this._headers.keys());
|
|
1054
|
-
}
|
|
1055
|
-
getHeaders() {
|
|
1056
|
-
const result = {};
|
|
1057
|
-
for (const [key, value] of this._headers)
|
|
1058
|
-
result[key] = value;
|
|
1059
|
-
return result;
|
|
1060
|
-
}
|
|
1061
|
-
// Writable stream state stub for frameworks that inspect internal state
|
|
1062
|
-
_writableState = { length: 0, ended: false, finished: false, objectMode: false, corked: 0 };
|
|
1063
|
-
// Fake socket for frameworks that access res.socket/res.connection
|
|
1064
|
-
socket = {
|
|
1065
|
-
writable: true,
|
|
1066
|
-
on: () => this.socket,
|
|
1067
|
-
once: () => this.socket,
|
|
1068
|
-
removeListener: () => this.socket,
|
|
1069
|
-
destroy: () => { },
|
|
1070
|
-
end: () => { },
|
|
1071
|
-
cork: () => { },
|
|
1072
|
-
uncork: () => { },
|
|
1073
|
-
write: () => true,
|
|
1074
|
-
};
|
|
1075
|
-
connection = this.socket;
|
|
1076
|
-
// Node.js http.ServerResponse socket/stream compatibility stubs
|
|
1077
|
-
assignSocket() { }
|
|
1078
|
-
detachSocket() { }
|
|
1079
|
-
writeContinue() { }
|
|
1080
|
-
writeProcessing() { }
|
|
1081
|
-
addTrailers() { }
|
|
1082
|
-
cork() { }
|
|
1083
|
-
uncork() { }
|
|
1084
|
-
setTimeout(_msecs) { return this; }
|
|
1085
|
-
flushHeaders() {
|
|
1086
|
-
this.headersSent = true;
|
|
1087
|
-
}
|
|
1088
|
-
destroy(err) {
|
|
1089
|
-
if (err) {
|
|
1090
|
-
this._emit("error", err);
|
|
1091
|
-
}
|
|
1092
|
-
this._finalize();
|
|
1093
|
-
}
|
|
1094
|
-
async waitForClose() {
|
|
1095
|
-
await this._closedPromise;
|
|
1096
|
-
}
|
|
1097
|
-
serialize() {
|
|
1098
|
-
const bodyBuffer = this._chunks.length > 0 ? Buffer.concat(this._chunks) : Buffer.alloc(0);
|
|
1099
|
-
return {
|
|
1100
|
-
status: this.statusCode,
|
|
1101
|
-
headers: Array.from(this._headers.entries()),
|
|
1102
|
-
body: bodyBuffer.toString("base64"),
|
|
1103
|
-
bodyEncoding: "base64",
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
_finalize() {
|
|
1107
|
-
if (this.writableFinished) {
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
this.writableFinished = true;
|
|
1111
|
-
this.writable = false;
|
|
1112
|
-
this._emit("finish");
|
|
1113
|
-
this._emit("close");
|
|
1114
|
-
this._resolveClosed?.();
|
|
1115
|
-
this._resolveClosed = null;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
/**
|
|
1119
|
-
* Polyfill of Node.js `http.Server`. Delegates actual listening to the host
|
|
1120
|
-
* via the `_networkHttpServerListenRaw` bridge. Incoming requests are
|
|
1121
|
-
* dispatched through `_httpServerDispatch` which invokes the request listener
|
|
1122
|
-
* inside the isolate. Registers an active handle to keep the sandbox alive.
|
|
1123
|
-
*/
|
|
1124
|
-
class Server {
|
|
1125
|
-
listening = false;
|
|
1126
|
-
_listeners = {};
|
|
1127
|
-
_serverId;
|
|
1128
|
-
_listenPromise = null;
|
|
1129
|
-
_address = null;
|
|
1130
|
-
_handleId = null;
|
|
1131
|
-
constructor(requestListener) {
|
|
1132
|
-
this._serverId = nextServerId++;
|
|
1133
|
-
if (requestListener) {
|
|
1134
|
-
serverRequestListeners.set(this._serverId, requestListener);
|
|
1135
|
-
}
|
|
1136
|
-
else {
|
|
1137
|
-
serverRequestListeners.set(this._serverId, () => undefined);
|
|
1138
|
-
}
|
|
1139
|
-
serverInstances.set(this._serverId, this);
|
|
1140
|
-
}
|
|
1141
|
-
/** @internal Emit an event — used by upgrade dispatch to fire 'upgrade' events. */
|
|
1142
|
-
_emit(event, ...args) {
|
|
1143
|
-
const listeners = this._listeners[event];
|
|
1144
|
-
if (!listeners || listeners.length === 0)
|
|
1145
|
-
return;
|
|
1146
|
-
listeners.slice().forEach((listener) => listener(...args));
|
|
1147
|
-
}
|
|
1148
|
-
async _start(port, hostname) {
|
|
1149
|
-
if (typeof _networkHttpServerListenRaw === "undefined") {
|
|
1150
|
-
throw new Error("http.createServer requires NetworkAdapter.httpServerListen support");
|
|
1151
|
-
}
|
|
1152
|
-
const resultJson = await _networkHttpServerListenRaw.apply(undefined, [JSON.stringify({ serverId: this._serverId, port, hostname })], { result: { promise: true } });
|
|
1153
|
-
const result = JSON.parse(resultJson);
|
|
1154
|
-
this._address = result.address;
|
|
1155
|
-
this.listening = true;
|
|
1156
|
-
this._handleId = `http-server:${this._serverId}`;
|
|
1157
|
-
if (typeof _registerHandle === "function") {
|
|
1158
|
-
_registerHandle(this._handleId, "http server");
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
listen(portOrCb, hostOrCb, cb) {
|
|
1162
|
-
const port = typeof portOrCb === "number" ? portOrCb : undefined;
|
|
1163
|
-
const hostname = typeof hostOrCb === "string" ? hostOrCb : undefined;
|
|
1164
|
-
const callback = typeof cb === "function"
|
|
1165
|
-
? cb
|
|
1166
|
-
: typeof hostOrCb === "function"
|
|
1167
|
-
? hostOrCb
|
|
1168
|
-
: typeof portOrCb === "function"
|
|
1169
|
-
? portOrCb
|
|
1170
|
-
: undefined;
|
|
1171
|
-
if (!this._listenPromise) {
|
|
1172
|
-
this._listenPromise = this._start(port, hostname)
|
|
1173
|
-
.then(() => {
|
|
1174
|
-
this._emit("listening");
|
|
1175
|
-
callback?.();
|
|
1176
|
-
})
|
|
1177
|
-
.catch((error) => {
|
|
1178
|
-
this._emit("error", error);
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
return this;
|
|
1182
|
-
}
|
|
1183
|
-
close(cb) {
|
|
1184
|
-
const run = async () => {
|
|
1185
|
-
try {
|
|
1186
|
-
if (this._listenPromise) {
|
|
1187
|
-
await this._listenPromise;
|
|
1188
|
-
}
|
|
1189
|
-
if (this.listening && typeof _networkHttpServerCloseRaw !== "undefined") {
|
|
1190
|
-
await _networkHttpServerCloseRaw.apply(undefined, [this._serverId], {
|
|
1191
|
-
result: { promise: true },
|
|
1192
|
-
});
|
|
1193
|
-
}
|
|
1194
|
-
this.listening = false;
|
|
1195
|
-
this._address = null;
|
|
1196
|
-
serverInstances.delete(this._serverId);
|
|
1197
|
-
if (this._handleId && typeof _unregisterHandle === "function") {
|
|
1198
|
-
_unregisterHandle(this._handleId);
|
|
1199
|
-
}
|
|
1200
|
-
this._handleId = null;
|
|
1201
|
-
cb?.();
|
|
1202
|
-
this._emit("close");
|
|
1203
|
-
}
|
|
1204
|
-
catch (err) {
|
|
1205
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1206
|
-
cb?.(error);
|
|
1207
|
-
this._emit("error", error);
|
|
1208
|
-
}
|
|
1209
|
-
};
|
|
1210
|
-
void run();
|
|
1211
|
-
return this;
|
|
1212
|
-
}
|
|
1213
|
-
address() {
|
|
1214
|
-
return this._address;
|
|
1215
|
-
}
|
|
1216
|
-
on(event, listener) {
|
|
1217
|
-
if (!this._listeners[event])
|
|
1218
|
-
this._listeners[event] = [];
|
|
1219
|
-
this._listeners[event].push(listener);
|
|
1220
|
-
return this;
|
|
1221
|
-
}
|
|
1222
|
-
once(event, listener) {
|
|
1223
|
-
const wrapped = (...args) => {
|
|
1224
|
-
this.off(event, wrapped);
|
|
1225
|
-
listener(...args);
|
|
1226
|
-
};
|
|
1227
|
-
return this.on(event, wrapped);
|
|
1228
|
-
}
|
|
1229
|
-
off(event, listener) {
|
|
1230
|
-
const listeners = this._listeners[event];
|
|
1231
|
-
if (!listeners)
|
|
1232
|
-
return this;
|
|
1233
|
-
const index = listeners.indexOf(listener);
|
|
1234
|
-
if (index !== -1)
|
|
1235
|
-
listeners.splice(index, 1);
|
|
1236
|
-
return this;
|
|
1237
|
-
}
|
|
1238
|
-
removeListener(event, listener) {
|
|
1239
|
-
return this.off(event, listener);
|
|
1240
|
-
}
|
|
1241
|
-
removeAllListeners(event) {
|
|
1242
|
-
if (event) {
|
|
1243
|
-
delete this._listeners[event];
|
|
1244
|
-
}
|
|
1245
|
-
else {
|
|
1246
|
-
this._listeners = {};
|
|
1247
|
-
}
|
|
1248
|
-
return this;
|
|
1249
|
-
}
|
|
1250
|
-
// Node.js Server timeout properties (no-op in sandbox)
|
|
1251
|
-
keepAliveTimeout = 5000;
|
|
1252
|
-
requestTimeout = 300000;
|
|
1253
|
-
headersTimeout = 60000;
|
|
1254
|
-
timeout = 0;
|
|
1255
|
-
maxRequestsPerSocket = 0;
|
|
1256
|
-
setTimeout(_msecs, _callback) {
|
|
1257
|
-
if (typeof _msecs === "number")
|
|
1258
|
-
this.timeout = _msecs;
|
|
1259
|
-
return this;
|
|
1260
|
-
}
|
|
1261
|
-
ref() {
|
|
1262
|
-
return this;
|
|
1263
|
-
}
|
|
1264
|
-
unref() {
|
|
1265
|
-
return this;
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
/** Route an incoming HTTP request to the server's request listener and return the serialized response. */
|
|
1269
|
-
async function dispatchServerRequest(serverId, requestJson) {
|
|
1270
|
-
const listener = serverRequestListeners.get(serverId);
|
|
1271
|
-
if (!listener) {
|
|
1272
|
-
throw new Error(`Unknown HTTP server: ${serverId}`);
|
|
1273
|
-
}
|
|
1274
|
-
const request = JSON.parse(requestJson);
|
|
1275
|
-
const incoming = new ServerIncomingMessage(request);
|
|
1276
|
-
const outgoing = new ServerResponseBridge();
|
|
1277
|
-
try {
|
|
1278
|
-
// Call listener synchronously — frameworks register event handlers here
|
|
1279
|
-
const listenerResult = listener(incoming, outgoing);
|
|
1280
|
-
// Emit readable stream events so body-parsing middleware (e.g. express.json()) can proceed
|
|
1281
|
-
if (incoming.rawBody && incoming.rawBody.length > 0) {
|
|
1282
|
-
incoming.emit("data", incoming.rawBody);
|
|
1283
|
-
}
|
|
1284
|
-
incoming.emit("end");
|
|
1285
|
-
await Promise.resolve(listenerResult);
|
|
1286
|
-
}
|
|
1287
|
-
catch (err) {
|
|
1288
|
-
outgoing.statusCode = 500;
|
|
1289
|
-
try {
|
|
1290
|
-
outgoing.end(err instanceof Error ? `Error: ${err.message}` : "Error");
|
|
1291
|
-
}
|
|
1292
|
-
catch {
|
|
1293
|
-
// Body cap may prevent writing error — finalize without data
|
|
1294
|
-
if (!outgoing.writableFinished)
|
|
1295
|
-
outgoing.end();
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
if (!outgoing.writableFinished) {
|
|
1299
|
-
outgoing.end();
|
|
1300
|
-
}
|
|
1301
|
-
await outgoing.waitForClose();
|
|
1302
|
-
return JSON.stringify(outgoing.serialize());
|
|
1303
|
-
}
|
|
1304
|
-
// Upgrade socket for bidirectional data relay through the host bridge
|
|
1305
|
-
const upgradeSocketInstances = new Map();
|
|
1306
|
-
class UpgradeSocket {
|
|
1307
|
-
remoteAddress;
|
|
1308
|
-
remotePort;
|
|
1309
|
-
localAddress = "127.0.0.1";
|
|
1310
|
-
localPort = 0;
|
|
1311
|
-
connecting = false;
|
|
1312
|
-
destroyed = false;
|
|
1313
|
-
writable = true;
|
|
1314
|
-
readable = true;
|
|
1315
|
-
readyState = "open";
|
|
1316
|
-
bytesWritten = 0;
|
|
1317
|
-
_listeners = {};
|
|
1318
|
-
_socketId;
|
|
1319
|
-
// Readable stream state stub for ws compatibility (socketOnClose checks _readableState.endEmitted)
|
|
1320
|
-
_readableState = { endEmitted: false };
|
|
1321
|
-
_writableState = { finished: false, errorEmitted: false };
|
|
1322
|
-
constructor(socketId, options) {
|
|
1323
|
-
this._socketId = socketId;
|
|
1324
|
-
this.remoteAddress = options?.host || "127.0.0.1";
|
|
1325
|
-
this.remotePort = options?.port || 80;
|
|
1326
|
-
}
|
|
1327
|
-
setTimeout(_ms, _cb) { return this; }
|
|
1328
|
-
setNoDelay(_noDelay) { return this; }
|
|
1329
|
-
setKeepAlive(_enable, _delay) { return this; }
|
|
1330
|
-
ref() { return this; }
|
|
1331
|
-
unref() { return this; }
|
|
1332
|
-
cork() { }
|
|
1333
|
-
uncork() { }
|
|
1334
|
-
pause() { return this; }
|
|
1335
|
-
resume() { return this; }
|
|
1336
|
-
address() {
|
|
1337
|
-
return { address: this.localAddress, family: "IPv4", port: this.localPort };
|
|
1338
|
-
}
|
|
1339
|
-
on(event, listener) {
|
|
1340
|
-
if (!this._listeners[event])
|
|
1341
|
-
this._listeners[event] = [];
|
|
1342
|
-
this._listeners[event].push(listener);
|
|
1343
|
-
return this;
|
|
1344
|
-
}
|
|
1345
|
-
addListener(event, listener) {
|
|
1346
|
-
return this.on(event, listener);
|
|
1347
|
-
}
|
|
1348
|
-
once(event, listener) {
|
|
1349
|
-
const wrapper = (...args) => {
|
|
1350
|
-
this.off(event, wrapper);
|
|
1351
|
-
listener(...args);
|
|
1352
|
-
};
|
|
1353
|
-
return this.on(event, wrapper);
|
|
1354
|
-
}
|
|
1355
|
-
off(event, listener) {
|
|
1356
|
-
if (this._listeners[event]) {
|
|
1357
|
-
const idx = this._listeners[event].indexOf(listener);
|
|
1358
|
-
if (idx !== -1)
|
|
1359
|
-
this._listeners[event].splice(idx, 1);
|
|
1360
|
-
}
|
|
1361
|
-
return this;
|
|
1362
|
-
}
|
|
1363
|
-
removeListener(event, listener) {
|
|
1364
|
-
return this.off(event, listener);
|
|
1365
|
-
}
|
|
1366
|
-
removeAllListeners(event) {
|
|
1367
|
-
if (event) {
|
|
1368
|
-
delete this._listeners[event];
|
|
1369
|
-
}
|
|
1370
|
-
else {
|
|
1371
|
-
this._listeners = {};
|
|
1372
|
-
}
|
|
1373
|
-
return this;
|
|
1374
|
-
}
|
|
1375
|
-
emit(event, ...args) {
|
|
1376
|
-
const handlers = this._listeners[event];
|
|
1377
|
-
if (handlers)
|
|
1378
|
-
handlers.slice().forEach((fn) => fn.call(this, ...args));
|
|
1379
|
-
return handlers !== undefined && handlers.length > 0;
|
|
1380
|
-
}
|
|
1381
|
-
listenerCount(event) {
|
|
1382
|
-
return this._listeners[event]?.length || 0;
|
|
1383
|
-
}
|
|
1384
|
-
write(data, encodingOrCb, cb) {
|
|
1385
|
-
if (this.destroyed)
|
|
1386
|
-
return false;
|
|
1387
|
-
const callback = typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
1388
|
-
if (typeof _upgradeSocketWriteRaw !== "undefined") {
|
|
1389
|
-
let base64;
|
|
1390
|
-
if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
|
|
1391
|
-
base64 = data.toString("base64");
|
|
1392
|
-
}
|
|
1393
|
-
else if (typeof data === "string") {
|
|
1394
|
-
base64 = typeof Buffer !== "undefined" ? Buffer.from(data).toString("base64") : btoa(data);
|
|
1395
|
-
}
|
|
1396
|
-
else if (data instanceof Uint8Array) {
|
|
1397
|
-
base64 = typeof Buffer !== "undefined" ? Buffer.from(data).toString("base64") : btoa(String.fromCharCode(...data));
|
|
1398
|
-
}
|
|
1399
|
-
else {
|
|
1400
|
-
base64 = typeof Buffer !== "undefined" ? Buffer.from(String(data)).toString("base64") : btoa(String(data));
|
|
1401
|
-
}
|
|
1402
|
-
this.bytesWritten += base64.length;
|
|
1403
|
-
_upgradeSocketWriteRaw.applySync(undefined, [this._socketId, base64]);
|
|
1404
|
-
}
|
|
1405
|
-
if (callback)
|
|
1406
|
-
callback();
|
|
1407
|
-
return true;
|
|
1408
|
-
}
|
|
1409
|
-
end(data) {
|
|
1410
|
-
if (data)
|
|
1411
|
-
this.write(data);
|
|
1412
|
-
if (typeof _upgradeSocketEndRaw !== "undefined" && !this.destroyed) {
|
|
1413
|
-
_upgradeSocketEndRaw.applySync(undefined, [this._socketId]);
|
|
1414
|
-
}
|
|
1415
|
-
this.writable = false;
|
|
1416
|
-
this.emit("finish");
|
|
1417
|
-
return this;
|
|
1418
|
-
}
|
|
1419
|
-
destroy(err) {
|
|
1420
|
-
if (this.destroyed)
|
|
1421
|
-
return this;
|
|
1422
|
-
this.destroyed = true;
|
|
1423
|
-
this.writable = false;
|
|
1424
|
-
this.readable = false;
|
|
1425
|
-
this._readableState.endEmitted = true;
|
|
1426
|
-
this._writableState.finished = true;
|
|
1427
|
-
if (typeof _upgradeSocketDestroyRaw !== "undefined") {
|
|
1428
|
-
_upgradeSocketDestroyRaw.applySync(undefined, [this._socketId]);
|
|
1429
|
-
}
|
|
1430
|
-
upgradeSocketInstances.delete(this._socketId);
|
|
1431
|
-
if (err)
|
|
1432
|
-
this.emit("error", err);
|
|
1433
|
-
this.emit("close", false);
|
|
1434
|
-
return this;
|
|
1435
|
-
}
|
|
1436
|
-
// Push data received from the host into this socket
|
|
1437
|
-
_pushData(data) {
|
|
1438
|
-
this.emit("data", data);
|
|
1439
|
-
}
|
|
1440
|
-
// Signal end-of-stream from the host
|
|
1441
|
-
_pushEnd() {
|
|
1442
|
-
this.readable = false;
|
|
1443
|
-
this._readableState.endEmitted = true;
|
|
1444
|
-
this._writableState.finished = true;
|
|
1445
|
-
this.emit("end");
|
|
1446
|
-
this.emit("close", false);
|
|
1447
|
-
upgradeSocketInstances.delete(this._socketId);
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
/** Route an incoming HTTP upgrade to the server's 'upgrade' event listeners. */
|
|
1451
|
-
function dispatchUpgradeRequest(serverId, requestJson, headBase64, socketId) {
|
|
1452
|
-
const server = serverInstances.get(serverId);
|
|
1453
|
-
if (!server) {
|
|
1454
|
-
throw new Error(`Unknown HTTP server for upgrade: ${serverId}`);
|
|
1455
|
-
}
|
|
1456
|
-
const request = JSON.parse(requestJson);
|
|
1457
|
-
const incoming = new ServerIncomingMessage(request);
|
|
1458
|
-
const head = typeof Buffer !== "undefined" ? Buffer.from(headBase64, "base64") : new Uint8Array(0);
|
|
1459
|
-
const socket = new UpgradeSocket(socketId, {
|
|
1460
|
-
host: incoming.headers["host"]?.split(":")[0] || "127.0.0.1",
|
|
1461
|
-
});
|
|
1462
|
-
upgradeSocketInstances.set(socketId, socket);
|
|
1463
|
-
// Emit 'upgrade' on the server — ws.WebSocketServer listens for this
|
|
1464
|
-
server._emit("upgrade", incoming, socket, head);
|
|
1465
|
-
}
|
|
1466
|
-
/** Push data from host to an upgrade socket. */
|
|
1467
|
-
function onUpgradeSocketData(socketId, dataBase64) {
|
|
1468
|
-
const socket = upgradeSocketInstances.get(socketId);
|
|
1469
|
-
if (socket) {
|
|
1470
|
-
const data = typeof Buffer !== "undefined" ? Buffer.from(dataBase64, "base64") : new Uint8Array(0);
|
|
1471
|
-
socket._pushData(data);
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
/** Signal end-of-stream from host to an upgrade socket. */
|
|
1475
|
-
function onUpgradeSocketEnd(socketId) {
|
|
1476
|
-
const socket = upgradeSocketInstances.get(socketId);
|
|
1477
|
-
if (socket) {
|
|
1478
|
-
socket._pushEnd();
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
// Function-based ServerResponse constructor — allows .call() inheritance
|
|
1482
|
-
// used by light-my-request (Fastify's inject), which does
|
|
1483
|
-
// http.ServerResponse.call(this, req) + util.inherits(Response, http.ServerResponse)
|
|
1484
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1485
|
-
function ServerResponseCallable() {
|
|
1486
|
-
this.statusCode = 200;
|
|
1487
|
-
this.statusMessage = "OK";
|
|
1488
|
-
this.headersSent = false;
|
|
1489
|
-
this.writable = true;
|
|
1490
|
-
this.writableFinished = false;
|
|
1491
|
-
this._headers = new Map();
|
|
1492
|
-
this._chunks = [];
|
|
1493
|
-
this._chunksBytes = 0;
|
|
1494
|
-
this._listeners = {};
|
|
1495
|
-
this._closedPromise = new Promise((resolve) => {
|
|
1496
|
-
this._resolveClosed = resolve;
|
|
1497
|
-
});
|
|
1498
|
-
// Writable stream state stub
|
|
1499
|
-
this._writableState = { length: 0, ended: false, finished: false, objectMode: false, corked: 0 };
|
|
1500
|
-
// Fake socket for frameworks/inject libraries that access res.socket
|
|
1501
|
-
const fakeSocket = {
|
|
1502
|
-
writable: true,
|
|
1503
|
-
on() { return fakeSocket; },
|
|
1504
|
-
once() { return fakeSocket; },
|
|
1505
|
-
removeListener() { return fakeSocket; },
|
|
1506
|
-
destroy() { },
|
|
1507
|
-
end() { },
|
|
1508
|
-
cork() { },
|
|
1509
|
-
uncork() { },
|
|
1510
|
-
write() { return true; },
|
|
1511
|
-
};
|
|
1512
|
-
this.socket = fakeSocket;
|
|
1513
|
-
this.connection = fakeSocket;
|
|
1514
|
-
}
|
|
1515
|
-
ServerResponseCallable.prototype = Object.create(ServerResponseBridge.prototype, {
|
|
1516
|
-
constructor: { value: ServerResponseCallable, writable: true, configurable: true },
|
|
1517
|
-
});
|
|
1518
|
-
// Create HTTP module
|
|
1519
|
-
function createHttpModule(protocol) {
|
|
1520
|
-
const defaultProtocol = protocol === "https" ? "https:" : "http:";
|
|
1521
|
-
const moduleAgent = new Agent({ keepAlive: false });
|
|
1522
|
-
// Set module-level globalAgent so ClientRequest defaults to it
|
|
1523
|
-
_moduleGlobalAgent = moduleAgent;
|
|
1524
|
-
// Ensure protocol is set on request options (defaults to module protocol)
|
|
1525
|
-
function ensureProtocol(opts) {
|
|
1526
|
-
if (!opts.protocol)
|
|
1527
|
-
return { ...opts, protocol: defaultProtocol };
|
|
1528
|
-
return opts;
|
|
1529
|
-
}
|
|
1530
|
-
return {
|
|
1531
|
-
request(options, callback) {
|
|
1532
|
-
let opts;
|
|
1533
|
-
if (typeof options === "string") {
|
|
1534
|
-
const url = new URL(options);
|
|
1535
|
-
opts = {
|
|
1536
|
-
protocol: url.protocol,
|
|
1537
|
-
hostname: url.hostname,
|
|
1538
|
-
port: url.port,
|
|
1539
|
-
path: url.pathname + url.search,
|
|
1540
|
-
};
|
|
1541
|
-
}
|
|
1542
|
-
else if (options instanceof URL) {
|
|
1543
|
-
opts = {
|
|
1544
|
-
protocol: options.protocol,
|
|
1545
|
-
hostname: options.hostname,
|
|
1546
|
-
port: options.port,
|
|
1547
|
-
path: options.pathname + options.search,
|
|
1548
|
-
};
|
|
1549
|
-
}
|
|
1550
|
-
else {
|
|
1551
|
-
opts = options;
|
|
1552
|
-
}
|
|
1553
|
-
return new ClientRequest(ensureProtocol(opts), callback);
|
|
1554
|
-
},
|
|
1555
|
-
get(options, callback) {
|
|
1556
|
-
let opts;
|
|
1557
|
-
if (typeof options === "string") {
|
|
1558
|
-
const url = new URL(options);
|
|
1559
|
-
opts = {
|
|
1560
|
-
protocol: url.protocol,
|
|
1561
|
-
hostname: url.hostname,
|
|
1562
|
-
port: url.port,
|
|
1563
|
-
path: url.pathname + url.search,
|
|
1564
|
-
method: "GET",
|
|
1565
|
-
};
|
|
1566
|
-
}
|
|
1567
|
-
else if (options instanceof URL) {
|
|
1568
|
-
opts = {
|
|
1569
|
-
protocol: options.protocol,
|
|
1570
|
-
hostname: options.hostname,
|
|
1571
|
-
port: options.port,
|
|
1572
|
-
path: options.pathname + options.search,
|
|
1573
|
-
method: "GET",
|
|
1574
|
-
};
|
|
1575
|
-
}
|
|
1576
|
-
else {
|
|
1577
|
-
opts = { ...options, method: "GET" };
|
|
1578
|
-
}
|
|
1579
|
-
const req = new ClientRequest(ensureProtocol(opts), callback);
|
|
1580
|
-
req.end();
|
|
1581
|
-
return req;
|
|
1582
|
-
},
|
|
1583
|
-
createServer(_optionsOrListener, maybeListener) {
|
|
1584
|
-
const listener = typeof _optionsOrListener === "function"
|
|
1585
|
-
? _optionsOrListener
|
|
1586
|
-
: maybeListener;
|
|
1587
|
-
return new Server(listener);
|
|
1588
|
-
},
|
|
1589
|
-
Agent,
|
|
1590
|
-
globalAgent: moduleAgent,
|
|
1591
|
-
Server: Server,
|
|
1592
|
-
ServerResponse: ServerResponseCallable,
|
|
1593
|
-
IncomingMessage: IncomingMessage,
|
|
1594
|
-
ClientRequest: ClientRequest,
|
|
1595
|
-
METHODS: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
|
1596
|
-
STATUS_CODES: {
|
|
1597
|
-
200: "OK",
|
|
1598
|
-
201: "Created",
|
|
1599
|
-
204: "No Content",
|
|
1600
|
-
301: "Moved Permanently",
|
|
1601
|
-
302: "Found",
|
|
1602
|
-
304: "Not Modified",
|
|
1603
|
-
400: "Bad Request",
|
|
1604
|
-
401: "Unauthorized",
|
|
1605
|
-
403: "Forbidden",
|
|
1606
|
-
404: "Not Found",
|
|
1607
|
-
500: "Internal Server Error",
|
|
1608
|
-
},
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
export const http = createHttpModule("http");
|
|
1612
|
-
export const https = createHttpModule("https");
|
|
1613
|
-
export const http2 = {
|
|
1614
|
-
Http2ServerRequest: class Http2ServerRequest {
|
|
1615
|
-
},
|
|
1616
|
-
Http2ServerResponse: class Http2ServerResponse {
|
|
1617
|
-
},
|
|
1618
|
-
createServer() {
|
|
1619
|
-
throw new Error("http2.createServer is not supported in sandbox");
|
|
1620
|
-
},
|
|
1621
|
-
createSecureServer() {
|
|
1622
|
-
throw new Error("http2.createSecureServer is not supported in sandbox");
|
|
1623
|
-
},
|
|
1624
|
-
};
|
|
1625
|
-
// ----------------------------------------------------------------
|
|
1626
|
-
// net module — TCP socket bridge
|
|
1627
|
-
// ----------------------------------------------------------------
|
|
1628
|
-
const netSocketInstances = new Map();
|
|
1629
|
-
class NetSocket {
|
|
1630
|
-
remoteAddress = "";
|
|
1631
|
-
remotePort = 0;
|
|
1632
|
-
remoteFamily = "";
|
|
1633
|
-
localAddress = "0.0.0.0";
|
|
1634
|
-
localPort = 0;
|
|
1635
|
-
connecting = true;
|
|
1636
|
-
pending = true;
|
|
1637
|
-
destroyed = false;
|
|
1638
|
-
writable = true;
|
|
1639
|
-
readable = true;
|
|
1640
|
-
readyState = "opening";
|
|
1641
|
-
bytesRead = 0;
|
|
1642
|
-
bytesWritten = 0;
|
|
1643
|
-
_listeners = {};
|
|
1644
|
-
/** @internal socket ID shared with TLS upgrade bridge */
|
|
1645
|
-
_socketId = -1;
|
|
1646
|
-
_connectHost = "";
|
|
1647
|
-
_connectPort = 0;
|
|
1648
|
-
// Stream state stubs for compatibility (ssh2 checks _readableState.ended)
|
|
1649
|
-
_readableState = { endEmitted: false, ended: false };
|
|
1650
|
-
_writableState = { finished: false, errorEmitted: false, ended: false };
|
|
1651
|
-
constructor(_options) {
|
|
1652
|
-
// Options like { allowHalfOpen } are accepted but ignored
|
|
1653
|
-
}
|
|
1654
|
-
connect(...args) {
|
|
1655
|
-
// Parse overloaded signatures: connect(port, host?, cb?) or connect({port, host}, cb?)
|
|
1656
|
-
let port;
|
|
1657
|
-
let host;
|
|
1658
|
-
let connectListener;
|
|
1659
|
-
if (typeof args[0] === "object" && args[0] !== null) {
|
|
1660
|
-
const opts = args[0];
|
|
1661
|
-
port = Number(opts.port);
|
|
1662
|
-
host = String(opts.host || "127.0.0.1");
|
|
1663
|
-
if (typeof args[1] === "function")
|
|
1664
|
-
connectListener = args[1];
|
|
1665
|
-
}
|
|
1666
|
-
else {
|
|
1667
|
-
port = Number(args[0]);
|
|
1668
|
-
host = typeof args[1] === "string" ? args[1] : "127.0.0.1";
|
|
1669
|
-
if (typeof args[1] === "function") {
|
|
1670
|
-
connectListener = args[1];
|
|
1671
|
-
host = "127.0.0.1";
|
|
1672
|
-
}
|
|
1673
|
-
else if (typeof args[2] === "function") {
|
|
1674
|
-
connectListener = args[2];
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
this._connectHost = host;
|
|
1678
|
-
this._connectPort = port;
|
|
1679
|
-
if (connectListener)
|
|
1680
|
-
this.once("connect", connectListener);
|
|
1681
|
-
if (typeof _netSocketConnectRaw === "undefined") {
|
|
1682
|
-
// Schedule error emission asynchronously like real Node
|
|
1683
|
-
Promise.resolve().then(() => {
|
|
1684
|
-
const err = new Error("net.Socket requires NetworkAdapter to be configured");
|
|
1685
|
-
this._onError(err.message);
|
|
1686
|
-
});
|
|
1687
|
-
return this;
|
|
1688
|
-
}
|
|
1689
|
-
// Register active handle
|
|
1690
|
-
if (typeof _registerHandle !== "undefined") {
|
|
1691
|
-
_registerHandle(`net.socket:${host}:${port}`, `TCP connection to ${host}:${port}`);
|
|
1692
|
-
}
|
|
1693
|
-
// Synchronous call: host creates socket, starts connecting, returns socketId
|
|
1694
|
-
this._socketId = _netSocketConnectRaw.applySync(undefined, [host, port]);
|
|
1695
|
-
netSocketInstances.set(this._socketId, this);
|
|
1696
|
-
return this;
|
|
1697
|
-
}
|
|
1698
|
-
setTimeout(_ms, _cb) { return this; }
|
|
1699
|
-
setNoDelay(_noDelay) { return this; }
|
|
1700
|
-
setKeepAlive(_enable, _delay) { return this; }
|
|
1701
|
-
setMaxListeners(_n) { return this; }
|
|
1702
|
-
getMaxListeners() { return 10; }
|
|
1703
|
-
ref() { return this; }
|
|
1704
|
-
unref() { return this; }
|
|
1705
|
-
cork() { }
|
|
1706
|
-
uncork() { }
|
|
1707
|
-
pause() { return this; }
|
|
1708
|
-
resume() { return this; }
|
|
1709
|
-
pipe(destination) { return destination; }
|
|
1710
|
-
address() {
|
|
1711
|
-
return { address: this.localAddress, family: "IPv4", port: this.localPort };
|
|
1712
|
-
}
|
|
1713
|
-
listeners(event) {
|
|
1714
|
-
return (this._listeners[event] || []).slice();
|
|
1715
|
-
}
|
|
1716
|
-
rawListeners(event) {
|
|
1717
|
-
return this.listeners(event);
|
|
1718
|
-
}
|
|
1719
|
-
eventNames() {
|
|
1720
|
-
return Object.keys(this._listeners).filter((k) => (this._listeners[k]?.length ?? 0) > 0);
|
|
1721
|
-
}
|
|
1722
|
-
prependListener(event, listener) {
|
|
1723
|
-
if (!this._listeners[event])
|
|
1724
|
-
this._listeners[event] = [];
|
|
1725
|
-
this._listeners[event].unshift(listener);
|
|
1726
|
-
return this;
|
|
1727
|
-
}
|
|
1728
|
-
prependOnceListener(event, listener) {
|
|
1729
|
-
const wrapper = (...args) => {
|
|
1730
|
-
this.off(event, wrapper);
|
|
1731
|
-
listener(...args);
|
|
1732
|
-
};
|
|
1733
|
-
return this.prependListener(event, wrapper);
|
|
1734
|
-
}
|
|
1735
|
-
on(event, listener) {
|
|
1736
|
-
if (!this._listeners[event])
|
|
1737
|
-
this._listeners[event] = [];
|
|
1738
|
-
this._listeners[event].push(listener);
|
|
1739
|
-
return this;
|
|
1740
|
-
}
|
|
1741
|
-
addListener(event, listener) { return this.on(event, listener); }
|
|
1742
|
-
once(event, listener) {
|
|
1743
|
-
const wrapper = (...args) => {
|
|
1744
|
-
this.off(event, wrapper);
|
|
1745
|
-
listener(...args);
|
|
1746
|
-
};
|
|
1747
|
-
return this.on(event, wrapper);
|
|
1748
|
-
}
|
|
1749
|
-
off(event, listener) {
|
|
1750
|
-
if (this._listeners[event]) {
|
|
1751
|
-
const idx = this._listeners[event].indexOf(listener);
|
|
1752
|
-
if (idx !== -1)
|
|
1753
|
-
this._listeners[event].splice(idx, 1);
|
|
1754
|
-
}
|
|
1755
|
-
return this;
|
|
1756
|
-
}
|
|
1757
|
-
removeListener(event, listener) { return this.off(event, listener); }
|
|
1758
|
-
removeAllListeners(event) {
|
|
1759
|
-
if (event) {
|
|
1760
|
-
delete this._listeners[event];
|
|
1761
|
-
}
|
|
1762
|
-
else {
|
|
1763
|
-
this._listeners = {};
|
|
1764
|
-
}
|
|
1765
|
-
return this;
|
|
1766
|
-
}
|
|
1767
|
-
emit(event, ...args) {
|
|
1768
|
-
const handlers = this._listeners[event];
|
|
1769
|
-
if (handlers)
|
|
1770
|
-
handlers.slice().forEach((fn) => fn.call(this, ...args));
|
|
1771
|
-
return handlers !== undefined && handlers.length > 0;
|
|
1772
|
-
}
|
|
1773
|
-
listenerCount(event) {
|
|
1774
|
-
return this._listeners[event]?.length || 0;
|
|
1775
|
-
}
|
|
1776
|
-
write(data, encodingOrCb, cb) {
|
|
1777
|
-
if (this.destroyed)
|
|
1778
|
-
return false;
|
|
1779
|
-
const callback = typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
1780
|
-
if (typeof _netSocketWriteRaw !== "undefined" && this._socketId >= 0) {
|
|
1781
|
-
let base64;
|
|
1782
|
-
if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
|
|
1783
|
-
base64 = data.toString("base64");
|
|
1784
|
-
}
|
|
1785
|
-
else if (typeof data === "string") {
|
|
1786
|
-
const encoding = typeof encodingOrCb === "string" ? encodingOrCb : "utf8";
|
|
1787
|
-
base64 = typeof Buffer !== "undefined" ? Buffer.from(data, encoding).toString("base64") : btoa(data);
|
|
1788
|
-
}
|
|
1789
|
-
else if (data instanceof Uint8Array) {
|
|
1790
|
-
base64 = typeof Buffer !== "undefined" ? Buffer.from(data).toString("base64") : btoa(String.fromCharCode(...data));
|
|
1791
|
-
}
|
|
1792
|
-
else {
|
|
1793
|
-
base64 = typeof Buffer !== "undefined" ? Buffer.from(String(data)).toString("base64") : btoa(String(data));
|
|
1794
|
-
}
|
|
1795
|
-
this.bytesWritten += base64.length;
|
|
1796
|
-
_netSocketWriteRaw.applySync(undefined, [this._socketId, base64]);
|
|
1797
|
-
}
|
|
1798
|
-
if (callback)
|
|
1799
|
-
callback();
|
|
1800
|
-
return true;
|
|
1801
|
-
}
|
|
1802
|
-
end(data, encodingOrCb, cb) {
|
|
1803
|
-
if (data !== undefined && data !== null)
|
|
1804
|
-
this.write(data, encodingOrCb, cb);
|
|
1805
|
-
if (typeof _netSocketEndRaw !== "undefined" && this._socketId >= 0 && !this.destroyed) {
|
|
1806
|
-
_netSocketEndRaw.applySync(undefined, [this._socketId]);
|
|
1807
|
-
}
|
|
1808
|
-
this.writable = false;
|
|
1809
|
-
this.readyState = this.readable ? "readOnly" : "closed";
|
|
1810
|
-
this.emit("finish");
|
|
1811
|
-
return this;
|
|
1812
|
-
}
|
|
1813
|
-
destroy(err) {
|
|
1814
|
-
if (this.destroyed)
|
|
1815
|
-
return this;
|
|
1816
|
-
this.destroyed = true;
|
|
1817
|
-
this.writable = false;
|
|
1818
|
-
this.readable = false;
|
|
1819
|
-
this.readyState = "closed";
|
|
1820
|
-
this._readableState.endEmitted = true;
|
|
1821
|
-
this._writableState.finished = true;
|
|
1822
|
-
if (typeof _netSocketDestroyRaw !== "undefined" && this._socketId >= 0) {
|
|
1823
|
-
_netSocketDestroyRaw.applySync(undefined, [this._socketId]);
|
|
1824
|
-
}
|
|
1825
|
-
this._cleanup();
|
|
1826
|
-
if (err)
|
|
1827
|
-
this.emit("error", err);
|
|
1828
|
-
this.emit("close", !!err);
|
|
1829
|
-
return this;
|
|
1830
|
-
}
|
|
1831
|
-
// Host→Guest event dispatch handlers
|
|
1832
|
-
_onConnect() {
|
|
1833
|
-
this.connecting = false;
|
|
1834
|
-
this.pending = false;
|
|
1835
|
-
this.remoteAddress = this._connectHost;
|
|
1836
|
-
this.remotePort = this._connectPort;
|
|
1837
|
-
this.remoteFamily = "IPv4";
|
|
1838
|
-
this.readyState = "open";
|
|
1839
|
-
this.emit("connect");
|
|
1840
|
-
this.emit("ready");
|
|
1841
|
-
}
|
|
1842
|
-
_onData(dataBase64) {
|
|
1843
|
-
const buf = typeof Buffer !== "undefined" ? Buffer.from(dataBase64, "base64") : new Uint8Array(0);
|
|
1844
|
-
this.bytesRead += buf.length;
|
|
1845
|
-
this.emit("data", buf);
|
|
1846
|
-
}
|
|
1847
|
-
_onEnd() {
|
|
1848
|
-
this.readable = false;
|
|
1849
|
-
this._readableState.endEmitted = true;
|
|
1850
|
-
this._readableState.ended = true;
|
|
1851
|
-
this.readyState = this.writable ? "writeOnly" : "closed";
|
|
1852
|
-
this.emit("end");
|
|
1853
|
-
}
|
|
1854
|
-
_onError(message) {
|
|
1855
|
-
const err = new Error(message);
|
|
1856
|
-
this.destroy(err);
|
|
1857
|
-
}
|
|
1858
|
-
_onClose(hadError) {
|
|
1859
|
-
this._cleanup();
|
|
1860
|
-
if (!this.destroyed) {
|
|
1861
|
-
this.destroyed = true;
|
|
1862
|
-
this.readable = false;
|
|
1863
|
-
this.writable = false;
|
|
1864
|
-
this.readyState = "closed";
|
|
1865
|
-
this.emit("close", hadError);
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
_cleanup() {
|
|
1869
|
-
if (this._socketId >= 0) {
|
|
1870
|
-
netSocketInstances.delete(this._socketId);
|
|
1871
|
-
if (typeof _unregisterHandle !== "undefined") {
|
|
1872
|
-
_unregisterHandle(`net.socket:${this._connectHost}:${this._connectPort}`);
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
/** Dispatch events from host to guest net sockets. */
|
|
1878
|
-
function onNetSocketDispatch(socketId, type, data) {
|
|
1879
|
-
const socket = netSocketInstances.get(socketId);
|
|
1880
|
-
if (!socket)
|
|
1881
|
-
return;
|
|
1882
|
-
switch (type) {
|
|
1883
|
-
case "connect":
|
|
1884
|
-
socket._onConnect();
|
|
1885
|
-
break;
|
|
1886
|
-
case "data":
|
|
1887
|
-
socket._onData(data);
|
|
1888
|
-
break;
|
|
1889
|
-
case "end":
|
|
1890
|
-
socket._onEnd();
|
|
1891
|
-
break;
|
|
1892
|
-
case "error":
|
|
1893
|
-
socket._onError(data);
|
|
1894
|
-
break;
|
|
1895
|
-
case "close":
|
|
1896
|
-
socket._onClose(data === "1");
|
|
1897
|
-
break;
|
|
1898
|
-
case "secureConnect":
|
|
1899
|
-
socket.emit("secureConnect");
|
|
1900
|
-
break;
|
|
1901
|
-
}
|
|
1902
|
-
}
|
|
1903
|
-
// Validate IP address format
|
|
1904
|
-
function netIsIP(input) {
|
|
1905
|
-
if (typeof input !== "string")
|
|
1906
|
-
return 0;
|
|
1907
|
-
// IPv4: four octets 0-255
|
|
1908
|
-
if (/^(\d{1,3}\.){3}\d{1,3}$/.test(input)) {
|
|
1909
|
-
const parts = input.split(".");
|
|
1910
|
-
if (parts.every((p) => { const n = Number(p); return n >= 0 && n <= 255; }))
|
|
1911
|
-
return 4;
|
|
1912
|
-
}
|
|
1913
|
-
// IPv6: simplified check
|
|
1914
|
-
if (/^(::)?([0-9a-fA-F]{1,4}(::?)){0,7}([0-9a-fA-F]{1,4})?$/.test(input))
|
|
1915
|
-
return 6;
|
|
1916
|
-
if (/^::ffff:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(input))
|
|
1917
|
-
return 6;
|
|
1918
|
-
return 0;
|
|
1919
|
-
}
|
|
1920
|
-
export const net = {
|
|
1921
|
-
Socket: NetSocket,
|
|
1922
|
-
connect(portOrOpts, hostOrCb, cb) {
|
|
1923
|
-
const socket = new NetSocket();
|
|
1924
|
-
socket.connect(portOrOpts, hostOrCb, cb);
|
|
1925
|
-
return socket;
|
|
1926
|
-
},
|
|
1927
|
-
createConnection(portOrOpts, hostOrCb, cb) {
|
|
1928
|
-
return net.connect(portOrOpts, hostOrCb, cb);
|
|
1929
|
-
},
|
|
1930
|
-
createServer() {
|
|
1931
|
-
throw new Error("net.createServer is not supported in sandbox");
|
|
1932
|
-
},
|
|
1933
|
-
isIP: netIsIP,
|
|
1934
|
-
isIPv4(input) { return netIsIP(input) === 4; },
|
|
1935
|
-
isIPv6(input) { return netIsIP(input) === 6; },
|
|
1936
|
-
};
|
|
1937
|
-
// ----------------------------------------------------------------
|
|
1938
|
-
// tls module — TLS socket upgrade bridge
|
|
1939
|
-
// ----------------------------------------------------------------
|
|
1940
|
-
/** TLS socket that wraps an existing NetSocket after host-side TLS upgrade. */
|
|
1941
|
-
class TLSSocket extends NetSocket {
|
|
1942
|
-
encrypted = true;
|
|
1943
|
-
authorized = false;
|
|
1944
|
-
authorizationError = null;
|
|
1945
|
-
alpnProtocol = false;
|
|
1946
|
-
_wrappedSocket = null;
|
|
1947
|
-
constructor(originalSocket) {
|
|
1948
|
-
super();
|
|
1949
|
-
this._wrappedSocket = originalSocket;
|
|
1950
|
-
// Copy connection state from original socket
|
|
1951
|
-
this.remoteAddress = originalSocket.remoteAddress;
|
|
1952
|
-
this.remotePort = originalSocket.remotePort;
|
|
1953
|
-
this.remoteFamily = originalSocket.remoteFamily;
|
|
1954
|
-
this.localAddress = originalSocket.localAddress;
|
|
1955
|
-
this.localPort = originalSocket.localPort;
|
|
1956
|
-
this.connecting = false;
|
|
1957
|
-
this.pending = false;
|
|
1958
|
-
this.readyState = "open";
|
|
1959
|
-
// Share the same socketId — bridge events route here after upgrade
|
|
1960
|
-
this._socketId = originalSocket._socketId;
|
|
1961
|
-
// Copy private connect info so _cleanup unregisters the correct handle
|
|
1962
|
-
this._connectHost = originalSocket._connectHost;
|
|
1963
|
-
this._connectPort = originalSocket._connectPort;
|
|
1964
|
-
netSocketInstances.set(this._socketId, this);
|
|
1965
|
-
}
|
|
1966
|
-
_onSecureConnect() {
|
|
1967
|
-
this.authorized = true;
|
|
1968
|
-
this.emit("secureConnect");
|
|
1969
|
-
}
|
|
1970
|
-
// Forward end/close to the wrapped raw socket — Node.js tls.TLSSocket
|
|
1971
|
-
// closes the underlying socket, which fires its 'close' event. Libraries
|
|
1972
|
-
// like pg rely on the original socket's 'close' listener to detect shutdown.
|
|
1973
|
-
_onEnd() {
|
|
1974
|
-
super._onEnd();
|
|
1975
|
-
if (this._wrappedSocket)
|
|
1976
|
-
this._wrappedSocket._onEnd();
|
|
1977
|
-
}
|
|
1978
|
-
_onClose(hadError) {
|
|
1979
|
-
super._onClose(hadError);
|
|
1980
|
-
if (this._wrappedSocket) {
|
|
1981
|
-
this._wrappedSocket._onClose(hadError);
|
|
1982
|
-
this._wrappedSocket = null;
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
export const tlsModule = {
|
|
1987
|
-
TLSSocket: TLSSocket,
|
|
1988
|
-
connect(options) {
|
|
1989
|
-
const existingSocket = options.socket;
|
|
1990
|
-
if (!existingSocket || existingSocket._socketId < 0) {
|
|
1991
|
-
throw new Error("tls.connect requires an existing connected socket via options.socket");
|
|
1992
|
-
}
|
|
1993
|
-
// Create TLS socket wrapper on sandbox side
|
|
1994
|
-
const tlsSocket = new TLSSocket(existingSocket);
|
|
1995
|
-
if (typeof _netSocketUpgradeTlsRaw === "undefined") {
|
|
1996
|
-
Promise.resolve().then(() => {
|
|
1997
|
-
tlsSocket._onError("tls.connect requires NetworkAdapter TLS support");
|
|
1998
|
-
});
|
|
1999
|
-
return tlsSocket;
|
|
2000
|
-
}
|
|
2001
|
-
// Tell host to wrap the underlying TCP socket with TLS
|
|
2002
|
-
_netSocketUpgradeTlsRaw.applySync(undefined, [
|
|
2003
|
-
existingSocket._socketId,
|
|
2004
|
-
JSON.stringify({
|
|
2005
|
-
rejectUnauthorized: options.rejectUnauthorized ?? true,
|
|
2006
|
-
servername: options.servername,
|
|
2007
|
-
}),
|
|
2008
|
-
]);
|
|
2009
|
-
return tlsSocket;
|
|
2010
|
-
},
|
|
2011
|
-
createSecureContext(_options) {
|
|
2012
|
-
return {};
|
|
2013
|
-
},
|
|
2014
|
-
};
|
|
2015
|
-
// Export modules and make them available as globals for require()
|
|
2016
|
-
exposeCustomGlobal("_httpModule", http);
|
|
2017
|
-
exposeCustomGlobal("_httpsModule", https);
|
|
2018
|
-
exposeCustomGlobal("_http2Module", http2);
|
|
2019
|
-
exposeCustomGlobal("_dnsModule", dns);
|
|
2020
|
-
exposeCustomGlobal("_netModule", net);
|
|
2021
|
-
exposeCustomGlobal("_tlsModule", tlsModule);
|
|
2022
|
-
exposeCustomGlobal("_httpServerDispatch", dispatchServerRequest);
|
|
2023
|
-
exposeCustomGlobal("_httpServerUpgradeDispatch", dispatchUpgradeRequest);
|
|
2024
|
-
exposeCustomGlobal("_upgradeSocketData", onUpgradeSocketData);
|
|
2025
|
-
exposeCustomGlobal("_upgradeSocketEnd", onUpgradeSocketEnd);
|
|
2026
|
-
exposeCustomGlobal("_netSocketDispatch", onNetSocketDispatch);
|
|
2027
|
-
// Harden fetch API globals (non-writable, non-configurable)
|
|
2028
|
-
exposeCustomGlobal("fetch", fetch);
|
|
2029
|
-
exposeCustomGlobal("Headers", Headers);
|
|
2030
|
-
exposeCustomGlobal("Request", Request);
|
|
2031
|
-
exposeCustomGlobal("Response", Response);
|
|
2032
|
-
if (typeof globalThis.Blob === "undefined") {
|
|
2033
|
-
// Minimal Blob stub used by server frameworks for instanceof checks.
|
|
2034
|
-
exposeCustomGlobal("Blob", class BlobStub {
|
|
2035
|
-
});
|
|
2036
|
-
}
|
|
2037
|
-
export default {
|
|
2038
|
-
fetch,
|
|
2039
|
-
Headers,
|
|
2040
|
-
Request,
|
|
2041
|
-
Response,
|
|
2042
|
-
dns,
|
|
2043
|
-
http,
|
|
2044
|
-
https,
|
|
2045
|
-
http2,
|
|
2046
|
-
net,
|
|
2047
|
-
tls: tlsModule,
|
|
2048
|
-
IncomingMessage,
|
|
2049
|
-
ClientRequest,
|
|
2050
|
-
};
|