mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 05:56:07 +00:00
137 lines
3.9 KiB
Zig
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;
|
|
};
|
|
}
|