//! HTTP routing — parity with the rust backend's routes. //! //! GET / → static/index.html //! GET /static/ → static assets from disk //! GET /e/[.ext] → bufo image bytes (resolver) //! GET /bufo-of-the-month → html page //! GET /api/health → "ok" //! GET /api/search?... → search (query string params) //! POST /api/search → search (json body) //! GET /api/similar?url=... → visually similar bufos for a first-party URL //! POST /api/similar → visually similar bufos for transient image bytes //! GET /api/bufo-of-the-month → json //! GET /api/image?url=&max_bytes= → image shrunk under a byte budget (bot) //! //! /api/* is rate limited per client ip (10 burst, 1 per 6s). const std = @import("std"); const Io = std.Io; const http = std.http; const mem = std.mem; const Allocator = mem.Allocator; const logfire = @import("logfire"); const search = @import("search.zig"); const resolver = @import("resolver.zig"); const botm = @import("botm.zig"); const ratelimit = @import("ratelimit.zig"); const image = @import("image.zig"); const HTTP_BUF_SIZE = 65536; const MAX_BODY = 16 * 1024; const MAX_STATIC_BYTES = 20 * 1024 * 1024; pub const App = struct { io: Io, allocator: Allocator, deps: search.Deps, resolver: resolver.Resolver, botm: botm.Source, limiter: *ratelimit.Limiter, upload_gate: *UploadGate, static_dir: []const u8, }; pub const UploadGate = struct { active: std.atomic.Value(u8) = .init(0), fn acquire(self: *UploadGate) bool { const previous = self.active.fetchAdd(1, .acquire); if (previous < 2) return true; _ = self.active.fetchSub(1, .release); return false; } fn release(self: *UploadGate) void { _ = self.active.fetchSub(1, .release); } }; pub fn handleConnection(stream: Io.net.Stream, app: *App) void { defer stream.close(app.io); var read_buffer: [HTTP_BUF_SIZE]u8 = undefined; var write_buffer: [HTTP_BUF_SIZE]u8 = undefined; var reader = stream.reader(app.io, &read_buffer); var writer = stream.writer(app.io, &write_buffer); var srv = http.Server.init(&reader.interface, &writer.interface); while (true) { var request = srv.receiveHead() catch |err| { if (err != error.HttpConnectionClosing and err != error.EndOfStream) { std.log.debug("http receive error: {t}", .{err}); } return; }; handleRequest(&request, app) catch |err| { std.log.err("request error: {t}", .{err}); return; }; if (!request.head.keep_alive) return; } } /// one span per request so traffic questions ("how many requests to which /// routes, with what status, in this window") are a single logfire query. /// the route is normalized (`/e/{name}`, `/static/{file}`, `unmatched`) so /// scanners can't explode span cardinality. fn handleRequest(request: *http.Server.Request, app: *App) !void { const target = request.head.target; const qmark = mem.indexOfScalar(u8, target, '?'); const path = if (qmark) |i| target[0..i] else target; const qs = if (qmark) |i| target[i + 1 ..] else ""; const span = logfire.httpSpan(@tagName(request.head.method), routeFor(path), .{}); defer span.end(); span.setAttribute("client", clientOf(request)); const status = try dispatch(request, app, path, qs); span.setAttribute("http.response.status_code", @as(i64, @intFromEnum(status))); } const known_routes = [_][]const u8{ "/", "/api/health", "/api/search", "/api/similar", "/api/bufo-of-the-month", "/api/image", "/bufo-of-the-month", }; fn routeFor(path: []const u8) []const u8 { for (known_routes) |r| if (mem.eql(u8, path, r)) return r; if (mem.startsWith(u8, path, "/e/")) return "/e/{name}"; if (mem.startsWith(u8, path, "/static/")) return "/static/{file}"; return "unmatched"; } fn dispatch(request: *http.Server.Request, app: *App, path: []const u8, qs: []const u8) !http.Status { if (request.head.method == .OPTIONS) return sendCorsPreflight(request); if (mem.startsWith(u8, path, "/api/")) { if (!app.limiter.allow(app.io, clientKey(request), ratelimit.nowMs(app.io))) { try request.respond("Too many requests, retry in 1s", .{ .status = .too_many_requests, .extra_headers = &.{.{ .name = "retry-after", .value = "1" }}, }); return .too_many_requests; } if (mem.eql(u8, path, "/api/health")) return sendText(request, .ok, "ok", "text/plain"); if (mem.eql(u8, path, "/api/search")) { return switch (request.head.method) { .GET => handleSearch(request, app, .{ .query_string = qs }), .POST => handleSearch(request, app, .body), else => sendJson(request, .method_not_allowed, "{\"error\":\"method not allowed\"}"), }; } if (mem.eql(u8, path, "/api/similar")) { return switch (request.head.method) { .GET => handleSimilarUrl(request, app, qs), .POST => handleSimilarUpload(request, app, qs), else => sendJson(request, .method_not_allowed, "{\"error\":\"method not allowed\"}"), }; } if (mem.eql(u8, path, "/api/bufo-of-the-month")) return handleBotm(request, app, .json); if (mem.eql(u8, path, "/api/image")) return handleImage(request, app, qs); return sendJson(request, .not_found, "{\"error\":\"not found\"}"); } if (mem.eql(u8, path, "/")) return sendStaticFile(request, app, "index.html"); if (mem.startsWith(u8, path, "/static/")) return sendStaticFile(request, app, path["/static/".len..]); if (mem.startsWith(u8, path, "/e/")) return handleResolve(request, app, path["/e/".len..]); if (mem.eql(u8, path, "/bufo-of-the-month")) return handleBotm(request, app, .page); return sendJson(request, .not_found, "{\"error\":\"not found\"}"); } /// fly puts the real client behind `fly-client-ip`; locally fall back to a /// shared bucket so the limiter still exercises /// which app a request came from, for the traffic breakdown: X-Client > /// Origin host > Referer host (our own → "homepage") > "unknown". the same /// precedence as typeahead.waow.tech, so consumers set one header for both. /// X-Client is caller-chosen bytes that end up on a stats page, so only a /// hostname-shaped value (≤64 chars) is accepted; anything else is unknown. fn clientOf(request: *http.Server.Request) []const u8 { var x_client: ?[]const u8 = null; var origin: ?[]const u8 = null; var referer: ?[]const u8 = null; var host: ?[]const u8 = null; var it = request.iterateHeaders(); while (it.next()) |h| { if (std.ascii.eqlIgnoreCase(h.name, "x-client")) x_client = h.value; if (std.ascii.eqlIgnoreCase(h.name, "origin")) origin = h.value; if (std.ascii.eqlIgnoreCase(h.name, "referer")) referer = h.value; if (std.ascii.eqlIgnoreCase(h.name, "host")) host = h.value; } return attributeClient(x_client, origin, referer, host orelse ""); } fn attributeClient(x_client: ?[]const u8, origin: ?[]const u8, referer: ?[]const u8, self_host: []const u8) []const u8 { const raw = if (x_client) |c| c else if (origin) |o| urlHost(o) orelse "unknown" else if (referer) |r| blk: { const h = urlHost(r) orelse break :blk "unknown"; break :blk if (mem.eql(u8, h, self_host)) "homepage" else h; } else "unknown"; if (mem.eql(u8, raw, "localhost") or mem.startsWith(u8, raw, "127.") or mem.eql(u8, raw, "[::1]")) return "unknown"; if (raw.len == 0 or raw.len > 64) return "unknown"; for (raw) |c| if (!(std.ascii.isAlphanumeric(c) or c == '.' or c == '_' or c == ':' or c == '[' or c == ']' or c == '-')) return "unknown"; return raw; } /// the host of an absolute http(s) url, without port fn urlHost(url: []const u8) ?[]const u8 { const scheme_end = mem.indexOf(u8, url, "://") orelse return null; const rest = url[scheme_end + 3 ..]; const end = mem.indexOfAny(u8, rest, "/?#") orelse rest.len; const authority = rest[0..end]; const at = mem.lastIndexOfScalar(u8, authority, '@'); const hostport = if (at) |i| authority[i + 1 ..] else authority; if (hostport.len == 0) return null; if (hostport[0] == '[') { const close = mem.indexOfScalar(u8, hostport, ']') orelse return null; return hostport[0 .. close + 1]; } const colon = mem.indexOfScalar(u8, hostport, ':'); return if (colon) |i| hostport[0..i] else hostport; } test "attributeClient: X-Client > Origin > Referer(self→homepage) > unknown; non-hostnames rejected" { const self = "find-bufo.com"; try std.testing.expectEqualStrings("plyr.fm", attributeClient("plyr.fm", "https://other.example", null, self)); try std.testing.expectEqualStrings("status.zzstoatzz.io", attributeClient(null, "https://status.zzstoatzz.io", "https://x.example/p", self)); try std.testing.expectEqualStrings("homepage", attributeClient(null, null, "https://find-bufo.com/?q=lgtm", self)); try std.testing.expectEqualStrings("blog.example", attributeClient(null, null, "https://blog.example:8443/post", self)); try std.testing.expectEqualStrings("unknown", attributeClient(null, null, null, self)); try std.testing.expectEqualStrings("unknown", attributeClient(null, "http://localhost:5173", null, self)); try std.testing.expectEqualStrings("unknown", attributeClient("", null, null, self)); try std.testing.expectEqualStrings("unknown", attributeClient("a" ** 65, null, null, self)); try std.testing.expectEqualStrings("unknown", attributeClient(null, "not a url", null, self)); try std.testing.expectEqualStrings("pi-extensions", attributeClient("pi-extensions", null, null, self)); } fn clientKey(request: *http.Server.Request) []const u8 { var it = request.iterateHeaders(); while (it.next()) |h| { if (std.ascii.eqlIgnoreCase(h.name, "fly-client-ip")) return h.value; } return "local"; } const ParamSource = union(enum) { query_string: []const u8, body, }; fn handleSearch(request: *http.Server.Request, app: *App, source: ParamSource) !http.Status { var arena = std.heap.ArenaAllocator.init(app.allocator); defer arena.deinit(); const alloc = arena.allocator(); const params: search.Params = switch (source) { .query_string => |qs| try search.parseQueryString(alloc, qs), .body => blk: { const body_reader = request.readerExpectContinue(&.{}) catch { return sendJson(request, .bad_request, "{\"error\":\"failed to read body\"}"); }; const body = body_reader.allocRemaining(alloc, Io.Limit.limited(MAX_BODY)) catch { return sendJson(request, .bad_request, "{\"error\":\"failed to read body\"}"); }; break :blk search.parseJsonBody(alloc, body) catch { return sendJson(request, .bad_request, "{\"error\":\"invalid json\"}"); }; }, }; if (mem.trim(u8, params.query, " ").len == 0) { return sendJson(request, .bad_request, "{\"error\":\"query is required\"}"); } // GET responses are shareable urls: an etag over the raw query string // lets browsers and edge caches revalidate without re-running the search var etag_buf: [24]u8 = undefined; const etag: ?[]const u8 = switch (source) { .query_string => |qs| std.fmt.bufPrint(&etag_buf, "\"{x}\"", .{std.hash.Wyhash.hash(0, qs)}) catch null, .body => null, }; if (etag) |tag| { var it = request.iterateHeaders(); while (it.next()) |h| { if (std.ascii.eqlIgnoreCase(h.name, "if-none-match") and mem.eql(u8, mem.trim(u8, h.value, " "), tag)) { try request.respond("", .{ .status = .not_modified, .extra_headers = &.{.{ .name = "etag", .value = tag }}, }); return .not_modified; } } } const results = search.perform(alloc, app.deps, params) catch |err| switch (err) { error.QueryTooLong => return sendText( request, .bad_request, "search query is too long (max 1024 characters for text search). try a shorter query.", "text/plain", ), error.Upstream => return sendJson(request, .internal_server_error, "{\"error\":\"search failed\"}"), error.OutOfMemory => return error.OutOfMemory, }; const body = try search.writeJson(alloc, results); try request.respond(body, .{ .status = .ok, .extra_headers = &.{ .{ .name = "content-type", .value = "application/json" }, .{ .name = "access-control-allow-origin", .value = "*" }, .{ .name = "access-control-expose-headers", .value = "etag, content-type, cache-control" }, .{ .name = "cache-control", .value = "public, max-age=300" }, .{ .name = "etag", .value = etag orelse "" }, }, }); return .ok; } fn handleResolve(request: *http.Server.Request, app: *App, raw: []const u8) !http.Status { var arena = std.heap.ArenaAllocator.init(app.allocator); defer arena.deinit(); const alloc = arena.allocator(); const decoded = try search.urlDecode(alloc, raw); const name = resolver.bufoName(decoded) orelse return sendText(request, .bad_request, "invalid bufo name", "text/plain"); const span = logfire.span("bufo_resolve", .{ .name = name }); defer span.end(); const img = app.resolver.resolve(alloc, name) catch |err| switch (err) { error.NotFound => return sendText(request, .not_found, "bufo not found", "text/plain"), error.Upstream => return sendText(request, .bad_gateway, "failed to read bufo", "text/plain"), error.OutOfMemory => return error.OutOfMemory, }; try request.respond(img.bytes, .{ .status = .ok, .extra_headers = &.{ .{ .name = "content-type", .value = img.content_type }, .{ .name = "cache-control", .value = resolver.IMAGE_CACHE_CONTROL }, .{ .name = "access-control-allow-origin", .value = "*" }, }, }); return .ok; } const SimilarParams = struct { url: ?[]const u8 = null, top_k: usize = 20, }; fn parseSimilarParams(alloc: Allocator, qs: []const u8) !SimilarParams { var params: SimilarParams = .{}; var it = mem.splitScalar(u8, qs, '&'); while (it.next()) |pair| { const eq = mem.indexOfScalar(u8, pair, '=') orelse continue; const key = pair[0..eq]; const value = try search.urlDecode(alloc, pair[eq + 1 ..]); if (mem.eql(u8, key, "url")) params.url = value; if (mem.eql(u8, key, "top_k")) { params.top_k = std.fmt.parseInt(usize, value, 10) catch params.top_k; params.top_k = std.math.clamp(params.top_k, 1, 100); } } return params; } fn handleSimilarUrl(request: *http.Server.Request, app: *App, qs: []const u8) !http.Status { var arena = std.heap.ArenaAllocator.init(app.allocator); defer arena.deinit(); const alloc = arena.allocator(); const params = try parseSimilarParams(alloc, qs); const source_url = params.url orelse return sendJson(request, .bad_request, "{\"error\":\"url is required\"}"); if (!image.isAllowedSimilarityUrl(source_url)) { return sendJson(request, .bad_request, "{\"error\":\"domain not allowed\"}"); } const results = search.performSimilar(alloc, app.deps, source_url, params.top_k) catch |err| switch (err) { error.Upstream, error.QueryTooLong => return sendJson(request, .bad_gateway, "{\"error\":\"similarity search failed\"}"), error.OutOfMemory => return error.OutOfMemory, }; return sendSearchResults(request, alloc, results, "public, max-age=300"); } const MAX_SIMILAR_IMAGE = 10 * 1024 * 1024; fn handleSimilarUpload(request: *http.Server.Request, app: *App, qs: []const u8) !http.Status { var arena = std.heap.ArenaAllocator.init(app.allocator); defer arena.deinit(); const alloc = arena.allocator(); // qs borrows the server read buffer, which body reads are allowed to refill. const params = try parseSimilarParams(alloc, qs); if (!app.upload_gate.acquire()) { request.head.keep_alive = false; try request.respond("{\"error\":\"image search is busy; try again shortly\"}", .{ .status = .service_unavailable, .extra_headers = &.{ .{ .name = "content-type", .value = "application/json" }, .{ .name = "access-control-allow-origin", .value = "*" }, .{ .name = "cache-control", .value = "no-store" }, .{ .name = "retry-after", .value = "1" }, }, }); return .service_unavailable; } defer app.upload_gate.release(); if (request.head.content_length) |size| { if (size > MAX_SIMILAR_IMAGE) { request.head.keep_alive = false; return sendJson(request, .payload_too_large, "{\"error\":\"image is too large (max 10 MB)\"}"); } } const body_reader = request.readerExpectContinue(&.{}) catch { request.head.keep_alive = false; return sendJson(request, .bad_request, "{\"error\":\"failed to read image\"}"); }; const bytes = body_reader.allocRemaining(alloc, Io.Limit.limited(MAX_SIMILAR_IMAGE)) catch { request.head.keep_alive = false; return sendJson(request, .payload_too_large, "{\"error\":\"image is too large (max 10 MB)\"}"); }; if (bytes.len == 0) return sendJson(request, .bad_request, "{\"error\":\"image is required\"}"); const content_type = image.uploadContentType(bytes) orelse return sendJson(request, .unsupported_media_type, "{\"error\":\"use a PNG, JPEG, GIF, or WebP image\"}"); const results = search.performSimilarBytes(alloc, app.deps, bytes, content_type, params.top_k) catch |err| switch (err) { error.Upstream, error.QueryTooLong => return sendJson(request, .bad_gateway, "{\"error\":\"similarity search failed\"}"), error.OutOfMemory => return error.OutOfMemory, }; return sendSearchResults(request, alloc, results, "no-store"); } fn sendSearchResults(request: *http.Server.Request, alloc: Allocator, results: []const search.Result, cache_control: []const u8) !http.Status { const body = try search.writeJson(alloc, results); try request.respond(body, .{ .status = .ok, .extra_headers = &.{ .{ .name = "content-type", .value = "application/json" }, .{ .name = "access-control-allow-origin", .value = "*" }, .{ .name = "cache-control", .value = cache_control }, }, }); return .ok; } const MAX_UPSTREAM_IMAGE = 32 * 1024 * 1024; fn handleImage(request: *http.Server.Request, app: *App, qs: []const u8) !http.Status { var arena = std.heap.ArenaAllocator.init(app.allocator); defer arena.deinit(); const alloc = arena.allocator(); var url: ?[]const u8 = null; var max_bytes: usize = image.DEFAULT_MAX_BYTES; var it = mem.splitScalar(u8, qs, '&'); while (it.next()) |pair| { const eq = mem.indexOfScalar(u8, pair, '=') orelse continue; const key = pair[0..eq]; const val = try search.urlDecode(alloc, pair[eq + 1 ..]); if (mem.eql(u8, key, "url")) url = val; if (mem.eql(u8, key, "max_bytes")) max_bytes = std.fmt.parseInt(usize, val, 10) catch max_bytes; } const target = url orelse return sendText(request, .bad_request, "url is required", "text/plain"); if (!image.isAllowedUrl(target)) return sendText(request, .bad_request, "domain not allowed", "text/plain"); const span = logfire.span("image_resize", .{ .url = target, .max_bytes = @as(i64, @intCast(max_bytes)) }); defer span.end(); var body: Io.Writer.Allocating = .init(alloc); const res = app.deps.voyage.http.fetch(.{ .location = .{ .url = target }, .method = .GET, .response_writer = &body.writer, }) catch |err| { std.log.err("failed to fetch image {s}: {t}", .{ target, err }); return sendText(request, .bad_gateway, "failed to fetch image", "text/plain"); }; if (res.status != .ok) return sendText(request, .bad_gateway, "upstream returned error", "text/plain"); const original = body.written(); if (original.len > MAX_UPSTREAM_IMAGE) return sendText(request, .bad_gateway, "image too large", "text/plain"); const out = try image.shrink(alloc, original, image.sniff(original, target), max_bytes); try request.respond(out.bytes, .{ .status = .ok, .extra_headers = &.{ .{ .name = "content-type", .value = out.content_type }, .{ .name = "cache-control", .value = image.CACHE_CONTROL }, .{ .name = "access-control-allow-origin", .value = "*" }, }, }); return .ok; } const BotmFormat = enum { json, page }; fn handleBotm(request: *http.Server.Request, app: *App, format: BotmFormat) !http.Status { var arena = std.heap.ArenaAllocator.init(app.allocator); defer arena.deinit(); const alloc = arena.allocator(); const winner = app.botm.winner(alloc) catch |err| switch (err) { error.NotFound => return sendText(request, .not_found, "no bot match stats available", "text/plain"), error.OutOfMemory => return error.OutOfMemory, else => return sendText(request, .bad_gateway, "bot stats unavailable", "text/plain"), }; const body = switch (format) { .json => try app.botm.writeJson(alloc, winner), .page => try app.botm.writePage(alloc, winner), }; try request.respond(body, .{ .status = .ok, .extra_headers = &.{ .{ .name = "content-type", .value = switch (format) { .json => "application/json", .page => "text/html; charset=utf-8", } }, .{ .name = "cache-control", .value = "public, max-age=3600" }, .{ .name = "access-control-allow-origin", .value = "*" }, }, }); return .ok; } const content_types = std.StaticStringMap([]const u8).initComptime(.{ .{ "html", "text/html; charset=utf-8" }, .{ "js", "application/javascript; charset=utf-8" }, .{ "css", "text/css; charset=utf-8" }, .{ "json", "application/json" }, .{ "png", "image/png" }, .{ "gif", "image/gif" }, .{ "jpg", "image/jpeg" }, .{ "jpeg", "image/jpeg" }, .{ "svg", "image/svg+xml" }, .{ "webp", "image/webp" }, }); fn contentTypeFor(file: []const u8) []const u8 { const dot = mem.lastIndexOfScalar(u8, file, '.') orelse return "application/octet-stream"; return content_types.get(file[dot + 1 ..]) orelse "application/octet-stream"; } /// only flat filenames; anything with a path separator or `..` is rejected fn safeStaticName(raw: []const u8) bool { if (raw.len == 0 or raw.len > 255) return false; if (mem.indexOf(u8, raw, "..") != null) return false; for (raw) |c| { if (!(std.ascii.isAlphanumeric(c) or c == '-' or c == '_' or c == '.' or c == '\'')) return false; } return true; } fn sendStaticFile(request: *http.Server.Request, app: *App, raw: []const u8) !http.Status { var arena = std.heap.ArenaAllocator.init(app.allocator); defer arena.deinit(); const alloc = arena.allocator(); const file = try search.urlDecode(alloc, raw); if (!safeStaticName(file)) return sendJson(request, .not_found, "{\"error\":\"not found\"}"); const path = try std.fmt.allocPrint(alloc, "{s}/{s}", .{ app.static_dir, file }); const bytes = resolver.readFile(app.io, alloc, path, MAX_STATIC_BYTES) catch { return sendJson(request, .not_found, "{\"error\":\"not found\"}"); }; try request.respond(bytes, .{ .status = .ok, .extra_headers = &.{ .{ .name = "content-type", .value = contentTypeFor(file) }, .{ .name = "cache-control", .value = "public, max-age=300" }, }, }); return .ok; } fn sendText(request: *http.Server.Request, status: http.Status, body: []const u8, content_type: []const u8) !http.Status { try request.respond(body, .{ .status = status, .extra_headers = &.{ .{ .name = "content-type", .value = content_type }, .{ .name = "access-control-allow-origin", .value = "*" }, }, }); return status; } fn sendJson(request: *http.Server.Request, status: http.Status, body: []const u8) !http.Status { return sendText(request, status, body, "application/json"); } fn sendCorsPreflight(request: *http.Server.Request) !http.Status { try request.respond("", .{ .status = .no_content, .extra_headers = &.{ .{ .name = "access-control-allow-origin", .value = "*" }, .{ .name = "access-control-allow-methods", .value = "GET, POST, OPTIONS" }, .{ .name = "access-control-allow-headers", .value = "content-type, x-client" }, }, }); return .no_content; } test "static names reject traversal" { try std.testing.expect(safeStaticName("index.html")); try std.testing.expect(safeStaticName("bufo-is-trapped-in-a-cameron-winter-phase.png")); try std.testing.expect(!safeStaticName("../Cargo.toml")); try std.testing.expect(!safeStaticName("a/b.png")); try std.testing.expect(!safeStaticName("")); } test "routes normalize to a bounded set" { try std.testing.expectEqualStrings("/api/search", routeFor("/api/search")); try std.testing.expectEqualStrings("/api/similar", routeFor("/api/similar")); try std.testing.expectEqualStrings("/e/{name}", routeFor("/e/bufo-lgtm.png")); try std.testing.expectEqualStrings("/static/{file}", routeFor("/static/index.html")); try std.testing.expectEqualStrings("unmatched", routeFor("/wp-login.php")); } test "similar params decode the source URL and clamp top_k" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const params = try parseSimilarParams( arena.allocator(), "url=https%3A%2F%2Fall-the.bufo.zone%2Fbufo-party.png&top_k=999", ); try std.testing.expectEqualStrings( "https://all-the.bufo.zone/bufo-party.png", params.url.?, ); try std.testing.expectEqual(@as(usize, 100), params.top_k); } test "similar params survive request buffer reuse" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); var query = "url=https%3A%2F%2Fx&top_k=2".*; const params = try parseSimilarParams(arena.allocator(), &query); @memset(&query, 'x'); try std.testing.expectEqualStrings("https://x", params.url.?); try std.testing.expectEqual(@as(usize, 2), params.top_k); } test "upload gate bounds memory-heavy image searches" { var gate: UploadGate = .{}; try std.testing.expect(gate.acquire()); try std.testing.expect(gate.acquire()); try std.testing.expect(!gate.acquire()); gate.release(); try std.testing.expect(gate.acquire()); gate.release(); gate.release(); }