Compare commits

..

No commits in common. "main" and "v0.1.0" have entirely different histories.
main ... v0.1.0

8 changed files with 80 additions and 48 deletions

View file

@ -28,7 +28,7 @@ _Zouter_ is an HTTP router library for Zig **zap** HTTP server. It's made to eas
## Versions ## Versions
Zouter 0.2.0 is made for zig 0.14.0 and tested with zap 0.10.1. Zouter 0.1.0 is made for zig 0.13.0 and tested with zap 0.8.0.
## How to use ## How to use
@ -37,7 +37,7 @@ Zouter 0.2.0 is made for zig 0.14.0 and tested with zap 0.10.1.
In your project directory: In your project directory:
```shell ```shell
$ zig fetch --save https://code.zeptotech.net/zedd/zouter/archive/v0.2.0.tar.gz $ zig fetch --save https://code.zeptotech.net/zedd/zouter/archive/v0.1.0.tar.gz
``` ```
In `build.zig`: In `build.zig`:

View file

@ -1,7 +1,15 @@
const std = @import("std"); const std = @import("std");
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
// Load zap dependency. // Load zap dependency.
@ -11,32 +19,54 @@ pub fn build(b: *std.Build) void {
.openssl = false, .openssl = false,
}); });
const lib_mod = b.addModule("zouter", .{ const lib = b.addSharedLibrary(.{
.name = "zouter",
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
b.installArtifact(lib);
// Add zouter module.
const zouter_module = b.addModule("zouter", .{
.root_source_file = b.path("src/root.zig"), .root_source_file = b.path("src/root.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
// Add zap dependency. // Add zap dependency.
lib_mod.addImport("zap", zap.module("zap")); lib.root_module.addImport("zap", zap.module("zap"));
zouter_module.addImport("zap", zap.module("zap"));
// Add unit tests. // Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{ const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("tests/root.zig"), .root_source_file = b.path("tests/root.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
// Add zap dependency.
lib_unit_tests.root_module.addImport("zap", zap.module("zap")); lib_unit_tests.root_module.addImport("zap", zap.module("zap"));
lib_unit_tests.root_module.addImport("zouter", lib_mod); // Add zouter dependency.
lib_unit_tests.root_module.addImport("zouter", zouter_module);
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests."); const test_step = b.step("test", "Run unit tests.");
test_step.dependOn(&run_lib_unit_tests.step); test_step.dependOn(&run_lib_unit_tests.step);
// Documentation generation. // Documentation generation.
const install_docs = b.addInstallDirectory(.{ const install_docs = b.addInstallDirectory(.{
.source_dir = lib_unit_tests.getEmittedDocs(), .source_dir = lib.getEmittedDocs(),
.install_dir = .prefix, .install_dir = .prefix,
.install_subdir = "docs", .install_subdir = "docs",
}); });

View file

@ -1,14 +1,12 @@
.{ .{
.name = .zouter, .name = "zouter",
.version = "0.2.0", .version = "0.1.0",
.minimum_zig_version = "0.14.0", .minimum_zig_version = "0.13.0",
.fingerprint = 0xa981704810c376a0,
.dependencies = .{ .dependencies = .{
.zap = .{ .zap = .{
.url = "git+https://github.com/zigzap/zap?ref=v0.10.1#8d187310c7ee4f86faa7ef0ecdac0bf747216dea", .url = "https://github.com/zigzap/zap/archive/v0.8.0.tar.gz",
.hash = "zap-0.9.1-GoeB84M8JACjZKDNq2LA5hB24Z-ZrZ_HUKRXd8qxL2JW", .hash = "12209936c3333b53b53edcf453b1670babb9ae8c2197b1ca627c01e72670e20c1a21",
}, },
}, },
@ -16,7 +14,5 @@
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",
"src", "src",
"README.md",
"LICENSE",
}, },
} }

View file

@ -1,3 +1,4 @@
const std = @import("std");
const router = @import("router.zig"); const router = @import("router.zig");
const route = @import("route.zig"); const route = @import("route.zig");

View file

@ -28,16 +28,8 @@ pub const RoutingResult = struct {
// Move bytes from the end to the beginning of the buffer. // Move bytes from the end to the beginning of the buffer.
std.mem.copyForwards(u8, buffer[0..(decodedValue.len)], buffer[(buffer.len - decodedValue.len)..]); std.mem.copyForwards(u8, buffer[0..(decodedValue.len)], buffer[(buffer.len - decodedValue.len)..]);
// Resize the buffer to free remaining bytes. // Resize the buffer to free remaining bytes.
if (self.allocator.resize(buffer, decodedValue.len)) _ = self.allocator.resize(buffer, decodedValue.len);
{ // The buffer could have been resized, change variable length.
buffer = buffer[0..decodedValue.len]; buffer = buffer[0..decodedValue.len];
}
else
{ // Could not resize the buffer, allocate a new one and free the old one.
const originalBuffer = buffer;
defer self.allocator.free(originalBuffer);
buffer = try self.allocator.dupe(u8, originalBuffer[0..decodedValue.len]);
}
// Add value to params. // Add value to params.
try self.params.put(try self.allocator.dupe(u8, key), buffer); try self.params.put(try self.allocator.dupe(u8, key), buffer);
@ -50,6 +42,18 @@ pub const RoutingResult = struct {
try self.params.put(try self.allocator.dupe(u8, key), try self.allocator.dupe(u8, value)); try self.params.put(try self.allocator.dupe(u8, key), try self.allocator.dupe(u8, value));
} }
/// Clone the given result to a new result pointer.
pub fn clone(self: *Self) !*Self
{
const cloned = try Self.init(self.allocator, self.route, self.handler);
cloned.params = try self.params.clone();
cloned.notFoundHandler = self.notFoundHandler;
cloned.preHandlers = try self.preHandlers.clone();
cloned.postHandlers = try self.postHandlers.clone();
cloned.errorHandlers = try self.errorHandlers.clone();
return cloned;
}
/// Initialize a routing result. /// Initialize a routing result.
pub fn init(allocator: std.mem.Allocator, route: *RouteNode, handler: router.RouteHandler) !*Self pub fn init(allocator: std.mem.Allocator, route: *RouteNode, handler: router.RouteHandler) !*Self
{ {
@ -191,8 +195,6 @@ pub const RouteNode = struct {
.handle = definition.handle, .handle = definition.handle,
.handleNotFound = definition.handleNotFound, .handleNotFound = definition.handleNotFound,
.handleError = definition.handleError, .handleError = definition.handleError,
.preHandle = definition.preHandle,
.postHandle = definition.postHandle,
}); });
} }
else else
@ -201,8 +203,6 @@ pub const RouteNode = struct {
childTree.handle = definition.handle; childTree.handle = definition.handle;
childTree.handleNotFound = definition.handleNotFound; childTree.handleNotFound = definition.handleNotFound;
childTree.handleError = definition.handleError; childTree.handleError = definition.handleError;
childTree.preHandle = definition.preHandle;
childTree.postHandle = definition.postHandle;
if (definition.children) |children| if (definition.children) |children|
{ // If there are children, recursively parse them. { // If there are children, recursively parse them.
@ -215,17 +215,17 @@ pub const RouteNode = struct {
} }
/// Get request handler depending on the request method. /// Get request handler depending on the request method.
pub fn getMethodHandler(self: Self, requestMethod: zap.http.Method) ?router.RouteHandler pub fn getMethodHandler(self: Self, requestMethod: zap.Method) ?router.RouteHandler
{ {
if (self.handle) |handle| if (self.handle) |handle|
{ // A handle object is defined, getting the right handler from it. { // A handle object is defined, getting the right handler from it.
return switch (requestMethod) return switch (requestMethod)
{ // Return the defined request handler from the request method. { // Return the defined request handler from the request method.
zap.http.Method.GET => handle.get orelse handle.any, zap.Method.GET => handle.get orelse handle.any,
zap.http.Method.POST => handle.post orelse handle.any, zap.Method.POST => handle.post orelse handle.any,
zap.http.Method.PATCH => handle.patch orelse handle.any, zap.Method.PATCH => handle.patch orelse handle.any,
zap.http.Method.PUT => handle.put orelse handle.any, zap.Method.PUT => handle.put orelse handle.any,
zap.http.Method.DELETE => handle.delete orelse handle.any, zap.Method.DELETE => handle.delete orelse handle.any,
else => handle.any, else => handle.any,
}; };
} }
@ -268,7 +268,7 @@ pub const RouteNode = struct {
/// Try to find a matching handler in the current route for the given path. /// Try to find a matching handler in the current route for the given path.
/// Return true when a route is matching the request correctly. /// Return true when a route is matching the request correctly.
pub fn match(self: *Self, requestMethod: zap.http.Method, path: *std.mem.SplitIterator(u8, std.mem.DelimiterType.scalar), result: *RoutingResult) !bool pub fn match(self: *Self, requestMethod: zap.Method, path: *std.mem.SplitIterator(u8, std.mem.DelimiterType.scalar), result: *RoutingResult) !bool
{ {
// Add pre, post, error and not found handlers, if defined. // Add pre, post, error and not found handlers, if defined.
try self.addHandlers(result); try self.addHandlers(result);

View file

@ -99,7 +99,7 @@ pub const Router = struct {
var errorHandlersIterator = std.mem.reverseIterator(routingResult.errorHandlers.items); var errorHandlersIterator = std.mem.reverseIterator(routingResult.errorHandlers.items);
while (errorHandlersIterator.next()) |errorHandler| while (errorHandlersIterator.next()) |errorHandler|
{ // For each error handler, try to run it with the given error. { // For each error handler, try to run it with the given error.
errorHandler(MatchedRoute{ errorHandler(.{
.route = routingResult.route, .route = routingResult.route,
.params = routingResult.params, .params = routingResult.params,
}, request, err) catch { }, request, err) catch {
@ -121,12 +121,12 @@ pub const Router = struct {
self.root.handleError.?(.{}, request, err) catch {}; self.root.handleError.?(.{}, request, err) catch {};
return; return;
}; };
defer routingResult.deinit();
// Matching the requested route. Put the result in routingResult pointer. // Matching the requested route. Put the result in routingResult pointer.
_ = self.root.match(request.methodAsEnum(), &path, routingResult) catch |err| { _ = self.root.match(request.methodAsEnum(), &path, routingResult) catch |err| {
Self.handleError(request, err, routingResult); Self.handleError(request, err, routingResult);
return; return;
}; };
defer routingResult.deinit();
// Try to run matched route handling. // Try to run matched route handling.
Self.runMatchedRouteHandling(routingResult, request) Self.runMatchedRouteHandling(routingResult, request)
@ -138,7 +138,7 @@ pub const Router = struct {
fn runMatchedRouteHandling(routingResult: *routeManager.RoutingResult, request: zap.Request) !void fn runMatchedRouteHandling(routingResult: *routeManager.RoutingResult, request: zap.Request) !void
{ {
// Initialized route data passed to handlers from the routing result. // Initialized route data passed to handlers from the routing result.
const routeData = MatchedRoute{ const routeData = .{
.route = routingResult.route, .route = routingResult.route,
.params = routingResult.params, .params = routingResult.params,
}; };
@ -165,7 +165,7 @@ pub const Router = struct {
} }
/// The on_request function of the HTTP listener. /// The on_request function of the HTTP listener.
pub fn onRequest(request: zap.Request) anyerror!void pub fn onRequest(request: zap.Request) void
{ {
// Call handle of the current router instance. // Call handle of the current router instance.
routerInstance.handle(request); routerInstance.handle(request);
@ -182,7 +182,7 @@ fn impossible(_: MatchedRoute, _: zap.Request) !void
fn defaultNotFoundHandler(_: MatchedRoute, request: zap.Request) !void fn defaultNotFoundHandler(_: MatchedRoute, request: zap.Request) !void
{ {
try request.setContentType(zap.ContentType.TEXT); try request.setContentType(zap.ContentType.TEXT);
request.setStatus(zap.http.StatusCode.not_found); request.setStatus(zap.StatusCode.not_found);
try request.sendBody("404: Not Found"); try request.sendBody("404: Not Found");
} }
@ -190,6 +190,6 @@ fn defaultNotFoundHandler(_: MatchedRoute, request: zap.Request) !void
fn defaultErrorHandler(_: MatchedRoute, request: zap.Request, _: anyerror) !void fn defaultErrorHandler(_: MatchedRoute, request: zap.Request, _: anyerror) !void
{ {
try request.setContentType(zap.ContentType.TEXT); try request.setContentType(zap.ContentType.TEXT);
request.setStatus(zap.http.StatusCode.internal_server_error); request.setStatus(zap.StatusCode.internal_server_error);
try request.sendBody("500: Internal Server Error"); try request.sendBody("500: Internal Server Error");
} }

View file

@ -1,5 +1,9 @@
const std = @import("std"); const std = @import("std");
const zouter = @import("zouter"); const zouter = @import("../src/root.zig");
test {
// try std.testing.refAllDecls(zouter);
}
comptime { comptime {
_ = @import("example.zig"); _ = @import("example.zig");

View file

@ -127,10 +127,11 @@ fn runHttp() !void {
var listener = zap.HttpListener.init(.{ var listener = zap.HttpListener.init(.{
.interface = "127.0.0.1", .interface = "127.0.0.1",
.port = 8112, .port = 8112,
.log = true, .log = false,
// Add zouter to the listener. // Add zouter to the listener.
.on_request = zouter.Router.onRequest, .on_request = zouter.Router.onRequest,
}); });
zap.enableDebugLog();
try listener.listen(); try listener.listen();
const notFoundThread = try makeRequestThread(allocator, std.http.Method.GET, "http://127.0.0.1:8112/notfound/query", &notFoundResponse); const notFoundThread = try makeRequestThread(allocator, std.http.Method.GET, "http://127.0.0.1:8112/notfound/query", &notFoundResponse);