move executor config to external file
This commit is contained in:
		
							parent
							
								
									c393792cd1
								
							
						
					
					
						commit
						02017da98e
					
				
					 3 changed files with 60 additions and 23 deletions
				
			
		
							
								
								
									
										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…
	
	Add table
		
		Reference in a new issue