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
signaturelock-open 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 {...@@ -352,7 +352,7 @@ pub const Response = struct {
352352
353 transfer_encoding: ResponseTransfer = .none,353 transfer_encoding: ResponseTransfer = .none,
354354
355 server: *Server,355 allocator: Allocator,
356 address: net.Address,356 address: net.Address,
357 connection: BufferedConnection,357 connection: BufferedConnection,
358358
...@@ -376,7 +376,7 @@ pub const Response = struct {...@@ -376,7 +376,7 @@ pub const Response = struct {
376 res.request.headers.deinit();376 res.request.headers.deinit();
377377
378 if (res.request.parser.header_bytes_owned) {378 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);
380 }380 }
381 }381 }
382382
...@@ -545,13 +545,13 @@ pub const Response = struct {...@@ -545,13 +545,13 @@ pub const Response = struct {
545 while (true) {545 while (true) {
546 try res.connection.fill();546 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());
549 res.connection.clear(@intCast(u16, nchecked));549 res.connection.clear(@intCast(u16, nchecked));
550550
551 if (res.request.parser.state.isContent()) break;551 if (res.request.parser.state.isContent()) break;
552 }552 }
553553
554 res.request.headers = .{ .allocator = res.server.allocator, .owned = true };554 res.request.headers = .{ .allocator = res.allocator, .owned = true };
555 try res.request.parse(res.request.parser.header_bytes.items);555 try res.request.parse(res.request.parser.header_bytes.items);
556556
557 if (res.request.transfer_encoding) |te| {557 if (res.request.transfer_encoding) |te| {
...@@ -573,13 +573,13 @@ pub const Response = struct {...@@ -573,13 +573,13 @@ pub const Response = struct {
573 if (res.request.transfer_compression) |tc| switch (tc) {573 if (res.request.transfer_compression) |tc| switch (tc) {
574 .compress => return error.CompressionNotSupported,574 .compress => return error.CompressionNotSupported,
575 .deflate => res.request.compression = .{575 .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,
577 },577 },
578 .gzip => res.request.compression = .{578 .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,
580 },580 },
581 .zstd => res.request.compression = .{581 .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()),
583 },583 },
584 };584 };
585 }585 }
...@@ -612,12 +612,12 @@ pub const Response = struct {...@@ -612,12 +612,12 @@ pub const Response = struct {
612 while (!res.request.parser.state.isContent()) { // read trailing headers612 while (!res.request.parser.state.isContent()) { // read trailing headers
613 try res.connection.fill();613 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());
616 res.connection.clear(@intCast(u16, nchecked));616 res.connection.clear(@intCast(u16, nchecked));
617 }617 }
618618
619 if (has_trail) {619 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
622 // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error.622 // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error.
623 // This will *only* fail for a malformed trailer.623 // This will *only* fail for a malformed trailer.
...@@ -731,24 +731,29 @@ pub const HeaderStrategy = union(enum) {...@@ -731,24 +731,29 @@ pub const HeaderStrategy = union(enum) {
731 static: []u8,731 static: []u8,
732};732};
733733
734pub const AcceptOptions = struct {
735 allocator: Allocator,
736 header_strategy: HeaderStrategy = .{ .dynamic = 8192 },
737};
738
734/// Accept a new connection.739/// Accept a new connection.
735pub fn accept(server: *Server, options: HeaderStrategy) AcceptError!Response {740pub fn accept(server: *Server, options: AcceptOptions) AcceptError!Response {
736 const in = try server.socket.accept();741 const in = try server.socket.accept();
737742
738 return Response{743 return Response{
739 .server = server,744 .allocator = options.allocator,
740 .address = in.address,745 .address = in.address,
741 .connection = .{ .conn = .{746 .connection = .{ .conn = .{
742 .stream = in.stream,747 .stream = in.stream,
743 .protocol = .plain,748 .protocol = .plain,
744 } },749 } },
745 .headers = .{ .allocator = server.allocator },750 .headers = .{ .allocator = options.allocator },
746 .request = .{751 .request = .{
747 .version = undefined,752 .version = undefined,
748 .method = undefined,753 .method = undefined,
749 .target = undefined,754 .target = undefined,
750 .headers = .{ .allocator = server.allocator, .owned = false },755 .headers = .{ .allocator = options.allocator, .owned = false },
751 .parser = switch (options) {756 .parser = switch (options.header_strategy) {
752 .dynamic => |max| proto.HeadersParser.initDynamic(max),757 .dynamic => |max| proto.HeadersParser.initDynamic(max),
753 .static => |buf| proto.HeadersParser.initStatic(buf),758 .static => |buf| proto.HeadersParser.initStatic(buf),
754 },759 },
test/standalone/http.zig+8-3
...@@ -15,6 +15,8 @@ var gpa_client = std.heap.GeneralPurposeAllocator(.{}){};...@@ -15,6 +15,8 @@ var gpa_client = std.heap.GeneralPurposeAllocator(.{}){};
15const salloc = gpa_server.allocator();15const salloc = gpa_server.allocator();
16const calloc = gpa_client.allocator();16const calloc = gpa_client.allocator();
1717
18var server: Server = undefined;
19
18fn handleRequest(res: *Server.Response) !void {20fn handleRequest(res: *Server.Response) !void {
19 const log = std.log.scoped(.server);21 const log = std.log.scoped(.server);
2022
...@@ -89,7 +91,7 @@ fn handleRequest(res: *Server.Response) !void {...@@ -89,7 +91,7 @@ fn handleRequest(res: *Server.Response) !void {
89 } else if (mem.eql(u8, res.request.target, "/redirect/3")) {91 } else if (mem.eql(u8, res.request.target, "/redirect/3")) {
90 res.transfer_encoding = .chunked;92 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()});
93 defer salloc.free(location);95 defer salloc.free(location);
9496
95 res.status = .found;97 res.status = .found;
...@@ -119,7 +121,10 @@ var handle_new_requests = true;...@@ -119,7 +121,10 @@ var handle_new_requests = true;
119121
120fn runServer(srv: *Server) !void {122fn runServer(srv: *Server) !void {
121 outer: while (handle_new_requests) {123 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 });
123 defer res.deinit();128 defer res.deinit();
124129
125 while (res.reset() != .closing) {130 while (res.reset() != .closing) {
...@@ -162,7 +167,7 @@ pub fn main() !void {...@@ -162,7 +167,7 @@ pub fn main() !void {
162167
163 defer _ = gpa_client.deinit();168 defer _ = gpa_client.deinit();
164169
165 var server = Server.init(salloc, .{ .reuse_address = true });170 server = Server.init(salloc, .{ .reuse_address = true });
166171
167 const addr = std.net.Address.parseIp("127.0.0.1", 0) catch unreachable;172 const addr = std.net.Address.parseIp("127.0.0.1", 0) catch unreachable;
168 try server.listen(addr);173 try server.listen(addr);