@ccheever/exact-ibex-runtime 0.1.0
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/package.json +63 -0
- package/src/abort/AbortController.ts +23 -0
- package/src/abort/AbortSignal.ts +152 -0
- package/src/abort/index.ts +2 -0
- package/src/accessibility.ts +12 -0
- package/src/arraybuffer-detach.ts +109 -0
- package/src/base64/base64.ts +168 -0
- package/src/base64/index.ts +1 -0
- package/src/blob/Blob.ts +259 -0
- package/src/blob/File.ts +59 -0
- package/src/blob/FormData.ts +323 -0
- package/src/blob/index.ts +3 -0
- package/src/bootstrap.ts +1946 -0
- package/src/broadcast/BroadcastChannel.ts +280 -0
- package/src/broadcast/index.ts +5 -0
- package/src/cache/Cache.ts +349 -0
- package/src/cache/CacheStorage.ts +89 -0
- package/src/cache/index.ts +27 -0
- package/src/camera/index.ts +6202 -0
- package/src/camera/processor.worker.ts +194 -0
- package/src/camera/scene.ts +195 -0
- package/src/clipboard/Clipboard.ts +129 -0
- package/src/clipboard/ClipboardItem.ts +97 -0
- package/src/clipboard/index.ts +6 -0
- package/src/clone/index.ts +1 -0
- package/src/clone/structuredClone.ts +389 -0
- package/src/clone/transferableSymbols.ts +2 -0
- package/src/compression/CompressionStream.ts +146 -0
- package/src/compression/DecompressionStream.ts +342 -0
- package/src/compression/index.ts +4 -0
- package/src/console/Console.ts +341 -0
- package/src/console/index.ts +2 -0
- package/src/core/accessibility-state.ts +263 -0
- package/src/core/accessibility.ts +184 -0
- package/src/core/agent-state.ts +37 -0
- package/src/core/diagnostics-logs.ts +144 -0
- package/src/core/host-call-bridge.ts +16 -0
- package/src/core/i18n-helpers.ts +189 -0
- package/src/core/locale-state.ts +253 -0
- package/src/core/locale.ts +95 -0
- package/src/crypto/Crypto.ts +2743 -0
- package/src/crypto/index.ts +1 -0
- package/src/diagnostics/logs.ts +7 -0
- package/src/encoding/TextDecoder.ts +1181 -0
- package/src/encoding/TextDecoderStream.ts +58 -0
- package/src/encoding/TextEncoder.ts +180 -0
- package/src/encoding/TextEncoderStream.ts +39 -0
- package/src/encoding/index.ts +8 -0
- package/src/events/CloseEvent.ts +91 -0
- package/src/events/DOMException.ts +409 -0
- package/src/events/ErrorEvent.ts +39 -0
- package/src/events/Event.ts +151 -0
- package/src/events/EventTarget.ts +280 -0
- package/src/events/FocusEvent.ts +27 -0
- package/src/events/KeyboardEvent.ts +46 -0
- package/src/events/MessageEvent.ts +61 -0
- package/src/events/ProgressEvent.ts +33 -0
- package/src/events/PromiseRejectionEvent.ts +31 -0
- package/src/events/index.ts +52 -0
- package/src/eventsource/EventSource.ts +371 -0
- package/src/eventsource/index.ts +2 -0
- package/src/fetch/Headers.ts +642 -0
- package/src/fetch/Request.ts +760 -0
- package/src/fetch/Response.ts +543 -0
- package/src/fetch/body.ts +1256 -0
- package/src/fetch/cookie-jar.ts +566 -0
- package/src/fetch/demo.ts +207 -0
- package/src/fetch/errors.ts +101 -0
- package/src/fetch/fetch.ts +2610 -0
- package/src/fetch/index.ts +101 -0
- package/src/fetch/native-bridge.ts +65 -0
- package/src/fetch/types.ts +258 -0
- package/src/filereader/FileReader.ts +236 -0
- package/src/filereader/index.ts +1 -0
- package/src/fs/Dirent.ts +39 -0
- package/src/fs/ExactFile.ts +450 -0
- package/src/fs/Stats.ts +80 -0
- package/src/fs/index.ts +944 -0
- package/src/fs/promises.ts +386 -0
- package/src/fs/shared.ts +328 -0
- package/src/http-server/index.js +697 -0
- package/src/http-server/index.ts +27 -0
- package/src/identity.generated.ts +14 -0
- package/src/index.ts +283 -0
- package/src/indexeddb/IDBCursor.ts +188 -0
- package/src/indexeddb/IDBDatabase.ts +343 -0
- package/src/indexeddb/IDBFactory.ts +269 -0
- package/src/indexeddb/IDBIndex.ts +194 -0
- package/src/indexeddb/IDBKeyRange.ts +109 -0
- package/src/indexeddb/IDBObjectStore.ts +468 -0
- package/src/indexeddb/IDBRequest.ts +163 -0
- package/src/indexeddb/IDBTransaction.ts +207 -0
- package/src/indexeddb/index.ts +34 -0
- package/src/indexeddb/utils.ts +52 -0
- package/src/inspect/index.ts +1 -0
- package/src/inspect/inspect.ts +465 -0
- package/src/internal/detect.ts +104 -0
- package/src/locale.ts +10 -0
- package/src/location/index.ts +1059 -0
- package/src/locks/LockManager.ts +460 -0
- package/src/locks/index.ts +12 -0
- package/src/media/VideoFrame.ts +58 -0
- package/src/messaging/MessageChannel.ts +31 -0
- package/src/messaging/MessagePort.ts +180 -0
- package/src/messaging/index.ts +2 -0
- package/src/messaging.ts +247 -0
- package/src/native/NativeModules.ts +354 -0
- package/src/native/index.ts +1 -0
- package/src/navigator/Navigator.ts +351 -0
- package/src/navigator/index.ts +1 -0
- package/src/node/Buffer.ts +1786 -0
- package/src/node/index.ts +4 -0
- package/src/node/path.ts +495 -0
- package/src/node/process.ts +2528 -0
- package/src/performance/Performance.ts +532 -0
- package/src/performance/index.ts +21 -0
- package/src/polyfills/array.ts +236 -0
- package/src/polyfills/arraybuffer.ts +172 -0
- package/src/polyfills/groupby.ts +85 -0
- package/src/polyfills/index.ts +85 -0
- package/src/polyfills/intl.ts +1956 -0
- package/src/polyfills/iterator.ts +479 -0
- package/src/polyfills/promise.ts +37 -0
- package/src/polyfills/set.ts +245 -0
- package/src/polyfills/string.ts +85 -0
- package/src/polyfills/typedarray.ts +110 -0
- package/src/promise-rejection-tracking.ts +464 -0
- package/src/react-native/index.ts +388 -0
- package/src/runtime-entry.ts +55 -0
- package/src/scheduling/AnimationFrame.ts +105 -0
- package/src/scheduling/IdleCallback.ts +167 -0
- package/src/scheduling/index.ts +13 -0
- package/src/security/Capabilities.ts +1146 -0
- package/src/security/Permissions.ts +392 -0
- package/src/security/capability-bits.generated.ts +63 -0
- package/src/security/index.ts +16 -0
- package/src/sqlite/Database.ts +456 -0
- package/src/sqlite/Statement.ts +206 -0
- package/src/sqlite/constants.ts +79 -0
- package/src/sqlite/errors.ts +25 -0
- package/src/sqlite/index.ts +34 -0
- package/src/sqlite/module.js +438 -0
- package/src/storage/Storage.ts +291 -0
- package/src/storage/StorageManager.ts +91 -0
- package/src/storage/index.ts +3 -0
- package/src/stream-compat.ts +47 -0
- package/src/streams/ReadableStream.ts +4131 -0
- package/src/streams/TransformStream.ts +375 -0
- package/src/streams/WritableStream.ts +866 -0
- package/src/streams/index.ts +41 -0
- package/src/timers/Timers.ts +296 -0
- package/src/timers/index.ts +11 -0
- package/src/url/URL.ts +656 -0
- package/src/url/URLPattern.ts +850 -0
- package/src/url/URLSearchParams.ts +244 -0
- package/src/url/index.ts +9 -0
- package/src/websocket/WebSocket.ts +770 -0
- package/src/websocket/WebSocketError.ts +52 -0
- package/src/websocket/WebSocketStream.ts +628 -0
- package/src/websocket/index.ts +7 -0
- package/src/window/index.ts +872 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
var g = globalThis;
|
|
2
|
+
|
|
3
|
+
function b64ToBytes(b64) {
|
|
4
|
+
if (!b64) return new Uint8Array(0);
|
|
5
|
+
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
6
|
+
var lookup = {};
|
|
7
|
+
for (var i = 0; i < chars.length; i++) lookup[chars[i]] = i;
|
|
8
|
+
var raw = b64.replace(/[^A-Za-z0-9+/]/g, "");
|
|
9
|
+
var len = raw.length;
|
|
10
|
+
var bytes = new Uint8Array(Math.ceil(len * 3 / 4));
|
|
11
|
+
var j = 0;
|
|
12
|
+
for (var i = 0; i < len; i += 4) {
|
|
13
|
+
var a = lookup[raw[i]] || 0;
|
|
14
|
+
var b = lookup[raw[i+1]] || 0;
|
|
15
|
+
var c = lookup[raw[i+2]] || 0;
|
|
16
|
+
var d = lookup[raw[i+3]] || 0;
|
|
17
|
+
bytes[j++] = (a << 2) | (b >> 4);
|
|
18
|
+
if (i + 2 < len) bytes[j++] = ((b & 15) << 4) | (c >> 2);
|
|
19
|
+
if (i + 3 < len) bytes[j++] = ((c & 3) << 6) | d;
|
|
20
|
+
}
|
|
21
|
+
return bytes.slice(0, j);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toBytes(data) {
|
|
25
|
+
if (!data) return new Uint8Array(0);
|
|
26
|
+
if (data instanceof Uint8Array) return data;
|
|
27
|
+
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
28
|
+
if (typeof data === "string") {
|
|
29
|
+
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(data);
|
|
30
|
+
var buf = new Uint8Array(data.length * 3), o = 0;
|
|
31
|
+
for (var i = 0; i < data.length; i++) {
|
|
32
|
+
var c = data.charCodeAt(i);
|
|
33
|
+
if (c < 0x80) buf[o++] = c;
|
|
34
|
+
else if (c < 0x800) { buf[o++] = 0xc0|(c>>6); buf[o++] = 0x80|(c&0x3f); }
|
|
35
|
+
else { buf[o++] = 0xe0|(c>>12); buf[o++] = 0x80|((c>>6)&0x3f); buf[o++] = 0x80|(c&0x3f); }
|
|
36
|
+
}
|
|
37
|
+
return buf.slice(0, o);
|
|
38
|
+
}
|
|
39
|
+
return new Uint8Array(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function responseBodyPayload(data) {
|
|
43
|
+
if (!data) return null;
|
|
44
|
+
if (data instanceof Uint8Array) {
|
|
45
|
+
return {
|
|
46
|
+
buffer: data.buffer,
|
|
47
|
+
byteOffset: data.byteOffset || 0,
|
|
48
|
+
byteLength: data.byteLength || data.length || 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (data instanceof ArrayBuffer) {
|
|
52
|
+
return {
|
|
53
|
+
buffer: data,
|
|
54
|
+
byteOffset: 0,
|
|
55
|
+
byteLength: data.byteLength || 0,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function createReadableRequestBody(streamId, requestId) {
|
|
62
|
+
return new ReadableStream({
|
|
63
|
+
pull: function(controller) {
|
|
64
|
+
return new Promise(function(resolve, reject) {
|
|
65
|
+
function poll() {
|
|
66
|
+
if (typeof g.__exactHttpReadBody !== "function") {
|
|
67
|
+
reject(new Error("Request body streaming unavailable"));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
var resultJson = g.__exactHttpReadBody(streamId, requestId);
|
|
72
|
+
if (!resultJson) {
|
|
73
|
+
resolve();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
var result = JSON.parse(resultJson);
|
|
78
|
+
if (result.error) {
|
|
79
|
+
reject(new Error(result.error));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (result.done) {
|
|
84
|
+
controller.close();
|
|
85
|
+
resolve();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (result.chunk) {
|
|
90
|
+
controller.enqueue(b64ToBytes(result.chunk));
|
|
91
|
+
resolve();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
setTimeout(poll, 0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
poll();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var MAX_REQUEST_HEADERS = 128;
|
|
105
|
+
var MAX_REQUEST_HEADER_BYTES = 16 * 1024;
|
|
106
|
+
var MAX_REQUEST_BODY_BYTES = 1024 * 1024;
|
|
107
|
+
var MAX_RESPONSE_BODY_BYTES = 4 * 1024 * 1024;
|
|
108
|
+
var HTTP_OK = 200;
|
|
109
|
+
var HTTP_BAD_REQUEST = 400;
|
|
110
|
+
var HTTP_REQUEST_TIMEOUT = 408;
|
|
111
|
+
var HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
|
|
112
|
+
var HTTP_INTERNAL_SERVER_ERROR = 500;
|
|
113
|
+
|
|
114
|
+
// Fast-path response functions available
|
|
115
|
+
var hasRespondText = typeof g.__exactHttpRespondText === "function";
|
|
116
|
+
var hasRespondJson = typeof g.__exactHttpRespondJson === "function";
|
|
117
|
+
var hasRespondString = typeof g.__exactHttpRespondString === "function";
|
|
118
|
+
var hasPoll = typeof g.__exactHttpPoll === "function";
|
|
119
|
+
var hasDrain = typeof g.__exactHttpDrain === "function";
|
|
120
|
+
|
|
121
|
+
function ensureHttpHostFunctions() {
|
|
122
|
+
if (typeof g.__exactHttpServe === "function") {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (typeof g.__exactEnsureHttp === "function") {
|
|
126
|
+
g.__exactEnsureHttp();
|
|
127
|
+
}
|
|
128
|
+
hasRespondText = typeof g.__exactHttpRespondText === "function";
|
|
129
|
+
hasRespondJson = typeof g.__exactHttpRespondJson === "function";
|
|
130
|
+
hasRespondString = typeof g.__exactHttpRespondString === "function";
|
|
131
|
+
hasPoll = typeof g.__exactHttpPoll === "function";
|
|
132
|
+
hasDrain = typeof g.__exactHttpDrain === "function";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Item 9: Cache common HTTP method strings to reduce allocations
|
|
136
|
+
var METHOD_GET = "GET";
|
|
137
|
+
var METHOD_POST = "POST";
|
|
138
|
+
var METHOD_PUT = "PUT";
|
|
139
|
+
var METHOD_DELETE = "DELETE";
|
|
140
|
+
var METHOD_PATCH = "PATCH";
|
|
141
|
+
var METHOD_HEAD = "HEAD";
|
|
142
|
+
var METHOD_OPTIONS = "OPTIONS";
|
|
143
|
+
|
|
144
|
+
// Item 9: Method string interning cache
|
|
145
|
+
var methodCache = {};
|
|
146
|
+
methodCache[METHOD_GET] = METHOD_GET;
|
|
147
|
+
methodCache[METHOD_POST] = METHOD_POST;
|
|
148
|
+
methodCache[METHOD_PUT] = METHOD_PUT;
|
|
149
|
+
methodCache[METHOD_DELETE] = METHOD_DELETE;
|
|
150
|
+
methodCache[METHOD_PATCH] = METHOD_PATCH;
|
|
151
|
+
methodCache[METHOD_HEAD] = METHOD_HEAD;
|
|
152
|
+
methodCache[METHOD_OPTIONS] = METHOD_OPTIONS;
|
|
153
|
+
|
|
154
|
+
function internMethod(m) {
|
|
155
|
+
return methodCache[m] || m;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function clampStatus(status) {
|
|
159
|
+
var n = Number(status);
|
|
160
|
+
if (!isFinite(n) || n < 100 || n > 999) {
|
|
161
|
+
return HTTP_INTERNAL_SERVER_ERROR;
|
|
162
|
+
}
|
|
163
|
+
return Math.floor(n);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function stringifyError(error) {
|
|
167
|
+
if (!error) {
|
|
168
|
+
return "";
|
|
169
|
+
}
|
|
170
|
+
if (typeof error.message === "string") {
|
|
171
|
+
return error.message;
|
|
172
|
+
}
|
|
173
|
+
if (typeof error === "string") {
|
|
174
|
+
return error;
|
|
175
|
+
}
|
|
176
|
+
return String(error);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function mapRequestErrorStatus(message) {
|
|
180
|
+
var lower = (message || "").toLowerCase();
|
|
181
|
+
if (lower.indexOf("payload too large") !== -1) {
|
|
182
|
+
return HTTP_REQUEST_ENTITY_TOO_LARGE;
|
|
183
|
+
}
|
|
184
|
+
if (lower.indexOf("request timeout") !== -1 || lower.indexOf("timeout") !== -1) {
|
|
185
|
+
return HTTP_REQUEST_TIMEOUT;
|
|
186
|
+
}
|
|
187
|
+
if (lower.indexOf("bad request") !== -1 || lower.indexOf("malformed") !== -1) {
|
|
188
|
+
return HTTP_BAD_REQUEST;
|
|
189
|
+
}
|
|
190
|
+
return HTTP_INTERNAL_SERVER_ERROR;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function normalizeHeaders(list) {
|
|
194
|
+
if (!list || !list.length) return {pairs: [], bytes: 0, ok: true};
|
|
195
|
+
var totalBytes = 0;
|
|
196
|
+
var pairs = [];
|
|
197
|
+
for (var i = 0; i < list.length; i++) {
|
|
198
|
+
if (pairs.length >= MAX_REQUEST_HEADERS) {
|
|
199
|
+
return {pairs: pairs, bytes: totalBytes, ok: false};
|
|
200
|
+
}
|
|
201
|
+
var item = list[i];
|
|
202
|
+
if (!item || !item.length || item.length < 2 || item.length > 2) {
|
|
203
|
+
return {pairs: pairs, bytes: totalBytes, ok: false};
|
|
204
|
+
}
|
|
205
|
+
var key = item[0];
|
|
206
|
+
var value = item[1];
|
|
207
|
+
if (typeof key !== "string" || typeof value !== "string") {
|
|
208
|
+
return {pairs: pairs, bytes: totalBytes, ok: false};
|
|
209
|
+
}
|
|
210
|
+
totalBytes += key.length + value.length;
|
|
211
|
+
if (totalBytes > MAX_REQUEST_HEADER_BYTES) {
|
|
212
|
+
return {pairs: pairs, bytes: totalBytes, ok: false};
|
|
213
|
+
}
|
|
214
|
+
pairs.push([key, value]);
|
|
215
|
+
}
|
|
216
|
+
return {pairs: pairs, bytes: totalBytes, ok: true};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function serve(options) {
|
|
220
|
+
if (!options || typeof options.fetch !== "function") {
|
|
221
|
+
throw new TypeError("serve() requires a fetch function");
|
|
222
|
+
}
|
|
223
|
+
ensureHttpHostFunctions();
|
|
224
|
+
if (typeof g.__exactHttpServe !== "function") {
|
|
225
|
+
throw new Error("Exact HTTP server bridge is unavailable in this runtime");
|
|
226
|
+
}
|
|
227
|
+
var port = options.port || 0;
|
|
228
|
+
var hostnameProvided = Object.prototype.hasOwnProperty.call(options, "hostname");
|
|
229
|
+
var hostname = hostnameProvided ? options.hostname : "127.0.0.1";
|
|
230
|
+
var fetchFn = options.fetch;
|
|
231
|
+
|
|
232
|
+
var resultJson = g.__exactHttpServe(port, hostname);
|
|
233
|
+
var result = JSON.parse(resultJson);
|
|
234
|
+
if (result.error) {
|
|
235
|
+
throw new Error(result.error);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
var serverId = result.id;
|
|
239
|
+
var boundPort = result.port;
|
|
240
|
+
var closed = false;
|
|
241
|
+
|
|
242
|
+
// Re-check fast-path availability after serve() init
|
|
243
|
+
hasRespondText = typeof g.__exactHttpRespondText === "function";
|
|
244
|
+
hasRespondJson = typeof g.__exactHttpRespondJson === "function";
|
|
245
|
+
hasRespondString = typeof g.__exactHttpRespondString === "function";
|
|
246
|
+
hasPoll = typeof g.__exactHttpPoll === "function";
|
|
247
|
+
hasDrain = typeof g.__exactHttpDrain === "function";
|
|
248
|
+
|
|
249
|
+
// Pre-compute URL prefix once (avoid per-request string concat)
|
|
250
|
+
var requestHost = !hostnameProvided || hostname === "0.0.0.0" ? "localhost" : hostname;
|
|
251
|
+
var urlPrefix = "http://" + requestHost + ":" + boundPort;
|
|
252
|
+
|
|
253
|
+
function failResponse(requestId, status, message) {
|
|
254
|
+
var body = responseBodyPayload(toBytes(message || ""));
|
|
255
|
+
g.__exactHttpRespond(serverId, requestId, status, null, body);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Concurrent request dispatch
|
|
259
|
+
var waitCount = 0;
|
|
260
|
+
var MAX_CONCURRENT_WAITS = 4;
|
|
261
|
+
|
|
262
|
+
function waitForNextRequest() {
|
|
263
|
+
if (closed) return;
|
|
264
|
+
if (waitCount >= MAX_CONCURRENT_WAITS) return;
|
|
265
|
+
|
|
266
|
+
// Item 6: Synchronous poll fast-path
|
|
267
|
+
// Try to dequeue a request synchronously before falling back to async wait.
|
|
268
|
+
if (hasPoll) {
|
|
269
|
+
var syncJson = g.__exactHttpPoll(serverId);
|
|
270
|
+
if (syncJson) {
|
|
271
|
+
// Got a request synchronously - handle it and keep polling
|
|
272
|
+
handleRequest(syncJson);
|
|
273
|
+
// Use setTimeout(0) to yield back to event loop before next sync poll
|
|
274
|
+
// This prevents starving timers/callbacks during high load
|
|
275
|
+
if (!closed) {
|
|
276
|
+
setTimeout(waitForNextRequest, 0);
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Item 8: Try batch drain before falling back to single async wait
|
|
283
|
+
if (hasDrain) {
|
|
284
|
+
var batchJson = g.__exactHttpDrain(serverId, 16);
|
|
285
|
+
if (batchJson) {
|
|
286
|
+
var batch;
|
|
287
|
+
try {
|
|
288
|
+
batch = JSON.parse(batchJson);
|
|
289
|
+
} catch(e) {
|
|
290
|
+
batch = null;
|
|
291
|
+
}
|
|
292
|
+
if (batch && batch.length > 0) {
|
|
293
|
+
for (var i = 0; i < batch.length; i++) {
|
|
294
|
+
handleRequestObj(batch[i]);
|
|
295
|
+
}
|
|
296
|
+
if (!closed) {
|
|
297
|
+
setTimeout(waitForNextRequest, 0);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (typeof g.__exactHttpWait !== "function") {
|
|
305
|
+
closed = true;
|
|
306
|
+
throw new Error("Exact HTTP dispatcher unavailable: __exactHttpWait is not defined");
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
waitCount++;
|
|
310
|
+
var waitPromise = g.__exactHttpWait(serverId, 0);
|
|
311
|
+
if (!waitPromise || typeof waitPromise.then !== "function") {
|
|
312
|
+
waitCount--;
|
|
313
|
+
if (!closed) {
|
|
314
|
+
waitForNextRequest();
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
waitPromise.then(function(reqJson) {
|
|
320
|
+
waitCount--;
|
|
321
|
+
if (closed) return;
|
|
322
|
+
|
|
323
|
+
if (reqJson) {
|
|
324
|
+
handleRequest(reqJson);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (!closed) {
|
|
328
|
+
waitForNextRequest();
|
|
329
|
+
}
|
|
330
|
+
}).catch(function() {
|
|
331
|
+
waitCount--;
|
|
332
|
+
if (!closed) {
|
|
333
|
+
waitForNextRequest();
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Item 9: Process a pre-parsed request object (from batch drain)
|
|
339
|
+
function handleRequestObj(req) {
|
|
340
|
+
if (!req || typeof req.method !== "string" || !req.url || typeof req.id !== "number") {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (req.url.charAt(0) !== "/") {
|
|
345
|
+
failResponse(req.id, HTTP_BAD_REQUEST, "Bad Request: malformed URL");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
processRequest(req);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function handleRequest(jsonStr) {
|
|
353
|
+
var req;
|
|
354
|
+
try {
|
|
355
|
+
req = JSON.parse(jsonStr);
|
|
356
|
+
} catch(e) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!req || typeof req.method !== "string" || !req.url || typeof req.id !== "number") {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (req.url.charAt(0) !== "/") {
|
|
365
|
+
failResponse(req.id, HTTP_BAD_REQUEST, "Bad Request: malformed URL");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
processRequest(req);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Item 9: Shared request processing logic to avoid duplication
|
|
373
|
+
function processRequest(req) {
|
|
374
|
+
var headerState = normalizeHeaders(req.headers || []);
|
|
375
|
+
if (!headerState.ok) {
|
|
376
|
+
failResponse(req.id, HTTP_BAD_REQUEST, "Bad Request: malformed headers");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Item 9: Use interned method strings
|
|
381
|
+
var method = internMethod(req.method);
|
|
382
|
+
|
|
383
|
+
var init = {
|
|
384
|
+
method: method,
|
|
385
|
+
headers: headerState.pairs
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// Item 9: Fast path for GET/HEAD - skip body processing entirely
|
|
389
|
+
if (req.hasBody && method !== METHOD_GET && method !== METHOD_HEAD) {
|
|
390
|
+
if (req.body) {
|
|
391
|
+
init.body = b64ToBytes(req.body);
|
|
392
|
+
} else {
|
|
393
|
+
init.body = createReadableRequestBody(serverId, req.id);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
var request;
|
|
398
|
+
// Use pre-computed URL prefix
|
|
399
|
+
var url = urlPrefix + req.url;
|
|
400
|
+
try {
|
|
401
|
+
request = new Request(url, init);
|
|
402
|
+
} catch(e) {
|
|
403
|
+
failResponse(req.id, HTTP_BAD_REQUEST, "Bad Request: " + (e && e.message || e));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
var p = fetchFn(request);
|
|
409
|
+
if (p && typeof p.then === "function") {
|
|
410
|
+
p.then(function(response) {
|
|
411
|
+
sendResponse(req.id, response);
|
|
412
|
+
}).catch(function(err) {
|
|
413
|
+
var message = stringifyError(err);
|
|
414
|
+
var status = mapRequestErrorStatus(message);
|
|
415
|
+
if (status === HTTP_INTERNAL_SERVER_ERROR) {
|
|
416
|
+
failResponse(req.id, status, "Internal Server Error: " + message);
|
|
417
|
+
} else {
|
|
418
|
+
failResponse(req.id, status, "Request Error: " + message);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
} else {
|
|
422
|
+
sendResponse(req.id, p);
|
|
423
|
+
}
|
|
424
|
+
} catch(err) {
|
|
425
|
+
var message = stringifyError(err);
|
|
426
|
+
var status = mapRequestErrorStatus(message);
|
|
427
|
+
if (status === HTTP_INTERNAL_SERVER_ERROR) {
|
|
428
|
+
failResponse(req.id, status, "Internal Server Error: " + message);
|
|
429
|
+
} else {
|
|
430
|
+
failResponse(req.id, status, "Request Error: " + message);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function sendResponse(requestId, response) {
|
|
436
|
+
if (!response || typeof response !== "object") {
|
|
437
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: malformed response");
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
var status = HTTP_OK;
|
|
442
|
+
if (response.status !== undefined) {
|
|
443
|
+
status = clampStatus(response.status);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Fast-path for simple text/json responses
|
|
447
|
+
var body = response.body;
|
|
448
|
+
var hasBodyStream = !!(body && typeof body.getReader === "function");
|
|
449
|
+
|
|
450
|
+
if (!hasBodyStream && typeof response.arrayBuffer === "function") {
|
|
451
|
+
var contentType = null;
|
|
452
|
+
var headerCount = 0;
|
|
453
|
+
var hasContentLength = false;
|
|
454
|
+
if (response.headers && typeof response.headers.get === "function") {
|
|
455
|
+
contentType = response.headers.get("content-type");
|
|
456
|
+
if (typeof response.headers.forEach === "function") {
|
|
457
|
+
response.headers.forEach(function(v, k) {
|
|
458
|
+
headerCount++;
|
|
459
|
+
if (k === "content-length") hasContentLength = true;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Use fast text/json path if only content-type (and maybe content-length) headers
|
|
465
|
+
if (contentType && headerCount <= 2) {
|
|
466
|
+
var isText = hasRespondText && contentType.indexOf("text/plain") === 0;
|
|
467
|
+
var isJson = hasRespondJson && (contentType.indexOf("application/json") === 0);
|
|
468
|
+
|
|
469
|
+
if (isText || isJson) {
|
|
470
|
+
response.arrayBuffer().then(function(ab) {
|
|
471
|
+
var bytes = new Uint8Array(ab);
|
|
472
|
+
if (bytes.length > MAX_RESPONSE_BODY_BYTES) {
|
|
473
|
+
failResponse(requestId, HTTP_REQUEST_ENTITY_TOO_LARGE, "Response Too Large");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
var payload = responseBodyPayload(bytes);
|
|
477
|
+
if (isText) {
|
|
478
|
+
g.__exactHttpRespondText(serverId, requestId, status, payload);
|
|
479
|
+
} else {
|
|
480
|
+
g.__exactHttpRespondJson(serverId, requestId, status, payload);
|
|
481
|
+
}
|
|
482
|
+
}).catch(function(err) {
|
|
483
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: " + (err && err.message || err));
|
|
484
|
+
});
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Item 10: Zero-copy string response path
|
|
491
|
+
// If the response body is a string and we have __exactHttpRespondString,
|
|
492
|
+
// pass it directly without converting to Uint8Array first
|
|
493
|
+
if (hasRespondString && !hasBodyStream && typeof response.text === "function") {
|
|
494
|
+
var ct = null;
|
|
495
|
+
if (response.headers && typeof response.headers.get === "function") {
|
|
496
|
+
ct = response.headers.get("content-type");
|
|
497
|
+
}
|
|
498
|
+
// Use string path for text-based content types
|
|
499
|
+
if (ct && (ct.indexOf("text/") === 0 || ct.indexOf("application/json") === 0 || ct.indexOf("application/xml") === 0)) {
|
|
500
|
+
// Serialize headers once
|
|
501
|
+
var strHeaderPairs = [];
|
|
502
|
+
var strTotalBytes = 0;
|
|
503
|
+
var strHeadersOk = true;
|
|
504
|
+
if (response.headers && typeof response.headers.forEach === "function") {
|
|
505
|
+
try {
|
|
506
|
+
response.headers.forEach(function(value, key) {
|
|
507
|
+
key = String(key);
|
|
508
|
+
value = String(value);
|
|
509
|
+
strHeaderPairs.push([key, value]);
|
|
510
|
+
strTotalBytes += key.length + value.length;
|
|
511
|
+
});
|
|
512
|
+
} catch(e) {
|
|
513
|
+
strHeadersOk = false;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (strHeadersOk && strHeaderPairs.length <= MAX_REQUEST_HEADERS && strTotalBytes <= MAX_REQUEST_HEADER_BYTES) {
|
|
517
|
+
var strHeadersJson = JSON.stringify(strHeaderPairs);
|
|
518
|
+
response.text().then(function(text) {
|
|
519
|
+
if (text.length > MAX_RESPONSE_BODY_BYTES) {
|
|
520
|
+
failResponse(requestId, HTTP_REQUEST_ENTITY_TOO_LARGE, "Response Too Large");
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
g.__exactHttpRespondString(serverId, requestId, status, strHeadersJson, text);
|
|
524
|
+
}).catch(function(err) {
|
|
525
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: " + (err && err.message || err));
|
|
526
|
+
});
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Standard path: serialize all headers
|
|
533
|
+
var headerPairs = [];
|
|
534
|
+
if (response.headers) {
|
|
535
|
+
if (typeof response.headers.forEach !== "function") {
|
|
536
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: malformed response headers");
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
var totalHeaderBytes = 0;
|
|
541
|
+
try {
|
|
542
|
+
response.headers.forEach(function(value, key) {
|
|
543
|
+
key = String(key);
|
|
544
|
+
value = String(value);
|
|
545
|
+
headerPairs.push([key, value]);
|
|
546
|
+
totalHeaderBytes += key.length + value.length;
|
|
547
|
+
});
|
|
548
|
+
} catch (err) {
|
|
549
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: malformed response headers");
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (headerPairs.length > MAX_REQUEST_HEADERS || totalHeaderBytes > MAX_REQUEST_HEADER_BYTES) {
|
|
554
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: response headers too large");
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
var headersJson = JSON.stringify(headerPairs);
|
|
559
|
+
|
|
560
|
+
if (!hasBodyStream && (typeof response.body === "undefined" || response.body === null)) {
|
|
561
|
+
g.__exactHttpRespond(serverId, requestId, status, headersJson, null);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function respondWithBytes(bytes) {
|
|
566
|
+
if (!bytes) {
|
|
567
|
+
g.__exactHttpRespond(serverId, requestId, status, headersJson, null);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (bytes.length > MAX_RESPONSE_BODY_BYTES) {
|
|
571
|
+
failResponse(requestId, HTTP_REQUEST_ENTITY_TOO_LARGE, "Response Too Large");
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
g.__exactHttpRespond(serverId, requestId, status, headersJson, responseBodyPayload(bytes));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (hasBodyStream && typeof g.__exactHttpRespondStream === "function" &&
|
|
578
|
+
typeof g.__exactHttpRespondChunk === "function" &&
|
|
579
|
+
typeof g.__exactHttpRespondEnd === "function") {
|
|
580
|
+
var respondStarted = g.__exactHttpRespondStream(serverId, requestId, status, headersJson);
|
|
581
|
+
if (respondStarted !== 0) {
|
|
582
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: failed to start response");
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
var streamReader = body.getReader();
|
|
587
|
+
function writeBody() {
|
|
588
|
+
return streamReader.read().then(function(result) {
|
|
589
|
+
if (result.done) {
|
|
590
|
+
g.__exactHttpRespondEnd(serverId, requestId);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
var chunk = result.value || new Uint8Array(0);
|
|
595
|
+
var writeResult = g.__exactHttpRespondChunk(serverId, requestId, responseBodyPayload(chunk));
|
|
596
|
+
if (writeResult === 0) {
|
|
597
|
+
return writeBody();
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (streamReader.cancel) {
|
|
601
|
+
streamReader.cancel("stream aborted");
|
|
602
|
+
}
|
|
603
|
+
g.__exactHttpRespondEnd(serverId, requestId);
|
|
604
|
+
throw new Error("Failed to write response chunk");
|
|
605
|
+
}).catch(function() {
|
|
606
|
+
g.__exactHttpRespondEnd(serverId, requestId);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
writeBody();
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (typeof response.arrayBuffer === "function" && !hasBodyStream) {
|
|
615
|
+
response.arrayBuffer().then(function(ab) {
|
|
616
|
+
respondWithBytes(new Uint8Array(ab));
|
|
617
|
+
}).catch(function(err) {
|
|
618
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: " + (err && err.message || err));
|
|
619
|
+
});
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (!hasBodyStream) {
|
|
624
|
+
g.__exactHttpRespond(serverId, requestId, status, headersJson, null);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
{
|
|
629
|
+
var chunks = [];
|
|
630
|
+
var totalSize = 0;
|
|
631
|
+
var reader = body.getReader();
|
|
632
|
+
function readBody() {
|
|
633
|
+
return reader.read().then(function(result) {
|
|
634
|
+
if (result.done) {
|
|
635
|
+
var combined = new Uint8Array(totalSize);
|
|
636
|
+
var offset = 0;
|
|
637
|
+
for (var i = 0; i < chunks.length; i++) {
|
|
638
|
+
var chunk = chunks[i];
|
|
639
|
+
combined.set(chunk, offset);
|
|
640
|
+
offset += chunk.length;
|
|
641
|
+
}
|
|
642
|
+
respondWithBytes(combined);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
var chunk = result.value || new Uint8Array(0);
|
|
646
|
+
totalSize += chunk.length;
|
|
647
|
+
if (totalSize > MAX_RESPONSE_BODY_BYTES) {
|
|
648
|
+
reader.cancel("response too large");
|
|
649
|
+
throw new Error("Response Too Large");
|
|
650
|
+
}
|
|
651
|
+
chunks.push(chunk);
|
|
652
|
+
return readBody();
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
readBody().catch(function(err) {
|
|
657
|
+
failResponse(requestId, HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error: " + (err && err.message || err));
|
|
658
|
+
});
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Start multiple concurrent waiters for better throughput
|
|
664
|
+
waitForNextRequest();
|
|
665
|
+
waitForNextRequest();
|
|
666
|
+
waitForNextRequest();
|
|
667
|
+
waitForNextRequest();
|
|
668
|
+
|
|
669
|
+
var handle = {
|
|
670
|
+
close: function(opts) {
|
|
671
|
+
if (closed) return Promise.resolve();
|
|
672
|
+
closed = true;
|
|
673
|
+
waitCount = 0;
|
|
674
|
+
var force = false;
|
|
675
|
+
if (opts && opts.force) {
|
|
676
|
+
force = !!opts.force;
|
|
677
|
+
}
|
|
678
|
+
g.__exactHttpClose(serverId, force ? 1 : 0);
|
|
679
|
+
return Promise.resolve();
|
|
680
|
+
},
|
|
681
|
+
ref: function() {
|
|
682
|
+
g.__exactHttpSetRef(serverId, 1);
|
|
683
|
+
},
|
|
684
|
+
unref: function() {
|
|
685
|
+
g.__exactHttpSetRef(serverId, 0);
|
|
686
|
+
},
|
|
687
|
+
address: function() {
|
|
688
|
+
var json = g.__exactHttpAddress(serverId);
|
|
689
|
+
if (!json) return null;
|
|
690
|
+
return JSON.parse(json);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
return Promise.resolve(handle);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export { serve };
|