Compare commits
2 commits
717e545e34
...
a2426dee1c
Author | SHA1 | Date | |
---|---|---|---|
a2426dee1c | |||
a95a61d005 |
3 changed files with 319 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
out
|
||||
|
||||
.genorg-cache
|
18
genserve/build.zig
Normal file
18
genserve/build.zig
Normal file
|
@ -0,0 +1,18 @@
|
|||
const std = @import("std");
|
||||
|
||||
// zig fetch --save git+https://github.com/karlseguin/http.zig#master
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "genserve",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
const httpz = b.dependency("httpz", .{
|
||||
.target = b.graph.host,
|
||||
// .optimize = .standardOptimizeOption
|
||||
});
|
||||
|
||||
exe.root_module.addImport("httpz", httpz.module("httpz"));
|
||||
b.installArtifact(exe);
|
||||
}
|
297
genserve/src/main.zig
Normal file
297
genserve/src/main.zig
Normal file
|
@ -0,0 +1,297 @@
|
|||
const std = @import("std");
|
||||
const httpz = @import("httpz");
|
||||
const expect = std.testing.expect;
|
||||
const log = std.log.info;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
|
||||
const IdStruct = struct {
|
||||
blog_file: []const u8,
|
||||
landing: []const u8,
|
||||
blog_dir: []const u8,
|
||||
main_rss_url: []const u8,
|
||||
rss_dir: []const u8,
|
||||
main_rss: []const u8,
|
||||
|
||||
files: []struct {
|
||||
art: []const u8,
|
||||
cat: []const u8,
|
||||
file: []const u8,
|
||||
},
|
||||
|
||||
cpath: []struct {
|
||||
file: []const u8,
|
||||
rss: []const u8,
|
||||
cat: []const u8
|
||||
},
|
||||
};
|
||||
|
||||
pub var fo_flags: std.fs.File.OpenFlags = .{.mode = .read_only, .lock = .shared};
|
||||
pub var cat_path_hash: std.StringHashMap(std.fs.File) = undefined;
|
||||
pub var cat_rss_hash: std.StringHashMap(std.fs.File) = undefined;
|
||||
pub var url_path_hash: std.StringHashMap(std.StringHashMap(std.fs.File)) = undefined;
|
||||
pub var cwd: std.fs.Dir = undefined;
|
||||
pub const max_buf_size = 1000000000;
|
||||
pub var blog_file_h: std.fs.File = undefined;
|
||||
pub var main_rss_h: std.fs.File = undefined;
|
||||
pub var land_h: std.fs.File = undefined;
|
||||
pub var server: httpz.Server(void) = undefined;
|
||||
pub var server_on: bool = true;
|
||||
|
||||
fn interrupt(_ : i32) callconv(.C) void {
|
||||
server.stop();
|
||||
server_on = false;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
|
||||
var sa: std.posix.Sigaction = .{
|
||||
.handler = .{ .handler = interrupt },
|
||||
.mask = std.posix.empty_sigset,
|
||||
.flags = std.posix.SA.RESTART,
|
||||
};
|
||||
|
||||
std.posix.sigaction(std.posix.SIG.INT, &sa, null);
|
||||
|
||||
cwd = std.fs.cwd();
|
||||
defer { cwd.close(); }
|
||||
var json_file: []const u8 = ".genorg.json";
|
||||
|
||||
var args = std.process.args();
|
||||
defer { args.deinit(); }
|
||||
_ = args.skip();
|
||||
if (args.next()) |val| {
|
||||
cwd = cwd.openDir(val, .{}) catch {
|
||||
log("Could not find directory {s}", .{val});
|
||||
return;
|
||||
};
|
||||
if (args.next()) |v| {
|
||||
json_file = v;
|
||||
}
|
||||
}
|
||||
|
||||
log("Using file {s}", .{json_file});
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alctr = gpa.allocator();
|
||||
|
||||
defer {
|
||||
const deinit_status = gpa.deinit();
|
||||
if (deinit_status == .leak) expect(false) catch @panic("TEST FAIL");
|
||||
}
|
||||
|
||||
const file =
|
||||
cwd.openFile(json_file, fo_flags) catch {
|
||||
log("Could not find file {s}", .{json_file});
|
||||
return;
|
||||
};
|
||||
defer file.close();
|
||||
var reader = std.json.reader(alctr, file.reader());
|
||||
defer reader.deinit();
|
||||
|
||||
const dom =
|
||||
try std.json.parseFromTokenSource(IdStruct, alctr, &reader, .{});
|
||||
defer dom.deinit();
|
||||
|
||||
log("{s}", .{dom.value.blog_file});
|
||||
|
||||
// We use arena allocator to help mitigate memory fragmentation
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
const alctr2 = arena.allocator();
|
||||
|
||||
url_path_hash = std.StringHashMap(std.StringHashMap(std.fs.File)).init(alctr2);
|
||||
defer {
|
||||
var it = url_path_hash.valueIterator();
|
||||
while (it.next()) |key| {
|
||||
var it2 = key.valueIterator();
|
||||
while (it2.next()) |val| {
|
||||
val.close();
|
||||
}
|
||||
key.deinit();
|
||||
}
|
||||
url_path_hash.deinit();
|
||||
}
|
||||
|
||||
for (dom.value.files) |fc| {
|
||||
const res = url_path_hash.getPtr(fc.cat);
|
||||
log("[{s}]->[{s}] = {s}", .{fc.cat,fc.art,fc.file});
|
||||
const fh = try cwd.openFile(fc.file, fo_flags);
|
||||
if (res) |p| {
|
||||
try p.*.put(fc.art, fh);
|
||||
} else {
|
||||
var newmap = std.StringHashMap(std.fs.File).init(alctr2);
|
||||
try newmap.put(fc.art, fh);
|
||||
try url_path_hash.put(fc.cat, newmap);
|
||||
}
|
||||
}
|
||||
|
||||
cat_path_hash = std.StringHashMap(std.fs.File).init(alctr2);
|
||||
cat_rss_hash = std.StringHashMap(std.fs.File).init(alctr2);
|
||||
defer cat_path_hash.deinit();
|
||||
for (dom.value.cpath) |s| {
|
||||
const fh = try cwd.openFile(s.file, fo_flags);
|
||||
log("File {s}", .{s.file});
|
||||
try cat_path_hash.put(s.cat, fh);
|
||||
const rfh = try cwd.openFile(s.rss, fo_flags);
|
||||
try cat_rss_hash.put(s.cat, rfh);
|
||||
}
|
||||
|
||||
defer {
|
||||
var it = cat_path_hash.valueIterator();
|
||||
while (it.next()) |val| {
|
||||
val.close();
|
||||
}
|
||||
it = cat_rss_hash.valueIterator();
|
||||
while (it.next()) |val| {
|
||||
val.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have done all the json stuff, we need to init web server now.
|
||||
|
||||
server = try httpz.Server(void).init(alctr, .{.port = 9669}, {});
|
||||
defer {
|
||||
if (server_on) {
|
||||
server.stop();
|
||||
}
|
||||
server.deinit();
|
||||
}
|
||||
|
||||
var router = try server.router(.{});
|
||||
|
||||
// We open the main file and the blog file
|
||||
blog_file_h = try cwd.openFile(dom.value.blog_file, fo_flags);
|
||||
defer blog_file_h.close();
|
||||
|
||||
main_rss_h = try cwd.openFile(dom.value.main_rss, fo_flags);
|
||||
defer main_rss_h.close();
|
||||
|
||||
log("Landing file set to {s}", .{dom.value.landing});
|
||||
land_h = try cwd.openFile(dom.value.landing, fo_flags);
|
||||
defer land_h.close();
|
||||
|
||||
// This is a speical value, we make this sepcial in perl file
|
||||
log("rss_dir is {s}", .{dom.value.rss_dir});
|
||||
log("main rss url is {s}", .{dom.value.main_rss_url});
|
||||
|
||||
router.get(dom.value.main_rss_url,
|
||||
struct {fn f(_: *httpz.Request, res: *httpz.Response) !void {
|
||||
res.body = try main_rss_h.readToEndAlloc(res.arena, max_buf_size);
|
||||
try main_rss_h.seekTo(0);
|
||||
}}.f
|
||||
,.{});
|
||||
|
||||
router.get(dom.value.rss_dir,
|
||||
struct {fn f(req: *httpz.Request, res: *httpz.Response) !void {
|
||||
const fh = cat_rss_hash.get(req.param("cat").?);
|
||||
if (fh) |ff| {
|
||||
res.body = try ff.readToEndAlloc(res.arena, max_buf_size);
|
||||
try ff.seekTo(0);
|
||||
} else {
|
||||
res.body = "Not found";
|
||||
}
|
||||
}}.f
|
||||
, .{});
|
||||
|
||||
router.get("/", struct {fn f(_: *httpz.Request, res: *httpz.Response) !void {
|
||||
res.body = try land_h.readToEndAlloc(res.arena, max_buf_size);
|
||||
try land_h.seekTo(0);
|
||||
}}.f, .{});
|
||||
|
||||
var blog_routes = router.group(dom.value.blog_dir, .{});
|
||||
blog_routes.get("/", struct {fn f(_: *httpz.Request, res: *httpz.Response) !void {
|
||||
res.body = try blog_file_h.readToEndAlloc(res.arena, max_buf_size);
|
||||
try blog_file_h.seekTo(0);
|
||||
}}.f, .{});
|
||||
blog_routes.get("/:cc", getCat, .{});
|
||||
blog_routes.get("/:cat/:id", getArt, .{});
|
||||
|
||||
router.all("/*", getFile, .{});
|
||||
|
||||
log("Starting http server", .{});
|
||||
try server.listen();
|
||||
log("Shutting down", .{});
|
||||
}
|
||||
|
||||
fn char_to_correct_number (c: u8) ?u8 {
|
||||
if (c > 'F') {
|
||||
return null;
|
||||
}
|
||||
if (c > 58) {
|
||||
return c - 55;
|
||||
} else {
|
||||
return c - 48;
|
||||
}
|
||||
}
|
||||
|
||||
fn getFile (req: *httpz.Request, res: *httpz.Response) !void {
|
||||
const strlen = req.url.path.len - 1;
|
||||
if (strlen <= 1) {
|
||||
return;
|
||||
}
|
||||
const path = req.url.path[1..];
|
||||
var ci: usize = 0;
|
||||
if (path[0] == req.url.path[0]) {
|
||||
res.body = "Nice try, not tolerating it";
|
||||
return;
|
||||
}
|
||||
var output = try res.arena.alloc(u8, strlen);
|
||||
{
|
||||
var pi: usize = 0;
|
||||
while (pi < strlen) : (ci += 1) {
|
||||
const char = path[pi];
|
||||
if (char == '%') {
|
||||
// We convert the hex to decimal
|
||||
pi += 1;
|
||||
const a = char_to_correct_number(path[pi]) orelse {
|
||||
res.body = "Nice try";
|
||||
return;
|
||||
};
|
||||
pi += 1;
|
||||
const b = char_to_correct_number(path[pi]) orelse {
|
||||
res.body = "Nice try";
|
||||
return;
|
||||
};
|
||||
output[ci] = (a * 16) + b;
|
||||
pi += 1;
|
||||
} else {
|
||||
output[ci] = char;
|
||||
pi += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
var file = cwd.openFile(output[0..ci], fo_flags) catch {
|
||||
res.body = "Not found";
|
||||
return;
|
||||
};
|
||||
defer file.close();
|
||||
res.body = try file.readToEndAlloc(res.arena, max_buf_size);
|
||||
}
|
||||
|
||||
fn getCat(req: *httpz.Request, res: *httpz.Response) !void {
|
||||
const fh = cat_path_hash.get(req.param("cc").?);
|
||||
if (fh) |f| {
|
||||
res.body = try f.readToEndAlloc(res.arena, max_buf_size);
|
||||
try f.seekTo(0);
|
||||
} else {
|
||||
res.body = "Not found";
|
||||
}
|
||||
}
|
||||
|
||||
fn getArt(req: *httpz.Request, res: *httpz.Response) !void {
|
||||
res.status = 200;
|
||||
const chash = url_path_hash.get(req.param("cat").?);
|
||||
if (chash) |ch| {
|
||||
const ahash = ch.get(req.param("id").?);
|
||||
if (ahash) |fh| {
|
||||
// This means we show the article
|
||||
res.body = try fh.readToEndAlloc(res.arena, max_buf_size);
|
||||
try fh.seekTo(0);
|
||||
} else {
|
||||
res.body = "Not found";
|
||||
}
|
||||
} else {
|
||||
res.body = "Not found";
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue