Tags: zigzap/zap
Tags
Update to Zig 0.15.1 This release introduces breaking changes that basically come from "unmanaged" containers being the default in Zig's std library now. The rest of Zap hasn't really changed much. All examples and tests have been updated. Also, the following contributions made it into the release: * 3c22e9d - chore: update to latest zig version <Tesseract22> * 512c630 - feat: tweak support for additional cookie fields <Michael Pollind> * 6174c3e - feat: add parition support for cookies in facil.io <Michael Pollind> Thanks Tesseract22 who kicked off the 0.15 work and Michael!
A quick one. I was so fascinated by @TesseractCat's PR that I impleme… …nted the same logic for all other forms of endpoints: regular endpoints, authenticating endpoints, and middleware endpoints. - see #171 from Tesseract22 : provide defaults to unprovided method handlers in zap.App - the default handlers now return 405 - method not allowed, and also log a message I also fixed port numbers in tests so they don't cross-talk to each other when run in parallel by the zig test runner.
Hi, it's been a while, and I've almost had no time for zap. However, ... ..., eventually I got to merge the following amazing PRs: - #171 from Tesseract22 : provide defaults to unprovided method handlers in zap.App - the default handlers now return 405 - method not allowed, and also log a message - #167 from yanis-fourel : file-uploads with missing content-type will default to application/octet-stream - #164 fwfurtado : added handlers for HEAD requests in Endpoints - #161 from unorsk : fixed example in the README - support for unhandledError() in zap.App's Context
__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
PreviousNext