Closed mbaracz closed 1 month ago
headers.keys and headers.values is a pre-allocate and re-used slice of, in your case, 10 values. You'd need to use headers.keys[0..headers.len]
but in your case you're probably going beyong headers.len.
I agree that isn't great, so I added an iterator:
var it = h.headers.iterator();
while (it.next) |kv| {
// use kv.key and kv.value
}
What about get function, can you verify that?
If you're iterating beyond headers.len
, then you could be seeing a header/value from a previous request. In such cases get
would return null as it respects the length. There is obviously a security issue here - data from one request is available to a later request. But this is also how malloc works. I've always been a bit on the fence about it.
The key is lower-cased, I'm not sure if that's the issue you're having.
The "upgrade", "connection", "sec-websocket-key" and "sec-websocket-version" aren't added to the headers lookup. Maybe that's a silly choice.
I added an iteration to check if the header is really not present. The first issue I encountered was getting a null value. It's not working for both lowercase and uppercase keys.
Does something like this crash?
var it = h.headers.iterator();
while (it.next()) |kv| {
std.debug.assert(std.mem.eql(u8, kv.value, h.headers.get(kv.key).?));
}
Do you have a reproducible example?
It's not crashing. Below code example (latest nonblocking branch), it does not matter which header I am trying to get, connecting to WebSocket server from browser and via ws in Node.js results in headers length 0.
const std = @import("std");
const ws = @import("websocket");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var server = try ws.Server(Handler).init(allocator, .{
.port = 9224,
.address = "127.0.0.1",
.handshake = .{
.timeout = 3,
.max_size = 1024,
.max_headers = 20,
},
});
var app = App{};
try server.listen(&app);
}
const Handler = struct {
app: *App,
conn: *ws.Conn,
pub fn init(h: ws.Handshake, conn: *ws.Conn, app: *App) !Handler {
std.debug.print("Len: {}\n", .{h.headers.len});
var it = h.headers.iterator();
while (it.next()) |kv| {
std.debug.assert(std.mem.eql(u8, kv.value, h.headers.get(kv.key).?));
}
const host = h.headers.get("host");
if (host != null) {
std.debug.print("Found host header\n", .{});
} else {
std.debug.print("Host header not found\n", .{});
}
return .{
.app = app,
.conn = conn,
};
}
pub fn clientMessage(self: *Handler, data: []const u8) !void {
try self.conn.write(data);
}
};
const App = struct {};
Ok, ya. That's fixed now.
Real issue was not having the integration tests fetch at least 1 header...
Hello, I encountered a problem in the nonblocking branch. Iterating over header names causes a panic error. Even if the header name is printed, getting it via
h.headers.get(name)
always returns null.Zig version: 0.14.0-dev.872+a60810b5a
Error log:
Code: