163 lines
5.3 KiB
Zig
163 lines
5.3 KiB
Zig
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);
|
|
}
|