const std = @import("std"); const web = @import("web"); const linux = std.os.linux; const errno = linux.E.init; const ssl = web.openssl; var running: std.atomic.Value(bool) = .init(true); const body_write_buffer_huge_pages = 1; fn interruptionHandler(sig: linux.SIG) callconv(.c) void { var buf: [32]u8 = undefined; const signal_name = switch (sig) { _ => std.fmt.bufPrint(&buf, "#{d}", .{sig}) catch unreachable, inline else => |x| "SIG" ++ @tagName(x), }; std.log.debug("Interrupted with signal {s}.", .{signal_name}); switch (sig) { linux.SIG.INT, linux.SIG.TERM => { running.store(false, .release); }, else => {}, } } const Handler = struct { fn notFound(response: *web.Response) !void { try response.body_writer.writeAll("Not Found\n"); try response.header_writer.writeAll(web.http.status.not_found); try response.header_writer.writeAll("Content-Type: text/plain; charset=utf-8\r\n"); try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); try response.header_writer.writeAll("\r\n"); response.sendHeadersAndBody(); } fn methodNotAllowed(response: *web.Response) !void { try response.body_writer.writeAll("Method Not Allowed\n"); try response.header_writer.writeAll(web.http.status.method_not_allowed); try response.header_writer.writeAll("Content-Type: text/plain; charset=utf-8\r\n"); try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); try response.header_writer.writeAll("\r\n"); response.sendHeadersAndBody(); } fn handle(_: *anyopaque, request: *web.Request, response: *web.Response) !void { std.log.info("{f} | {s} {s}", .{ response.connection.address, @tagName(request.method), request.pathname, }); if (std.mem.eql(u8, request.pathname, "/")) { if (request.method != .GET) { try methodNotAllowed(response); return; } try response.body_writer.writeAll("{\"ok\":true}\n"); try response.header_writer.writeAll(web.http.status.ok); try response.header_writer.writeAll("Content-Type: application/json\r\n"); try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); try response.header_writer.writeAll("\r\n"); response.sendHeadersAndBody(); } else if (std.mem.startsWith(u8, request.pathname, "/zeroes/")) { const bytes = std.fmt.parseUnsigned(usize, request.pathname[8..], 10) catch { try notFound(response); return; }; if (bytes > body_write_buffer_huge_pages * 2 * 1024 * 1024) { try notFound(response); return; } if (request.method != .GET) { try methodNotAllowed(response); return; } const buffer = [_]u8{0}; var data = [_][]const u8{&buffer}; try response.body_writer.writeSplatAll(&data, bytes); try response.header_writer.writeAll(web.http.status.ok); try response.header_writer.writeAll("Content-Type: application/octet-stream\r\n"); try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); try response.header_writer.writeAll("\r\n"); response.sendHeadersAndBody(); } else { try notFound(response); } } fn interface() web.RequestHandler { return .{ .ptr = undefined, .vtable = &.{ .handle = handle, }, }; } }; pub fn main(init: std.process.Init) !void { const allocator = init.gpa; _ = ssl.c_ssl.SSL_library_init(); _ = ssl.c_ssl.OpenSSL_add_all_algorithms(); _ = ssl.c_ssl.SSL_load_error_strings(); const ssl_ctx = try ssl.SslContext.new(.tlsServerMethod()); defer ssl_ctx.free(); _ = ssl_ctx.setMinProtoVersion(ssl.c_ssl.TLS1_2_VERSION); _ = ssl_ctx.setMaxProtoVersion(ssl.c_ssl.TLS1_2_VERSION); _ = ssl_ctx.setOptions(ssl.c_ssl.SSL_OP_NO_SSLv3 | ssl.c_ssl.SSL_OP_NO_TLSv1 | ssl.c_ssl.SSL_OP_NO_TLSv1_1 | ssl.c_ssl.SSL_OP_NO_TLSv1_3 | ssl.c_ssl.SSL_OP_ENABLE_KTLS); try ssl_ctx.useCertificateFile("cert.pem", ssl.c_ssl.SSL_FILETYPE_PEM); try ssl_ctx.usePrivateKeyFile("key.pem", ssl.c_ssl.SSL_FILETYPE_PEM); try ssl_ctx.checkPrivateKey(); var server = try web.Server.init(allocator, init.io, .{ .request_handler = Handler.interface(), .body_write_buffer_huge_pages = body_write_buffer_huge_pages, .ssl_ctx = ssl_ctx, }); defer server.deinit(allocator); const sigaction: linux.Sigaction = .{ .handler = .{ .handler = interruptionHandler }, .mask = linux.sigemptyset(), .flags = linux.SA.RESETHAND, }; signal(.INT, &sigaction); signal(.TERM, &sigaction); try server.listen(&running); } fn signal(sig: linux.SIG, action: *const linux.Sigaction) void { var old_action = std.mem.zeroes(linux.Sigaction); _ = linux.sigaction(sig, null, &old_action); if (old_action.handler.handler == linux.SIG.IGN) return; _ = linux.sigaction(sig, action, null); }