web: unnecessary refactor before compilation
This commit is contained in:
@@ -4,17 +4,68 @@ const Worker = @This();
|
||||
const Connection = @import("Connection.zig");
|
||||
const FileDescriptor = @import("FileDescriptor.zig").FileDescriptor;
|
||||
const http = @import("http.zig");
|
||||
const Request = @import("Request.zig");
|
||||
const RequestHandler = @import("RequestHandler.zig");
|
||||
const RequestRouter = @import("RequestRouter.zig");
|
||||
const Response = @import("Response.zig");
|
||||
const Server = @import("Server.zig");
|
||||
|
||||
/// Integer unique for this worker. Has no functional meaning. Can be used for
|
||||
/// debugging and profiling.
|
||||
worker_id: usize,
|
||||
|
||||
read_buffer_ptr: [*]u8,
|
||||
read_buffer_size: usize,
|
||||
read_head: usize,
|
||||
read_tail: usize,
|
||||
|
||||
write_buffer: []u8,
|
||||
header_write_buffer: []u8,
|
||||
body_write_buffer: []u8,
|
||||
|
||||
header_hash_map: Request.HeaderHashMap,
|
||||
header_value_buffer: []Request.HeaderValue,
|
||||
|
||||
pub const Options = struct {
|
||||
worker_id: usize,
|
||||
|
||||
max_header_fields: u32,
|
||||
|
||||
read_buffer_ptr: [*]u8,
|
||||
read_buffer_size: usize,
|
||||
|
||||
header_write_buffer: []u8,
|
||||
body_write_buffer: []u8,
|
||||
};
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, options: Options) !Worker {
|
||||
var header_hash_map: Request.HeaderHashMap = .empty;
|
||||
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);
|
||||
|
||||
return .{
|
||||
.worker_id = options.worker_id,
|
||||
|
||||
.read_buffer_ptr = options.read_buffer_ptr,
|
||||
.read_buffer_size = options.read_buffer_size,
|
||||
.read_head = 0,
|
||||
.read_tail = 0,
|
||||
|
||||
.header_write_buffer = options.header_write_buffer,
|
||||
.body_write_buffer = options.body_write_buffer,
|
||||
|
||||
.header_hash_map = header_hash_map,
|
||||
.header_value_buffer = header_list_buffer,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Worker, allocator: std.mem.Allocator) void {
|
||||
allocator.free(self.header_value_buffer);
|
||||
self.header_hash_map.deinit(allocator);
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn worker(
|
||||
self: *Worker,
|
||||
@@ -35,7 +86,7 @@ pub fn worker(
|
||||
server.cond_connection_freed.signal();
|
||||
}
|
||||
|
||||
self.handleConnection(server.request_router, connection, running) catch |err| {
|
||||
self.handleConnection(server.request_handler, connection, running) catch |err| {
|
||||
std.log.err("Error while handling connection: {}", .{err});
|
||||
};
|
||||
} else {
|
||||
@@ -46,14 +97,14 @@ pub fn worker(
|
||||
|
||||
fn handleConnection(
|
||||
self: *Worker,
|
||||
request_router: RequestRouter,
|
||||
request_handler: RequestHandler,
|
||||
connection: *Connection,
|
||||
running: *const std.atomic.Value(bool),
|
||||
) !void {
|
||||
defer connection.deinit();
|
||||
|
||||
while (running.load(.acquire)) {
|
||||
const res = self.handleRequest(request_router, connection) catch |err| {
|
||||
const res = self.handleRequest(request_handler, connection) catch |err| {
|
||||
std.log.err("Error while handling request: {}", .{err});
|
||||
return err;
|
||||
};
|
||||
@@ -64,16 +115,22 @@ fn handleConnection(
|
||||
|
||||
fn handleRequest(
|
||||
self: *Worker,
|
||||
request_router: RequestRouter,
|
||||
request_handler: RequestHandler,
|
||||
connection: *Connection,
|
||||
) !bool {
|
||||
self.header_hash_map.clearRetainingCapacity();
|
||||
|
||||
var request: Request = .{
|
||||
.method = undefined,
|
||||
.pathname = undefined,
|
||||
.headers = &self.header_hash_map,
|
||||
.body = undefined,
|
||||
};
|
||||
var response: Response = .init(connection, self.write_buffer);
|
||||
var parser: http.Parser = .init(request_router, &response);
|
||||
defer {
|
||||
if (parser.request_handler) |rh| {
|
||||
rh.rawFinalize();
|
||||
}
|
||||
}
|
||||
var parser: http.Parser = .init();
|
||||
|
||||
var next_header_index: usize = 0;
|
||||
var ignore: bool = false;
|
||||
|
||||
var leftover_bytes = self.read_tail - self.read_head;
|
||||
const max_read_tail = self.read_head + self.read_buffer_size;
|
||||
@@ -84,7 +141,7 @@ fn handleRequest(
|
||||
|
||||
if (leftover_bytes > 0) {
|
||||
bytes_read = leftover_bytes;
|
||||
chunk = self.read_buffer_ptr[self.read_head..self.read_tail];
|
||||
chunk = self.read_buffer_ptr[self.read_tail - leftover_bytes .. self.read_tail];
|
||||
leftover_bytes = 0;
|
||||
} else {
|
||||
const read_tail = self.read_tail;
|
||||
@@ -101,51 +158,16 @@ fn handleRequest(
|
||||
error.HttpVersionNotSupported => {
|
||||
try response.sendClose(.{ .status_text = http.status.http_version_not_supported });
|
||||
},
|
||||
error.MissingLineFeed => {
|
||||
error.SyntaxError => {
|
||||
try response.sendClose(.{ .status_text = http.status.bad_request });
|
||||
},
|
||||
error.InvalidContentLength => {
|
||||
try response.sendClose(.{ .status_text = http.status.bad_request });
|
||||
},
|
||||
error.RouterError => {
|
||||
const cause = parser.last_router_error;
|
||||
const cause_name = @errorName(cause);
|
||||
|
||||
// TODO Could really use separate header and content write
|
||||
// buffers and this code shows why. We can't start writing
|
||||
// until we know the exact byte length of the response body,
|
||||
// because the value of "Content-Length" header depends on
|
||||
// it. I guess we could do preemptive padding and in-place
|
||||
// patch it later, but two buffers would be ideal anyway.
|
||||
// We could then use writev syscall and it still will be
|
||||
// exactly one syscall per response (unless we want to
|
||||
// sendfile, but then it will be two syscalls either way).
|
||||
|
||||
try response.writer.print("{s}", .{http.status.internal_server_error});
|
||||
try response.writer.print("Content-Type: {s}\r\n", .{"text/plain; charset=utf-8"});
|
||||
try response.writer.print("Content-Length: {d}\r\n", .{cause_name.len});
|
||||
try response.writer.print("\r\n", .{});
|
||||
try response.writer.print("{s}", .{cause_name});
|
||||
|
||||
response.finalize();
|
||||
},
|
||||
error.HandlerError => {
|
||||
const cause = parser.last_handler_error;
|
||||
const cause_name = @errorName(cause);
|
||||
|
||||
try response.writer.print("{s}", .{http.status.internal_server_error});
|
||||
try response.writer.print("Content-Type: {s}\r\n", .{"text/plain; charset=utf-8"});
|
||||
try response.writer.print("Content-Length: {d}\r\n", .{cause_name.len});
|
||||
try response.writer.print("\r\n", .{});
|
||||
try response.writer.print("{s}", .{cause_name});
|
||||
|
||||
response.finalize();
|
||||
},
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (self.read_tail - self.read_head >= self.read_buffer_size and !res.done) {
|
||||
const done = if (res.result) |result| std.meta.activeTag(result) == .body else false;
|
||||
|
||||
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 });
|
||||
} else {
|
||||
@@ -154,11 +176,94 @@ fn handleRequest(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res.done) {
|
||||
leftover_bytes = bytes_read - res.consumed;
|
||||
self.read_head = (self.read_tail - leftover_bytes) & ~(self.read_buffer_size - 1);
|
||||
self.read_tail = self.read_head + leftover_bytes;
|
||||
return true;
|
||||
if (res.result) |result| {
|
||||
switch (result) {
|
||||
.method => |method| request.method = method,
|
||||
.pathname => |pathname| request.pathname = pathname,
|
||||
.header => |header| {
|
||||
if (ignore) {
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
const entry = self.header_hash_map.getOrPutAssumeCapacity(header.name);
|
||||
const header_value = &self.header_value_buffer[next_header_index];
|
||||
header_value.* = .{ .node = .{}, .value = header.value };
|
||||
next_header_index += 1;
|
||||
|
||||
if (!entry.found_existing) {
|
||||
entry.value_ptr.* = .{
|
||||
.len = 0,
|
||||
.list = .{},
|
||||
};
|
||||
}
|
||||
|
||||
entry.value_ptr.list.prepend(&header_value.node);
|
||||
entry.value_ptr.len += 1;
|
||||
}
|
||||
},
|
||||
.end_of_headers => {},
|
||||
.body => |body| {
|
||||
request.body = body;
|
||||
|
||||
if (!ignore) {
|
||||
request_handler.handle(&request, &response) catch |err| {
|
||||
if (response.state == .init) {
|
||||
response.header_writer.end = 0;
|
||||
response.body_writer.end = 0;
|
||||
|
||||
const error_name = @errorName(err);
|
||||
|
||||
try response.body_writer.print("Internal Server Error\n{s}\n", .{error_name});
|
||||
|
||||
try response.header_writer.writeAll(http.status.internal_server_error);
|
||||
try response.header_writer.writeAll("Content-Type: text/plain; charset=utf-8\r\n");
|
||||
try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end});
|
||||
try response.header_writer.writeAll("\r\n");
|
||||
|
||||
response.sendHeadersAndBody();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (response.state == .init) {
|
||||
const no_headers = response.header_writer.end > 0;
|
||||
const no_body = response.body_writer.end > 0;
|
||||
|
||||
if (no_headers) {
|
||||
if (no_body) {
|
||||
try response.header_writer.writeAll(http.status.no_content);
|
||||
try response.header_writer.writeAll("\r\n");
|
||||
|
||||
response.sendHeadersOnly();
|
||||
} else {
|
||||
try response.header_writer.writeAll(http.status.ok);
|
||||
try response.header_writer.writeAll("Content-Type: application/octet-stream");
|
||||
try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end});
|
||||
try response.header_writer.writeAll("\r\n");
|
||||
|
||||
response.sendHeadersAndBody();
|
||||
}
|
||||
} else {
|
||||
if (no_body) {
|
||||
response.sendHeadersOnly();
|
||||
} else {
|
||||
response.sendHeadersAndBody();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leftover_bytes = bytes_read - res.consumed;
|
||||
self.read_head = (self.read_tail - leftover_bytes) & ~(self.read_buffer_size - 1);
|
||||
self.read_tail = self.read_head + leftover_bytes;
|
||||
return true;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
leftover_bytes = bytes_read - res.consumed;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user