aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/http/Response.zig35
-rw-r--r--src/http/Route.zig4
-rw-r--r--src/http/RouteSet.zig2
-rw-r--r--src/http/Server.zig22
-rw-r--r--src/http/handler/Interface.zig44
-rw-r--r--src/http/handler/context.zig (renamed from src/http/handler.zig)87
-rw-r--r--src/http/handler/root.zig35
-rw-r--r--src/http/handler/static.zig13
-rw-r--r--src/http/root.zig2
-rw-r--r--src/main.zig14
10 files changed, 163 insertions, 95 deletions
diff --git a/src/http/Response.zig b/src/http/Response.zig
index a5f7727..8d10992 100644
--- a/src/http/Response.zig
+++ b/src/http/Response.zig
@@ -3,14 +3,14 @@ const std = @import("std");
pub const Message = union(enum) {
void: void,
static: []const u8,
- file_content: std.Io.File,
+ streaming: std.Io.File,
pub fn string(content: []const u8) Message {
return .{ .static = content };
}
pub fn file(io: std.Io, path: []const u8) !Message {
- return .{ .file_content = try std.Io.Dir.cwd().openFile(io, path, .{
+ return .{ .streaming = try std.Io.Dir.cwd().openFile(io, path, .{
.allow_directory = false,
}) };
}
@@ -26,3 +26,34 @@ pub fn with(status: std.http.Status, message: Message) @This() {
pub fn ok(message: Message) @This() {
return with(.ok, message);
}
+
+pub fn send(
+ self: *const @This(),
+ io: std.Io,
+ request: *std.http.Server.Request,
+) !void {
+ switch (self.message) {
+ .void => {
+ try request.respond("", .{ .status = self.status });
+ },
+ .static => |content| {
+ try request.respond(content, .{ .status = self.status });
+ },
+ .streaming => |file| {
+ var write_buffer: [1024]u8 = undefined;
+ var read_buffer: [1024]u8 = undefined;
+
+ var body_writer = try request.respondStreaming(&write_buffer, .{
+ .respond_options = .{
+ .status = self.status,
+ .transfer_encoding = .chunked,
+ },
+ });
+
+ var file_reader = file.reader(io, &read_buffer);
+
+ _ = try file_reader.interface.streamRemaining(&body_writer.writer);
+ try body_writer.end();
+ },
+ }
+}
diff --git a/src/http/Route.zig b/src/http/Route.zig
index f979d04..7dd8ec0 100644
--- a/src/http/Route.zig
+++ b/src/http/Route.zig
@@ -19,6 +19,10 @@ pub const index: @This() = .{ .segments = &.{} };
pub fn fromAny(comptime route: anytype) @This() {
var segments: []const Segment = &.{};
+ if (route == .index) {
+ return index;
+ }
+
inline for (route) |r| {
const segment: []const Segment = &.{switch (@TypeOf(r)) {
@EnumLiteral() => .{ .identifier = @tagName(r) },
diff --git a/src/http/RouteSet.zig b/src/http/RouteSet.zig
index 623a2a9..de8b3c2 100644
--- a/src/http/RouteSet.zig
+++ b/src/http/RouteSet.zig
@@ -1,7 +1,7 @@
const std = @import("std");
const Route = @import("Route.zig");
-const handler = @import("handler.zig");
+const handler = @import("handler/root.zig");
const Interface = handler.Interface;
const Context = handler.Context;
diff --git a/src/http/Server.zig b/src/http/Server.zig
index 71037bb..955b8cc 100644
--- a/src/http/Server.zig
+++ b/src/http/Server.zig
@@ -1,23 +1,28 @@
const std = @import("std");
+const RouteSet = @import("RouteSet.zig");
+
const Self = @This();
server: std.Io.net.Server,
threaded: std.Io.Threaded,
+routes: RouteSet,
pub fn init(
gpa: std.mem.Allocator,
address: std.Io.net.IpAddress,
+ comptime routes: RouteSet,
) !Self {
var threaded: std.Io.Threaded = .init(gpa, .{});
return .{
.threaded = threaded,
.server = try address.listen(threaded.io(), .{ .reuse_address = true }),
+ .routes = routes,
};
}
-pub fn serve(self: *Self, content: []const u8) !void {
+pub fn serve(self: *Self, gpa: std.mem.Allocator) !void {
var read_buffer: [1024 * 8]u8 = undefined;
var write_buffer: [1024 * 8]u8 = undefined;
@@ -36,15 +41,14 @@ pub fn serve(self: *Self, content: []const u8) !void {
std.debug.print("{s} {s}\n", .{ @tagName(request.head.method), request.head.target });
- if (std.mem.eql(u8, request.head.target, "/")) {
- request.respond(content, .{
- .status = .ok,
- .extra_headers = &.{
- .{ .name = "Content-Type", .value = "text/html" },
- },
- }) catch continue;
+ var arena: std.heap.ArenaAllocator = .init(gpa);
+ defer arena.deinit();
+
+ if (self.routes.getInterface(request.head.target)) |interface| {
+ const message = interface.handle(gpa, arena.allocator(), self.threaded.io(), &request);
+ message.send(self.threaded.io(), &request) catch continue;
} else {
- request.respond("Not Found", .{ .status = .not_found }) catch continue;
+ request.respond("", .{ .status = .not_found }) catch continue;
}
}
}
diff --git a/src/http/handler/Interface.zig b/src/http/handler/Interface.zig
new file mode 100644
index 0000000..2238424
--- /dev/null
+++ b/src/http/handler/Interface.zig
@@ -0,0 +1,44 @@
+const std = @import("std");
+const Response = @import("../Response.zig");
+const Route = @import("../Route.zig");
+
+route: Route,
+base: *const anyopaque,
+handler: *const fn (
+ *const anyopaque,
+ std.mem.Allocator,
+ std.mem.Allocator,
+ std.Io,
+ *std.http.Server.Request,
+) Response,
+
+pub fn from(instance: anytype, route: Route) @This() {
+ const PtrT = @TypeOf(instance);
+ const func = struct {
+ pub fn handle(
+ base: *const anyopaque,
+ gpa: std.mem.Allocator,
+ arena: std.mem.Allocator,
+ io: std.Io,
+ request: *std.http.Server.Request,
+ ) Response {
+ return @as(PtrT, @ptrCast(@alignCast(base))).handle(gpa, arena, io, request);
+ }
+ }.handle;
+
+ return .{
+ .base = @ptrCast(instance),
+ .handler = func,
+ .route = route,
+ };
+}
+
+pub fn handle(
+ self: *const @This(),
+ gpa: std.mem.Allocator,
+ arena: std.mem.Allocator,
+ io: std.Io,
+ request: *std.http.Server.Request,
+) Response {
+ return self.handler(self.base, gpa, arena, io, request);
+}
diff --git a/src/http/handler.zig b/src/http/handler/context.zig
index 2220fd0..4e55446 100644
--- a/src/http/handler.zig
+++ b/src/http/handler/context.zig
@@ -1,15 +1,15 @@
const std = @import("std");
-
-const Response = @import("Response.zig");
-const Route = @import("Route.zig");
-const Parameter = @import("Parameter.zig");
+const Response = @import("../Response.zig");
+const Route = @import("../Route.zig");
+const Parameter = @import("../Parameter.zig");
+const Interface = @import("Interface.zig");
pub fn Context(comptime r: anytype) type {
return struct {
- const route = Route.fromAny(r);
- const Params = Parameter.Struct(route);
+ pub const route = Route.fromAny(r);
+ pub const Params = Parameter.Struct(route);
- const Request = struct {
+ pub const Request = struct {
base_request: *std.http.Server.Request,
params: Params,
arena: std.mem.Allocator,
@@ -74,76 +74,3 @@ pub fn Context(comptime r: anytype) type {
}
};
}
-
-pub const Interface = struct {
- route: Route,
- base: *const anyopaque,
- handler: *const fn (
- *const anyopaque,
- std.mem.Allocator,
- std.mem.Allocator,
- std.Io,
- *std.http.Server.Request,
- ) Response,
-
- pub fn from(instance: anytype, route: Route) @This() {
- const PtrT = @TypeOf(instance);
- const func = struct {
- pub fn handle(
- base: *const anyopaque,
- gpa: std.mem.Allocator,
- arena: std.mem.Allocator,
- io: std.Io,
- request: *std.http.Server.Request,
- ) Response {
- return @as(PtrT, @ptrCast(@alignCast(base))).handle(gpa, arena, io, request);
- }
- }.handle;
-
- return .{
- .base = @ptrCast(instance),
- .handler = func,
- .route = route,
- };
- }
-
- pub fn handle(
- self: *const @This(),
- gpa: std.mem.Allocator,
- arena: std.mem.Allocator,
- io: std.Io,
- request: std.http.Server.Request,
- ) Response {
- return self.handler(self.base, gpa, arena, io, request);
- }
-};
-
-test "interface" {
- const M = struct {
- const C = Context(.{ "api", "images", .id, "metadata" });
- const context: C = .{
- .post = post,
- .get = get,
- };
-
- const Query = struct {
- condition: []const u8,
- start: usize,
- length: usize,
- };
-
- fn post(request: C.Request) Response {
- const query = request.json(Query) catch return .with(.bad_request, .void);
- _ = query;
- _ = request.params.id;
-
- return .ok(.string(request.params.id.as([]const u8)));
- }
-
- fn get(request: C.Request) Response {
- return .ok(Response.Message.file(request.io, "./some/path.txt") catch return .with(.not_found, .void));
- }
- };
-
- _ = M.context.interface();
-}
diff --git a/src/http/handler/root.zig b/src/http/handler/root.zig
new file mode 100644
index 0000000..33fdb84
--- /dev/null
+++ b/src/http/handler/root.zig
@@ -0,0 +1,35 @@
+pub const Context = @import("context.zig").Context;
+pub const Interface = @import("Interface.zig");
+pub const Static = @import("static.zig").Static;
+
+test "interface" {
+ const M = struct {
+ const Response = @import("../Response.zig");
+
+ const C = Context(.{ "api", "images", .id, "metadata" });
+ const context: C = .{
+ .post = post,
+ .get = get,
+ };
+
+ const Query = struct {
+ condition: []const u8,
+ start: usize,
+ length: usize,
+ };
+
+ fn post(request: C.Request) Response {
+ const query = request.json(Query) catch return .with(.bad_request, .void);
+ _ = query;
+ _ = request.params.id;
+
+ return .ok(.string(request.params.id.as([]const u8)));
+ }
+
+ fn get(request: C.Request) Response {
+ return .ok(Response.Message.file(request.io, "./some/path.txt") catch return .with(.not_found, .void));
+ }
+ };
+
+ _ = M.context.interface();
+}
diff --git a/src/http/handler/static.zig b/src/http/handler/static.zig
new file mode 100644
index 0000000..d53f213
--- /dev/null
+++ b/src/http/handler/static.zig
@@ -0,0 +1,13 @@
+const Interface = @import("Interface.zig");
+const Context = @import("context.zig").Context;
+const Response = @import("../Response.zig");
+
+pub fn Static(route: anytype, content: []const u8) Context(route) {
+ const get = struct {
+ fn get(_: Context(route).Request) Response {
+ return .ok(.string(content));
+ }
+ }.get;
+
+ return .{ .get = get };
+}
diff --git a/src/http/root.zig b/src/http/root.zig
index 2fa4042..88d1420 100644
--- a/src/http/root.zig
+++ b/src/http/root.zig
@@ -3,7 +3,7 @@
const std = @import("std");
pub const Server = @import("Server.zig");
-pub const handler = @import("handler.zig");
+pub const handler = @import("handler/root.zig");
pub const Response = @import("Response.zig");
pub const Route = @import("Route.zig");
pub const RouteSet = @import("RouteSet.zig");
diff --git a/src/main.zig b/src/main.zig
index 4b2b025..6d2b9ce 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -8,6 +8,8 @@ const http = @import("http");
const html = @import("html");
const z = @import("z");
+const Static = http.handler.Static;
+
const components = @import("web/component/root.zig").components;
const document: html.Element = .native(.html, .{}, .children(&.{
@@ -26,9 +28,17 @@ const document: html.Element = .native(.html, .{}, .children(&.{
})),
}));
+const routes: http.RouteSet = .init(&.{
+ Static(.index, document.toDocument()).interface(),
+});
+
pub fn main(init: std.process.Init) !void {
- var server: http.Server = try .init(init.gpa, try .parseLiteral("0.0.0.0:8080"));
- try server.serve(comptime document.toDocument());
+ var server: http.Server = try .init(
+ init.gpa,
+ try .parseLiteral("0.0.0.0:8080"),
+ routes,
+ );
+ try server.serve(init.gpa);
}
test {