polls on atproto
pollz.waow.tech
atproto
zig
1const std = @import("std");
2const net = std.net;
3const posix = std.posix;
4const Thread = std.Thread;
5const db = @import("db.zig");
6const http_server = @import("http.zig");
7const tap = @import("tap.zig");
8
9// max concurrent http connections (prevents resource exhaustion)
10const MAX_HTTP_WORKERS = 16;
11
12// socket timeout in seconds
13const SOCKET_TIMEOUT_SECS = 30;
14
15pub fn main() !void {
16 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
17 defer _ = gpa.deinit();
18 const allocator = gpa.allocator();
19
20 // init sqlite - use DATA_PATH env or default to /data/pollz.db
21 const db_path = posix.getenv("DATA_PATH") orelse "/data/pollz.db";
22 try db.init(db_path);
23 defer db.close();
24
25 // start tap consumer in background
26 const tap_thread = try Thread.spawn(.{}, tap.consumer, .{allocator});
27 defer tap_thread.join();
28
29 // init thread pool for http connections
30 var pool: Thread.Pool = undefined;
31 try pool.init(.{
32 .allocator = allocator,
33 .n_jobs = MAX_HTTP_WORKERS,
34 });
35 defer pool.deinit();
36
37 // start http server (bind to 0.0.0.0 for containerized deployments)
38 const address = try net.Address.parseIp("0.0.0.0", 3000);
39 var server = try address.listen(.{ .reuse_address = true });
40 defer server.deinit();
41
42 std.debug.print("pollz backend listening on http://127.0.0.1:3000 (max {} workers)\n", .{MAX_HTTP_WORKERS});
43
44 while (true) {
45 const conn = server.accept() catch |err| {
46 std.debug.print("accept error: {}\n", .{err});
47 continue;
48 };
49
50 // set socket timeouts to prevent slow client attacks
51 setSocketTimeout(conn.stream.handle, SOCKET_TIMEOUT_SECS) catch |err| {
52 std.debug.print("failed to set socket timeout: {}\n", .{err});
53 };
54
55 pool.spawn(http_server.handleConnection, .{conn}) catch |err| {
56 std.debug.print("pool spawn error: {}\n", .{err});
57 conn.stream.close();
58 };
59 }
60}
61
62fn setSocketTimeout(fd: posix.fd_t, secs: u32) !void {
63 const timeout = std.mem.toBytes(posix.timeval{
64 .sec = @intCast(secs),
65 .usec = 0,
66 });
67 try posix.setsockopt(fd, posix.SOL.SOCKET, posix.SO.RCVTIMEO, &timeout);
68 try posix.setsockopt(fd, posix.SOL.SOCKET, posix.SO.SNDTIMEO, &timeout);
69}