@aresdefencelabs/wasm-http-runtime 0.0.1
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/abi.d.ts +7 -0
- package/dist/abi.js +35 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/memory.d.ts +4 -0
- package/dist/memory.js +24 -0
- package/dist/runtime.d.ts +10 -0
- package/dist/runtime.js +64 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +1 -0
- package/dist/worker.d.ts +4 -0
- package/dist/worker.js +12 -0
- package/package.json +40 -0
package/dist/abi.d.ts
ADDED
package/dist/abi.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readCString, writeCString } from "./memory";
|
|
2
|
+
function getInstanceOrThrow(state) {
|
|
3
|
+
if (!state.instance) {
|
|
4
|
+
throw new Error("Wasm instance has not been initialised yet.");
|
|
5
|
+
}
|
|
6
|
+
return state.instance;
|
|
7
|
+
}
|
|
8
|
+
function getRequestContextOrThrow(state) {
|
|
9
|
+
if (!state.requestContext) {
|
|
10
|
+
throw new Error("No active request context.");
|
|
11
|
+
}
|
|
12
|
+
return state.requestContext;
|
|
13
|
+
}
|
|
14
|
+
export function createAresAbiImports(state) {
|
|
15
|
+
return {
|
|
16
|
+
ares_abi: {
|
|
17
|
+
abi_log(messagePtr) {
|
|
18
|
+
const instance = getInstanceOrThrow(state);
|
|
19
|
+
const message = readCString(instance.exports.memory, messagePtr);
|
|
20
|
+
if (state.options.onLog) {
|
|
21
|
+
state.options.onLog(message);
|
|
22
|
+
}
|
|
23
|
+
else if (state.options.debug) {
|
|
24
|
+
console.log("[AresWasm]", message);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
abi_http_get_user_agent_name() {
|
|
28
|
+
const instance = getInstanceOrThrow(state);
|
|
29
|
+
const ctx = getRequestContextOrThrow(state);
|
|
30
|
+
const userAgent = ctx.request.headers.get("user-agent") ?? "Cloudflare-Worker";
|
|
31
|
+
return writeCString(instance.exports.memory, instance.exports.alloc, userAgent);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/memory.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function readCString(memory: WebAssembly.Memory, ptr: number): string;
|
|
2
|
+
export declare function writeCString(memory: WebAssembly.Memory, alloc: (size: number) => number, value: string): number;
|
|
3
|
+
export declare function writeJsonCString(memory: WebAssembly.Memory, alloc: (size: number) => number, value: unknown): number;
|
|
4
|
+
export declare function headersToObject(headers: Headers): Record<string, string>;
|
package/dist/memory.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const encoder = new TextEncoder();
|
|
2
|
+
const decoder = new TextDecoder();
|
|
3
|
+
export function readCString(memory, ptr) {
|
|
4
|
+
const bytes = new Uint8Array(memory.buffer);
|
|
5
|
+
let end = ptr;
|
|
6
|
+
while (bytes[end] !== 0) {
|
|
7
|
+
end++;
|
|
8
|
+
}
|
|
9
|
+
return decoder.decode(bytes.subarray(ptr, end));
|
|
10
|
+
}
|
|
11
|
+
export function writeCString(memory, alloc, value) {
|
|
12
|
+
const encoded = encoder.encode(value);
|
|
13
|
+
const ptr = alloc(encoded.length + 1);
|
|
14
|
+
const bytes = new Uint8Array(memory.buffer);
|
|
15
|
+
bytes.set(encoded, ptr);
|
|
16
|
+
bytes[ptr + encoded.length] = 0;
|
|
17
|
+
return ptr;
|
|
18
|
+
}
|
|
19
|
+
export function writeJsonCString(memory, alloc, value) {
|
|
20
|
+
return writeCString(memory, alloc, JSON.stringify(value));
|
|
21
|
+
}
|
|
22
|
+
export function headersToObject(headers) {
|
|
23
|
+
return Object.fromEntries(headers.entries());
|
|
24
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RequestScopedContext, RuntimeOptions, WasmInstanceWithExports, WorkerExecutionContext } from "./types";
|
|
2
|
+
export declare class AresWorkerRuntime<Env = unknown> {
|
|
3
|
+
private readonly state;
|
|
4
|
+
constructor(options: RuntimeOptions<Env>);
|
|
5
|
+
init(): Promise<void>;
|
|
6
|
+
setRequestContext(context: RequestScopedContext<Env>): void;
|
|
7
|
+
clearRequestContext(): void;
|
|
8
|
+
get instance(): WasmInstanceWithExports;
|
|
9
|
+
handleFetch(request: Request, env: Env, ctx: WorkerExecutionContext): Promise<Response>;
|
|
10
|
+
}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createAresAbiImports } from "./abi";
|
|
2
|
+
import { headersToObject, readCString, writeJsonCString } from "./memory";
|
|
3
|
+
export class AresWorkerRuntime {
|
|
4
|
+
state;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.state = {
|
|
7
|
+
instance: null,
|
|
8
|
+
options,
|
|
9
|
+
requestContext: null
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
async init() {
|
|
13
|
+
if (this.state.instance) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const imports = createAresAbiImports(this.state);
|
|
17
|
+
const instance = await WebAssembly.instantiate(this.state.options.wasm, imports);
|
|
18
|
+
this.state.instance = instance;
|
|
19
|
+
if (instance.exports.app_init) {
|
|
20
|
+
instance.exports.app_init();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
setRequestContext(context) {
|
|
24
|
+
this.state.requestContext = context;
|
|
25
|
+
}
|
|
26
|
+
clearRequestContext() {
|
|
27
|
+
this.state.requestContext = null;
|
|
28
|
+
}
|
|
29
|
+
get instance() {
|
|
30
|
+
if (!this.state.instance) {
|
|
31
|
+
throw new Error("Runtime not initialised. Call init() first.");
|
|
32
|
+
}
|
|
33
|
+
return this.state.instance;
|
|
34
|
+
}
|
|
35
|
+
async handleFetch(request, env, ctx) {
|
|
36
|
+
await this.init();
|
|
37
|
+
this.setRequestContext({ request, env, ctx });
|
|
38
|
+
try {
|
|
39
|
+
if (!this.instance.exports.handle_http) {
|
|
40
|
+
throw new Error("Wasm export handle_http() was not found.");
|
|
41
|
+
}
|
|
42
|
+
const requestBody = await request.text();
|
|
43
|
+
const inboundEnvelope = {
|
|
44
|
+
url: request.url,
|
|
45
|
+
method: request.method,
|
|
46
|
+
headers: headersToObject(request.headers),
|
|
47
|
+
body: requestBody
|
|
48
|
+
};
|
|
49
|
+
const requestPtr = writeJsonCString(this.instance.exports.memory, this.instance.exports.alloc, inboundEnvelope);
|
|
50
|
+
const responsePtr = this.instance.exports.handle_http(requestPtr);
|
|
51
|
+
const responseJson = readCString(this.instance.exports.memory, responsePtr);
|
|
52
|
+
const responseEnvelope = JSON.parse(responseJson);
|
|
53
|
+
return new Response(typeof responseEnvelope.body === "string"
|
|
54
|
+
? responseEnvelope.body
|
|
55
|
+
: JSON.stringify(responseEnvelope.body), {
|
|
56
|
+
status: responseEnvelope.status,
|
|
57
|
+
headers: responseEnvelope.headers
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
this.clearRequestContext();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface WorkerExecutionContext {
|
|
2
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
3
|
+
passThroughOnException?(): void;
|
|
4
|
+
}
|
|
5
|
+
export type WasmExports = {
|
|
6
|
+
memory: WebAssembly.Memory;
|
|
7
|
+
alloc: (size: number) => number;
|
|
8
|
+
free_mem?: (ptr: number, size: number) => void;
|
|
9
|
+
app_init?: () => number;
|
|
10
|
+
handle_http?: (requestJsonPtr: number) => number;
|
|
11
|
+
};
|
|
12
|
+
export type WasmInstanceWithExports = WebAssembly.Instance & {
|
|
13
|
+
exports: WasmExports;
|
|
14
|
+
};
|
|
15
|
+
export type RequestEnvelope = {
|
|
16
|
+
url: string;
|
|
17
|
+
method: string;
|
|
18
|
+
headers: Record<string, string>;
|
|
19
|
+
body?: string | null;
|
|
20
|
+
};
|
|
21
|
+
export type ResponseEnvelope = {
|
|
22
|
+
status: number;
|
|
23
|
+
headers: Record<string, string>;
|
|
24
|
+
body: unknown;
|
|
25
|
+
};
|
|
26
|
+
export type RuntimeOptions<Env = unknown> = {
|
|
27
|
+
wasm: WebAssembly.Module;
|
|
28
|
+
debug?: boolean;
|
|
29
|
+
onLog?: (message: string) => void;
|
|
30
|
+
env?: Env;
|
|
31
|
+
};
|
|
32
|
+
export type RequestScopedContext<Env = unknown> = {
|
|
33
|
+
request: Request;
|
|
34
|
+
env: Env;
|
|
35
|
+
ctx: WorkerExecutionContext;
|
|
36
|
+
};
|
|
37
|
+
export type RuntimeState<Env = unknown> = {
|
|
38
|
+
instance: WasmInstanceWithExports | null;
|
|
39
|
+
options: RuntimeOptions<Env>;
|
|
40
|
+
requestContext: RequestScopedContext<Env> | null;
|
|
41
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RuntimeOptions, WorkerExecutionContext } from "./types";
|
|
2
|
+
export declare function createAresWorkerHandler<Env = unknown>(wasm: WebAssembly.Module, options?: Omit<RuntimeOptions<Env>, "wasm">): {
|
|
3
|
+
fetch(request: Request, env: Env, ctx: WorkerExecutionContext): Promise<Response>;
|
|
4
|
+
};
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AresWorkerRuntime } from "./runtime";
|
|
2
|
+
export function createAresWorkerHandler(wasm, options) {
|
|
3
|
+
const runtime = new AresWorkerRuntime({
|
|
4
|
+
wasm,
|
|
5
|
+
...options
|
|
6
|
+
});
|
|
7
|
+
return {
|
|
8
|
+
async fetch(request, env, ctx) {
|
|
9
|
+
return runtime.handleFetch(request, env, ctx);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aresdefencelabs/wasm-http-runtime",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Runtime adapter that connects C++ WebAssembly workers to the Cloudflare Workers runtime via an ABI bridge.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Ares-Defence-Labs/wasm-workers-http.git"
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
"homepage": "https://github.com/Ares-Defence-Labs/wasm-workers-http",
|
|
13
|
+
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/Ares-Defence-Labs/wasm-workers-http/issues"
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
"author": "Ares Defence Labs",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
|
|
21
|
+
"keywords": [
|
|
22
|
+
"cloudflare",
|
|
23
|
+
"cloudflare-workers",
|
|
24
|
+
"webassembly",
|
|
25
|
+
"wasm",
|
|
26
|
+
"cpp",
|
|
27
|
+
"wasm-runtime",
|
|
28
|
+
"workers-runtime",
|
|
29
|
+
"http-client",
|
|
30
|
+
"serverless",
|
|
31
|
+
"edge"
|
|
32
|
+
],
|
|
33
|
+
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json"
|
|
36
|
+
},
|
|
37
|
+
"main": "./dist/index.js",
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"files": ["dist"]
|
|
40
|
+
}
|