Skip to content

Tags: zigzap/zap

Tags

v0.11.0

Toggle v0.11.0's commit message
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!

v0.10.6

Toggle v0.10.6's commit message
Bugfix release:

The latest changes to parsing the mimetype of uploaded files required a
type check, as, apparently, they _can_ also be arrays instead of
strings.

v0.10.5

Toggle v0.10.5's commit message
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.

v0.10.4

Toggle v0.10.4's commit message
For some reason, the zon version hadn't come through before creating the

tag.

This time it will work 😄

v0.10.3

Toggle v0.10.3's commit message
Sorry I (again) forgot to bump the version in build.zig.zon

To avoid confusion, I created this release. It's identical to v0.10.2,
but with the zon version fixed, so it will show up with the correct
version in your zon files after a zig fetch.

v0.10.2

Toggle v0.10.2's commit message
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

v0.10.1

Toggle v0.10.1's commit message
__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`.

v0.10.0

Toggle v0.10.0's commit message
**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 😊!

v0.9.1

Toggle v0.9.1's commit message
__**Bugfix for Middleware.EnpointHandler checkPath logic**__

I introduced a bug by wrongly trying to de-morgan the logic.

Thx @andr3h3nriqu3s11 for submitting issue #136 <#136>

I reverted the commit.

v0.9.0

Toggle v0.9.0's commit message
__**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