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,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
pub const ParsedConfig = struct {
|
pub const ParsedConfig = struct {
|
||||||
key_value_map: *std.StringArrayHashMap([]u8),
|
key_value_map: *std.StringArrayHashMap([:0]u8),
|
||||||
value_key_map: *std.StringArrayHashMap(*std.ArrayList([]u8)),
|
value_key_map: *std.StringArrayHashMap(*std.ArrayList([:0]u8)),
|
||||||
|
|
||||||
config_allocator: std.mem.Allocator,
|
config_allocator: std.mem.Allocator,
|
||||||
const SelfConfig = @This();
|
const SelfConfig = @This();
|
||||||
|
|
||||||
var by_keys: std.StringArrayHashMap([]u8) = undefined;
|
var by_keys: std.StringArrayHashMap([:0]u8) = undefined;
|
||||||
var by_values: std.StringArrayHashMap(*std.ArrayList([]u8)) = undefined;
|
var by_values: std.StringArrayHashMap(*std.ArrayList([:0]u8)) = undefined;
|
||||||
|
|
||||||
pub fn init(config_allocator: std.mem.Allocator) SelfConfig {
|
pub fn init(config_allocator: std.mem.Allocator) SelfConfig {
|
||||||
by_keys = std.StringArrayHashMap([]u8).init(config_allocator);
|
by_keys = std.StringArrayHashMap([:0]u8).init(config_allocator);
|
||||||
by_values = std.StringArrayHashMap(*std.ArrayList([]u8)).init(config_allocator);
|
by_values = std.StringArrayHashMap(*std.ArrayList([:0]u8)).init(config_allocator);
|
||||||
return SelfConfig{
|
return SelfConfig{
|
||||||
.config_allocator = config_allocator,
|
.config_allocator = config_allocator,
|
||||||
.key_value_map = &by_keys,
|
.key_value_map = &by_keys,
|
||||||
|
@ -25,7 +25,8 @@ pub const ParsedConfig = struct {
|
||||||
|
|
||||||
pub fn deinit(self: *SelfConfig) void {
|
pub fn deinit(self: *SelfConfig) void {
|
||||||
for (self.key_value_map.keys(), self.key_value_map.values()) |k, v| {
|
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);
|
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.
|
// keys should be putNoClobber, but values can be put.
|
||||||
// Because we have to dup the memory here though, we want to
|
// Because we have to dup the memory here though, we want to
|
||||||
// manage duplicate values seperately
|
// manage duplicate values seperately
|
||||||
var dup_key = try self.allocator.dupe(u8, key);
|
var dup_key = try self.allocator.dupeZ(u8, key);
|
||||||
var dup_value = try self.allocator.dupe(u8, value);
|
var dup_value = try self.allocator.dupeZ(u8, value);
|
||||||
try rc.key_value_map.putNoClobber(dup_key, dup_value);
|
try rc.key_value_map.putNoClobber(dup_key, dup_value);
|
||||||
if (!rc.value_key_map.contains(value)) {
|
if (!rc.value_key_map.contains(value)) {
|
||||||
var keys = try self.allocator.create(std.ArrayList([]u8));
|
var keys = try self.allocator.create(std.ArrayList([:0]u8));
|
||||||
keys.* = std.ArrayList([]u8).init(self.allocator);
|
keys.* = std.ArrayList([:0]u8).init(self.allocator);
|
||||||
try rc.value_key_map.put(dup_value, keys);
|
try rc.value_key_map.put(dup_value, keys);
|
||||||
}
|
}
|
||||||
try rc.value_key_map.get(value).?.append(dup_key);
|
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 {
|
const Executor = struct {
|
||||||
// configuration
|
// configuration
|
||||||
|
target_prefix: []const u8,
|
||||||
path: [:0]const u8,
|
path: [:0]const u8,
|
||||||
|
|
||||||
// fields used at runtime to do real work
|
// fields used at runtime to do real work
|
||||||
|
@ -30,13 +31,6 @@ const Executor = struct {
|
||||||
in_request_lock: bool = false,
|
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 = Watch.init(executorChanged);
|
||||||
var watcher_thread: ?std.Thread = null;
|
var watcher_thread: ?std.Thread = null;
|
||||||
var timer: ?std.time.Timer = null; // timer used by processRequest
|
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 SERVE_FN_NAME = "handle_request";
|
||||||
const PORT = 8069;
|
const PORT = 8069;
|
||||||
|
|
||||||
|
var executors: []Executor = undefined;
|
||||||
|
|
||||||
fn serve(allocator: *std.mem.Allocator, response: *std.http.Server.Response) !*FullReturn {
|
fn serve(allocator: *std.mem.Allocator, response: *std.http.Server.Response) !*FullReturn {
|
||||||
// if (some path routing thing) {
|
// if (some path routing thing) {
|
||||||
const executor = try getExecutor(0);
|
const executor = try getExecutor(response.request.target);
|
||||||
if (executor.zigInitFn) |f|
|
if (executor.zigInitFn) |f|
|
||||||
f(allocator);
|
f(allocator);
|
||||||
|
|
||||||
|
@ -104,8 +100,16 @@ fn serve(allocator: *std.mem.Allocator, response: *std.http.Server.Response) !*F
|
||||||
rc.response = slice;
|
rc.response = slice;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
fn getExecutor(key: usize) !*Executor {
|
fn getExecutor(requested_path: []const u8) !*Executor {
|
||||||
var executor = &executors[key];
|
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;
|
if (executor.serveFn != null) return executor;
|
||||||
|
|
||||||
executor.library = blk: {
|
executor.library = blk: {
|
||||||
|
@ -146,7 +150,7 @@ fn loadOptionalSymbols(executor: *Executor) void {
|
||||||
fn executorChanged(watch: usize) void {
|
fn executorChanged(watch: usize) void {
|
||||||
// NOTE: This will be called off the main thread
|
// NOTE: This will be called off the main thread
|
||||||
log.debug("executor with watch {d} changed", .{watch});
|
log.debug("executor with watch {d} changed", .{watch});
|
||||||
for (&executors) |*executor| {
|
for (executors) |*executor| {
|
||||||
if (executor.watch) |w| {
|
if (executor.watch) |w| {
|
||||||
if (w == watch) {
|
if (w == watch) {
|
||||||
if (executor.library) |l| {
|
if (executor.library) |l| {
|
||||||
|
@ -231,9 +235,13 @@ pub fn main() !void {
|
||||||
var bw = std.io.bufferedWriter(stdout_file);
|
var bw = std.io.bufferedWriter(stdout_file);
|
||||||
const stdout = bw.writer();
|
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});
|
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 });
|
var server = std.http.Server.init(allocator, .{ .reuse_address = true });
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
|
@ -270,6 +278,23 @@ pub fn main() !void {
|
||||||
try bw.flush();
|
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 {
|
fn processRequest(allocator: *std.mem.Allocator, server: *std.http.Server, writer: anytype) !void {
|
||||||
const max_header_size = 8192;
|
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 {
|
fn testRequest(request_bytes: []const u8) !void {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
|
executors = try loadConfig(allocator);
|
||||||
|
defer allocator.free(executors);
|
||||||
|
defer parsed_config.deinit();
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user