load wasm dynamically
This commit is contained in:
parent
6f5e0c361d
commit
cd40e504ab
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@ zig-cache/
|
||||||
zig-out/
|
zig-out/
|
||||||
core
|
core
|
||||||
src/worker_name.txt
|
src/worker_name.txt
|
||||||
|
*.wasm
|
||||||
|
|
BIN
src/demo.wasm
BIN
src/demo.wasm
Binary file not shown.
106
src/main.zig
106
src/main.zig
|
@ -5,7 +5,7 @@ var x_auth_key: [:0]const u8 = undefined;
|
||||||
|
|
||||||
const cf_api_base = "https://api.cloudflare.com/client/v4";
|
const cf_api_base = "https://api.cloudflare.com/client/v4";
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !u8 {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const allocator = arena.allocator();
|
const allocator = arena.allocator();
|
||||||
|
@ -21,7 +21,7 @@ pub fn main() !void {
|
||||||
// We might actually want a "run this wasm" upload vs a "these are my
|
// We might actually want a "run this wasm" upload vs a "these are my
|
||||||
// js files" upload. But for now we'll optimize for wasm
|
// js files" upload. But for now we'll optimize for wasm
|
||||||
const script =
|
const script =
|
||||||
\\import demoWasm from "./24526702f6c3ed7fb02b15125f614dd38804525f-demo.wasm";
|
\\import demoWasm from "demo.wasm";
|
||||||
\\var src_default = {
|
\\var src_default = {
|
||||||
\\ async fetch(request, _env2, ctx) {
|
\\ async fetch(request, _env2, ctx) {
|
||||||
\\ const stdout = new TransformStream();
|
\\ const stdout = new TransformStream();
|
||||||
|
@ -56,7 +56,8 @@ pub fn main() !void {
|
||||||
\\ src_default as default
|
\\ src_default as default
|
||||||
\\};
|
\\};
|
||||||
;
|
;
|
||||||
const wasm = @embedFile("demo.wasm");
|
var wasm = try loadWasm(allocator, script);
|
||||||
|
defer wasm.deinit();
|
||||||
|
|
||||||
// stdout is for the actual output of your application, for example if you
|
// stdout is for the actual output of your application, for example if you
|
||||||
// are implementing gzip, then only the compressed bytes should be sent to
|
// are implementing gzip, then only the compressed bytes should be sent to
|
||||||
|
@ -79,18 +80,91 @@ pub fn main() !void {
|
||||||
.{if (worker_exists) "Worker exists, will not re-enable" else "Worker is new. Will enable after code update"},
|
.{if (worker_exists) "Worker exists, will not re-enable" else "Worker is new. Will enable after code update"},
|
||||||
);
|
);
|
||||||
|
|
||||||
try putNewWorker(allocator, &client, .{
|
var worker = Worker{
|
||||||
.account_id = accountid.?,
|
.account_id = accountid.?,
|
||||||
.name = worker_name,
|
.name = worker_name,
|
||||||
.wasm_file_data = wasm,
|
.wasm_file_data = wasm.data,
|
||||||
.main_module = script,
|
.main_module = script,
|
||||||
});
|
};
|
||||||
|
putNewWorker(allocator, &client, &worker) catch |err| {
|
||||||
|
if (worker.errors == null) return err;
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
try stderr.print("{d} errors returned from CloudFlare:\n\n", .{worker.errors.?.len});
|
||||||
|
for (worker.errors.?) |cf_err| {
|
||||||
|
try stderr.print("{s}\n", .{cf_err});
|
||||||
|
allocator.free(cf_err);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
const subdomain = try getSubdomain(allocator, &client, accountid.?);
|
const subdomain = try getSubdomain(allocator, &client, accountid.?);
|
||||||
defer allocator.free(subdomain);
|
defer allocator.free(subdomain);
|
||||||
try stdout.print("Worker available at: https://{s}.{s}.workers.dev/\n", .{ worker_name, subdomain });
|
try stdout.print("Worker available at: https://{s}.{s}.workers.dev/\n", .{ worker_name, subdomain });
|
||||||
if (!worker_exists)
|
if (!worker_exists)
|
||||||
try enableWorker(allocator, &client, accountid.?, worker_name);
|
try enableWorker(allocator, &client, accountid.?, worker_name);
|
||||||
try bw.flush(); // don't forget to flush!
|
try bw.flush(); // don't forget to flush!
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wasm = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
name: []const u8,
|
||||||
|
data: []const u8,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.allocator.free(self.name);
|
||||||
|
self.allocator.free(self.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn loadWasm(allocator: std.mem.Allocator, script: []const u8) !Wasm {
|
||||||
|
// Looking for a string like this: import demoWasm from "demo.wasm"
|
||||||
|
// JavaScript may or may not have ; characters. We're not doing
|
||||||
|
// a full JS parsing here, so this may not be the most robust
|
||||||
|
|
||||||
|
var inx: usize = 0;
|
||||||
|
|
||||||
|
var name: ?[]const u8 = null;
|
||||||
|
while (true) {
|
||||||
|
inx = std.mem.indexOf(u8, script[inx..], "import ") orelse if (inx == 0) return error.NoImportFound else break;
|
||||||
|
inx += "import ".len;
|
||||||
|
|
||||||
|
// oh god, we're not doing this: https://262.ecma-international.org/5.1/#sec-7.5
|
||||||
|
// advance to next token - we don't care what the name is
|
||||||
|
while (inx < script.len and script[inx] != ' ') inx += 1;
|
||||||
|
// continue past space(s)
|
||||||
|
while (inx < script.len and script[inx] == ' ') inx += 1;
|
||||||
|
// We expect "from " to be next
|
||||||
|
if (!std.mem.startsWith(u8, script[inx..], "from ")) continue;
|
||||||
|
inx += "from ".len;
|
||||||
|
// continue past space(s)
|
||||||
|
while (inx < script.len and script[inx] == ' ') inx += 1;
|
||||||
|
// We now expect the name of our file...
|
||||||
|
if (script[inx] != '"' and script[inx] != '\'') continue; // we're not where we think we are
|
||||||
|
const quote = script[inx]; // there are two possibilities here
|
||||||
|
// we don't need to advance inx any more, as we're on the name, and if
|
||||||
|
// we loop, that's ok
|
||||||
|
inx += 1; // move off the quote onto the name
|
||||||
|
const end_quote_inx = std.mem.indexOfScalar(u8, script[inx..], quote);
|
||||||
|
if (end_quote_inx == null) continue;
|
||||||
|
const candidate_name = script[inx .. inx + end_quote_inx.?];
|
||||||
|
if (std.mem.endsWith(u8, candidate_name, ".wasm")) {
|
||||||
|
if (name != null) // we are importing two wasm files, and we are now lost
|
||||||
|
return error.MultipleWasmImportsUnsupported;
|
||||||
|
name = candidate_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name == null) return error.NoWasmImportFound;
|
||||||
|
|
||||||
|
const nm = try allocator.dupe(u8, name.?);
|
||||||
|
errdefer allocator.free(nm);
|
||||||
|
const data = try std.fs.cwd().readFileAlloc(allocator, nm, std.math.maxInt(usize));
|
||||||
|
return Wasm{
|
||||||
|
.allocator = allocator,
|
||||||
|
.name = nm,
|
||||||
|
.data = data,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getAccountId(allocator: std.mem.Allocator, client: *std.http.Client) ![:0]const u8 {
|
fn getAccountId(allocator: std.mem.Allocator, client: *std.http.Client) ![:0]const u8 {
|
||||||
|
@ -167,8 +241,9 @@ const Worker = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
main_module: []const u8,
|
main_module: []const u8,
|
||||||
wasm_file_data: []const u8,
|
wasm_file_data: []const u8,
|
||||||
|
errors: ?[][]const u8 = null,
|
||||||
};
|
};
|
||||||
fn putNewWorker(allocator: std.mem.Allocator, client: *std.http.Client, worker: Worker) !void {
|
fn putNewWorker(allocator: std.mem.Allocator, client: *std.http.Client, worker: *Worker) !void {
|
||||||
const put_script = cf_api_base ++ "/accounts/{s}/workers/scripts/{s}?include_subdomain_availability=true&excludeScript=true";
|
const put_script = cf_api_base ++ "/accounts/{s}/workers/scripts/{s}?include_subdomain_availability=true&excludeScript=true";
|
||||||
const url = try std.fmt.allocPrint(allocator, put_script, .{ worker.account_id, worker.name });
|
const url = try std.fmt.allocPrint(allocator, put_script, .{ worker.account_id, worker.name });
|
||||||
defer allocator.free(url);
|
defer allocator.free(url);
|
||||||
|
@ -226,10 +301,27 @@ fn putNewWorker(allocator: std.mem.Allocator, client: *std.http.Client, worker:
|
||||||
// std.debug.print("Url is {s}\n", .{url});
|
// std.debug.print("Url is {s}\n", .{url});
|
||||||
if (req.response.status != .ok) {
|
if (req.response.status != .ok) {
|
||||||
std.debug.print("Status is {}\n", .{req.response.status});
|
std.debug.print("Status is {}\n", .{req.response.status});
|
||||||
|
if (req.response.status == .bad_request)
|
||||||
|
worker.errors = getErrors(allocator, &req) catch null;
|
||||||
return error.RequestFailed;
|
return error.RequestFailed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn getErrors(allocator: std.mem.Allocator, req: *std.http.Client.Request) !?[][]const u8 {
|
||||||
|
var json_reader = std.json.reader(allocator, req.reader());
|
||||||
|
defer json_reader.deinit();
|
||||||
|
var body = try std.json.parseFromTokenSource(std.json.Value, allocator, &json_reader, .{});
|
||||||
|
defer body.deinit();
|
||||||
|
const arr = body.value.object.get("errors").?.array.items;
|
||||||
|
if (arr.len == 0) return null;
|
||||||
|
var error_list = try std.ArrayList([]const u8).initCapacity(allocator, arr.len);
|
||||||
|
defer error_list.deinit();
|
||||||
|
for (arr) |item| {
|
||||||
|
error_list.appendAssumeCapacity(item.object.get("message").?.string);
|
||||||
|
}
|
||||||
|
return try error_list.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
fn workerExists(allocator: std.mem.Allocator, client: *std.http.Client, account_id: []const u8, name: []const u8) !bool {
|
fn workerExists(allocator: std.mem.Allocator, client: *std.http.Client, account_id: []const u8, name: []const u8) !bool {
|
||||||
const existence_check = cf_api_base ++ "/accounts/{s}/workers/services/{s}";
|
const existence_check = cf_api_base ++ "/accounts/{s}/workers/services/{s}";
|
||||||
const url = try std.fmt.allocPrint(allocator, existence_check, .{ account_id, name });
|
const url = try std.fmt.allocPrint(allocator, existence_check, .{ account_id, name });
|
||||||
|
|
Loading…
Reference in New Issue
Block a user