This commit is contained in:
Bob Farrell 2024-05-12 17:55:36 +01:00
parent 2fbd2f2f6a
commit 2a955de4b3
5 changed files with 47 additions and 17 deletions

View File

@ -20,11 +20,11 @@ const jetzig = @import("jetzig");
/// can also be modified.
my_custom_value: []const u8,
const Self = @This();
const DemoMiddleware = @This();
/// Initialize middleware.
pub fn init(request: *jetzig.http.Request) !*Self {
var middleware = try request.allocator.create(Self);
pub fn init(request: *jetzig.http.Request) !*DemoMiddleware {
var middleware = try request.allocator.create(DemoMiddleware);
middleware.my_custom_value = "initial value";
return middleware;
}
@ -32,7 +32,7 @@ pub fn init(request: *jetzig.http.Request) !*Self {
/// Invoked immediately after the request is received but before it has started processing.
/// Any calls to `request.render` or `request.redirect` will prevent further processing of the
/// request, including any other middleware in the chain.
pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void {
pub fn afterRequest(self: *DemoMiddleware, request: *jetzig.http.Request) !void {
try request.server.logger.DEBUG(
"[DemoMiddleware:afterRequest] my_custom_value: {s}",
.{self.my_custom_value},
@ -42,7 +42,11 @@ pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void {
/// Invoked immediately before the response renders to the client.
/// The response can be modified here if needed.
pub fn beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
pub fn beforeResponse(
self: *DemoMiddleware,
request: *jetzig.http.Request,
response: *jetzig.http.Response,
) !void {
try request.server.logger.DEBUG(
"[DemoMiddleware:beforeResponse] my_custom_value: {s}, response status: {s}",
.{ self.my_custom_value, @tagName(response.status_code) },
@ -51,7 +55,11 @@ pub fn beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jet
/// Invoked immediately after the response has been finalized and sent to the client.
/// Response data can be accessed for logging, but any modifications will have no impact.
pub fn afterResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
pub fn afterResponse(
self: *DemoMiddleware,
request: *jetzig.http.Request,
response: *jetzig.http.Response,
) !void {
_ = self;
_ = response;
try request.server.logger.DEBUG("[DemoMiddleware:afterResponse] response completed", .{});
@ -60,6 +68,6 @@ pub fn afterResponse(self: *Self, request: *jetzig.http.Request, response: *jetz
/// Invoked after `afterResponse` is called. Use this function to do any clean-up.
/// Note that `request.allocator` is an arena allocator, so any allocations are automatically
/// freed before the next request starts processing.
pub fn deinit(self: *Self, request: *jetzig.http.Request) void {
pub fn deinit(self: *DemoMiddleware, request: *jetzig.http.Request) void {
request.allocator.destroy(self);
}

View File

@ -37,6 +37,12 @@ pub const jetzig_options = struct {
// performance. Log messages should aim to fit in the message buffer.
// pub const log_message_buffer_len: usize = 4096;
// Maximum log pool size. When a log buffer is no longer required it is returned to a pool
// for recycling. When logging i/o is slow, a high volume of requests will result in this
// pool growing. When the pool size reaches the maximum value defined here, log events are
// freed instead of recycled.
// pub const max_log_pool_len: usize = 256;
// Path relative to cwd() to serve public content from. Symlinks are not followed.
// pub const public_content_path = "public";

View File

@ -88,6 +88,12 @@ pub const config = struct {
/// performance. Log messages should aim to fit in the message buffer.
pub const log_message_buffer_len: usize = 4096;
/// Maximum log pool size. When a log buffer is no longer required it is returned to a pool
/// for recycling. When logging i/o is slow, a high volume of requests will result in this
/// pool growing. When the pool size reaches the maximum value defined here, log events are
/// freed instead of recycled.
pub const max_log_pool_len: usize = 256;
/// Path relative to cwd() to serve public content from. Symlinks are not followed.
pub const public_content_path = "public";

View File

@ -4,6 +4,7 @@ const builtin = @import("builtin");
const jetzig = @import("../../jetzig.zig");
const buffer_size = jetzig.config.get(usize, "log_message_buffer_len");
const max_pool_len = jetzig.config.get(usize, "max_log_pool_len");
const List = std.DoublyLinkedList(Event);
const Buffer = [buffer_size]u8;
@ -41,8 +42,8 @@ const Event = struct {
pub fn init(allocator: std.mem.Allocator) LogQueue {
return .{
.allocator = allocator,
.node_allocator = std.heap.MemoryPool(List.Node).init(allocator),
.buffer_allocator = std.heap.MemoryPool(Buffer).init(allocator),
.node_allocator = initPool(allocator, List.Node),
.buffer_allocator = initPool(allocator, Buffer),
.list = List{},
.condition = std.Thread.Condition{},
.condition_mutex = std.Thread.Mutex{},
@ -58,11 +59,6 @@ pub fn deinit(self: *LogQueue) void {
self.node_pool.deinit();
self.buffer_pool.deinit();
while (self.list.popFirst()) |node| {
if (node.data.ptr) |ptr| self.allocator.free(ptr);
self.allocator.destroy(node.data.message);
}
self.buffer_allocator.deinit();
self.node_allocator.deinit();
@ -211,7 +207,12 @@ pub const Reader = struct {
if (self.queue.writer.position < self.queue.buffer_pool.items.len) {
self.queue.buffer_pool.items[self.queue.writer.position] = event.message;
} else {
try self.queue.buffer_pool.append(event.message); // TODO: Prevent unlimited inflation
if (self.queue.buffer_pool.items.len >= max_pool_len) {
self.queue.buffer_allocator.destroy(@alignCast(event.message));
self.queue.writer.position += 1;
} else {
try self.queue.buffer_pool.append(event.message);
}
}
}
@ -253,7 +254,12 @@ fn popFirst(self: *LogQueue) !?Event {
if (self.position < self.node_pool.items.len) {
self.node_pool.items[self.position] = node;
} else {
try self.node_pool.append(node); // TODO: Set a maximum here to avoid never-ending inflation.
if (self.node_pool.items.len >= max_pool_len) {
self.node_allocator.destroy(node);
self.position += 1;
} else {
try self.node_pool.append(node);
}
}
return value;
} else {
@ -261,6 +267,10 @@ fn popFirst(self: *LogQueue) !?Event {
}
}
fn initPool(allocator: std.mem.Allocator, T: type) std.heap.MemoryPool(T) {
return std.heap.MemoryPool(T).initPreheated(allocator, max_pool_len) catch @panic("OOM");
}
fn writeWindows(file: std.fs.File, writer: anytype, event: Event) !void {
var info: std.os.windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
const config: std.io.tty.Config = if (std.os.windows.kernel32.GetConsoleScreenBufferInfo(

View File

@ -15,7 +15,7 @@ pub fn init(request: *jetzig.http.Request) !*Self {
/// content rendered directly by the view function.
pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void {
_ = self;
if (request.getHeader("HX-Target")) |target| {
if (request.headers.get("HX-Target")) |target| {
try request.server.logger.DEBUG(
"[middleware-htmx] htmx request detected, disabling layout. (#{s})",
.{target},