Skip to content

Releases: zigzap/zap

Release v0.10.5

23 Jul 18:32
e67b672
Compare
Choose a tag to compare

ZAP Release v0.10.5

Updates

A quick one. I was so fascinated by @TesseractCat's PR that I implemented
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.

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.10.5"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.10.4

23 Jul 00:26
87415e1
Compare
Choose a tag to compare

ZAP Release v0.10.4

Updates

For some reason, the zon version hadn't come through before creating the
tag.

This time it will work 😄

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.10.4"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.10.3

23 Jul 00:10
e768c85
Compare
Choose a tag to compare

ZAP Release v0.10.3

Updates

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.

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.10.3"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.10.2

23 Jul 00:06
ef523d7
Compare
Choose a tag to compare

ZAP Release v0.10.2

Updates

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

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.10.2"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.10.1

01 Apr 14:41
Compare
Choose a tag to compare

ZAP Release v0.10.1

Updates

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:

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.

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.10.1"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.10.0

30 Mar 19:15
a01c614
Compare
Choose a tag to compare

ZAP Release v0.10.0

Updates

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:

  • http.zig : Pure Zig! Close to Zap's model. Performance = good!
  • jetzig : Comfortably develop modern web applications quickly, using http.zig under the hood
  • 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:

//!
//! 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:

//!
//! 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 😊!

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.10.0"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.9.1

20 Oct 21:36
ae5c927
Compare
Choose a tag to compare
Release v0.9.1 Pre-release
Pre-release

ZAP Release v0.9.1

Updates

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.

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.9.1"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.9.0

14 Oct 23:04
9543ede
Compare
Choose a tag to compare
Release v0.9.0 Pre-release
Pre-release

ZAP Release v0.9.0

Updates

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:
/// 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

Using it

In your zig project folder (where build.zig is located), run:

zig fetch --save "git+https://github.com/zigzap/zap#v0.9.0"

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.8.0

28 Jun 10:22
Compare
Choose a tag to compare
Release v0.8.0 Pre-release
Pre-release

ZAP Release v0.8.0

Updates

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

Using it

To use in your own projects, put this dependency into your build.zig.zon:

        // zap v0.8.0
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.8.0.tar.gz",
            .hash = "12209936c3333b53b53edcf453b1670babb9ae8c2197b1ca627c01e72670e20c1a21",
        }

Here is a complete build.zig.zon example:

.{
    .name = "My example project",
    .version = "0.0.1",

    .dependencies = .{
        // zap v0.8.0
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.8.0.tar.gz",
            .hash = "12209936c3333b53b53edcf453b1670babb9ae8c2197b1ca627c01e72670e20c1a21",
        },
    },
    .paths = .{
        "",
    },
}

Then, in your build.zig's build function, add the following before
b.installArtifact(exe):

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });
    exe.root_module.addImport("zap", zap.module("zap"));

Release v0.7.0

21 Apr 18:29
Compare
Choose a tag to compare
Release v0.7.0 Pre-release
Pre-release

ZAP Release v0.7.0

Updates

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:

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!

Using it

To use in your own projects, put this dependency into your build.zig.zon:

        // zap v0.7.0
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.7.0.tar.gz",
            .hash = "1220a1cb1822ea77083045d246db5d7a6f07a8ddafa69c98dee367560f9ce667fd8d",
        }

Here is a complete build.zig.zon example:

.{
    .name = "My example project",
    .version = "0.0.1",

    .dependencies = .{
        // zap v0.7.0
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.7.0.tar.gz",
            .hash = "1220a1cb1822ea77083045d246db5d7a6f07a8ddafa69c98dee367560f9ce667fd8d",
        },
    },
    .paths = .{
        "",
    },
}

Then, in your build.zig's build function, add the following before exe.install():

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
    });
    exe.addModule("zap", zap.module("zap"));
    exe.linkLibrary(zap.artifact("facil.io"));