fix: std.http dispatch + error responses run under a per-request arena
Handler and serialization allocations through the implicit context die with the request; response bytes survive via the own_alloc copy made inside the push scope. Without this every request leaked its render concats into the loop's long-lived context.
This commit is contained in:
@@ -439,21 +439,29 @@ Server :: struct {
|
|||||||
if ascii_ieq(cnv, "close") { req.keep_alive = false; }
|
if ascii_ieq(cnv, "close") { req.keep_alive = false; }
|
||||||
if ascii_ieq(cnv, "keep-alive") { req.keep_alive = true; }
|
if ascii_ieq(cnv, "keep-alive") { req.keep_alive = true; }
|
||||||
|
|
||||||
// dispatch (the field must be loaded — `self.handler(...)` would
|
|
||||||
// be parsed as a dot-call on a function named `handler`)
|
|
||||||
h := self.handler;
|
|
||||||
resp : Response = .{};
|
|
||||||
h(@req, @resp, self.ctx);
|
|
||||||
|
|
||||||
c.served += 1;
|
c.served += 1;
|
||||||
keep := req.keep_alive and c.served < self.cfg.request_count;
|
keep := req.keep_alive and c.served < self.cfg.request_count;
|
||||||
|
|
||||||
|
// Dispatch under a per-request arena: everything the handler
|
||||||
|
// (and serialization) allocates through the implicit context
|
||||||
|
// dies with the request — response bytes survive because
|
||||||
|
// serialize copies them into own_alloc inside the push scope.
|
||||||
// Serialize while the request views are still valid (the body
|
// Serialize while the request views are still valid (the body
|
||||||
// may reference the read buffer), THEN drop the served bytes —
|
// may reference the read buffer), THEN drop the served bytes —
|
||||||
// write_more's pipelining check must see only the remainder —
|
// write_more's pipelining check must see only the remainder —
|
||||||
// and only then start sending. Overlapping copy: dst < src, so
|
// and only then start sending. Overlapping copy: dst < src, so
|
||||||
// forward byte-wise is safe.
|
// forward byte-wise is safe.
|
||||||
self.serialize_response(slot, @resp, keep);
|
// (The handler field must be loaded — `self.handler(...)` would
|
||||||
|
// be parsed as a dot-call on a function named `handler`.)
|
||||||
|
h := self.handler;
|
||||||
|
resp : Response = .{};
|
||||||
|
req_gpa := GPA.init();
|
||||||
|
req_arena := Arena.init(xx req_gpa, 65536);
|
||||||
|
push Context.{ allocator = xx req_arena } {
|
||||||
|
h(@req, @resp, self.ctx);
|
||||||
|
self.serialize_response(slot, @resp, keep);
|
||||||
|
}
|
||||||
|
req_arena.deinit();
|
||||||
rest := c.read_len - total;
|
rest := c.read_len - total;
|
||||||
m : i64 = 0;
|
m : i64 = 0;
|
||||||
while m < rest {
|
while m < rest {
|
||||||
@@ -529,10 +537,17 @@ Server :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A terminal error response: serialize, send, close when done.
|
// A terminal error response: serialize, send, close when done.
|
||||||
|
// Same arena discipline as dispatch — serialization concats must
|
||||||
|
// not accumulate in the long-lived loop context.
|
||||||
respond_error_close :: (self: *Server, slot: i64, status: i64) {
|
respond_error_close :: (self: *Server, slot: i64, status: i64) {
|
||||||
resp : Response = .{ status = status, body = reason_for(status) };
|
resp : Response = .{ status = status, body = reason_for(status) };
|
||||||
self.conns[slot].read_len = 0;
|
self.conns[slot].read_len = 0;
|
||||||
self.serialize_response(slot, @resp, false);
|
err_gpa := GPA.init();
|
||||||
|
err_arena := Arena.init(xx err_gpa, 4096);
|
||||||
|
push Context.{ allocator = xx err_arena } {
|
||||||
|
self.serialize_response(slot, @resp, false);
|
||||||
|
}
|
||||||
|
err_arena.deinit();
|
||||||
self.write_more(slot);
|
self.write_more(slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user