move executor config to external file
This commit is contained in:
parent
c393792cd1
commit
02017da98e
8
proxy.ini
Normal file
8
proxy.ini
Normal file
|
@ -0,0 +1,8 @@
|
|||
# This is a simple "path prefix" = dynamic library path mapping
|
||||
# no reordering will be done, so you must do things most -> least specific
|
||||
# because all paths start with a '/', we may be able to later add the ability
|
||||
# for libraries to self-select whether they can handle a request, which opens
|
||||
# up additional possibilities
|
||||
|
||||
/c = zig-out/lib/libfaas-proxy-sample-lib-in-c.so
|
||||
/ = zig-out/lib/libfaas-proxy-sample-lib.so
|
|
@ -4,18 +4,18 @@ const std = @import("std");
|
|||
allocator: std.mem.Allocator,
|
||||
|
||||
pub const ParsedConfig = struct {
|
||||
key_value_map: *std.StringArrayHashMap([]u8),
|
||||
value_key_map: *std.StringArrayHashMap(*std.ArrayList([]u8)),
|
||||
key_value_map: *std.StringArrayHashMap([:0]u8),
|
||||
value_key_map: *std.StringArrayHashMap(*std.ArrayList([:0]u8)),
|
||||
|
||||
config_allocator: std.mem.Allocator,
|
||||
const SelfConfig = @This();
|
||||
|
||||
var by_keys: std.StringArrayHashMap([]u8) = undefined;
|
||||
var by_values: std.StringArrayHashMap(*std.ArrayList([]u8)) = undefined;
|
||||
var by_keys: std.StringArrayHashMap([:0]u8) = undefined;
|
||||
var by_values: std.StringArrayHashMap(*std.ArrayList([:0]u8)) = undefined;
|
||||
|
||||
pub fn init(config_allocator: std.mem.Allocator) SelfConfig {
|
||||
by_keys = std.StringArrayHashMap([]u8).init(config_allocator);
|
||||
by_values = std.StringArrayHashMap(*std.ArrayList([]u8)).init(config_allocator);
|
||||
by_keys = std.StringArrayHashMap([:0]u8).init(config_allocator);
|
||||
by_values = std.StringArrayHashMap(*std.ArrayList([:0]u8)).init(config_allocator);
|
||||
return SelfConfig{
|
||||
.config_allocator = config_allocator,
|
||||
.key_value_map = &by_keys,
|
||||
|
@ -25,7 +25,8 @@ pub const ParsedConfig = struct {
|
|||
|
||||
pub fn deinit(self: *SelfConfig) void {
|
||||
for (self.key_value_map.keys(), self.key_value_map.values()) |k, v| {
|
||||
self.config_allocator.free(k); // this is also the key in value_key_map
|
||||
// StringArrayHashMap assumes []const u8, but what we've allocated is null terminated
|
||||
self.config_allocator.free(@ptrCast([:0]const u8, k)); // this is also the key in value_key_map
|
||||
self.config_allocator.free(v);
|
||||
}
|
||||
|
||||
|
@ -70,12 +71,12 @@ pub fn parse(self: Self, reader: anytype) !ParsedConfig {
|
|||
// keys should be putNoClobber, but values can be put.
|
||||
// Because we have to dup the memory here though, we want to
|
||||
// manage duplicate values seperately
|
||||
var dup_key = try self.allocator.dupe(u8, key);
|
||||
var dup_value = try self.allocator.dupe(u8, value);
|
||||
var dup_key = try self.allocator.dupeZ(u8, key);
|
||||
var dup_value = try self.allocator.dupeZ(u8, value);
|
||||
try rc.key_value_map.putNoClobber(dup_key, dup_value);
|
||||
if (!rc.value_key_map.contains(value)) {
|
||||
var keys = try self.allocator.create(std.ArrayList([]u8));
|
||||
keys.* = std.ArrayList([]u8).init(self.allocator);
|
||||
var keys = try self.allocator.create(std.ArrayList([:0]u8));
|
||||
keys.* = std.ArrayList([:0]u8).init(self.allocator);
|
||||
try rc.value_key_map.put(dup_value, keys);
|
||||
}
|
||||
try rc.value_key_map.get(value).?.append(dup_key);
|
||||
|
|
52
src/main.zig
52
src/main.zig
|
@ -16,6 +16,7 @@ const FullReturn = struct {
|
|||
|
||||
const Executor = struct {
|
||||
// configuration
|
||||
target_prefix: []const u8,
|
||||
path: [:0]const u8,
|
||||
|
||||
// fields used at runtime to do real work
|
||||
|
@ -30,13 +31,6 @@ const Executor = struct {
|
|||
in_request_lock: bool = false,
|
||||
};
|
||||
|
||||
// TODO: This should be in a file that maybe gets reloaded with SIGHUP
|
||||
// rather than a file watch. Touching this config is pretty dangerous
|
||||
var executors = [_]Executor{
|
||||
.{ .path = "zig-out/lib/libfaas-proxy-sample-lib.so" },
|
||||
.{ .path = "zig-out/lib/libfaas-proxy-sample-lib2.so" },
|
||||
};
|
||||
|
||||
var watcher = Watch.init(executorChanged);
|
||||
var watcher_thread: ?std.Thread = null;
|
||||
var timer: ?std.time.Timer = null; // timer used by processRequest
|
||||
|
@ -52,9 +46,11 @@ pub const std_options = struct {
|
|||
const SERVE_FN_NAME = "handle_request";
|
||||
const PORT = 8069;
|
||||
|
||||
var executors: []Executor = undefined;
|
||||
|
||||
fn serve(allocator: *std.mem.Allocator, response: *std.http.Server.Response) !*FullReturn {
|
||||
// if (some path routing thing) {
|
||||
const executor = try getExecutor(0);
|
||||
const executor = try getExecutor(response.request.target);
|
||||
if (executor.zigInitFn) |f|
|
||||
f(allocator);
|
||||
|
||||
|
@ -104,8 +100,16 @@ fn serve(allocator: *std.mem.Allocator, response: *std.http.Server.Response) !*F
|
|||
rc.response = slice;
|
||||
return rc;
|
||||
}
|
||||
fn getExecutor(key: usize) !*Executor {
|
||||
var executor = &executors[key];
|
||||
fn getExecutor(requested_path: []const u8) !*Executor {
|
||||
var executor = blk: {
|
||||
for (executors) |*exec| {
|
||||
if (std.mem.startsWith(u8, requested_path, exec.target_prefix)) {
|
||||
break :blk exec;
|
||||
}
|
||||
}
|
||||
log.err("Could not find executor for target path '{s}'", .{requested_path});
|
||||
return error.NoApplicableExecutor;
|
||||
};
|
||||
if (executor.serveFn != null) return executor;
|
||||
|
||||
executor.library = blk: {
|
||||
|
@ -146,7 +150,7 @@ fn loadOptionalSymbols(executor: *Executor) void {
|
|||
fn executorChanged(watch: usize) void {
|
||||
// NOTE: This will be called off the main thread
|
||||
log.debug("executor with watch {d} changed", .{watch});
|
||||
for (&executors) |*executor| {
|
||||
for (executors) |*executor| {
|
||||
if (executor.watch) |w| {
|
||||
if (w == watch) {
|
||||
if (executor.library) |l| {
|
||||
|
@ -231,9 +235,13 @@ pub fn main() !void {
|
|||
var bw = std.io.bufferedWriter(stdout_file);
|
||||
const stdout = bw.writer();
|
||||
|
||||
var allocator = std.heap.raw_c_allocator; // raw allocator recommended for use in arenas
|
||||
executors = try loadConfig(allocator);
|
||||
defer allocator.free(executors);
|
||||
defer parsed_config.deinit();
|
||||
|
||||
watcher_thread = try std.Thread.spawn(.{}, Watch.startWatch, .{&watcher});
|
||||
|
||||
var allocator = std.heap.raw_c_allocator; // raw allocator recommended for use in arenas
|
||||
var server = std.http.Server.init(allocator, .{ .reuse_address = true });
|
||||
defer server.deinit();
|
||||
|
||||
|
@ -270,6 +278,23 @@ pub fn main() !void {
|
|||
try bw.flush();
|
||||
}
|
||||
}
|
||||
var parsed_config: config.ParsedConfig = undefined;
|
||||
fn loadConfig(allocator: std.mem.Allocator) ![]Executor {
|
||||
log.info("loading config", .{});
|
||||
// We will not watch this file - let it reload on SIGHUP
|
||||
var config_file = try std.fs.cwd().openFile("proxy.ini", .{});
|
||||
defer config_file.close();
|
||||
parsed_config = try config.init(allocator).parse(config_file.reader());
|
||||
var al = try std.ArrayList(Executor).initCapacity(allocator, parsed_config.key_value_map.keys().len);
|
||||
defer al.deinit();
|
||||
for (parsed_config.key_value_map.keys(), parsed_config.key_value_map.values()) |k, v| {
|
||||
al.appendAssumeCapacity(.{
|
||||
.target_prefix = k,
|
||||
.path = v,
|
||||
});
|
||||
}
|
||||
return al.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn processRequest(allocator: *std.mem.Allocator, server: *std.http.Server, writer: anytype) !void {
|
||||
const max_header_size = 8192;
|
||||
|
@ -371,6 +396,9 @@ fn writeToTestBuffers(response: []const u8, res: *std.http.Server.Response) void
|
|||
}
|
||||
fn testRequest(request_bytes: []const u8) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
executors = try loadConfig(allocator);
|
||||
defer allocator.free(executors);
|
||||
defer parsed_config.deinit();
|
||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user