implement header matching in proxy.ini
This commit is contained in:
parent
2004d97919
commit
1f9fd19771
|
@ -38,6 +38,11 @@ pub const ZigRequest = struct {
|
||||||
headers: []Header,
|
headers: []Header,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ZigHeader = struct {
|
||||||
|
name: []u8,
|
||||||
|
value: []u8,
|
||||||
|
};
|
||||||
|
|
||||||
pub const ZigResponse = struct {
|
pub const ZigResponse = struct {
|
||||||
body: *std.ArrayList(u8),
|
body: *std.ArrayList(u8),
|
||||||
headers: *std.StringHashMap([]const u8),
|
headers: *std.StringHashMap([]const u8),
|
||||||
|
@ -55,6 +60,13 @@ pub fn zigInit(parent_allocator: *anyopaque) callconv(.C) void {
|
||||||
allocator = @ptrCast(*std.mem.Allocator, @alignCast(@alignOf(*std.mem.Allocator), parent_allocator));
|
allocator = @ptrCast(*std.mem.Allocator, @alignCast(@alignOf(*std.mem.Allocator), parent_allocator));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toZigHeader(header: Header) ZigHeader {
|
||||||
|
return .{
|
||||||
|
.name = header.name_ptr[0..header.name_len],
|
||||||
|
.value = header.value_ptr[0..header.value_len],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts a StringHashMap to the structure necessary for passing through the
|
/// Converts a StringHashMap to the structure necessary for passing through the
|
||||||
/// C boundary. This will be called automatically for you via the handleRequest function
|
/// C boundary. This will be called automatically for you via the handleRequest function
|
||||||
/// and is also used by the main processing loop to coerce request headers
|
/// and is also used by the main processing loop to coerce request headers
|
||||||
|
|
|
@ -40,7 +40,16 @@ fn handleRequest(allocator: std.mem.Allocator, request: interface.ZigRequest, re
|
||||||
// setup
|
// setup
|
||||||
var response_writer = response.body.writer();
|
var response_writer = response.body.writer();
|
||||||
// real work
|
// real work
|
||||||
response_writer.print(" {d}", .{request.headers.len}) catch unreachable;
|
for (request.headers) |h| {
|
||||||
|
const header = interface.toZigHeader(h);
|
||||||
|
if (!std.ascii.eqlIgnoreCase(header.name, "host")) continue;
|
||||||
|
if (std.mem.startsWith(u8, header.value, "iam")) {
|
||||||
|
try response_writer.print("iam response", .{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try response_writer.print(" {d}", .{request.headers.len});
|
||||||
try response.headers.put("X-custom-foo", "bar");
|
try response.headers.put("X-custom-foo", "bar");
|
||||||
log.info("handlerequest header count {d}", .{response.headers.count()});
|
log.info("handlerequest header count {d}", .{response.headers.count()});
|
||||||
}
|
}
|
||||||
|
|
46
src/main.zig
46
src/main.zig
|
@ -37,7 +37,7 @@ const FullReturn = struct {
|
||||||
// applies
|
// applies
|
||||||
const Executor = struct {
|
const Executor = struct {
|
||||||
// configuration
|
// configuration
|
||||||
target_prefix: []const u8,
|
match_data: []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
|
||||||
|
@ -62,8 +62,7 @@ var parsed_config: config.ParsedConfig = undefined;
|
||||||
/// Serves a single request. Finds executor, marshalls request data for the C
|
/// Serves a single request. Finds executor, marshalls request data for the C
|
||||||
/// interface, calls the executor and marshalls data back
|
/// interface, calls the executor and marshalls data back
|
||||||
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) {
|
const executor = try getExecutor(response.request.target, response.request.headers);
|
||||||
const executor = try getExecutor(response.request.target);
|
|
||||||
if (executor.zigInitFn) |f|
|
if (executor.zigInitFn) |f|
|
||||||
f(allocator);
|
f(allocator);
|
||||||
|
|
||||||
|
@ -115,10 +114,10 @@ fn serve(allocator: *std.mem.Allocator, response: *std.http.Server.Response) !*F
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets and executor based on request data
|
/// Gets and executor based on request data
|
||||||
fn getExecutor(requested_path: []const u8) !*Executor {
|
fn getExecutor(requested_path: []const u8, headers: std.http.Headers) !*Executor {
|
||||||
var executor = blk: {
|
var executor = blk: {
|
||||||
for (executors) |*exec| {
|
for (executors) |*exec| {
|
||||||
if (std.mem.startsWith(u8, requested_path, exec.target_prefix)) {
|
if (executorIsMatch(exec.match_data, requested_path, headers)) {
|
||||||
break :blk exec;
|
break :blk exec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +153,41 @@ fn getExecutor(requested_path: []const u8) !*Executor {
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn executorIsMatch(match_data: []const u8, requested_path: []const u8, headers: std.http.Headers) bool {
|
||||||
|
if (!std.mem.containsAtLeast(u8, match_data, 1, ":")) {
|
||||||
|
// match_data does not have a ':'. This means this is a straight path, without
|
||||||
|
// any header requirement. We can simply return a match prefix on the
|
||||||
|
// requested path
|
||||||
|
const rc = std.mem.startsWith(u8, requested_path, match_data);
|
||||||
|
if (rc) log.debug("executor match for path prefix '{s}'", .{match_data});
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
const colon = std.mem.indexOf(u8, match_data, ":").?;
|
||||||
|
const header_needle = match_data[0..colon];
|
||||||
|
const header_inx = headers.firstIndexOf(header_needle) orelse return false;
|
||||||
|
// Apparently std.mem.split will return an empty first when the haystack starts
|
||||||
|
// with the delimiter
|
||||||
|
var split = std.mem.split(u8, std.mem.trim(u8, match_data[colon + 1 ..], "\t "), " ");
|
||||||
|
const header_value_needle = split.first();
|
||||||
|
const path_needle = split.next() orelse {
|
||||||
|
std.log.warn(
|
||||||
|
"Incorrect configuration. Header matching requires both header value and path prefix, space delimited. Key was '{s}'",
|
||||||
|
.{match_data},
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
// match_data includes some sort of header match as well. We assume the
|
||||||
|
// header match is a full match on the key (handled above)
|
||||||
|
// but a prefix match on the value
|
||||||
|
const request_header_value = headers.list.items[header_inx].value;
|
||||||
|
// (shoud this be case insensitive?)
|
||||||
|
if (!std.mem.startsWith(u8, request_header_value, header_value_needle)) return false;
|
||||||
|
// header value matches...return the path prefix match
|
||||||
|
const rc = std.mem.startsWith(u8, requested_path, path_needle);
|
||||||
|
if (rc) log.debug("executor match for header and path prefix '{s}'", .{match_data});
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads all optional symbols from the dynamic library. This has two entry
|
/// Loads all optional symbols from the dynamic library. This has two entry
|
||||||
/// points, though the logic around the primary request handler is slighly
|
/// points, though the logic around the primary request handler is slighly
|
||||||
/// different in each case so we can't combine those two.
|
/// different in each case so we can't combine those two.
|
||||||
|
@ -346,7 +380,7 @@ fn loadConfig(allocator: std.mem.Allocator) ![]Executor {
|
||||||
defer al.deinit();
|
defer al.deinit();
|
||||||
for (parsed_config.key_value_map.keys(), parsed_config.key_value_map.values()) |k, v| {
|
for (parsed_config.key_value_map.keys(), parsed_config.key_value_map.values()) |k, v| {
|
||||||
al.appendAssumeCapacity(.{
|
al.appendAssumeCapacity(.{
|
||||||
.target_prefix = k,
|
.match_data = k,
|
||||||
.path = v,
|
.path = v,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user