authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-05-01 17:49:34-05:00
committergravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-05-06 21:35:16-05:00
log5f219a2d118cac1410888fb2c0abc0cc91d092de
tree7757194344e21d873952e20e18b80132e4a83db5
parent7b0962938859a955fa8e057ef34f4abd925bb1ca
signature Commit is signed but in an unrecognized format.

std.http.Server: give Response access to their own allocator

* This makes it easier for threaded servers to use a different allocator for each request.

2 files changed, 27 insertions(+), 17 deletions(-)

lib/std/http/Server.zig+19-14
......@@ -352,7 +352,7 @@ pub const Response = struct {
352352
353353 transfer_encoding: ResponseTransfer = .none,
354354
355 server: *Server,
355 allocator: Allocator,
356356 address: net.Address,
357357 connection: BufferedConnection,
358358
......@@ -376,7 +376,7 @@ pub const Response = struct {
376376 res.request.headers.deinit();
377377
378378 if (res.request.parser.header_bytes_owned) {
379 res.request.parser.header_bytes.deinit(res.server.allocator);
379 res.request.parser.header_bytes.deinit(res.allocator);
380380 }
381381 }
382382
......@@ -545,13 +545,13 @@ pub const Response = struct {
545545 while (true) {
546546 try res.connection.fill();
547547
548 const nchecked = try res.request.parser.checkCompleteHead(res.server.allocator, res.connection.peek());
548 const nchecked = try res.request.parser.checkCompleteHead(res.allocator, res.connection.peek());
549549 res.connection.clear(@intCast(u16, nchecked));
550550
551551 if (res.request.parser.state.isContent()) break;
552552 }
553553
554 res.request.headers = .{ .allocator = res.server.allocator, .owned = true };
554 res.request.headers = .{ .allocator = res.allocator, .owned = true };
555555 try res.request.parse(res.request.parser.header_bytes.items);
556556
557557 if (res.request.transfer_encoding) |te| {
......@@ -573,13 +573,13 @@ pub const Response = struct {
573573 if (res.request.transfer_compression) |tc| switch (tc) {
574574 .compress => return error.CompressionNotSupported,
575575 .deflate => res.request.compression = .{
576 .deflate = std.compress.zlib.zlibStream(res.server.allocator, res.transferReader()) catch return error.CompressionInitializationFailed,
576 .deflate = std.compress.zlib.zlibStream(res.allocator, res.transferReader()) catch return error.CompressionInitializationFailed,
577577 },
578578 .gzip => res.request.compression = .{
579 .gzip = std.compress.gzip.decompress(res.server.allocator, res.transferReader()) catch return error.CompressionInitializationFailed,
579 .gzip = std.compress.gzip.decompress(res.allocator, res.transferReader()) catch return error.CompressionInitializationFailed,
580580 },
581581 .zstd => res.request.compression = .{
582 .zstd = std.compress.zstd.decompressStream(res.server.allocator, res.transferReader()),
582 .zstd = std.compress.zstd.decompressStream(res.allocator, res.transferReader()),
583583 },
584584 };
585585 }
......@@ -612,12 +612,12 @@ pub const Response = struct {
612612 while (!res.request.parser.state.isContent()) { // read trailing headers
613613 try res.connection.fill();
614614
615 const nchecked = try res.request.parser.checkCompleteHead(res.server.allocator, res.connection.peek());
615 const nchecked = try res.request.parser.checkCompleteHead(res.allocator, res.connection.peek());
616616 res.connection.clear(@intCast(u16, nchecked));
617617 }
618618
619619 if (has_trail) {
620 res.request.headers = http.Headers{ .allocator = res.server.allocator, .owned = false };
620 res.request.headers = http.Headers{ .allocator = res.allocator, .owned = false };
621621
622622 // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error.
623623 // This will *only* fail for a malformed trailer.
......@@ -731,24 +731,29 @@ pub const HeaderStrategy = union(enum) {
731731 static: []u8,
732732};
733733
734pub const AcceptOptions = struct {
735 allocator: Allocator,
736 header_strategy: HeaderStrategy = .{ .dynamic = 8192 },
737};
738
734739/// Accept a new connection.
735pub fn accept(server: *Server, options: HeaderStrategy) AcceptError!Response {
740pub fn accept(server: *Server, options: AcceptOptions) AcceptError!Response {
736741 const in = try server.socket.accept();
737742
738743 return Response{
739 .server = server,
744 .allocator = options.allocator,
740745 .address = in.address,
741746 .connection = .{ .conn = .{
742747 .stream = in.stream,
743748 .protocol = .plain,
744749 } },
745 .headers = .{ .allocator = server.allocator },
750 .headers = .{ .allocator = options.allocator },
746751 .request = .{
747752 .version = undefined,
748753 .method = undefined,
749754 .target = undefined,
750 .headers = .{ .allocator = server.allocator, .owned = false },
751 .parser = switch (options) {
755 .headers = .{ .allocator = options.allocator, .owned = false },
756 .parser = switch (options.header_strategy) {
752757 .dynamic => |max| proto.HeadersParser.initDynamic(max),
753758 .static => |buf| proto.HeadersParser.initStatic(buf),
754759 },
test/standalone/http.zig+8-3
......@@ -15,6 +15,8 @@ var gpa_client = std.heap.GeneralPurposeAllocator(.{}){};
1515const salloc = gpa_server.allocator();
1616const calloc = gpa_client.allocator();
1717
18var server: Server = undefined;
19
1820fn handleRequest(res: *Server.Response) !void {
1921 const log = std.log.scoped(.server);
2022
......@@ -89,7 +91,7 @@ fn handleRequest(res: *Server.Response) !void {
8991 } else if (mem.eql(u8, res.request.target, "/redirect/3")) {
9092 res.transfer_encoding = .chunked;
9193
92 const location = try std.fmt.allocPrint(salloc, "http://127.0.0.1:{d}/redirect/2", .{res.server.socket.listen_address.getPort()});
94 const location = try std.fmt.allocPrint(salloc, "http://127.0.0.1:{d}/redirect/2", .{server.socket.listen_address.getPort()});
9395 defer salloc.free(location);
9496
9597 res.status = .found;
......@@ -119,7 +121,10 @@ var handle_new_requests = true;
119121
120122fn runServer(srv: *Server) !void {
121123 outer: while (handle_new_requests) {
122 var res = try srv.accept(.{ .dynamic = max_header_size });
124 var res = try srv.accept(.{
125 .allocator = salloc,
126 .header_strategy = .{ .dynamic = max_header_size },
127 });
123128 defer res.deinit();
124129
125130 while (res.reset() != .closing) {
......@@ -162,7 +167,7 @@ pub fn main() !void {
162167
163168 defer _ = gpa_client.deinit();
164169
165 var server = Server.init(salloc, .{ .reuse_address = true });
170 server = Server.init(salloc, .{ .reuse_address = true });
166171
167172 const addr = std.net.Address.parseIp("127.0.0.1", 0) catch unreachable;
168173 try server.listen(addr);