diff --git a/packages/web/src/FileDescriptor.zig b/packages/web/src/FileDescriptor.zig index df985c1..70bc788 100644 --- a/packages/web/src/FileDescriptor.zig +++ b/packages/web/src/FileDescriptor.zig @@ -139,7 +139,7 @@ pub const FileDescriptor = enum(i32) { } pub fn setsockopt(self: FileDescriptor, level: i32, optname: u32, opt: []const u8) !void { - const rc = linux.setsockopt(@intFromEnum(self), level, optname, opt.ptr, opt.len); + const rc = linux.setsockopt(@intFromEnum(self), level, optname, opt.ptr, @intCast(opt.len)); return switch (errno(rc)) { .SUCCESS => {}, else => error.SystemError, diff --git a/packages/web/src/Response.zig b/packages/web/src/Response.zig index 445a91a..0aa60d6 100644 --- a/packages/web/src/Response.zig +++ b/packages/web/src/Response.zig @@ -27,7 +27,7 @@ pub fn init(connection: *Connection, header_write_buffer: []u8, body_write_buffe return .{ .connection = connection, .header_writer = .fixed(header_write_buffer), - .writer = .fixed(body_write_buffer), + .body_writer = .fixed(body_write_buffer), .state = .init, }; } diff --git a/packages/web/src/Server.zig b/packages/web/src/Server.zig index 16b7a08..708f16c 100644 --- a/packages/web/src/Server.zig +++ b/packages/web/src/Server.zig @@ -103,7 +103,7 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { // Allocate and remap read buffers - const single_read_buffer_size = @as(usize, options.read_buffer_pages) * huge_page_size; + const single_read_buffer_size = @as(usize, options.read_buffer_huge_pages) * huge_page_size; const all_read_buffers_size = worker_count * single_read_buffer_size; const double_single_read_buffer_size = 2 * single_read_buffer_size; @@ -165,7 +165,7 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Server { // Allocate body write buffer - const single_body_write_buffer_size = @as(usize, options.write_buffer_pages) * huge_page_size; + const single_body_write_buffer_size = @as(usize, options.body_write_buffer_huge_pages) * huge_page_size; const all_body_write_buffers_size = worker_count * single_body_write_buffer_size; const body_write_buffer_ptr = try errOrPtr(linux.mmap( diff --git a/packages/web/src/Worker.zig b/packages/web/src/Worker.zig index fe522a0..94fb845 100644 --- a/packages/web/src/Worker.zig +++ b/packages/web/src/Worker.zig @@ -41,8 +41,8 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Worker { try header_hash_map.ensureTotalCapacity(allocator, options.max_header_fields); errdefer header_hash_map.deinit(allocator); - const header_list_buffer = try allocator.alloc(Request.HeaderList, options.max_header_fields); - errdefer allocator.free(header_list_buffer); + const header_value_buffer = try allocator.alloc(Request.HeaderValue, options.max_header_fields); + errdefer allocator.free(header_value_buffer); return .{ .worker_id = options.worker_id, @@ -56,7 +56,7 @@ pub fn init(allocator: std.mem.Allocator, options: Options) !Worker { .body_write_buffer = options.body_write_buffer, .header_hash_map = header_hash_map, - .header_value_buffer = header_list_buffer, + .header_value_buffer = header_value_buffer, }; } @@ -126,7 +126,7 @@ fn handleRequest( .headers = &self.header_hash_map, .body = undefined, }; - var response: Response = .init(connection, self.write_buffer); + var response: Response = .init(connection, self.header_write_buffer, self.body_write_buffer); var parser: http.Parser = .init(); var next_header_index: usize = 0; @@ -152,15 +152,9 @@ fn handleRequest( const res = parser.consume(chunk) catch |err| { switch (err) { - error.MethodNotSupported => { - try response.sendClose(.{ .status_text = http.status.method_not_allowed }); - }, - error.HttpVersionNotSupported => { - try response.sendClose(.{ .status_text = http.status.http_version_not_supported }); - }, - error.SyntaxError => { - try response.sendClose(.{ .status_text = http.status.bad_request }); - }, + error.MethodNotSupported => try closeWith(&response, http.status.method_not_allowed), + error.HttpVersionNotSupported => try closeWith(&response, http.status.http_version_not_supported), + error.SyntaxError => try closeWith(&response, http.status.bad_request), } return false; }; @@ -169,9 +163,9 @@ fn handleRequest( if (self.read_tail - self.read_head >= self.read_buffer_size and !done) { if (parser.state == .body) { - try response.sendClose(.{ .status_text = http.status.content_too_large }); + try closeWith(&response, http.status.content_too_large); } else { - try response.sendClose(.{ .status_text = http.status.request_header_fields_too_large }); + try closeWith(&response, http.status.request_header_fields_too_large); } return false; } @@ -180,14 +174,19 @@ fn handleRequest( switch (result) { .method => |method| request.method = method, .pathname => |pathname| request.pathname = pathname, - .header => |header| { + .header => |header| blk: { if (ignore) { - break; + break :blk; } if (next_header_index >= self.header_value_buffer.len or self.header_hash_map.available == 0) { - try response.send(.{ .status_text = http.status.request_header_fields_too_large }); - ignore = true; + // TODO Here, we could ignore, but make sure this does + // not clash with the other "request too long" checks + // (i.e. be careful not to double respond). + _ = &ignore; + + try closeWith(&response, http.status.request_header_fields_too_large); + return false; } else { const entry = self.header_hash_map.getOrPutAssumeCapacity(header.name); const header_value = &self.header_value_buffer[next_header_index]; @@ -267,3 +266,15 @@ fn handleRequest( leftover_bytes = bytes_read - res.consumed; } } + +fn closeWith(response: *Response, status_line: []const u8) !void { + // This function is meant to be called before a request handler gets to do + // anything. + std.debug.assert(response.header_writer.end == 0); + std.debug.assert(response.body_writer.end == 0); + std.debug.assert(response.state == .init); + + try response.header_writer.writeAll(status_line); + try response.header_writer.writeAll("Connection: close\r\n"); + try response.header_writer.writeAll("\r\n"); +} diff --git a/packages/web/src/http/FieldName.zig b/packages/web/src/http/FieldName.zig index d2aa890..837b546 100644 --- a/packages/web/src/http/FieldName.zig +++ b/packages/web/src/http/FieldName.zig @@ -12,7 +12,7 @@ pub const FieldName = extern struct { const tag_short_bias: u8 = 0x02; pub fn init(name: []const u8) FieldName { - var data: [16]u8 = @splat(0); + var data: [16]u8 align(8) = @splat(0); if (KnownFieldName.isKnownFieldName(name)) |known| { data[0] = tag_known; @as(*KnownFieldName, @ptrCast(data[8..16])).* = known; @@ -29,7 +29,7 @@ pub const FieldName = extern struct { } pub fn initKnown(known: KnownFieldName) FieldName { - var data: [16]u8 = @splat(0); + var data: [16]u8 align(8) = @splat(0); data[0] = tag_known; @as(*KnownFieldName, @ptrCast(data[8..16])).* = known; @@ -39,7 +39,8 @@ pub const FieldName = extern struct { fn getKnown(self: FieldName) KnownFieldName { std.debug.assert(self.data[0] == tag_known); - return @bitCast(self.data[8..16].*); + const intval: u64 = @bitCast(self.data[8..16].*); + return @enumFromInt(intval); } fn getLong(self: FieldName) []const u8 { @@ -57,7 +58,7 @@ pub const FieldName = extern struct { return str; } - pub fn hash(self: FieldName) u32 { + pub fn hash(self: FieldName) u64 { return switch (self.data[0]) { tag_known => Wyhash.hash(0, self.data[8..16]), tag_long => Wyhash.hash(1, self.getLong()), @@ -79,11 +80,11 @@ pub const FieldName = extern struct { } pub const HashMapContext = struct { - pub fn hash(_: HashMapContext, key: FieldName) u32 { + pub fn hash(_: HashMapContext, key: FieldName) u64 { return key.hash(); } - pub fn eql(_: HashMapContext, a: FieldName, b: FieldName, _: usize) bool { + pub fn eql(_: HashMapContext, a: FieldName, b: FieldName) bool { return FieldName.eql(a, b); } }; diff --git a/packages/web/src/http/Header.zig b/packages/web/src/http/Header.zig index 451e2e8..901c7a4 100644 --- a/packages/web/src/http/Header.zig +++ b/packages/web/src/http/Header.zig @@ -19,5 +19,5 @@ pub fn isNamed(self: Header, name: FieldName) bool { } pub fn isNamedKnown(self: Header, known: KnownFieldName) bool { - return FieldName.eql(self.name, .initKnonw(known)); + return FieldName.eql(self.name, .initKnown(known)); } diff --git a/packages/web/src/http/Parser.zig b/packages/web/src/http/Parser.zig index 6a384b7..752d7d1 100644 --- a/packages/web/src/http/Parser.zig +++ b/packages/web/src/http/Parser.zig @@ -240,11 +240,11 @@ fn consumeChar(self: *Parser, char_ptr: *const u8) Error!?Result { 'O' => self.state = .method_o, 'P' => self.state = .method_p, 'T' => self.state = .method_t, - else => error.MethodNotSupported, + else => return error.MethodNotSupported, }, .method_c => switch (char) { 'O' => self.state = .method_co, - else => error.MethodNotSupported, + else => return error.MethodNotSupported, }, .method_d => switch (char) { 'E' => self.state = .method_de, @@ -514,7 +514,7 @@ fn consumeChar(self: *Parser, char_ptr: *const u8) Error!?Result { self.state = .done; return .initBody(new_body); } else { - self.state = new_body; + self.state = .initBody(new_body); } }, .done => unreachable, diff --git a/packages/web/src/main.zig b/packages/web/src/main.zig index 8f0cf55..123df8e 100644 --- a/packages/web/src/main.zig +++ b/packages/web/src/main.zig @@ -19,7 +19,7 @@ fn interruptionHandler(signal: i32) callconv(.c) void { const Handler = struct { fn handle(_: *anyopaque, request: *web.Request, response: *web.Response) !void { - if (!std.mem.eql(request.pathname, "/")) { + if (!std.mem.eql(u8, request.pathname, "/")) { try response.body_writer.writeAll("Not Found\n"); try response.header_writer.writeAll(web.http.status.not_found); @@ -31,7 +31,7 @@ const Handler = struct { return; } - if (!std.mem.eql(request.method, "GET")) { + if (request.method != .GET) { try response.body_writer.writeAll("Method Not Allowed\n"); try response.header_writer.writeAll(web.http.status.method_not_allowed); @@ -47,7 +47,7 @@ const Handler = struct { try response.header_writer.writeAll(web.http.status.ok); try response.header_writer.writeAll("Content-Type: application/json\r\n"); - try response.header_writer.writeAll("Content-Length: {d}\r\n", .{response.body_writer.end}); + try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); try response.header_writer.writeAll("\r\n"); response.sendHeadersAndBody();