jetzig/main.zig
2023-11-29 20:43:24 +00:00

137 lines
3.9 KiB
Zig

const std = @import("std");
const HttpServerOptions = struct {
use_cache: bool,
};
const HttpServer = struct {
server: std.http.Server,
allocator: std.mem.Allocator,
page_cache: std.StringHashMap([]const u8),
port: u16,
host: []const u8,
options: HttpServerOptions,
const Self = @This();
pub fn init(
allocator: std.mem.Allocator,
cache: std.StringHashMap([]const u8),
host: []const u8,
port: u16,
options: HttpServerOptions,
) HttpServer {
const server = std.http.Server.init(allocator, .{ .reuse_address = true });
return .{
.server = server,
.allocator = allocator,
.page_cache = cache,
.host = host,
.port = port,
.options = options,
};
}
pub fn deinit(self: *Self) void {
self.server.deinit();
}
pub fn listen(self: *Self) !void {
const address = std.net.Address.parseIp(self.host, self.port) catch unreachable;
try self.server.listen(address);
std.debug.print(
"Listening on http://{s}:{} [cache:{s}]\n",
.{ self.host, self.port, if (self.options.use_cache) "enabled" else "disabled" }
);
try self.processRequests();
}
fn processRequests(self: *Self) !void {
while (true) {
self.processNextRequest() catch |err| {
switch(err) {
error.EndOfStream => continue,
error.ConnectionResetByPeer => continue,
else => return err,
}
};
}
}
fn processNextRequest(self: *Self) !void {
var response = try self.server.accept(.{ .allocator = self.allocator });
defer response.deinit();
try response.wait();
const content = try self.pageContent(response.request.method, response.request.target);
response.transfer_encoding = .{ .content_length = content.len };
try response.send();
try response.writeAll(content);
try response.finish();
}
fn pageContent(self: *Self, method: std.http.Method, target: []const u8) ![]const u8 {
var buffer: [1<<16]u8 = undefined;
const method_str = switch(method) {
.POST => "post",
else => "get"
};
const path = try std.mem.concat(self.allocator, u8, &[_][]const u8{ method_str, target });
// std.debug.print("{s} {s}\n", .{ method_str, target });
if (self.options.use_cache and self.page_cache.contains(path)) {
defer self.allocator.free(path);
if (self.page_cache.get(path)) |cached_content| return cached_content;
}
const content = std.fs.cwd().readFile(path, &buffer) catch |err| {
return switch(err) {
error.FileNotFound => {
std.debug.print("File not found: {s}", .{path});
return "";
},
else => err
};
};
try self.page_cache.put(path, content);
return content;
}
};
pub fn main() !void {
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(general_purpose_allocator.deinit() == .ok);
const allocator = general_purpose_allocator.allocator();
var page_cache = std.StringHashMap([]const u8).init(allocator);
defer page_cache.deinit();
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
const host: []const u8 = if (args.len > 1) args[1] else "127.0.0.1";
const port: u16 = if (args.len > 2) try std.fmt.parseInt(u16, args[2], 10) else 3040;
var server: HttpServer = HttpServer.init(
allocator,
page_cache,
host,
port,
HttpServerOptions{ .use_cache = false },
);
defer server.deinit();
server.listen() catch |err| {
std.debug.print("{}\nExiting.\n", .{err});
return err;
};
}