require timestamp passed in with cache request

This commit is contained in:
Muki Kiboigo
2026-03-26 18:50:46 -07:00
parent 094b8ed3e2
commit 0d0ad718a9
3 changed files with 56 additions and 5 deletions

View File

@@ -362,7 +362,7 @@ fn processRequest(self: *Client, req: Request) !void {
const arena = try self.network.app.arena_pool.acquire(.{ .debug = "HttpClient.processRequest.cache" }); const arena = try self.network.app.arena_pool.acquire(.{ .debug = "HttpClient.processRequest.cache" });
defer self.network.app.arena_pool.release(arena); defer self.network.app.arena_pool.release(arena);
if (cache.get(arena, .{ .url = req.url })) |cached| { if (cache.get(arena, .{ .url = req.url, .timestamp = std.time.timestamp() })) |cached| {
log.debug(.browser, "http.cache.get", .{ log.debug(.browser, "http.cache.get", .{
.url = req.url, .url = req.url,
.found = true, .found = true,

View File

@@ -126,6 +126,7 @@ pub const CachedMetadata = struct {
pub const CacheRequest = struct { pub const CacheRequest = struct {
url: []const u8, url: []const u8,
timestamp: i64,
}; };
pub const CachedData = union(enum) { pub const CachedData = union(enum) {

View File

@@ -127,7 +127,7 @@ pub fn get(self: *FsCache, arena: std.mem.Allocator, req: CacheRequest) ?Cache.C
return null; return null;
} }
const now = std.time.timestamp(); const now = req.timestamp;
const age = (now - metadata.stored_at) + @as(i64, @intCast(metadata.age_at_store)); const age = (now - metadata.stored_at) + @as(i64, @intCast(metadata.age_at_store));
if (age < 0 or @as(u64, @intCast(age)) >= metadata.cache_control.max_age) { if (age < 0 or @as(u64, @intCast(age)) >= metadata.cache_control.max_age) {
self.dir.deleteFile(&meta_p) catch {}; self.dir.deleteFile(&meta_p) catch {};
@@ -208,7 +208,7 @@ test "FsCache: basic put and get" {
var fs_cache = try FsCache.init(path); var fs_cache = try FsCache.init(path);
defer fs_cache.deinit(); defer fs_cache.deinit();
var c = Cache{ .kind = .{ .fs = fs_cache } }; var cache = Cache{ .kind = .{ .fs = fs_cache } };
var arena = std.heap.ArenaAllocator.init(testing.allocator); var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit(); defer arena.deinit();
@@ -228,9 +228,9 @@ test "FsCache: basic put and get" {
}; };
const body = "hello world"; const body = "hello world";
try c.put(meta, body); try cache.put(meta, body);
const result = c.get(arena.allocator(), .{ .url = "https://example.com" }) orelse return error.CacheMiss; const result = cache.get(arena.allocator(), .{ .url = "https://example.com", .timestamp = now }) orelse return error.CacheMiss;
defer result.data.file.close(); defer result.data.file.close();
var buf: [64]u8 = undefined; var buf: [64]u8 = undefined;
@@ -241,3 +241,53 @@ test "FsCache: basic put and get" {
try testing.expectEqualStrings(body, read_buf); try testing.expectEqualStrings(body, read_buf);
} }
test "FsCache: get expiration" {
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
const path = try tmp.dir.realpathAlloc(testing.allocator, ".");
defer testing.allocator.free(path);
var fs_cache = try FsCache.init(path);
defer fs_cache.deinit();
var cache = Cache{ .kind = .{ .fs = fs_cache } };
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
const now = 5000;
const max_age = 1000;
const meta = CachedMetadata{
.url = "https://example.com",
.content_type = "text/html",
.status = 200,
.stored_at = now,
.age_at_store = 900,
.etag = null,
.last_modified = null,
.cache_control = .{ .max_age = max_age },
.vary = null,
.headers = &.{},
};
const body = "hello world";
try cache.put(meta, body);
const result = cache.get(
arena.allocator(),
.{ .url = "https://example.com", .timestamp = now + 50 },
) orelse return error.CacheMiss;
result.data.file.close();
try testing.expectEqual(null, cache.get(
arena.allocator(),
.{ .url = "https://example.com", .timestamp = now + 200 },
));
try testing.expectEqual(null, cache.get(
arena.allocator(),
.{ .url = "https://example.com", .timestamp = now },
));
}