srctree

Gregory Mullen parent a96d0414 169d77b8
small optimization for known/expected zwsgi headers

inlinesplit
contrib/uwsgi_params added: 111, removed: 80, total 31
@@ -1,8 +1,9 @@
# These are minimum params that nginx should send to verse/zwsgi in order for a
# reqeust to be handled correctly.
# request to be handled correctly.
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param REQUEST_PATH $document_uri;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param QUERY_STRING $query_string;
@@ -16,7 +17,7 @@ uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param HTTPS $https if_not_empty;
 
# Required for MTLS Auth support in verse. Can be ommitted if https is not being
# Required for MTLS Auth support in verse. Can be omitted if https is not being
# used
uwsgi_param MTLS_ENABLED $ssl_client_verify;
uwsgi_param MTLS_CERT $ssl_client_cert if_not_empty;
 
src/frame.zig added: 111, removed: 80, total 31
@@ -368,8 +368,15 @@ fn HttpHeader(vrs: *Frame, comptime ver: []const u8) [:0]const u8 {
pub fn dumpDebugData(frame: *const Frame) void {
switch (frame.request.raw) {
.zwsgi => |zw| {
for (zw.vars) |varr| {
std.debug.print("DumpDebug '{s}' => '''{s}'''\n", .{ varr.key, varr.val });
var itr = zw.known.iterator();
while (itr.next()) |entry| {
std.debug.print(
"DumpDebug '{s}' => '{s}'\n",
.{ @tagName(entry.key), entry.value.* orelse "[empty]" },
);
}
for (zw.vars.items) |varr| {
std.debug.print("DumpDebug '{s}' => '{s}'\n", .{ varr.key, varr.val });
}
},
.http => |http| {
 
src/request.zig added: 111, removed: 80, total 31
@@ -164,56 +164,32 @@ fn initCommon(
}
 
pub fn initZWSGI(a: Allocator, zwsgi: *zWSGIRequest, data: Data) !Request {
var uri: ?[]const u8 = null;
var method: ?Methods = null;
var remote_addr: ?RemoteAddr = null;
var headers = Headers.init(a);
var accept: ?Accept = null;
var host: ?Host = null;
var ua_slice: ?[]const u8 = null;
var referer: ?Referer = null;
var encoding: Encoding = Encoding.default;
var authorization: ?Authorization = null;
var cookie_header: ?[]const u8 = null;
var proto: []const u8 = "ERROR";
var secure: bool = false;
const zk = &zwsgi.known;
const uri: ?[]const u8 = zk.get(.REQUEST_PATH);
const method = Methods.fromStr(zk.get(.REQUEST_METHOD) orelse "GET") catch {
std.debug.print("Unsupported Method seen '{any}'", .{zk.get(.REQUEST_METHOD)});
return error.InvalidRequest;
};
const remote_addr: ?RemoteAddr = zk.get(.REMOTE_ADDR);
const accept: ?Accept = zk.get(.HTTP_ACCEPT);
const host: ?Host = zk.get(.HTTP_HOST);
const ua_slice: ?[]const u8 = zk.get(.HTTP_USER_AGENT);
const referer: ?Referer = zk.get(.HTTP_REFERER);
const encoding: Encoding = if (zk.get(.HTTP_ACCEPT_ENCODING)) |ae| .fromStr(ae) else .default;
const authorization: ?Authorization = zk.get(.HTTP_AUTHORIZATION);
const cookie_header: ?[]const u8 = zk.get(.HTTP_COOKIE);
const proto: []const u8 = zk.get(.SERVER_PROTOCOL) orelse "ERROR";
const secure: bool = if (zk.get(.HTTPS)) |sec| eql(u8, sec, "on") else false;
 
for (zwsgi.vars) |v| {
var headers = Headers.init(a);
for (zwsgi.vars.items) |v| {
try headers.addCustom(v.key, v.val);
if (eql(u8, v.key, "PATH_INFO")) {
uri = v.val;
} else if (eql(u8, v.key, "REQUEST_METHOD")) {
method = Methods.fromStr(v.val) catch {
std.debug.print("Unsupported Method seen '{any}'", .{v.val});
return error.InvalidRequest;
};
} else if (eql(u8, v.key, "REMOTE_ADDR")) {
remote_addr = v.val;
} else if (eqlIgnoreCase("HTTP_ACCEPT", v.key)) {
accept = v.val;
} else if (eqlIgnoreCase("HTTP_HOST", v.key)) {
host = v.val;
} else if (eqlIgnoreCase("HTTP_USER_AGENT", v.key)) {
ua_slice = v.val;
} else if (eqlIgnoreCase("HTTP_REFERER", v.key)) {
referer = v.val;
} else if (eqlIgnoreCase("HTTP_ACCEPT_ENCODING", v.key)) {
encoding = Encoding.fromStr(v.val);
} else if (eqlIgnoreCase("HTTP_AUTHORIZATION", v.key)) {
authorization = v.val;
} else if (eqlIgnoreCase("HTTP_COOKIE", v.key)) {
cookie_header = v.val;
} else if (eqlIgnoreCase("SERVER_PROTOCOL", v.key)) {
proto = v.val;
} else if (eqlIgnoreCase("REQUEST_SCHEME", v.key)) {
secure = eqlIgnoreCase("https", v.val);
}
}
 
return initCommon(
a,
remote_addr orelse return error.InvalidRequest,
method orelse return error.InvalidRequest,
method,
uri orelse return error.InvalidRequest,
host,
ua_slice,
 
src/zwsgi.zig added: 111, removed: 80, total 31
@@ -130,10 +130,48 @@ fn signalListen(signal: u6) void {
}, null);
}
 
pub const zWSGIParam = enum {
// These are minimum expected values
REMOTE_ADDR,
REMOTE_PORT,
REQUEST_URI,
REQUEST_PATH,
REQUEST_METHOD,
REQUEST_SCHEME,
QUERY_STRING,
CONTENT_TYPE,
CONTENT_LENGTH,
SERVER_NAME,
SERVER_PORT,
SERVER_PROTOCOL,
HTTPS,
MTLS_ENABLED,
MTLS_CERT,
MTLS_FINGERPRINT,
// These are seen often enough to be worth including here
HTTP_HOST,
HTTP_ACCEPT,
HTTP_USER_AGENT,
HTTP_ACCEPT_ENCODING,
HTTP_AUTHORIZATION,
HTTP_COOKIE,
HTTP_REFERER,
 
pub const fields = @typeInfo(zWSGIParam).@"enum".fields;
 
pub fn fromStr(str: []const u8) ?zWSGIParam {
inline for (fields) |f| {
if (eqlIgnoreCase(f.name, str)) return @enumFromInt(f.value);
} else return null;
}
};
 
pub const zWSGIRequest = struct {
conn: *net.Server.Connection,
header: uProtoHeader = uProtoHeader{},
vars: []uWSGIVar = &[0]uWSGIVar{},
buffer: []u8,
known: std.EnumArray(zWSGIParam, ?[]const u8) = .initFill(null),
vars: std.ArrayListUnmanaged(uWSGIVar) = .{},
 
pub fn init(a: Allocator, c: *net.Server.Connection) !zWSGIRequest {
const uwsgi_header = try uProtoHeader.init(c);
@@ -142,39 +180,51 @@ pub const zWSGIRequest = struct {
const read = try c.stream.read(buf);
if (read != uwsgi_header.size) {
std.log.err("unexpected read size {} {}", .{ read, uwsgi_header.size });
@panic("unreachable");
}
 
const vars = try readVars(a, buf);
for (vars) |v| {
log.debug("{}", .{v});
}
 
return .{
var zr: zWSGIRequest = .{
.conn = c,
.header = uwsgi_header,
.vars = vars,
.buffer = buf,
};
try zr.readVars(a);
return zr;
}
fn readVars(a: Allocator, b: []const u8) ![]uWSGIVar {
var list = std.ArrayList(uWSGIVar).init(a);
var buf = b;
 
fn readVars(zr: *zWSGIRequest, a: Allocator) !void {
var buf = zr.buffer;
try zr.vars.ensureTotalCapacity(a, 10);
while (buf.len > 0) {
const keysize = readU16(buf[0..2]);
const key_len = readU16(buf[0..2]);
buf = buf[2..];
const key = try a.dupe(u8, buf[0..keysize]);
buf = buf[keysize..];
const key_str = buf[0..key_len];
buf = buf[key_len..];
const expected = zWSGIParam.fromStr(key_str);
 
const valsize = readU16(buf[0..2]);
const val_len = readU16(buf[0..2]);
buf = buf[2..];
const val = try a.dupe(u8, if (valsize == 0) "" else buf[0..valsize]);
buf = buf[valsize..];
if (val_len > 0) {
const val_str = buf[0..val_len];
buf = buf[val_len..];
 
try list.append(uWSGIVar{
.key = key,
.val = val,
});
if (expected) |k| {
if (zr.known.get(k)) |old| {
log.err(
"Duplicate key found (dropping) {s} :: original {s} | new {s}",
.{ @tagName(k), old, val_str },
);
continue;
}
zr.known.set(k, val_str);
} else {
try zr.vars.append(a, uWSGIVar{
.key = key_str,
.val = val_str,
});
}
}
}
return try list.toOwnedSlice();
}
};
 
@@ -234,8 +284,8 @@ fn findOr(list: []uWSGIVar, search: []const u8) []const u8 {
fn requestData(a: Allocator, zreq: *zWSGIRequest) !Request.Data {
var post_data: ?Request.Data.PostData = null;
 
if (find(zreq.vars, "HTTP_CONTENT_LENGTH")) |h_len| {
const h_type = findOr(zreq.vars, "HTTP_CONTENT_TYPE");
if (find(zreq.vars.items, "HTTP_CONTENT_LENGTH")) |h_len| {
const h_type = findOr(zreq.vars.items, "HTTP_CONTENT_TYPE");
 
const post_size = try std.fmt.parseInt(usize, h_len, 10);
if (post_size > 0) {
@@ -252,13 +302,9 @@ fn requestData(a: Allocator, zreq: *zWSGIRequest) !Request.Data {
}
}
 
var query: Request.Data.QueryData = undefined;
if (find(zreq.vars, "QUERY_STRING")) |qs| {
query = try Request.Data.readQuery(a, qs);
}
return .{
.post = post_data,
.query = query,
.query = try Request.Data.readQuery(a, zreq.known.get(.QUERY_STRING) orelse ""),
};
}
 
@@ -277,6 +323,7 @@ const net = std.net;
const siginfo_t = std.posix.siginfo_t;
const SIG = std.posix.SIG;
const SA = std.posix.SA;
const eqlIgnoreCase = std.ascii.eqlIgnoreCase;
 
const Server = @import("server.zig");
const Frame = @import("frame.zig");