milestone 18: collapse duplicated infrastructure into shared listener core, crud list helper, resource shells, transport race, name and line helpers, ui modules
This commit is contained in:
@@ -238,6 +238,19 @@ fn get(path: []const u8, buf: []u8) []const u8 {
|
||||
return std.fmt.bufPrint(buf, "GET {s} HTTP/1.1\r\nhost: t\r\n\r\n", .{path}) catch unreachable;
|
||||
}
|
||||
|
||||
/// The listener's counters, read once after `f` finished and before the
|
||||
/// listener is torn down. Plain values, because they are a report of a run that
|
||||
/// is over: the shared core's counters and the web listener's own one land in
|
||||
/// the same struct here.
|
||||
const Counters = struct {
|
||||
connections: u64,
|
||||
rejected_at_capacity: u64,
|
||||
rejected_at_shutdown: u64,
|
||||
accept_errors: u64,
|
||||
connection_errors: u64,
|
||||
requests: u64,
|
||||
};
|
||||
|
||||
/// Starts a listener on 127.0.0.1:0 with `state` and runs `f` against it under
|
||||
/// the budget, then shuts the listener down through the drain path.
|
||||
fn withServer(
|
||||
@@ -247,7 +260,7 @@ fn withServer(
|
||||
max_connections: u16,
|
||||
comptime f: anytype,
|
||||
extra: anytype,
|
||||
) !server.Stats {
|
||||
) !Counters {
|
||||
const listen_address: net.IpAddress = try .parse("127.0.0.1", 0);
|
||||
var web = try server.Server.listen(gpa, io, listen_address, state, .{ .max_connections = max_connections });
|
||||
const address = web.boundAddress();
|
||||
@@ -257,16 +270,16 @@ fn withServer(
|
||||
|
||||
const result = bounded(io, f, .{ io, address } ++ extra);
|
||||
|
||||
const stats: server.Stats = .{
|
||||
.accepted = .init(web.stats.accepted.load(.monotonic)),
|
||||
.rejected_at_capacity = .init(web.stats.rejected_at_capacity.load(.monotonic)),
|
||||
.rejected_at_shutdown = .init(web.stats.rejected_at_shutdown.load(.monotonic)),
|
||||
.accept_errors = .init(web.stats.accept_errors.load(.monotonic)),
|
||||
.connection_errors = .init(web.stats.connection_errors.load(.monotonic)),
|
||||
.requests = .init(web.stats.requests.load(.monotonic)),
|
||||
const stats: Counters = .{
|
||||
.connections = web.core.stats.connections.load(.monotonic),
|
||||
.rejected_at_capacity = web.core.stats.rejected_at_capacity.load(.monotonic),
|
||||
.rejected_at_shutdown = web.core.stats.rejected_at_shutdown.load(.monotonic),
|
||||
.accept_errors = web.core.stats.accept_errors.load(.monotonic),
|
||||
.connection_errors = web.core.stats.connection_errors.load(.monotonic),
|
||||
.requests = web.stats.requests.load(.monotonic),
|
||||
};
|
||||
|
||||
web.deinit(gpa, io);
|
||||
web.deinit(io);
|
||||
group.await(io) catch |err| switch (err) {
|
||||
error.Canceled => unreachable,
|
||||
};
|
||||
@@ -302,9 +315,9 @@ test "one connection carries two requests" {
|
||||
const stats = try withServer(gpa, io, &state, 4, twoRequestsOnOneConnection, .{});
|
||||
|
||||
// One accept for two requests is the whole point of keep-alive.
|
||||
try testing.expectEqual(@as(u64, 1), stats.accepted.load(.monotonic));
|
||||
try testing.expectEqual(@as(u64, 2), stats.requests.load(.monotonic));
|
||||
try testing.expectEqual(@as(u64, 0), stats.connection_errors.load(.monotonic));
|
||||
try testing.expectEqual(@as(u64, 1), stats.connections);
|
||||
try testing.expectEqual(@as(u64, 2), stats.requests);
|
||||
try testing.expectEqual(@as(u64, 0), stats.connection_errors);
|
||||
}
|
||||
|
||||
fn routingMatrix(io: std.Io, address: net.IpAddress) anyerror!void {
|
||||
@@ -352,7 +365,7 @@ test "routing answers 404, 405 with allow, and rejects malformed targets" {
|
||||
|
||||
var state = testState(gpa);
|
||||
const stats = try withServer(gpa, io, &state, 4, routingMatrix, .{});
|
||||
try testing.expectEqual(@as(u64, 5), stats.requests.load(.monotonic));
|
||||
try testing.expectEqual(@as(u64, 5), stats.requests);
|
||||
}
|
||||
|
||||
fn postBody(io: std.Io, address: net.IpAddress, length: usize, expected_status: u16) anyerror!void {
|
||||
@@ -448,7 +461,7 @@ test "a POST with no content-length and no transfer-encoding is an empty body, n
|
||||
|
||||
var state = testState(gpa);
|
||||
const stats = try withServer(gpa, io, &state, 4, postWithoutLength, .{});
|
||||
try testing.expectEqual(@as(u64, 0), stats.connection_errors.load(.monotonic));
|
||||
try testing.expectEqual(@as(u64, 0), stats.connection_errors);
|
||||
}
|
||||
|
||||
fn bodyThenTarget(io: std.Io, address: net.IpAddress) anyerror!void {
|
||||
@@ -509,8 +522,8 @@ test "a connection over the cap is told 503, not silently dropped" {
|
||||
|
||||
var state = testState(gpa);
|
||||
const stats = try withServer(gpa, io, &state, 1, refusedOverCapacity, .{});
|
||||
try testing.expectEqual(@as(u64, 1), stats.accepted.load(.monotonic));
|
||||
try testing.expectEqual(@as(u64, 1), stats.rejected_at_capacity.load(.monotonic));
|
||||
try testing.expectEqual(@as(u64, 1), stats.connections);
|
||||
try testing.expectEqual(@as(u64, 1), stats.rejected_at_capacity);
|
||||
}
|
||||
|
||||
fn deniedAndLimited(io: std.Io, address: net.IpAddress) anyerror!void {
|
||||
@@ -642,7 +655,7 @@ test "cancellation returns promptly with an idle keep-alive connection open" {
|
||||
const elapsed = start.durationTo(std.Io.Clock.awake.now(io));
|
||||
|
||||
client.cancel(io);
|
||||
web.deinit(gpa, io);
|
||||
web.deinit(io);
|
||||
|
||||
try testing.expect(elapsed.toMilliseconds() < settle.raw.toMilliseconds());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user