diff --git a/src/http.zig b/src/http.zig index 44fc7a7..2168efa 100644 --- a/src/http.zig +++ b/src/http.zig @@ -5,8 +5,9 @@ const ciutf8 = @import("./util.zig").ciutf8; const Reader = std.net.Stream.Reader; const Writer = std.net.Stream.Writer; -pub const Handler = fn (*Context) HttpError!void; -pub const HttpError = error{Http404}; +const Status = std.http.Status; +const Method = std.http.Method; +pub const Handler = fn (*Context) anyerror!void; const HeaderMap = std.HashMap([]const u8, []const u8, struct { pub fn eql(_: @This(), a: []const u8, b: []const u8) bool { @@ -33,17 +34,6 @@ fn handleInternalError(writer: Writer) !void { try writer.writeAll("HTTP/1.1 500 Internal Server Error"); } -pub const Method = enum { - GET, - //HEAD, - POST, - //PUT, - //DELETE, - //CONNECT, - //OPTIONS, - //TRACE, -}; - fn parseHttpMethod(reader: Reader) !Method { var buf: [8]u8 = undefined; const str = reader.readUntilDelimiter(&buf, ' ') catch |err| switch (err) { diff --git a/src/main.zig b/src/main.zig index cbde0e8..93a0dca 100644 --- a/src/main.zig +++ b/src/main.zig @@ -8,13 +8,13 @@ pub const routing = @import("./routing.zig"); pub const Uuid = util.Uuid; pub const ciutf8 = util.ciutf8; -pub const io_mode = .evented; +//pub const io_mode = .evented; -pub const router = routing.makeRouter(*http.Context, [_]routing.RouteFn(*http.Context){ +pub const router = routing.makeRouter(*http.Context, &[_]routing.RouteFn(*http.Context){ routing.makeRoute(.GET, "/", staticString("Index Page")), routing.makeRoute(.GET, "/abc", staticString("abc")), - routing.makeRoute(.GET, "/user/:id", getUser), - routing.makeRoute(.POST, "/note/", postNote), + //routing.makeRoute(.GET, "/user/:id", getUser), + //routing.makeRoute(.POST, "/note/", postNote), }); const this_scheme = "http"; @@ -58,9 +58,10 @@ fn getUser(ctx: *http.Context, args: struct { id: []const u8 }) anyerror!void { try writer.writeAll("}"); } -fn staticString(comptime str: []const u8) routing.RouteFn(http.Context) { +const DummyArgs = struct {}; +fn staticString(comptime str: []const u8) fn (*http.Context, DummyArgs) anyerror!void { return (struct { - fn func(ctx: *http.Context, _: struct {}) http.HttpError!void { + fn func(ctx: *http.Context, _: DummyArgs) anyerror!void { try ctx.response.headers.put("Content-Type", "text/plain"); try ctx.response.write(200, str); } @@ -80,8 +81,8 @@ pub fn main() anyerror!void { // todo: keep track of connections _ = async http.handleConnection(conn, struct { - fn func(ctx: *http.Context) http.HttpError!void { - //try router(ctx, ctx.method, ctx.path); + fn func(ctx: *http.Context) anyerror!void { + try router(ctx.request.method, ctx.request.path, ctx); _ = ctx; return; } @@ -91,4 +92,6 @@ pub fn main() anyerror!void { test { _ = http; + _ = util; + _ = routing; } diff --git a/src/routing.zig b/src/routing.zig index 74c2d2e..d370ff3 100644 --- a/src/routing.zig +++ b/src/routing.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const http = @import("./http.zig"); const ciutf8 = @import("./util.zig").ciutf8; const PathIter = struct { @@ -70,42 +69,39 @@ const RouteSegment = union(enum) { param: []const u8, }; -// convention: return http.HttpError IFF a situation you can't finish the request in happens. -// If status line/headers were written, always return void - pub fn RouteFn(comptime Context: type) type { - return fn (Context, http.Method, []const u8) http.HttpError!void; + return fn (Context, std.http.Method, []const u8) anyerror!void; } -/// `makeRoute` takes a route definition and a handler of the form `fn(, ) http.HttpError!void` +/// `makeRoute` takes a route definition and a handler of the form `fn(, ) anyerror!void` /// where `Params` is a struct containing one field of type `[]const u8` for each path parameter /// /// Arguments: /// method: The HTTP method to match /// path: The path spec to match against. Path segments beginning with a `:` will cause the rest of /// the segment to be treated as the name of a path parameter -/// handler: The code to execute on route match. This must be a function of form `fn(, ) http.HttpError!void` +/// handler: The code to execute on route match. This must be a function of form `fn(, ) anyerror!void` /// /// Implicit Arguments: -/// Context: the type of a user-supplied Context that is passed through the route. typically `http.Context` but +/// Context: the type of a user-supplied Context that is passed through the route. typically `std.http.Context` but /// made generic for ease of testing. There are no restrictions on this type /// Params: the type of a struct representing the path parameters expressed in ``. This must be /// a struct, with a one-one map between fields and path parameters. Each field must be of type /// `[]const u8` and it must have the same name as a single path parameter. /// /// Returns: -/// A new route function of type `fn(, http.Method, []const u8) http.HttpError!void`. When called, +/// A new route function of type `fn(, std.http.Method, []const u8) anyerror!void`. When called, /// this function will test the provided values against its specification. If they match, then /// this function will parse path parameters and will be called with the supplied /// context and params. If they do not match, this function will return error.Http404 /// /// Example: /// route(.GET, "/user/:id/followers", struct{ -/// fn getFollowers(ctx: http.Context, params: struct{ id: []const u8 } http.HttpError { ... } +/// fn getFollowers(ctx: std.http.Context, params: struct{ id: []const u8 } anyerror { ... } /// ).getFollowers) /// pub fn makeRoute( - comptime method: http.Method, + comptime method: std.http.Method, comptime path: []const u8, comptime handler: anytype, ) return_type: { @@ -114,7 +110,7 @@ pub fn makeRoute( break :return_type RouteFn(@typeInfo(@TypeOf(handler)).Fn.args[0].arg_type.?); } { const handler_args = @typeInfo(@TypeOf(handler)).Fn.args; - if (handler_args.len != 2) @compileError("handler function must have signature fn(Context, Params) http.HttpError"); + if (handler_args.len != 2) @compileError("handler function must have signature fn(Context, Params) anyerror"); if (@typeInfo(handler_args[1].arg_type.?) != .Struct) @compileError("Params in handler(Context, Params) must be struct"); const Context = handler_args[0].arg_type.?; @@ -142,7 +138,7 @@ pub fn makeRoute( } return struct { - fn func(ctx: Context, req_method: http.Method, req_path: []const u8) http.HttpError!void { + fn func(ctx: Context, req_method: std.http.Method, req_path: []const u8) anyerror!void { if (req_method != method) return error.Http404; var params: Params = undefined; @@ -168,7 +164,7 @@ pub fn makeRoute( } pub fn RouterFn(comptime Context: type) type { - return fn (http.Method, path: []const u8, Context) http.HttpError!void; + return fn (std.http.Method, path: []const u8, Context) anyerror!void; } pub fn makeRouter( @@ -176,7 +172,7 @@ pub fn makeRouter( comptime routes: []const RouteFn(Context), ) RouterFn(Context) { return struct { - fn dispatch(method: http.Method, path: []const u8, ctx: Context) http.HttpError!void { + fn dispatch(method: std.http.Method, path: []const u8, ctx: Context) anyerror!void { for (routes) |r| { return r(ctx, method, path) catch |err| switch (err) { error.Http404 => continue, @@ -302,7 +298,7 @@ const _tests = struct { fn dummyHandler(comptime Args: type) type { comptime { return struct { - fn func(_: TestContext, _: Args) http.HttpError!void {} + fn func(_: TestContext, _: Args) anyerror!void {} }; } }