Tags: zigzap/zap
Tags
__Rebased Zap's logging on Zig's std.log__ Zap's logging is now based on zig's `std.log`. You can set a custom log level just for Zap in your Zig projects like this: ```ts pub const std_options: std.Options = .{ // general log level .log_level = .info, .log_scope_levels = &[_]std.log.ScopeLevel{ // log level specific to zap .{ .scope = .zap, .level = .debug }, }, }; ``` Low-level access to facil.io's logging facilities is provided by `zap.Logging`.
**What's new in ZAP?** - Upgraded to Zig 0.14! - Breaking Changes for `zap.Endpoint` - New `zap.App`! - Updated README - Contributions! After a break, I'm about to work a lot more with Zap, and in preparation made a few improvements which might also work in favor of newcomers. BTW newcomers: please, also check out these other, pure-zig (which Zap is not) HTTP server projects: - [httpz](https://github.com/karlseguin/http.zig) : Pure Zig! Close to Zap's model. Performance = good! - [jetzig](https://github.com/jetzig-framework/jetzig) : Comfortably develop modern web applications quickly, using http.zig under the hood - [zzz](https://github.com/tardy-org/zzz) : Super promising, super-fast, especially for IO-heavy tasks, io_uring support - need I say more? I can't wait for the day that Zap becomes obsolete. It would be a very good sign for the Zig HTTP server space! **Breaking Changes for zap.Endpoint** These breaking changes are meant to be improvements. - no `@fieldParentPtr`: Endpoints now directly get their `@This()` pointer passed into their methods - request handlers are allowed to return errors! - the `.error_strategy` decides if errors are logged to console or reported as HTML to the client (for debugging in the browser) - no "Settings": - `path` and `error_strategy` are required for Endpoints - all http method handlers must be present, but of course may be empty - all of the above are checked at comptime, with meaningful compile error messages - you register your custom Endpoint instances directly with the `zap.Endpoint.Listener`, no need to provide an `.endpoint()` method. It's best illustrated by example of `error.zig` (of the updated `endpoints` example) which creates the `ErrorEndpoint`: ```zig //! //! An ErrorEndpoint //! const std = @import("std"); const zap = @import("zap"); /// A simple endpoint listening on the /error route that causes an error on GET /// requests, which gets logged to the response (=browser) by default pub const ErrorEndpoint = @this(); path: []const u8 = "/error", error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response, pub fn get(_: *ErrorEndpoint, _: zap.Request) !void { return error.@"Oh-no!"; } // unused: pub fn post(_: *ErrorEndpoint, _: zap.Request) !void {} pub fn put(_: *ErrorEndpoint, _: zap.Request) !void {} pub fn delete(_: *ErrorEndpoint, _: zap.Request) !void {} pub fn patch(_: *ErrorEndpoint, _: zap.Request) !void {} pub fn options(_: *ErrorEndpoint, _: zap.Request) !void {} ``` All relevant examples have been updated accordingly. **The New `zap.App`** In a way, `zap.App` takes the `zap.Endpoint` concept one step further: instead of having only per-endpoint instance data (fields of your Endpoint struct), endpoints in a `zap.App` easily share a global 'App Context'. In addition to the global App Context, all Endpoint request handlers also receive an arena allocator for easy, care-free allocations. There is one arena allocator per thread, and arenas are reset after each request. Just like regular / legacy `zap.Endpoints`, returning errors from request handlers is OK. It's decided on a per-endpoint basis how errors are dealt with, via the `ErrorStrategy` enum field. Here is a complete `zap.App` example: ```zig //! //! Part of the Zap examples. //! //! Build me with `zig build app_basic`. //! Run me with `zig build run-app_basic`. //! const std = @import("std"); const Allocator = std.mem.Allocator; const zap = @import("zap"); // The global Application Context const MyContext = struct { db_connection: []const u8, pub fn init(connection: []const u8) MyContext { return .{ .db_connection = connection, }; } }; // A very simple endpoint handling only GET requests const SimpleEndpoint = struct { // zap.App.Endpoint Interface part path: []const u8, error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response, // data specific for this endpoint some_data: []const u8, pub fn init(path: []const u8, data: []const u8) SimpleEndpoint { return .{ .path = path, .some_data = data, }; } // handle GET requests pub fn get(e: *SimpleEndpoint, arena: Allocator, context: *MyContext, r: zap.Request) !void { const thread_id = std.Thread.getCurrentId(); r.setStatus(.ok); // look, we use the arena allocator here -> no need to free the response_text later! // and we also just `try` it, not worrying about errors const response_text = try std.fmt.allocPrint( arena, \\Hello! \\context.db_connection: {s} \\endpoint.data: {s} \\arena: {} \\thread_id: {} \\ , .{ context.db_connection, e.some_data, arena.ptr, thread_id }, ); try r.sendBody(response_text); std.time.sleep(std.time.ns_per_ms * 300); } // empty stubs for all other request methods pub fn post(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn put(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn delete(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn patch(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn options(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} }; const StopEndpoint = struct { path: []const u8, error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response, pub fn get(_: *StopEndpoint, _: Allocator, context: *MyContext, _: zap.Request) !void { std.debug.print( \\Before I stop, let me dump the app context: \\db_connection='{s}' \\ \\ , .{context.*.db_connection}); zap.stop(); } pub fn post(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn put(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn delete(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn patch(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} pub fn options(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {} }; pub fn main() !void { // setup allocations var gpa: std.heap.GeneralPurposeAllocator(.{ // just to be explicit .thread_safe = true, }) = .{}; defer std.debug.print("\n\nLeaks detected: {}\n\n", .{gpa.deinit() != .ok}); const allocator = gpa.allocator(); // create an app context var my_context = MyContext.init("db connection established!"); // create an App instance const App = zap.App.Create(MyContext); var app = try App.init(allocator, &my_context, .{}); defer app.deinit(); // create the endpoints var my_endpoint = SimpleEndpoint.init("/test", "some endpoint specific data"); var stop_endpoint: StopEndpoint = .{ .path = "/stop" }; // // register the endpoints with the app try app.register(&my_endpoint); try app.register(&stop_endpoint); // listen on the network try app.listen(.{ .interface = "0.0.0.0", .port = 3000, }); std.debug.print("Listening on 0.0.0.0:3000\n", .{}); std.debug.print( \\ Try me via: \\ curl http://localhost:3000/test \\ Stop me via: \\ curl http://localhost:3000/stop \\ , .{}); // start worker threads -- only 1 process!!! zap.start(.{ .threads = 2, .workers = 1, }); } ``` **Updated README** - restructured the examples section a bit - got rid of all the microbenchmark stuff - shout-outs to great Zap alternatives (http.zig, Jetzig, zzz) **Contributions!** Special thanks to: - Victor Moin (vctrmn): Fix deprecated warning in facil.io #154 - Joshua B. (OsakiTsukiko): updated .gitignore, Endpoint improvements - Thom Dickson (cosmicboots): Add type checking to simple_router's handle_func #125 **What's coming up...?** I am contemplating upgrading the underlying facil.io library to the new and improved version 0.8! Thanks for reading and helping out 😊!
__**Small API Refactors**__ This is a small update from recent PRs with little breaking changes in `zap.Mustache` and `zap.Middleware.EndpointHandler`. Thanks for the great contributions! See the changelog below. **Also: we now use zig fetch!** This greatly simplifies the instructions in the README and release notes. __**Breaking Changes:**__ Mustache: - renamed `zap.Mustache.MustacheLoadArgs` to `zap.Mustache.LoadArgs` - `zap.Mustache.BuildResult` is a public type now Middleware: - `zap.Middleware.EndpointHandler` now takes more than one option: ```ts /// Options used to change the behavior of an `EndpointHandler` pub const EndpointHandlerOptions = struct { /// If `true`, the handler will stop handing requests down the chain if the /// endpoint processed the request. breakOnFinish: bool = true, /// If `true`, the handler will only execute against requests that match /// the endpoint's `path` setting. checkPath: bool = false, }; ``` I updated the docs and zig-master branch, too. __**Changelog:**__ Rene Schallner (5): Merge pull request #117 from cosmicboots/mustache-build doc: getHeader need lowercase keys Merge pull request #120 from cosmicboots/endpoint-middleware Merge pull request #127 from iacore/patch-1 docs, announceybot: switch to using zig fetch Thom Dickson (4): include BuildResult in public Mustache API rename MustacheLoadArgs to LoadArgs Create options for EndpointHandler update docs and examples for endpoint middleware iacore (1): update docs for zap.start
__Update to Zig 0.13__ With the help of our awesome contributers, we have a new release: - Zap is now officially based on Zig 0.13.0! - Thx to Lois Pearson, we now have `mimetypeRegister` and `mimetypeClear` - Thx to geemili, we don't need to link facil.io in our build.zigs anymore - Thx to Sören Michaels, `methodAsEnum` supports the `HEAD` method. - ... and more, see the changelog below **Note:** there now is a `zig-master` branch that gets updated with breaking changes of Zig master on a somewhat regular basis. Please feel free to send PRs. Many thanks again to everyone who helped out: Giuseppe Cesarano (1): fix: _debug typo in startWithLogging Joe Koop (1): update http.zig to rfc9110 using MDN as a reference Lord Asdi (1): fix: use std.process.getEnvVarOwned instead of std.posix.getenv Louis Pearson (8): fix: last_modifed -> last_modified fix: docserver: server wasm with correct mimetype feat: Wrap mimetypeRegister and mimetypeClear fix: move getHeaderCommon to zap.zig feat: add parseAccept feat: make example for parseAccept fix: simplify accept header api somewhat feat: pre-allocate enough space for accept items Michael Wendt (1): feat: remove deprecated path Rene Schallner (18): Update hello_json.zig fix docserver invocation from build.zig proposed change to parseAccept API make zap master build with zig master update zig version updated zig-master check in CI update badge in README corrected release templates Sören Michaels (1): feat: add HEAD Method to `methodAsEnum` geemili (1): feat: streamline depending on zap by linking facil.io to module
__Update to Zig 0.12__ With the help of our awesome contributers, we have a new release: - zap is now officially based on Zig 0.12.0! - tests have been updated to use the latest `std.http` client - @desttinghim added `getHeaderCommon` to `zap.Request` - @leroycep improved error return traces in `zap.Request.sendError()` - @dasimmet and @dweiller fixed the use of @fieldParentPtr to the new style - @xflow-systems improved the write function of websockets Users of `sendError()`: the API has changed! The doc comment has been updated. The new way of using it is: ```ts r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505); ``` The new version outputs much nicer error traces. **Note:** there will likely be a `zig-master` branch in the future that gets updated with breaking changes of Zig master. For sanity reasons, it will not be called `zig-0.13.0` but `zig-master`. I'll update the README accordingly then. **Note 2:** I merged PRs and fixed the tests while waiting for my plane to board, then finished on the plane. If I might have rushed it and oopsied something up, I'll apologize and follow up with a bugfix release. One open issue is using openssl on macOS, especially with openssl in custom locations, like homebrew-installed versions. If anyone wants to look into that: PRs are welcome 😊! Many thanks again to everyone who helped out!
__Breaking change in zap.Router__ Latest zig master does not allow for multiple copies of the same anonymous type anymore. This broke zap.RequestHandler used by zap.Router. So I rewrote zap.Router and zap.RequestHandler is gone. To keep the APIs identical for both zig versions, I applied the rewrite patch also to the zig 0.11.0 (master) branch. - [simple_router example](https://github.com/zigzap/zap/blob/master/examples/simple_router/simple_router.zig) - [zap.Router documentation](https://zigzap.org/zap/#zap.router) From a user perspective not much changed: - for unbound route handlers, use `zap.Router.handle_route_unbound`. - use `zap.Router.on_request_handler()` to get the request function to pass to a Listener. **Note:** the 0.12.0 branch is currently broken with regard to tests due to changes in `std.http`. __**Changelog**__ in alphabetical order: GitHub Action (1): Update README Rene Schallner (2): trying to add mastercheck badge to README re-write of zap.Router, fix #83
__Request.methodAsEnum() and Windows build error message__ This is a small update of recent PRs. Thanks for the great contributions! See the changelog below for individual contributions. - `zap.Request.methodAsEnum()` returns HTTP method as enum or .UNKNOWN - the reason the method is not an enum by default is to avoid the string comparisons on every request when we might not need them - build attempts on Windows now produce a meaningful error message - `zap.Request` now supports `getParamSlice()` and `getParamSlices()` which return optional string slices of the raw query string. - PRO: no allocation - CON: no decoding: "hello+zap" will not be decoded into "hello zap" - if you need decoding, you can still use `getParamStr()`. I updated the docs and zig-0.12.0 branch, too, as with all recent and future releases. __**Changelog**__ in alphabetical order: Froxcey (4): Use std.http.Method for Request.method Use custom method enum Provide Windows error message Use debug.err and exit 1 for windows fail message Joe Liotta (1): fixed unneeded optional unwrap in hello_json Rene Schallner (8): Update README.md to point out even more prominently the zig master situation Merge pull request #72 from Chiissu/master Merge pull request #75 from Chiissu/windows-errmsg access raw query params w/o allocator, close #40 cosmetics Merge pull request #79 from joeypas/master fix workflow to detect failing builds of individual samples in http.methodToEnum use std.meta.stringToEnum performance: revert r.method enum back to ?[]const u8 (new http.Method enum is available via r.methodAsEnum()) use methodAsEnum() in Endpoint, and in json example
__Introducing: zap.Router__ Thanks to StringNick, we now have `zap.Router` with handler closures support! See the `simple_router` example. `zap.Router` is missing doc comments, so if anyone wants to step up, please feel free to send a PR against the `zig-0.12.0` my way. BTW: Documentation (built on zig-0.12.0 branch) is now live at: <https://zigzap.org/zap> Doc update PRs are welcome. I am especially excited about the _guides_ feature: <https://zigzap.org/zap/#G;> __**Introduced:**__ - `zap.Router`: the router itself - `zap.RequestHandler : a nice way to capture "self" pointers of containers of request functions. - `simple_router`: example demonstrating the above Thanks again to StringNick! I updated the zig-0.12.0 branch, too, as with all recent and future releases.
__Breaking API Cleanup__ **Documentation (built on zig-0.12.0 branch) is now live at: <https://zigzap.org/zap>** Doc update PRs are welcome. I am especially excited about the _guides_ feature: <https://zigzap.org/zap/#G;> So, I spent a few days with a first pass of cleaning up Zap's API, informed by using it in production for over half a year now. **__Refactored:__** - no more type names starting with `Simple`. - zap.SimpleEndpoint -> zap.Endpoint - zap.SimpleRequest -> zap.Request - zap.SimpleHttpListener -> zap.HttpListener - ... - zap.Endpoint : zap.Endpoint, zap.Endpoint.Authenticating - zap.Endpoint.Listener.register() // was: zap.EndpointListener.addEndpoint - zap.Auth : zap.Auth.Basic, zap.Auth.BearerSingle, ... - zap.Mustache : stayed the same - zap.Request : refactored into its own file, along with supporting types and functions (e.g. http params related) - added setContentTypeFromFilename thx @hauleth. - zap.Middleware: no more MixContexts - (zig structs are fine) - check example - zap.fio : facilio C FFI stuff does not pollute zap namespace anymore - it is still available via `zap.fio`. - allocators are always first-ish param: either first or after self - more docstrings All examples and tests have been updated. Also, check out the documentation (work in progress).
PreviousNext