mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
Merge branch 'api_clean' into zig-0.12.0
This commit is contained in:
commit
652f0e2277
36 changed files with 664 additions and 569 deletions
|
@ -113,9 +113,9 @@ port and docs dir: `zig build docserver && zig-out/bin/docserver --port=8989
|
||||||
less type-safe than `zap.Middleware`'s use of contexts.
|
less type-safe than `zap.Middleware`'s use of contexts.
|
||||||
- [**Per Request Contexts**](./src/zap.zig#L102) : With the introduction of
|
- [**Per Request Contexts**](./src/zap.zig#L102) : With the introduction of
|
||||||
`setUserContext()` and `getUserContext()`, you can, of course use those two in
|
`setUserContext()` and `getUserContext()`, you can, of course use those two in
|
||||||
projects that don't use `zap.SimpleEndpoint` or `zap.Middleware`, too, if you
|
projects that don't use `zap.Endpoint` or `zap.Middleware`, too, if you
|
||||||
really, really, absolutely don't find another way to solve your context
|
really, really, absolutely don't find another way to solve your context
|
||||||
problem. **We recommend using a `zap.SimpleEndpoint`** inside of a struct that
|
problem. **We recommend using a `zap.Endpoint`** inside of a struct that
|
||||||
can provide all the context you need **instead**. You get access to your
|
can provide all the context you need **instead**. You get access to your
|
||||||
struct in the callbacks via the `@fieldParentPtr()` trick that is used
|
struct in the callbacks via the `@fieldParentPtr()` trick that is used
|
||||||
extensively in Zap's examples, like the [endpoint
|
extensively in Zap's examples, like the [endpoint
|
||||||
|
@ -376,7 +376,7 @@ $ zig build run-routes
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
if (r.path) |the_path| {
|
if (r.path) |the_path| {
|
||||||
std.debug.print("PATH: {s}\n", .{the_path});
|
std.debug.print("PATH: {s}\n", .{the_path});
|
||||||
}
|
}
|
||||||
|
@ -388,7 +388,7 @@ fn on_request(r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.log = true,
|
.log = true,
|
||||||
|
|
|
@ -12,7 +12,7 @@ For convenience, Authenticator types exist that can authenticate requests.
|
||||||
Zap also provides an `AuthenticatingEndpoint` endpoint-wrapper. Have a look at the [example](../examples/endpoint_auth) and the [tests](../src/tests/test_auth.zig).
|
Zap also provides an `AuthenticatingEndpoint` endpoint-wrapper. Have a look at the [example](../examples/endpoint_auth) and the [tests](../src/tests/test_auth.zig).
|
||||||
|
|
||||||
The following describes the Authenticator types. All of them provide the
|
The following describes the Authenticator types. All of them provide the
|
||||||
`authenticateRequest()` function, which takes a `zap.SimpleRequest` and returns
|
`authenticateRequest()` function, which takes a `zap.Request` and returns
|
||||||
a bool value whether it could be authenticated or not.
|
a bool value whether it could be authenticated or not.
|
||||||
|
|
||||||
Further down, we show how to use the Authenticators, and also the
|
Further down, we show how to use the Authenticators, and also the
|
||||||
|
@ -60,7 +60,7 @@ var auth = try zap.BearerAuthSingle.init(allocator, token, null);
|
||||||
defer auth.deinit();
|
defer auth.deinit();
|
||||||
|
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
if(authenticator.authenticateRequest(r)) {
|
if(authenticator.authenticateRequest(r)) {
|
||||||
r.sendBody(
|
r.sendBody(
|
||||||
\\ <html><body>
|
\\ <html><body>
|
||||||
|
@ -94,7 +94,7 @@ var auth = try zap.BearerAuthMulti(Set).init(allocator, &set, null);
|
||||||
defer auth.deinit();
|
defer auth.deinit();
|
||||||
|
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
if(authenticator.authenticateRequest(r)) {
|
if(authenticator.authenticateRequest(r)) {
|
||||||
r.sendBody(
|
r.sendBody(
|
||||||
\\ <html><body>
|
\\ <html><body>
|
||||||
|
@ -132,7 +132,7 @@ var auth = try Authenticator.init(a, &map, null);
|
||||||
defer auth.deinit();
|
defer auth.deinit();
|
||||||
|
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
if(authenticator.authenticateRequest(r)) {
|
if(authenticator.authenticateRequest(r)) {
|
||||||
r.sendBody(
|
r.sendBody(
|
||||||
\\ <html><body>
|
\\ <html><body>
|
||||||
|
@ -168,7 +168,7 @@ var auth = try Authenticator.init(allocator, &set, null);
|
||||||
defer auth.deinit();
|
defer auth.deinit();
|
||||||
|
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
if(authenticator.authenticateRequest(r)) {
|
if(authenticator.authenticateRequest(r)) {
|
||||||
r.sendBody(
|
r.sendBody(
|
||||||
\\ <html><body>
|
\\ <html><body>
|
||||||
|
@ -206,13 +206,13 @@ const HTTP_RESPONSE: []const u8 =
|
||||||
;
|
;
|
||||||
|
|
||||||
// authenticated requests go here
|
// authenticated requests go here
|
||||||
fn endpoint_http_get(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn endpoint_http_get(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
r.sendBody(HTTP_RESPONSE) catch return;
|
r.sendBody(HTTP_RESPONSE) catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just for fun, we also catch the unauthorized callback
|
// just for fun, we also catch the unauthorized callback
|
||||||
fn endpoint_http_unauthorized(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn endpoint_http_unauthorized(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
r.sendBody("UNAUTHORIZED ACCESS") catch return;
|
r.sendBody("UNAUTHORIZED ACCESS") catch return;
|
||||||
|
@ -220,7 +220,7 @@ fn endpoint_http_unauthorized(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -233,7 +233,7 @@ pub fn main() !void {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = zap.SimpleEndpoint.init(.{
|
var ep = zap.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -248,7 +248,7 @@ pub fn main() !void {
|
||||||
const BearerAuthEndpoint = zap.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = zap.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
std.debug.print(
|
std.debug.print(
|
||||||
|
|
|
@ -58,14 +58,14 @@ Now that I think I've made my point 😊, you can, of course always do the
|
||||||
following:
|
following:
|
||||||
|
|
||||||
```zig
|
```zig
|
||||||
fn on_request_with_errors(r: zap.SimpleHttpRequest) !void {
|
fn on_request_with_errors(r: zap.HttpRequest) !void {
|
||||||
// do all the try stuff here
|
// do all the try stuff here
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```zig
|
```zig
|
||||||
// THIS IS WHAT YOU PASS TO THE LISTENER / ENDPONT / ...
|
// THIS IS WHAT YOU PASS TO THE LISTENER / ENDPONT / ...
|
||||||
fn on_request(r: zap.SimpleHttpRequest) void {
|
fn on_request(r: zap.HttpRequest) void {
|
||||||
on_request_with_errors(r) catch |err| {
|
on_request_with_errors(r) catch |err| {
|
||||||
// log the error or use:
|
// log the error or use:
|
||||||
return r.returnWithErrorStackTrace(err);
|
return r.returnWithErrorStackTrace(err);
|
||||||
|
|
|
@ -10,29 +10,12 @@ Here is how it is used in user-code:
|
||||||
|
|
||||||
```zig
|
```zig
|
||||||
// create a combined context struct
|
// create a combined context struct
|
||||||
const Context = zap.Middleware.MixContexts(.{
|
const Context = struct {
|
||||||
.{ .name = "?user", .type = UserMiddleWare.User },
|
user: ?UserMiddleWare.User = null,
|
||||||
.{ .name = "?session", .type = SessionMiddleWare.Session },
|
session: ?SessionMiddleWare.Session = null,
|
||||||
});
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
The result of this function call is a struct that has a `user` field of type
|
|
||||||
`?UserMiddleWare.User`, which is the `User` struct inside of its containing
|
|
||||||
struct - and a `session` field of type `?SessionMiddleWare.Session`.
|
|
||||||
|
|
||||||
So `MixContexts` accepts a **tuple** of structs that each contain a
|
|
||||||
`name` field and a `type` field. As a hack, we support the `?` in the name to
|
|
||||||
indicate we want the resulting struct field to be an optional.
|
|
||||||
|
|
||||||
A **tuple** means that we can "mix" as many structs as we like. Not just two
|
|
||||||
like in the example above.
|
|
||||||
|
|
||||||
`MixContexts` inspects the passed-in `type` fields and **composes a new struct
|
|
||||||
type at comptime**! Have a look at its [source code](../src/middleware.zig).
|
|
||||||
You'll be blown away if this kind of metaprogramming stuff isn't what you do
|
|
||||||
everyday. I was totally blown away by trying it out and seeing it that it
|
|
||||||
_actually_ worked.
|
|
||||||
|
|
||||||
Why do we create combined structs? Because all our Middleware handler functions
|
Why do we create combined structs? Because all our Middleware handler functions
|
||||||
need to receive a per-request context. But each wants their own data: the User
|
need to receive a per-request context. But each wants their own data: the User
|
||||||
middleware might want to access a User struct, the Session middleware might want
|
middleware might want to access a User struct, the Session middleware might want
|
||||||
|
@ -62,10 +45,10 @@ Have a look at an excerpt of the example:
|
||||||
|
|
||||||
```zig
|
```zig
|
||||||
// create a combined context struct
|
// create a combined context struct
|
||||||
const Context = zap.Middleware.MixContexts(.{
|
const Context = struct {
|
||||||
.{ .name = "?user", .type = UserMiddleWare.User },
|
user: ?UserMiddleWare.User = null,
|
||||||
.{ .name = "?session", .type = SessionMiddleWare.Session },
|
session: ?SessionMiddleWare.Session = null,
|
||||||
});
|
};
|
||||||
|
|
||||||
// we create a Handler type based on our Context
|
// we create a Handler type based on our Context
|
||||||
const Handler = zap.Middleware.Handler(Context);
|
const Handler = zap.Middleware.Handler(Context);
|
||||||
|
|
|
@ -4,7 +4,7 @@ const zap = @import("zap");
|
||||||
const Handler = struct {
|
const Handler = struct {
|
||||||
var alloc: std.mem.Allocator = undefined;
|
var alloc: std.mem.Allocator = undefined;
|
||||||
|
|
||||||
pub fn on_request(r: zap.SimpleRequest) void {
|
pub fn on_request(r: zap.Request) void {
|
||||||
// check for FORM parameters
|
// check for FORM parameters
|
||||||
r.parseBody() catch |err| {
|
r.parseBody() catch |err| {
|
||||||
std.log.err("Parse Body error: {any}. Expected if body is empty", .{err});
|
std.log.err("Parse Body error: {any}. Expected if body is empty", .{err});
|
||||||
|
@ -56,7 +56,7 @@ const Handler = struct {
|
||||||
else => {
|
else => {
|
||||||
// might be a string param, we don't care
|
// might be a string param, we don't care
|
||||||
// let's just get it as string
|
// let's just get it as string
|
||||||
if (r.getParamStr(kv.key.str, Handler.alloc, false)) |maybe_str| {
|
if (r.getParamStr(Handler.alloc, kv.key.str, false)) |maybe_str| {
|
||||||
const value: []const u8 = if (maybe_str) |s| s.str else "(no value)";
|
const value: []const u8 = if (maybe_str) |s| s.str else "(no value)";
|
||||||
std.log.debug(" {s} = {s}", .{ kv.key.str, value });
|
std.log.debug(" {s} = {s}", .{ kv.key.str, value });
|
||||||
} else |err| {
|
} else |err| {
|
||||||
|
@ -68,12 +68,12 @@ const Handler = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we received a terminate=true parameter
|
// check if we received a terminate=true parameter
|
||||||
if (r.getParamStr("terminate", Handler.alloc, false)) |maybe_str| {
|
if (r.getParamStr(Handler.alloc, "terminate", false)) |maybe_str| {
|
||||||
if (maybe_str) |*s| {
|
if (maybe_str) |*s| {
|
||||||
defer s.deinit();
|
defer s.deinit();
|
||||||
std.log.info("?terminate={s}\n", .{s.str});
|
std.log.info("?terminate={s}\n", .{s.str});
|
||||||
if (std.mem.eql(u8, s.str, "true")) {
|
if (std.mem.eql(u8, s.str, "true")) {
|
||||||
zap.fio_stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else |err| {
|
} else |err| {
|
||||||
|
@ -92,7 +92,7 @@ pub fn main() !void {
|
||||||
Handler.alloc = allocator;
|
Handler.alloc = allocator;
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleHttpListener.init(
|
var listener = zap.HttpListener.init(
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = Handler.on_request,
|
.on_request = Handler.on_request,
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub fn main() !void {
|
||||||
const Handler = struct {
|
const Handler = struct {
|
||||||
var alloc: std.mem.Allocator = undefined;
|
var alloc: std.mem.Allocator = undefined;
|
||||||
|
|
||||||
pub fn on_request(r: zap.SimpleRequest) void {
|
pub fn on_request(r: zap.Request) void {
|
||||||
std.debug.print("\n=====================================================\n", .{});
|
std.debug.print("\n=====================================================\n", .{});
|
||||||
defer std.debug.print("=====================================================\n\n", .{});
|
defer std.debug.print("=====================================================\n\n", .{});
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
// let's get cookie "ZIG_ZAP" by name
|
// let's get cookie "ZIG_ZAP" by name
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
if (r.getCookieStr("ZIG_ZAP", alloc, false)) |maybe_str| {
|
if (r.getCookieStr(alloc, "ZIG_ZAP", false)) |maybe_str| {
|
||||||
if (maybe_str) |*s| {
|
if (maybe_str) |*s| {
|
||||||
defer s.deinit();
|
defer s.deinit();
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ pub fn main() !void {
|
||||||
Handler.alloc = allocator;
|
Handler.alloc = allocator;
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleHttpListener.init(
|
var listener = zap.HttpListener.init(
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = Handler.on_request,
|
.on_request = Handler.on_request,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
const Endpoint = @import("endpoint.zig");
|
const UserWeb = @import("userweb.zig");
|
||||||
const StopEndpoint = @import("stopendpoint.zig");
|
const StopEndpoint = @import("stopendpoint.zig");
|
||||||
|
|
||||||
// this is just to demo that we can catch arbitrary slugs
|
// this is just to demo that we can catch arbitrary slugs
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
if (r.path) |the_path| {
|
if (r.path) |the_path| {
|
||||||
std.debug.print("REQUESTED PATH: {s}\n", .{the_path});
|
std.debug.print("REQUESTED PATH: {s}\n", .{the_path});
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ pub fn main() !void {
|
||||||
// we scope everything that can allocate within this block for leak detection
|
// we scope everything that can allocate within this block for leak detection
|
||||||
{
|
{
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
allocator,
|
allocator,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -34,19 +34,20 @@ pub fn main() !void {
|
||||||
);
|
);
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
var endpoint = Endpoint.init(allocator, "/users");
|
// /users endpoint
|
||||||
defer endpoint.deinit();
|
var userWeb = UserWeb.init(allocator, "/users");
|
||||||
|
defer userWeb.deinit();
|
||||||
|
|
||||||
var stopEp = StopEndpoint.init("/stop");
|
var stopEp = StopEndpoint.init("/stop");
|
||||||
|
|
||||||
// add endpoint
|
// register endpoints with the listener
|
||||||
try listener.addEndpoint(endpoint.getUserEndpoint());
|
try listener.register(userWeb.endpoint());
|
||||||
try listener.addEndpoint(stopEp.getEndpoint());
|
try listener.register(stopEp.endpoint());
|
||||||
|
|
||||||
// fake some users
|
// fake some users
|
||||||
var uid: usize = undefined;
|
var uid: usize = undefined;
|
||||||
uid = try endpoint.getUsers().addByName("renerocksai", null);
|
uid = try userWeb.users().addByName("renerocksai", null);
|
||||||
uid = try endpoint.getUsers().addByName("renerocksai", "your mom");
|
uid = try userWeb.users().addByName("renerocksai", "your mom");
|
||||||
|
|
||||||
// listen
|
// listen
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
|
|
|
@ -5,24 +5,24 @@ const zap = @import("zap");
|
||||||
/// the main thread usually continues at the instructions after the call to zap.start().
|
/// the main thread usually continues at the instructions after the call to zap.start().
|
||||||
pub const Self = @This();
|
pub const Self = @This();
|
||||||
|
|
||||||
endpoint: zap.SimpleEndpoint = undefined,
|
ep: zap.Endpoint = undefined,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
) Self {
|
) Self {
|
||||||
return .{
|
return .{
|
||||||
.endpoint = zap.SimpleEndpoint.init(.{
|
.ep = zap.Endpoint.init(.{
|
||||||
.path = path,
|
.path = path,
|
||||||
.get = get,
|
.get = get,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getEndpoint(self: *Self) *zap.SimpleEndpoint {
|
pub fn endpoint(self: *Self) *zap.Endpoint {
|
||||||
return &self.endpoint;
|
return &self.ep;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn get(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
_ = r;
|
_ = r;
|
||||||
zap.stop();
|
zap.stop();
|
||||||
|
|
|
@ -8,8 +8,8 @@ const User = Users.User;
|
||||||
pub const Self = @This();
|
pub const Self = @This();
|
||||||
|
|
||||||
alloc: std.mem.Allocator = undefined,
|
alloc: std.mem.Allocator = undefined,
|
||||||
endpoint: zap.SimpleEndpoint = undefined,
|
ep: zap.Endpoint = undefined,
|
||||||
users: Users = undefined,
|
_users: Users = undefined,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
a: std.mem.Allocator,
|
a: std.mem.Allocator,
|
||||||
|
@ -17,8 +17,8 @@ pub fn init(
|
||||||
) Self {
|
) Self {
|
||||||
return .{
|
return .{
|
||||||
.alloc = a,
|
.alloc = a,
|
||||||
.users = Users.init(a),
|
._users = Users.init(a),
|
||||||
.endpoint = zap.SimpleEndpoint.init(.{
|
.ep = zap.Endpoint.init(.{
|
||||||
.path = user_path,
|
.path = user_path,
|
||||||
.get = getUser,
|
.get = getUser,
|
||||||
.post = postUser,
|
.post = postUser,
|
||||||
|
@ -31,30 +31,30 @@ pub fn init(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.users.deinit();
|
self._users.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getUsers(self: *Self) *Users {
|
pub fn users(self: *Self) *Users {
|
||||||
return &self.users;
|
return &self._users;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getUserEndpoint(self: *Self) *zap.SimpleEndpoint {
|
pub fn endpoint(self: *Self) *zap.Endpoint {
|
||||||
return &self.endpoint;
|
return &self.ep;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn userIdFromPath(self: *Self, path: []const u8) ?usize {
|
fn userIdFromPath(self: *Self, path: []const u8) ?usize {
|
||||||
if (path.len >= self.endpoint.settings.path.len + 2) {
|
if (path.len >= self.ep.settings.path.len + 2) {
|
||||||
if (path[self.endpoint.settings.path.len] != '/') {
|
if (path[self.ep.settings.path.len] != '/') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const idstr = path[self.endpoint.settings.path.len + 1 ..];
|
const idstr = path[self.ep.settings.path.len + 1 ..];
|
||||||
return std.fmt.parseUnsigned(usize, idstr, 10) catch null;
|
return std.fmt.parseUnsigned(usize, idstr, 10) catch null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn getUser(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
const self = @fieldParentPtr(Self, "endpoint", e);
|
const self = @fieldParentPtr(Self, "ep", e);
|
||||||
if (r.path) |path| {
|
if (r.path) |path| {
|
||||||
// /users
|
// /users
|
||||||
if (path.len == e.settings.path.len) {
|
if (path.len == e.settings.path.len) {
|
||||||
|
@ -62,7 +62,7 @@ fn getUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
var jsonbuf: [256]u8 = undefined;
|
var jsonbuf: [256]u8 = undefined;
|
||||||
if (self.userIdFromPath(path)) |id| {
|
if (self.userIdFromPath(path)) |id| {
|
||||||
if (self.users.get(id)) |user| {
|
if (self._users.get(id)) |user| {
|
||||||
if (zap.stringifyBuf(&jsonbuf, user, .{})) |json| {
|
if (zap.stringifyBuf(&jsonbuf, user, .{})) |json| {
|
||||||
r.sendJson(json) catch return;
|
r.sendJson(json) catch return;
|
||||||
}
|
}
|
||||||
|
@ -71,8 +71,8 @@ fn getUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn listUsers(self: *Self, r: zap.SimpleRequest) void {
|
fn listUsers(self: *Self, r: zap.Request) void {
|
||||||
if (self.users.toJSON()) |json| {
|
if (self._users.toJSON()) |json| {
|
||||||
defer self.alloc.free(json);
|
defer self.alloc.free(json);
|
||||||
r.sendJson(json) catch return;
|
r.sendJson(json) catch return;
|
||||||
} else |err| {
|
} else |err| {
|
||||||
|
@ -80,13 +80,13 @@ fn listUsers(self: *Self, r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn postUser(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
const self = @fieldParentPtr(Self, "endpoint", e);
|
const self = @fieldParentPtr(Self, "ep", e);
|
||||||
if (r.body) |body| {
|
if (r.body) |body| {
|
||||||
const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null;
|
const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null;
|
||||||
if (maybe_user) |u| {
|
if (maybe_user) |u| {
|
||||||
defer u.deinit();
|
defer u.deinit();
|
||||||
if (self.users.addByName(u.value.first_name, u.value.last_name)) |id| {
|
if (self._users.addByName(u.value.first_name, u.value.last_name)) |id| {
|
||||||
var jsonbuf: [128]u8 = undefined;
|
var jsonbuf: [128]u8 = undefined;
|
||||||
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
||||||
r.sendJson(json) catch return;
|
r.sendJson(json) catch return;
|
||||||
|
@ -99,17 +99,17 @@ fn postUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn putUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn putUser(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
const self = @fieldParentPtr(Self, "endpoint", e);
|
const self = @fieldParentPtr(Self, "ep", e);
|
||||||
if (r.path) |path| {
|
if (r.path) |path| {
|
||||||
if (self.userIdFromPath(path)) |id| {
|
if (self.userIdFromPath(path)) |id| {
|
||||||
if (self.users.get(id)) |_| {
|
if (self._users.get(id)) |_| {
|
||||||
if (r.body) |body| {
|
if (r.body) |body| {
|
||||||
const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null;
|
const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null;
|
||||||
if (maybe_user) |u| {
|
if (maybe_user) |u| {
|
||||||
defer u.deinit();
|
defer u.deinit();
|
||||||
var jsonbuf: [128]u8 = undefined;
|
var jsonbuf: [128]u8 = undefined;
|
||||||
if (self.users.update(id, u.value.first_name, u.value.last_name)) {
|
if (self._users.update(id, u.value.first_name, u.value.last_name)) {
|
||||||
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
||||||
r.sendJson(json) catch return;
|
r.sendJson(json) catch return;
|
||||||
}
|
}
|
||||||
|
@ -125,12 +125,12 @@ fn putUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deleteUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn deleteUser(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
const self = @fieldParentPtr(Self, "endpoint", e);
|
const self = @fieldParentPtr(Self, "ep", e);
|
||||||
if (r.path) |path| {
|
if (r.path) |path| {
|
||||||
if (self.userIdFromPath(path)) |id| {
|
if (self.userIdFromPath(path)) |id| {
|
||||||
var jsonbuf: [128]u8 = undefined;
|
var jsonbuf: [128]u8 = undefined;
|
||||||
if (self.users.delete(id)) {
|
if (self._users.delete(id)) {
|
||||||
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
||||||
r.sendJson(json) catch return;
|
r.sendJson(json) catch return;
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ fn deleteUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optionsUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn optionsUser(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
r.setHeader("Access-Control-Allow-Origin", "*") catch return;
|
r.setHeader("Access-Control-Allow-Origin", "*") catch return;
|
||||||
r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") catch return;
|
r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") catch return;
|
|
@ -11,13 +11,13 @@ const HTTP_RESPONSE: []const u8 =
|
||||||
;
|
;
|
||||||
|
|
||||||
// authenticated requests go here
|
// authenticated requests go here
|
||||||
fn endpoint_http_get(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn endpoint_http_get(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
r.sendBody(HTTP_RESPONSE) catch return;
|
r.sendBody(HTTP_RESPONSE) catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just for fun, we also catch the unauthorized callback
|
// just for fun, we also catch the unauthorized callback
|
||||||
fn endpoint_http_unauthorized(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn endpoint_http_unauthorized(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
r.sendBody("UNAUTHORIZED ACCESS") catch return;
|
r.sendBody("UNAUTHORIZED ACCESS") catch return;
|
||||||
|
@ -25,7 +25,7 @@ fn endpoint_http_unauthorized(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -38,7 +38,7 @@ pub fn main() !void {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = zap.SimpleEndpoint.init(.{
|
var ep = zap.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -53,7 +53,7 @@ pub fn main() !void {
|
||||||
const BearerAuthEndpoint = zap.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = zap.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
std.debug.print(
|
std.debug.print(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request_verbose(r: zap.SimpleRequest) void {
|
fn on_request_verbose(r: zap.Request) void {
|
||||||
if (r.path) |the_path| {
|
if (r.path) |the_path| {
|
||||||
std.debug.print("PATH: {s}\n", .{the_path});
|
std.debug.print("PATH: {s}\n", .{the_path});
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,12 @@ fn on_request_verbose(r: zap.SimpleRequest) void {
|
||||||
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request_minimal(r: zap.SimpleRequest) void {
|
fn on_request_minimal(r: zap.Request) void {
|
||||||
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request_verbose,
|
.on_request = on_request_verbose,
|
||||||
.log = true,
|
.log = true,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
const m = r.method orelse "";
|
const m = r.method orelse "";
|
||||||
const p = r.path orelse "/";
|
const p = r.path orelse "/";
|
||||||
const qm = if (r.query) |_| "?" else "";
|
const qm = if (r.query) |_| "?" else "";
|
||||||
|
@ -35,7 +35,7 @@ fn on_request(r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.log = false,
|
.log = false,
|
||||||
|
|
|
@ -6,7 +6,7 @@ const User = struct {
|
||||||
last_name: ?[]const u8 = null,
|
last_name: ?[]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
if (!std.mem.eql(u8, r.method.?, "GET"))
|
if (!std.mem.eql(u8, r.method.?, "GET"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ fn setupUserData(a: std.mem.Allocator) !void {
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const a = std.heap.page_allocator;
|
const a = std.heap.page_allocator;
|
||||||
try setupUserData(a);
|
try setupUserData(a);
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.log = false,
|
.log = false,
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub fn main() !void {
|
||||||
const Handler = struct {
|
const Handler = struct {
|
||||||
var alloc: std.mem.Allocator = undefined;
|
var alloc: std.mem.Allocator = undefined;
|
||||||
|
|
||||||
pub fn on_request(r: zap.SimpleRequest) void {
|
pub fn on_request(r: zap.Request) void {
|
||||||
std.debug.print("\n=====================================================\n", .{});
|
std.debug.print("\n=====================================================\n", .{});
|
||||||
defer std.debug.print("=====================================================\n\n", .{});
|
defer std.debug.print("=====================================================\n\n", .{});
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
// let's get param "one" by name
|
// let's get param "one" by name
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
if (r.getParamStr("one", alloc, false)) |maybe_str| {
|
if (r.getParamStr(alloc, "one", false)) |maybe_str| {
|
||||||
if (maybe_str) |*s| {
|
if (maybe_str) |*s| {
|
||||||
defer s.deinit();
|
defer s.deinit();
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we received a terminate=true parameter
|
// check if we received a terminate=true parameter
|
||||||
if (r.getParamStr("terminate", alloc, false)) |maybe_str| {
|
if (r.getParamStr(alloc, "terminate", false)) |maybe_str| {
|
||||||
if (maybe_str) |*s| {
|
if (maybe_str) |*s| {
|
||||||
defer s.deinit();
|
defer s.deinit();
|
||||||
if (std.mem.eql(u8, s.str, "true")) {
|
if (std.mem.eql(u8, s.str, "true")) {
|
||||||
zap.fio_stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else |err| {
|
} else |err| {
|
||||||
|
@ -98,7 +98,7 @@ pub fn main() !void {
|
||||||
Handler.alloc = allocator;
|
Handler.alloc = allocator;
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleHttpListener.init(
|
var listener = zap.HttpListener.init(
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = Handler.on_request,
|
.on_request = Handler.on_request,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request_verbose(r: zap.SimpleRequest) void {
|
fn on_request_verbose(r: zap.Request) void {
|
||||||
if (r.path) |the_path| {
|
if (r.path) |the_path| {
|
||||||
std.debug.print("PATH: {s}\n", .{the_path});
|
std.debug.print("PATH: {s}\n", .{the_path});
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ fn on_request_verbose(r: zap.SimpleRequest) void {
|
||||||
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request_minimal(r: zap.SimpleRequest) void {
|
fn on_request_minimal(r: zap.Request) void {
|
||||||
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ pub fn main() !void {
|
||||||
});
|
});
|
||||||
defer tls.deinit();
|
defer tls.deinit();
|
||||||
|
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 4443,
|
.port = 4443,
|
||||||
.on_request = on_request_verbose,
|
.on_request = on_request_verbose,
|
||||||
.log = true,
|
.log = true,
|
||||||
|
|
|
@ -69,7 +69,7 @@ const UserMiddleWare = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that the first parameter is of type *Handler, not *Self !!!
|
// note that the first parameter is of type *Handler, not *Self !!!
|
||||||
pub fn onRequest(handler: *Handler, r: zap.SimpleRequest, context: *Context) bool {
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool {
|
||||||
|
|
||||||
// this is how we would get our self pointer
|
// this is how we would get our self pointer
|
||||||
const self = @fieldParentPtr(Self, "handler", handler);
|
const self = @fieldParentPtr(Self, "handler", handler);
|
||||||
|
@ -113,7 +113,7 @@ const SessionMiddleWare = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that the first parameter is of type *Handler, not *Self !!!
|
// note that the first parameter is of type *Handler, not *Self !!!
|
||||||
pub fn onRequest(handler: *Handler, r: zap.SimpleRequest, context: *Context) bool {
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool {
|
||||||
// this is how we would get our self pointer
|
// this is how we would get our self pointer
|
||||||
const self = @fieldParentPtr(Self, "handler", handler);
|
const self = @fieldParentPtr(Self, "handler", handler);
|
||||||
_ = self;
|
_ = self;
|
||||||
|
@ -148,7 +148,7 @@ const HtmlMiddleWare = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that the first parameter is of type *Handler, not *Self !!!
|
// note that the first parameter is of type *Handler, not *Self !!!
|
||||||
pub fn onRequest(handler: *Handler, r: zap.SimpleRequest, context: *Context) bool {
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool {
|
||||||
|
|
||||||
// this is how we would get our self pointer
|
// this is how we would get our self pointer
|
||||||
const self = @fieldParentPtr(Self, "handler", handler);
|
const self = @fieldParentPtr(Self, "handler", handler);
|
||||||
|
|
|
@ -20,10 +20,11 @@ const SharedAllocator = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
// create a combined context struct
|
// create a combined context struct
|
||||||
const Context = zap.Middleware.MixContexts(.{
|
// NOTE: context struct members need to be optionals which default to null!!!
|
||||||
.{ .name = "?user", .type = UserMiddleWare.User },
|
const Context = struct {
|
||||||
.{ .name = "?session", .type = SessionMiddleWare.Session },
|
user: ?UserMiddleWare.User = null,
|
||||||
});
|
session: ?SessionMiddleWare.Session = null,
|
||||||
|
};
|
||||||
|
|
||||||
// we create a Handler type based on our Context
|
// we create a Handler type based on our Context
|
||||||
const Handler = zap.Middleware.Handler(Context);
|
const Handler = zap.Middleware.Handler(Context);
|
||||||
|
@ -56,7 +57,7 @@ const UserMiddleWare = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that the first parameter is of type *Handler, not *Self !!!
|
// note that the first parameter is of type *Handler, not *Self !!!
|
||||||
pub fn onRequest(handler: *Handler, r: zap.SimpleRequest, context: *Context) bool {
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool {
|
||||||
|
|
||||||
// this is how we would get our self pointer
|
// this is how we would get our self pointer
|
||||||
const self = @fieldParentPtr(Self, "handler", handler);
|
const self = @fieldParentPtr(Self, "handler", handler);
|
||||||
|
@ -102,7 +103,7 @@ const SessionMiddleWare = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that the first parameter is of type *Handler, not *Self !!!
|
// note that the first parameter is of type *Handler, not *Self !!!
|
||||||
pub fn onRequest(handler: *Handler, r: zap.SimpleRequest, context: *Context) bool {
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool {
|
||||||
// this is how we would get our self pointer
|
// this is how we would get our self pointer
|
||||||
const self = @fieldParentPtr(Self, "handler", handler);
|
const self = @fieldParentPtr(Self, "handler", handler);
|
||||||
_ = self;
|
_ = self;
|
||||||
|
@ -137,24 +138,24 @@ const SessionMiddleWare = struct {
|
||||||
// parameter.
|
// parameter.
|
||||||
//
|
//
|
||||||
const HtmlEndpoint = struct {
|
const HtmlEndpoint = struct {
|
||||||
endpoint: zap.SimpleEndpoint = undefined,
|
ep: zap.Endpoint = undefined,
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init() Self {
|
pub fn init() Self {
|
||||||
return .{
|
return .{
|
||||||
.endpoint = zap.SimpleEndpoint.init(.{
|
.ep = zap.Endpoint.init(.{
|
||||||
.path = "/doesn'tmatter",
|
.path = "/doesn't+matter",
|
||||||
.get = get,
|
.get = get,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getEndpoint(self: *Self) *zap.SimpleEndpoint {
|
pub fn endpoint(self: *Self) *zap.Endpoint {
|
||||||
return &self.endpoint;
|
return &self.ep;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(ep: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
pub fn get(ep: *zap.Endpoint, r: zap.Request) void {
|
||||||
const self = @fieldParentPtr(Self, "endpoint", ep);
|
const self = @fieldParentPtr(Self, "ep", ep);
|
||||||
_ = self;
|
_ = self;
|
||||||
|
|
||||||
var buf: [1024]u8 = undefined;
|
var buf: [1024]u8 = undefined;
|
||||||
|
@ -207,7 +208,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
// we wrap the endpoint with a middleware handler
|
// we wrap the endpoint with a middleware handler
|
||||||
var htmlHandler = zap.Middleware.EndpointHandler(Handler, Context).init(
|
var htmlHandler = zap.Middleware.EndpointHandler(Handler, Context).init(
|
||||||
htmlEndpoint.getEndpoint(), // the endpoint
|
htmlEndpoint.endpoint(), // the endpoint
|
||||||
null, // no other handler (we are the last in the chain)
|
null, // no other handler (we are the last in the chain)
|
||||||
true, // break on finish. See EndpointHandler for this. Not applicable here.
|
true, // break on finish. See EndpointHandler for this. Not applicable here.
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
const Mustache = @import("zap").Mustache;
|
const Mustache = @import("zap").Mustache;
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
const template =
|
const template =
|
||||||
\\ {{=<< >>=}}
|
\\ {{=<< >>=}}
|
||||||
\\ * Users:
|
\\ * Users:
|
||||||
|
@ -49,7 +49,7 @@ fn on_request(r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.log = true,
|
.log = true,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn dispatch_routes(r: zap.SimpleRequest) void {
|
// NOTE: this is a super simplified example, just using a hashmap to map
|
||||||
|
// from HTTP path to request function.
|
||||||
|
fn dispatch_routes(r: zap.Request) void {
|
||||||
// dispatch
|
// dispatch
|
||||||
if (r.path) |the_path| {
|
if (r.path) |the_path| {
|
||||||
if (routes.get(the_path)) |foo| {
|
if (routes.get(the_path)) |foo| {
|
||||||
|
@ -20,12 +22,12 @@ fn dispatch_routes(r: zap.SimpleRequest) void {
|
||||||
) catch return;
|
) catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_site(r: zap.SimpleRequest) void {
|
fn static_site(r: zap.Request) void {
|
||||||
r.sendBody("<html><body><h1>Hello from STATIC ZAP!</h1></body></html>") catch return;
|
r.sendBody("<html><body><h1>Hello from STATIC ZAP!</h1></body></html>") catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dynamic_counter: i32 = 0;
|
var dynamic_counter: i32 = 0;
|
||||||
fn dynamic_site(r: zap.SimpleRequest) void {
|
fn dynamic_site(r: zap.Request) void {
|
||||||
dynamic_counter += 1;
|
dynamic_counter += 1;
|
||||||
var buf: [128]u8 = undefined;
|
var buf: [128]u8 = undefined;
|
||||||
const filled_buf = std.fmt.bufPrintZ(
|
const filled_buf = std.fmt.bufPrintZ(
|
||||||
|
@ -37,16 +39,16 @@ fn dynamic_site(r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_routes(a: std.mem.Allocator) !void {
|
fn setup_routes(a: std.mem.Allocator) !void {
|
||||||
routes = std.StringHashMap(zap.SimpleHttpRequestFn).init(a);
|
routes = std.StringHashMap(zap.HttpRequestFn).init(a);
|
||||||
try routes.put("/static", static_site);
|
try routes.put("/static", static_site);
|
||||||
try routes.put("/dynamic", dynamic_site);
|
try routes.put("/dynamic", dynamic_site);
|
||||||
}
|
}
|
||||||
|
|
||||||
var routes: std.StringHashMap(zap.SimpleHttpRequestFn) = undefined;
|
var routes: std.StringHashMap(zap.HttpRequestFn) = undefined;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
try setup_routes(std.heap.page_allocator);
|
try setup_routes(std.heap.page_allocator);
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = dispatch_routes,
|
.on_request = dispatch_routes,
|
||||||
.log = true,
|
.log = true,
|
||||||
|
|
|
@ -5,14 +5,14 @@ fn MAKE_MEGA_ERROR() !void {
|
||||||
return error.MEGA_ERROR;
|
return error.MEGA_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn MY_REQUEST_HANDLER(r: zap.SimpleRequest) void {
|
fn MY_REQUEST_HANDLER(r: zap.Request) void {
|
||||||
MAKE_MEGA_ERROR() catch |err| {
|
MAKE_MEGA_ERROR() catch |err| {
|
||||||
r.sendError(err, 505);
|
r.sendError(err, 505);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = MY_REQUEST_HANDLER,
|
.on_request = MY_REQUEST_HANDLER,
|
||||||
.log = true,
|
.log = true,
|
||||||
|
|
|
@ -6,7 +6,7 @@ var read_len: ?usize = null;
|
||||||
|
|
||||||
const testfile = @embedFile("testfile.txt");
|
const testfile = @embedFile("testfile.txt");
|
||||||
|
|
||||||
pub fn on_request(r: zap.SimpleRequest) void {
|
pub fn on_request(r: zap.Request) void {
|
||||||
// Sends a file if present in the filesystem orelse returns an error.
|
// Sends a file if present in the filesystem orelse returns an error.
|
||||||
//
|
//
|
||||||
// - efficiently sends a file using gzip compression
|
// - efficiently sends a file using gzip compression
|
||||||
|
@ -27,7 +27,7 @@ pub fn on_request(r: zap.SimpleRequest) void {
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleHttpListener.init(
|
var listener = zap.HttpListener.init(
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
r.setStatus(.not_found);
|
r.setStatus(.not_found);
|
||||||
r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return;
|
r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.public_folder = "examples/serve",
|
.public_folder = "examples/serve",
|
||||||
|
|
|
@ -20,17 +20,17 @@ const loginpage = @embedFile("html/login.html");
|
||||||
const img = @embedFile("./html/Ziggy_the_Ziguana.svg.png");
|
const img = @embedFile("./html/Ziggy_the_Ziguana.svg.png");
|
||||||
|
|
||||||
// global vars yeah!
|
// global vars yeah!
|
||||||
// in bigger projects, we'd probably make use of zap.SimpleEndpoint or
|
// in bigger projects, we'd probably make use of zap.Endpoint or
|
||||||
// zap.Middleware and "hide" stuff like authenticators in there
|
// zap.Middleware and "hide" stuff like authenticators in there
|
||||||
var authenticator: Authenticator = undefined;
|
var authenticator: Authenticator = undefined;
|
||||||
|
|
||||||
// the login page (embedded)
|
// the login page (embedded)
|
||||||
fn on_login(r: zap.SimpleRequest) void {
|
fn on_login(r: zap.Request) void {
|
||||||
r.sendBody(loginpage) catch return;
|
r.sendBody(loginpage) catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the "normal page"
|
// the "normal page"
|
||||||
fn on_normal_page(r: zap.SimpleRequest) void {
|
fn on_normal_page(r: zap.Request) void {
|
||||||
zap.debug("on_normal_page()\n", .{});
|
zap.debug("on_normal_page()\n", .{});
|
||||||
r.sendBody(
|
r.sendBody(
|
||||||
\\ <html><body>
|
\\ <html><body>
|
||||||
|
@ -42,7 +42,7 @@ fn on_normal_page(r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the logged-out page
|
// the logged-out page
|
||||||
fn on_logout(r: zap.SimpleRequest) void {
|
fn on_logout(r: zap.Request) void {
|
||||||
zap.debug("on_logout()\n", .{});
|
zap.debug("on_logout()\n", .{});
|
||||||
authenticator.logout(&r);
|
authenticator.logout(&r);
|
||||||
// note, the link below doesn't matter as the authenticator will send us
|
// note, the link below doesn't matter as the authenticator will send us
|
||||||
|
@ -56,7 +56,7 @@ fn on_logout(r: zap.SimpleRequest) void {
|
||||||
) catch return;
|
) catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
switch (authenticator.authenticateRequest(&r)) {
|
switch (authenticator.authenticateRequest(&r)) {
|
||||||
.Handled => {
|
.Handled => {
|
||||||
// the authenticator handled the entire request for us.
|
// the authenticator handled the entire request for us.
|
||||||
|
@ -124,7 +124,7 @@ pub fn main() !void {
|
||||||
// to detect leaks
|
// to detect leaks
|
||||||
{
|
{
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.log = true,
|
.log = true,
|
||||||
|
|
|
@ -114,6 +114,7 @@ fn on_close_websocket(context: ?*Context, uuid: isize) void {
|
||||||
std.log.info("websocket closed: {s}", .{message});
|
std.log.info("websocket closed: {s}", .{message});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_websocket_message(
|
fn handle_websocket_message(
|
||||||
context: ?*Context,
|
context: ?*Context,
|
||||||
handle: WebSockets.WsHandle,
|
handle: WebSockets.WsHandle,
|
||||||
|
@ -162,7 +163,7 @@ fn handle_websocket_message(
|
||||||
//
|
//
|
||||||
// HTTP stuff
|
// HTTP stuff
|
||||||
//
|
//
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
r.setHeader("Server", "zap.example") catch unreachable;
|
r.setHeader("Server", "zap.example") catch unreachable;
|
||||||
r.sendBody(
|
r.sendBody(
|
||||||
\\ <html><body>
|
\\ <html><body>
|
||||||
|
@ -171,7 +172,7 @@ fn on_request(r: zap.SimpleRequest) void {
|
||||||
) catch return;
|
) catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_upgrade(r: zap.SimpleRequest, target_protocol: []const u8) void {
|
fn on_upgrade(r: zap.Request, target_protocol: []const u8) void {
|
||||||
// make sure we're talking the right protocol
|
// make sure we're talking the right protocol
|
||||||
if (!std.mem.eql(u8, target_protocol, "websocket")) {
|
if (!std.mem.eql(u8, target_protocol, "websocket")) {
|
||||||
std.log.warn("received illegal protocol: {s}", .{target_protocol});
|
std.log.warn("received illegal protocol: {s}", .{target_protocol});
|
||||||
|
@ -207,7 +208,7 @@ pub fn main() !void {
|
||||||
defer GlobalContextManager.deinit();
|
defer GlobalContextManager.deinit();
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleHttpListener.init(
|
var listener = zap.HttpListener.init(
|
||||||
.{
|
.{
|
||||||
.port = 3010,
|
.port = 3010,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
|
|
202
src/endpoint.zig
202
src/endpoint.zig
|
@ -2,29 +2,83 @@ const std = @import("std");
|
||||||
const zap = @import("zap.zig");
|
const zap = @import("zap.zig");
|
||||||
const auth = @import("http_auth.zig");
|
const auth = @import("http_auth.zig");
|
||||||
|
|
||||||
const Request = zap.SimpleRequest;
|
// zap types
|
||||||
const ListenerSettings = zap.SimpleHttpListenerSettings;
|
const Request = zap.Request;
|
||||||
const Listener = zap.SimpleHttpListener;
|
const ListenerSettings = zap.HttpListenerSettings;
|
||||||
|
const Listener = zap.HttpListener;
|
||||||
|
|
||||||
pub const RequestFn = *const fn (self: *SimpleEndpoint, r: Request) void;
|
/// Type of the request function callbacks.
|
||||||
pub const SimpleEndpointSettings = struct {
|
pub const RequestFn = *const fn (self: *Endpoint, r: Request) void;
|
||||||
|
|
||||||
|
/// Settings to initialize an Endpoint
|
||||||
|
pub const EndpointSettings = struct {
|
||||||
|
/// path / slug of the endpoint
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
|
/// callback to GET request handler
|
||||||
get: ?RequestFn = null,
|
get: ?RequestFn = null,
|
||||||
|
/// callback to POST request handler
|
||||||
post: ?RequestFn = null,
|
post: ?RequestFn = null,
|
||||||
|
/// callback to PUT request handler
|
||||||
put: ?RequestFn = null,
|
put: ?RequestFn = null,
|
||||||
|
/// callback to DELETE request handler
|
||||||
delete: ?RequestFn = null,
|
delete: ?RequestFn = null,
|
||||||
|
/// callback to PATCH request handler
|
||||||
patch: ?RequestFn = null,
|
patch: ?RequestFn = null,
|
||||||
|
/// callback to OPTIONS request handler
|
||||||
options: ?RequestFn = null,
|
options: ?RequestFn = null,
|
||||||
/// only applicable to AuthenticatingEndpoint
|
/// Only applicable to AuthenticatingEndpoint: handler for unauthorized requests
|
||||||
unauthorized: ?RequestFn = null,
|
unauthorized: ?RequestFn = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SimpleEndpoint = struct {
|
/// The simple Endpoint struct. Create one and pass in your callbacks. Then,
|
||||||
settings: SimpleEndpointSettings,
|
/// pass it to a HttpListener's `register()` function to register with the
|
||||||
|
/// listener.
|
||||||
|
///
|
||||||
|
/// **NOTE**: A common endpoint pattern for zap is to create your own struct
|
||||||
|
/// that embeds an Endpoint, provides specific callbacks, and uses
|
||||||
|
/// `@fieldParentPtr` to get a reference to itself.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// A simple endpoint listening on the /stop route that shuts down zap.
|
||||||
|
/// The main thread usually continues at the instructions after the call to zap.start().
|
||||||
|
///
|
||||||
|
/// ```zig
|
||||||
|
/// const StopEndpoint = struct {
|
||||||
|
/// ep: zap.Endpoint = undefined,
|
||||||
|
///
|
||||||
|
/// pub fn init(
|
||||||
|
/// path: []const u8,
|
||||||
|
/// ) StopEndpoint {
|
||||||
|
/// return .{
|
||||||
|
/// .ep = zap.Endpoint.init(.{
|
||||||
|
/// .path = path,
|
||||||
|
/// .get = get,
|
||||||
|
/// }),
|
||||||
|
/// };
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // access the internal Endpoint
|
||||||
|
/// pub fn endpoint(self: *StopEndpoint) *zap.Endpoint {
|
||||||
|
/// return &self.ep;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn get(e: *zap.Endpoint, r: zap.Request) void {
|
||||||
|
/// const self: *StopEndpoint = @fieldParentPtr(StopEndpoint, "ep", e);
|
||||||
|
/// _ = self;
|
||||||
|
/// _ = r;
|
||||||
|
/// zap.stop();
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
pub const Endpoint = struct {
|
||||||
|
settings: EndpointSettings,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(s: SimpleEndpointSettings) Self {
|
/// Initialize the endpoint.
|
||||||
|
/// Set only the callbacks you need. Requests of HTTP methods without a
|
||||||
|
/// provided callback will be ignored.
|
||||||
|
pub fn init(s: EndpointSettings) Self {
|
||||||
return .{
|
return .{
|
||||||
.settings = .{
|
.settings = .{
|
||||||
.path = s.path,
|
.path = s.path,
|
||||||
|
@ -39,12 +93,14 @@ pub const SimpleEndpoint = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nop(self: *SimpleEndpoint, r: Request) void {
|
// no operation. Dummy handler function for ignoring unset request types.
|
||||||
|
fn nop(self: *Endpoint, r: Request) void {
|
||||||
_ = self;
|
_ = self;
|
||||||
_ = r;
|
_ = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onRequest(self: *SimpleEndpoint, r: zap.SimpleRequest) void {
|
/// The global request handler for this Endpoint, called by the listener.
|
||||||
|
pub fn onRequest(self: *Endpoint, r: zap.Request) void {
|
||||||
if (r.method) |m| {
|
if (r.method) |m| {
|
||||||
if (std.mem.eql(u8, m, "GET"))
|
if (std.mem.eql(u8, m, "GET"))
|
||||||
return self.settings.get.?(self, r);
|
return self.settings.get.?(self, r);
|
||||||
|
@ -62,19 +118,23 @@ pub const SimpleEndpoint = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Wrap an endpoint with an authenticator
|
/// Wrap an endpoint with an Authenticator -> new Endpoint of type Endpoint
|
||||||
|
/// is available via the `endpoint()` function.
|
||||||
pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
authenticator: *Authenticator,
|
authenticator: *Authenticator,
|
||||||
endpoint: *SimpleEndpoint,
|
ep: *Endpoint,
|
||||||
auth_endpoint: SimpleEndpoint,
|
auth_endpoint: Endpoint,
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(e: *SimpleEndpoint, authenticator: *Authenticator) Self {
|
/// Init the authenticating endpoint. Pass in a pointer to the endpoint
|
||||||
|
/// you want to wrap, and the Authenticator that takes care of authenticating
|
||||||
|
/// requests.
|
||||||
|
pub fn init(e: *Endpoint, authenticator: *Authenticator) Self {
|
||||||
return .{
|
return .{
|
||||||
.authenticator = authenticator,
|
.authenticator = authenticator,
|
||||||
.endpoint = e,
|
.ep = e,
|
||||||
.auth_endpoint = SimpleEndpoint.init(.{
|
.auth_endpoint = Endpoint.init(.{
|
||||||
.path = e.settings.path,
|
.path = e.settings.path,
|
||||||
// we override only the set ones. the other ones
|
// we override only the set ones. the other ones
|
||||||
// are set to null anyway -> will be nopped out
|
// are set to null anyway -> will be nopped out
|
||||||
|
@ -89,20 +149,21 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the auth endpoint struct so we can be stored in the listener
|
/// Get the auth endpoint struct of type Endpoint so it can be stored in the listener.
|
||||||
/// when the listener calls the auth_endpoint, onRequest will have
|
/// When the listener calls the auth_endpoint, onRequest will have
|
||||||
/// access to all of us via fieldParentPtr
|
/// access to all of this via fieldParentPtr
|
||||||
pub fn getEndpoint(self: *Self) *SimpleEndpoint {
|
pub fn endpoint(self: *Self) *Endpoint {
|
||||||
return &self.auth_endpoint;
|
return &self.auth_endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// here, the auth_endpoint will be passed in
|
/// GET: here, the auth_endpoint will be passed in as endpoint.
|
||||||
pub fn get(e: *SimpleEndpoint, r: zap.SimpleRequest) void {
|
/// Authenticates GET requests using the Authenticator.
|
||||||
|
pub fn get(e: *Endpoint, r: zap.Request) void {
|
||||||
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
||||||
switch (authEp.authenticator.authenticateRequest(&r)) {
|
switch (authEp.authenticator.authenticateRequest(&r)) {
|
||||||
.AuthFailed => {
|
.AuthFailed => {
|
||||||
if (e.settings.unauthorized) |unauthorized| {
|
if (e.settings.unauthorized) |unauthorized| {
|
||||||
unauthorized(authEp.endpoint, r);
|
unauthorized(authEp.ep, r);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
|
@ -110,18 +171,19 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.AuthOK => authEp.endpoint.settings.get.?(authEp.endpoint, r),
|
.AuthOK => authEp.ep.settings.get.?(authEp.ep, r),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// here, the auth_endpoint will be passed in
|
/// POST: here, the auth_endpoint will be passed in as endpoint.
|
||||||
pub fn post(e: *SimpleEndpoint, r: zap.SimpleRequest) void {
|
/// Authenticates POST requests using the Authenticator.
|
||||||
|
pub fn post(e: *Endpoint, r: zap.Request) void {
|
||||||
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
||||||
switch (authEp.authenticator.authenticateRequest(&r)) {
|
switch (authEp.authenticator.authenticateRequest(&r)) {
|
||||||
.AuthFailed => {
|
.AuthFailed => {
|
||||||
if (e.settings.unauthorized) |unauthorized| {
|
if (e.settings.unauthorized) |unauthorized| {
|
||||||
unauthorized(authEp.endpoint, r);
|
unauthorized(authEp.ep, r);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
|
@ -129,18 +191,19 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.AuthOK => authEp.endpoint.settings.post.?(authEp.endpoint, r),
|
.AuthOK => authEp.ep.settings.post.?(authEp.ep, r),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// here, the auth_endpoint will be passed in
|
/// PUT: here, the auth_endpoint will be passed in as endpoint.
|
||||||
pub fn put(e: *SimpleEndpoint, r: zap.SimpleRequest) void {
|
/// Authenticates PUT requests using the Authenticator.
|
||||||
|
pub fn put(e: *Endpoint, r: zap.Request) void {
|
||||||
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
||||||
switch (authEp.authenticator.authenticateRequest(&r)) {
|
switch (authEp.authenticator.authenticateRequest(&r)) {
|
||||||
.AuthFailed => {
|
.AuthFailed => {
|
||||||
if (e.settings.unauthorized) |unauthorized| {
|
if (e.settings.unauthorized) |unauthorized| {
|
||||||
unauthorized(authEp.endpoint, r);
|
unauthorized(authEp.ep, r);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
|
@ -148,18 +211,19 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.AuthOK => authEp.endpoint.settings.put.?(authEp.endpoint, r),
|
.AuthOK => authEp.ep.settings.put.?(authEp.ep, r),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// here, the auth_endpoint will be passed in
|
/// DELETE: here, the auth_endpoint will be passed in as endpoint.
|
||||||
pub fn delete(e: *SimpleEndpoint, r: zap.SimpleRequest) void {
|
/// Authenticates DELETE requests using the Authenticator.
|
||||||
|
pub fn delete(e: *Endpoint, r: zap.Request) void {
|
||||||
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
||||||
switch (authEp.authenticator.authenticateRequest(&r)) {
|
switch (authEp.authenticator.authenticateRequest(&r)) {
|
||||||
.AuthFailed => {
|
.AuthFailed => {
|
||||||
if (e.settings.unauthorized) |unauthorized| {
|
if (e.settings.unauthorized) |unauthorized| {
|
||||||
unauthorized(authEp.endpoint, r);
|
unauthorized(authEp.ep, r);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
|
@ -167,18 +231,19 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.AuthOK => authEp.endpoint.settings.delete.?(authEp.endpoint, r),
|
.AuthOK => authEp.ep.settings.delete.?(authEp.ep, r),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// here, the auth_endpoint will be passed in
|
/// PATCH: here, the auth_endpoint will be passed in as endpoint.
|
||||||
pub fn patch(e: *SimpleEndpoint, r: zap.SimpleRequest) void {
|
/// Authenticates PATCH requests using the Authenticator.
|
||||||
|
pub fn patch(e: *Endpoint, r: zap.Request) void {
|
||||||
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
||||||
switch (authEp.authenticator.authenticateRequest(&r)) {
|
switch (authEp.authenticator.authenticateRequest(&r)) {
|
||||||
.AuthFailed => {
|
.AuthFailed => {
|
||||||
if (e.settings.unauthorized) |unauthorized| {
|
if (e.settings.unauthorized) |unauthorized| {
|
||||||
unauthorized(authEp.endpoint, r);
|
unauthorized(authEp.ep, r);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
|
@ -186,18 +251,19 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.AuthOK => authEp.endpoint.settings.patch.?(authEp.endpoint, r),
|
.AuthOK => authEp.ep.settings.patch.?(authEp.ep, r),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// here, the auth_endpoint will be passed in
|
/// OPTIONS: here, the auth_endpoint will be passed in as endpoint.
|
||||||
pub fn options(e: *SimpleEndpoint, r: zap.SimpleRequest) void {
|
/// Authenticates OPTIONS requests using the Authenticator.
|
||||||
|
pub fn options(e: *Endpoint, r: zap.Request) void {
|
||||||
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
|
||||||
switch (authEp.authenticator.authenticateRequest(&r)) {
|
switch (authEp.authenticator.authenticateRequest(&r)) {
|
||||||
.AuthFailed => {
|
.AuthFailed => {
|
||||||
if (e.settings.unauthorized) |unauthorized| {
|
if (e.settings.unauthorized) |unauthorized| {
|
||||||
unauthorized(authEp.endpoint, r);
|
unauthorized(authEp.ep, r);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
|
@ -205,7 +271,7 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.AuthOK => authEp.endpoint.settings.put.?(authEp.endpoint, r),
|
.AuthOK => authEp.ep.settings.put.?(authEp.ep, r),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,26 +279,44 @@ pub fn AuthenticatingEndpoint(comptime Authenticator: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const EndpointListenerError = error{
|
pub const EndpointListenerError = error{
|
||||||
|
/// Since we use .startsWith to check for matching paths, you cannot use
|
||||||
|
/// endpoint paths that overlap at the beginning. --> When trying to register
|
||||||
|
/// an endpoint whose path would shadow an already registered one, you will
|
||||||
|
/// receive this error.
|
||||||
EndpointPathShadowError,
|
EndpointPathShadowError,
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: We switch on path.startsWith -> so use endpoints with distinctly
|
/// The listener with ednpoint support
|
||||||
// starting names!!
|
///
|
||||||
pub const SimpleEndpointListener = struct {
|
/// NOTE: It switches on path.startsWith -> so use endpoints with distinctly starting names!!
|
||||||
|
pub const EndpointListener = struct {
|
||||||
listener: Listener,
|
listener: Listener,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
/// static struct member endpoints
|
/// Internal static struct of member endpoints
|
||||||
var endpoints: std.ArrayList(*SimpleEndpoint) = undefined;
|
var endpoints: std.ArrayList(*Endpoint) = undefined;
|
||||||
var on_request: ?zap.SimpleHttpRequestFn = null;
|
|
||||||
|
|
||||||
|
/// Internal, static request handler callback. Will be set to the optional,
|
||||||
|
/// user-defined request callback that only gets called if no endpoints match
|
||||||
|
/// a request.
|
||||||
|
var on_request: ?zap.HttpRequestFn = null;
|
||||||
|
|
||||||
|
/// Initialize a new endpoint listener. Note, if you pass an `on_request`
|
||||||
|
/// callback in the provided ListenerSettings, this request callback will be
|
||||||
|
/// called every time a request arrives that no endpoint matches.
|
||||||
pub fn init(a: std.mem.Allocator, l: ListenerSettings) Self {
|
pub fn init(a: std.mem.Allocator, l: ListenerSettings) Self {
|
||||||
endpoints = std.ArrayList(*SimpleEndpoint).init(a);
|
endpoints = std.ArrayList(*Endpoint).init(a);
|
||||||
|
|
||||||
var ls = l; // take copy of listener settings
|
// take copy of listener settings before modifying the callback field
|
||||||
|
var ls = l;
|
||||||
|
|
||||||
|
// override the settings with our internal, actul callback function
|
||||||
|
// so that "we" will be called on request
|
||||||
ls.on_request = onRequest;
|
ls.on_request = onRequest;
|
||||||
|
|
||||||
|
// store the settings-provided request callback for later use
|
||||||
on_request = l.on_request;
|
on_request = l.on_request;
|
||||||
return .{
|
return .{
|
||||||
.listener = Listener.init(ls),
|
.listener = Listener.init(ls),
|
||||||
|
@ -240,16 +324,25 @@ pub const SimpleEndpointListener = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// De-init the listener and free its resources.
|
||||||
|
/// Registered endpoints will not be de-initialized automatically; just removed
|
||||||
|
/// from the internal map.
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
_ = self;
|
_ = self;
|
||||||
endpoints.deinit();
|
endpoints.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call this to start listening. After this, no more endpoints can be
|
||||||
|
/// registered.
|
||||||
pub fn listen(self: *Self) !void {
|
pub fn listen(self: *Self) !void {
|
||||||
try self.listener.listen();
|
try self.listener.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addEndpoint(self: *Self, e: *SimpleEndpoint) !void {
|
/// Register an endpoint with this listener.
|
||||||
|
/// NOTE: endpoint paths are matched with startsWith -> so use endpoints with distinctly starting names!!
|
||||||
|
/// If you try to register an endpoint whose path would shadow an already registered one, you will
|
||||||
|
/// receive an EndpointPathShadowError.
|
||||||
|
pub fn register(self: *Self, e: *Endpoint) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
for (endpoints.items) |other| {
|
for (endpoints.items) |other| {
|
||||||
if (std.mem.startsWith(
|
if (std.mem.startsWith(
|
||||||
|
@ -276,6 +369,7 @@ pub const SimpleEndpointListener = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if set, call the user-provided default callback
|
||||||
if (on_request) |foo| {
|
if (on_request) |foo| {
|
||||||
foo(r);
|
foo(r);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap.zig");
|
const zap = @import("zap.zig");
|
||||||
|
|
||||||
|
/// Authentication Scheme enum: Basic or Bearer.
|
||||||
pub const AuthScheme = enum {
|
pub const AuthScheme = enum {
|
||||||
Basic,
|
Basic,
|
||||||
Bearer,
|
Bearer,
|
||||||
|
@ -27,6 +28,7 @@ pub const AuthScheme = enum {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Used internally: check for presence of the requested auth header.
|
||||||
pub fn checkAuthHeader(scheme: AuthScheme, auth_header: []const u8) bool {
|
pub fn checkAuthHeader(scheme: AuthScheme, auth_header: []const u8) bool {
|
||||||
return switch (scheme) {
|
return switch (scheme) {
|
||||||
.Basic => |b| std.mem.startsWith(u8, auth_header, b.str()) and auth_header.len > b.str().len,
|
.Basic => |b| std.mem.startsWith(u8, auth_header, b.str()) and auth_header.len > b.str().len,
|
||||||
|
@ -34,13 +36,15 @@ pub fn checkAuthHeader(scheme: AuthScheme, auth_header: []const u8) bool {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extractAuthHeader(scheme: AuthScheme, r: *const zap.SimpleRequest) ?[]const u8 {
|
/// Used internally: return the requested auth header.
|
||||||
|
pub fn extractAuthHeader(scheme: AuthScheme, r: *const zap.Request) ?[]const u8 {
|
||||||
return switch (scheme) {
|
return switch (scheme) {
|
||||||
.Basic => |b| r.getHeader(b.headerFieldStrFio()),
|
.Basic => |b| r.getHeader(b.headerFieldStrFio()),
|
||||||
.Bearer => |b| r.getHeader(b.headerFieldStrFio()),
|
.Bearer => |b| r.getHeader(b.headerFieldStrFio()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decoding Strategy for Basic Authentication
|
||||||
const BasicAuthStrategy = enum {
|
const BasicAuthStrategy = enum {
|
||||||
/// decode into user and pass, then check pass
|
/// decode into user and pass, then check pass
|
||||||
UserPass,
|
UserPass,
|
||||||
|
@ -48,20 +52,21 @@ const BasicAuthStrategy = enum {
|
||||||
Token68,
|
Token68,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Authentication result
|
||||||
pub const AuthResult = enum {
|
pub const AuthResult = enum {
|
||||||
/// authentication / authorization was successful
|
/// authentication / authorization was successful
|
||||||
AuthOK,
|
AuthOK,
|
||||||
/// authentication / authorization failed
|
/// authentication / authorization failed
|
||||||
AuthFailed,
|
AuthFailed,
|
||||||
/// the authenticator handled the request that didn't pass authentication /
|
/// The authenticator handled the request that didn't pass authentication /
|
||||||
/// authorization .
|
/// authorization.
|
||||||
/// this is used to implement authenticators that redirect to a login
|
/// This is used to implement authenticators that redirect to a login
|
||||||
/// page. An AuthenticatingEndpoint will not do the default, which is trying
|
/// page. An AuthenticatingEndpoint will not do the default, which is trying
|
||||||
/// to call the `unauthorized` callback or.
|
/// to call the `unauthorized` callback if one exists orelse ignore the request.
|
||||||
Handled,
|
Handled,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// HTTP Basic Authentication RFC 7617
|
/// HTTP Basic Authentication RFC 7617.
|
||||||
/// "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
/// "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||||
/// user-pass strings: "$username:$password" -> base64
|
/// user-pass strings: "$username:$password" -> base64
|
||||||
///
|
///
|
||||||
|
@ -73,10 +78,9 @@ pub const AuthResult = enum {
|
||||||
/// Errors:
|
/// Errors:
|
||||||
/// WWW-Authenticate: Basic realm="this"
|
/// WWW-Authenticate: Basic realm="this"
|
||||||
///
|
///
|
||||||
/// T : any kind of map that implements get([]const u8) -> []const u8
|
/// Lookup : any kind of map that implements get([]const u8) -> []const u8
|
||||||
pub fn BasicAuth(comptime Lookup: type, comptime kind: BasicAuthStrategy) type {
|
pub fn BasicAuth(comptime Lookup: type, comptime kind: BasicAuthStrategy) type {
|
||||||
return struct {
|
return struct {
|
||||||
// kind: BasicAuthStrategy,
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
realm: ?[]const u8,
|
realm: ?[]const u8,
|
||||||
lookup: *Lookup,
|
lookup: *Lookup,
|
||||||
|
@ -87,23 +91,24 @@ pub fn BasicAuth(comptime Lookup: type, comptime kind: BasicAuthStrategy) type {
|
||||||
/// different implementations can
|
/// different implementations can
|
||||||
/// - either decode, lookup and compare passwords
|
/// - either decode, lookup and compare passwords
|
||||||
/// - or just check for existence of the base64-encoded user:pass combination
|
/// - or just check for existence of the base64-encoded user:pass combination
|
||||||
/// if realm is provided (not null), a copy is taken -> call deinit() to clean up
|
/// if realm is provided (not null), a copy of it is taken -> call deinit() to clean up
|
||||||
pub fn init(allocator: std.mem.Allocator, lookup: *Lookup, realm: ?[]const u8) !Self {
|
pub fn init(allocator: std.mem.Allocator, lookup: *Lookup, realm: ?[]const u8) !Self {
|
||||||
return .{
|
return .{
|
||||||
// .kind = kind,
|
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.lookup = lookup,
|
.lookup = lookup,
|
||||||
.realm = if (realm) |the_realm| try allocator.dupe(u8, the_realm) else null,
|
.realm = if (realm) |the_realm| try allocator.dupe(u8, the_realm) else null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deinit the authenticator.
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
if (self.realm) |the_realm| {
|
if (self.realm) |the_realm| {
|
||||||
self.allocator.free(the_realm);
|
self.allocator.free(the_realm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use this to decode the auth_header into user:pass, lookup pass in lookup
|
/// Use this to decode the auth_header into user:pass, lookup pass in lookup.
|
||||||
|
/// Note: usually, you don't want to use this; you'd go for `authenticateRequest()`.
|
||||||
pub fn authenticateUserPass(self: *Self, auth_header: []const u8) AuthResult {
|
pub fn authenticateUserPass(self: *Self, auth_header: []const u8) AuthResult {
|
||||||
zap.debug("AuthenticateUserPass\n", .{});
|
zap.debug("AuthenticateUserPass\n", .{});
|
||||||
const encoded = auth_header[AuthScheme.Basic.str().len..];
|
const encoded = auth_header[AuthScheme.Basic.str().len..];
|
||||||
|
@ -165,22 +170,28 @@ pub fn BasicAuth(comptime Lookup: type, comptime kind: BasicAuthStrategy) type {
|
||||||
return .AuthFailed;
|
return .AuthFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use this to just look up if the base64-encoded auth_header exists in lookup
|
/// Use this to just look up if the base64-encoded auth_header exists in lookup.
|
||||||
|
/// Note: usually, you don't want to use this; you'd go for `authenticateRequest()`.
|
||||||
pub fn authenticateToken68(self: *Self, auth_header: []const u8) AuthResult {
|
pub fn authenticateToken68(self: *Self, auth_header: []const u8) AuthResult {
|
||||||
const token = auth_header[AuthScheme.Basic.str().len..];
|
const token = auth_header[AuthScheme.Basic.str().len..];
|
||||||
return if (self.lookup.*.contains(token)) .AuthOK else .AuthFailed;
|
return if (self.lookup.*.contains(token)) .AuthOK else .AuthFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch based on kind
|
/// dispatch based on kind (.UserPass / .Token689) and try to authenticate based on the header.
|
||||||
|
/// Note: usually, you don't want to use this; you'd go for `authenticateRequest()`.
|
||||||
pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
|
pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
|
||||||
zap.debug("AUTHENTICATE\n", .{});
|
zap.debug("AUTHENTICATE\n", .{});
|
||||||
// switch (self.kind) {
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
.UserPass => return self.authenticateUserPass(auth_header),
|
.UserPass => return self.authenticateUserPass(auth_header),
|
||||||
.Token68 => return self.authenticateToken68(auth_header),
|
.Token68 => return self.authenticateToken68(auth_header),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn authenticateRequest(self: *Self, r: *const zap.SimpleRequest) AuthResult {
|
|
||||||
|
/// The zap authentication request handler.
|
||||||
|
///
|
||||||
|
/// Tries to extract the authentication header and perform the authentication.
|
||||||
|
/// If no authentication header is found, an authorization header is tried.
|
||||||
|
pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
|
||||||
zap.debug("AUTHENTICATE REQUEST\n", .{});
|
zap.debug("AUTHENTICATE REQUEST\n", .{});
|
||||||
if (extractAuthHeader(.Basic, r)) |auth_header| {
|
if (extractAuthHeader(.Basic, r)) |auth_header| {
|
||||||
zap.debug("Authentication Header found!\n", .{});
|
zap.debug("Authentication Header found!\n", .{});
|
||||||
|
@ -215,10 +226,9 @@ pub const BearerAuthSingle = struct {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
/// Creates a Single-Token Bearer Authenticator
|
/// Creates a Single-Token Bearer Authenticator.
|
||||||
/// takes a copy of the token
|
/// Takes a copy of the token.
|
||||||
/// if realm is provided (not null), a copy is taken
|
/// If realm is provided (not null), a copy is taken call deinit() to clean up.
|
||||||
/// call deinit() to clean up
|
|
||||||
pub fn init(allocator: std.mem.Allocator, token: []const u8, realm: ?[]const u8) !Self {
|
pub fn init(allocator: std.mem.Allocator, token: []const u8, realm: ?[]const u8) !Self {
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
@ -226,6 +236,9 @@ pub const BearerAuthSingle = struct {
|
||||||
.realm = if (realm) |the_realm| try allocator.dupe(u8, the_realm) else null,
|
.realm = if (realm) |the_realm| try allocator.dupe(u8, the_realm) else null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to authenticate based on the header.
|
||||||
|
/// Note: usually, you don't want to use this; you'd go for `authenticateRequest()`.
|
||||||
pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
|
pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
|
||||||
if (checkAuthHeader(.Bearer, auth_header) == false) {
|
if (checkAuthHeader(.Bearer, auth_header) == false) {
|
||||||
return .AuthFailed;
|
return .AuthFailed;
|
||||||
|
@ -234,13 +247,17 @@ pub const BearerAuthSingle = struct {
|
||||||
return if (std.mem.eql(u8, token, self.token)) .AuthOK else .AuthFailed;
|
return if (std.mem.eql(u8, token, self.token)) .AuthOK else .AuthFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticateRequest(self: *Self, r: *const zap.SimpleRequest) AuthResult {
|
/// The zap authentication request handler.
|
||||||
|
///
|
||||||
|
/// Tries to extract the authentication header and perform the authentication.
|
||||||
|
pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
|
||||||
if (extractAuthHeader(.Bearer, r)) |auth_header| {
|
if (extractAuthHeader(.Bearer, r)) |auth_header| {
|
||||||
return self.authenticate(auth_header);
|
return self.authenticate(auth_header);
|
||||||
}
|
}
|
||||||
return .AuthFailed;
|
return .AuthFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deinits the authenticator.
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
if (self.realm) |the_realm| {
|
if (self.realm) |the_realm| {
|
||||||
self.allocator.free(the_realm);
|
self.allocator.free(the_realm);
|
||||||
|
@ -267,9 +284,9 @@ pub fn BearerAuthMulti(comptime Lookup: type) type {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
/// Creates a BasicAuth. `lookup` must implement `.get([]const u8) -> []const u8`
|
/// Creates a Multi Token Bearer Authenticator. `lookup` must implement
|
||||||
/// to look up tokens
|
/// `.get([]const u8) -> []const u8` to look up tokens.
|
||||||
/// if realm is provided (not null), a copy is taken -> call deinit() to clean up
|
/// If realm is provided (not null), a copy of it is taken -> call deinit() to clean up.
|
||||||
pub fn init(allocator: std.mem.Allocator, lookup: *Lookup, realm: ?[]const u8) !Self {
|
pub fn init(allocator: std.mem.Allocator, lookup: *Lookup, realm: ?[]const u8) !Self {
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
@ -278,12 +295,16 @@ pub fn BearerAuthMulti(comptime Lookup: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deinit the authenticator. Only required if a realm was provided at
|
||||||
|
/// init() time.
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
if (self.realm) |the_realm| {
|
if (self.realm) |the_realm| {
|
||||||
self.allocator.free(the_realm);
|
self.allocator.free(the_realm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to authenticate based on the header.
|
||||||
|
/// Note: usually, you don't want to use this; you'd go for `authenticateRequest()`.
|
||||||
pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
|
pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
|
||||||
if (checkAuthHeader(.Bearer, auth_header) == false) {
|
if (checkAuthHeader(.Bearer, auth_header) == false) {
|
||||||
return .AuthFailed;
|
return .AuthFailed;
|
||||||
|
@ -292,7 +313,10 @@ pub fn BearerAuthMulti(comptime Lookup: type) type {
|
||||||
return if (self.lookup.*.contains(token)) .AuthOK else .AuthFailed;
|
return if (self.lookup.*.contains(token)) .AuthOK else .AuthFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticateRequest(self: *Self, r: *const zap.SimpleRequest) AuthResult {
|
/// The zap authentication request handler.
|
||||||
|
///
|
||||||
|
/// Tries to extract the authentication header and perform the authentication.
|
||||||
|
pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
|
||||||
if (extractAuthHeader(.Bearer, r)) |auth_header| {
|
if (extractAuthHeader(.Bearer, r)) |auth_header| {
|
||||||
return self.authenticate(auth_header);
|
return self.authenticate(auth_header);
|
||||||
}
|
}
|
||||||
|
@ -301,6 +325,7 @@ pub fn BearerAuthMulti(comptime Lookup: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Settings to initialize a UserPassSessionAuth authenticator.
|
||||||
pub const UserPassSessionAuthArgs = struct {
|
pub const UserPassSessionAuthArgs = struct {
|
||||||
/// username body parameter
|
/// username body parameter
|
||||||
usernameParam: []const u8,
|
usernameParam: []const u8,
|
||||||
|
@ -308,7 +333,7 @@ pub const UserPassSessionAuthArgs = struct {
|
||||||
passwordParam: []const u8,
|
passwordParam: []const u8,
|
||||||
/// redirect to this page if auth fails
|
/// redirect to this page if auth fails
|
||||||
loginPage: []const u8,
|
loginPage: []const u8,
|
||||||
/// name of the cookie
|
/// name of the auth cookie
|
||||||
cookieName: []const u8,
|
cookieName: []const u8,
|
||||||
/// cookie max age in seconds; 0 -> session cookie
|
/// cookie max age in seconds; 0 -> session cookie
|
||||||
cookieMaxAge: u8 = 0,
|
cookieMaxAge: u8 = 0,
|
||||||
|
@ -330,8 +355,8 @@ pub const UserPassSessionAuthArgs = struct {
|
||||||
///
|
///
|
||||||
/// Please note the implications of this simple approach: IF YOU REUSE "username"
|
/// Please note the implications of this simple approach: IF YOU REUSE "username"
|
||||||
/// and "password" body params for anything else in your application, then the
|
/// and "password" body params for anything else in your application, then the
|
||||||
/// mechanisms described above will kick in. For that reason: please know what you're
|
/// mechanisms described above will still kick in. For that reason: please know what
|
||||||
/// doing.
|
/// you're doing.
|
||||||
///
|
///
|
||||||
/// See UserPassSessionAuthArgs:
|
/// See UserPassSessionAuthArgs:
|
||||||
/// - username & password param names can be defined by you
|
/// - username & password param names can be defined by you
|
||||||
|
@ -357,7 +382,7 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
lookup: *Lookup,
|
lookup: *Lookup,
|
||||||
settings: UserPassSessionAuthArgs,
|
settings: UserPassSessionAuthArgs,
|
||||||
|
|
||||||
// TODO: cookie store per user
|
// TODO: cookie store per user?
|
||||||
sessionTokens: SessionTokenMap,
|
sessionTokens: SessionTokenMap,
|
||||||
passwordLookupLock: std.Thread.Mutex = .{},
|
passwordLookupLock: std.Thread.Mutex = .{},
|
||||||
tokenLookupLock: std.Thread.Mutex = .{},
|
tokenLookupLock: std.Thread.Mutex = .{},
|
||||||
|
@ -368,6 +393,8 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
|
|
||||||
const Token = [Hash.digest_length * 2]u8;
|
const Token = [Hash.digest_length * 2]u8;
|
||||||
|
|
||||||
|
/// Construct this authenticator. See above and related types for more
|
||||||
|
/// information.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
lookup: *Lookup,
|
lookup: *Lookup,
|
||||||
|
@ -390,6 +417,7 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// De-init this authenticator.
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.allocator.free(self.settings.usernameParam);
|
self.allocator.free(self.settings.usernameParam);
|
||||||
self.allocator.free(self.settings.passwordParam);
|
self.allocator.free(self.settings.passwordParam);
|
||||||
|
@ -405,8 +433,9 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check for session token cookie, remove the token from the valid tokens
|
/// Check for session token cookie, remove the token from the valid tokens
|
||||||
pub fn logout(self: *Self, r: *const zap.SimpleRequest) void {
|
pub fn logout(self: *Self, r: *const zap.Request) void {
|
||||||
// we erase the list of valid tokens server-side
|
// we erase the list of valid tokens server-side (later) and set the
|
||||||
|
// cookie to "invalid" on the client side.
|
||||||
if (r.setCookie(.{
|
if (r.setCookie(.{
|
||||||
.name = self.settings.cookieName,
|
.name = self.settings.cookieName,
|
||||||
.value = "invalid",
|
.value = "invalid",
|
||||||
|
@ -420,7 +449,7 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
r.parseCookies(false);
|
r.parseCookies(false);
|
||||||
|
|
||||||
// check for session cookie
|
// check for session cookie
|
||||||
if (r.getCookieStr(self.settings.cookieName, self.allocator, false)) |maybe_cookie| {
|
if (r.getCookieStr(self.allocator, self.settings.cookieName, false)) |maybe_cookie| {
|
||||||
if (maybe_cookie) |cookie| {
|
if (maybe_cookie) |cookie| {
|
||||||
defer cookie.deinit();
|
defer cookie.deinit();
|
||||||
self.tokenLookupLock.lock();
|
self.tokenLookupLock.lock();
|
||||||
|
@ -439,7 +468,7 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _internal_authenticateRequest(self: *Self, r: *const zap.SimpleRequest) AuthResult {
|
fn _internal_authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
|
||||||
// if we're requesting the login page, let the request through
|
// if we're requesting the login page, let the request through
|
||||||
if (r.path) |p| {
|
if (r.path) |p| {
|
||||||
if (std.mem.startsWith(u8, p, self.settings.loginPage)) {
|
if (std.mem.startsWith(u8, p, self.settings.loginPage)) {
|
||||||
|
@ -456,7 +485,7 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
r.parseCookies(false);
|
r.parseCookies(false);
|
||||||
|
|
||||||
// check for session cookie
|
// check for session cookie
|
||||||
if (r.getCookieStr(self.settings.cookieName, self.allocator, false)) |maybe_cookie| {
|
if (r.getCookieStr(self.allocator, self.settings.cookieName, false)) |maybe_cookie| {
|
||||||
if (maybe_cookie) |cookie| {
|
if (maybe_cookie) |cookie| {
|
||||||
defer cookie.deinit();
|
defer cookie.deinit();
|
||||||
// locked or unlocked token lookup
|
// locked or unlocked token lookup
|
||||||
|
@ -478,10 +507,10 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// get params of username and password
|
// get params of username and password
|
||||||
if (r.getParamStr(self.settings.usernameParam, self.allocator, false)) |maybe_username| {
|
if (r.getParamStr(self.allocator, self.settings.usernameParam, false)) |maybe_username| {
|
||||||
if (maybe_username) |*username| {
|
if (maybe_username) |*username| {
|
||||||
defer username.deinit();
|
defer username.deinit();
|
||||||
if (r.getParamStr(self.settings.passwordParam, self.allocator, false)) |maybe_pw| {
|
if (r.getParamStr(self.allocator, self.settings.passwordParam, false)) |maybe_pw| {
|
||||||
if (maybe_pw) |*pw| {
|
if (maybe_pw) |*pw| {
|
||||||
defer pw.deinit();
|
defer pw.deinit();
|
||||||
|
|
||||||
|
@ -530,7 +559,10 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
return .AuthFailed;
|
return .AuthFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticateRequest(self: *Self, r: *const zap.SimpleRequest) AuthResult {
|
/// The zap authentication request handler.
|
||||||
|
///
|
||||||
|
/// See above for how it works.
|
||||||
|
pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
|
||||||
switch (self._internal_authenticateRequest(r)) {
|
switch (self._internal_authenticateRequest(r)) {
|
||||||
.AuthOK => {
|
.AuthOK => {
|
||||||
// username and pass are ok -> created token, set header, caller can continue
|
// username and pass are ok -> created token, set header, caller can continue
|
||||||
|
@ -550,7 +582,7 @@ pub fn UserPassSessionAuth(comptime Lookup: type, comptime lockedPwLookups: bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redirect(self: *Self, r: *const zap.SimpleRequest) !void {
|
fn redirect(self: *Self, r: *const zap.Request) !void {
|
||||||
try r.redirectTo(self.settings.loginPage, self.settings.redirectCode);
|
try r.redirectTo(self.settings.loginPage, self.settings.redirectCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
// TODO: rework logging in zap
|
||||||
|
|
||||||
debugOn: bool,
|
debugOn: bool,
|
||||||
|
|
||||||
|
/// Access to facil.io's logging facilities
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(comptime debug: bool) Self {
|
pub fn init(comptime debug: bool) Self {
|
||||||
|
|
|
@ -1,43 +1,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap.zig");
|
const zap = @import("zap.zig");
|
||||||
|
|
||||||
pub const ContextDescriptor = struct {
|
/// Your middleware components need to contain a handler.
|
||||||
name: []const u8,
|
///
|
||||||
type: type,
|
/// A Handler is one element in the chain of request handlers that will be tried
|
||||||
};
|
/// by the listener when a request arrives. Handlers indicate to the previous
|
||||||
|
/// handler whether they processed a request by returning `true` from their
|
||||||
/// Provide a tuple of structs of type like ContextDescriptor
|
/// `on_request` function, in which case a typical request handler would stop
|
||||||
/// a name starting with '?', such as "?user" will be treated as Optional with default `null`.
|
/// trying to pass the request on to the next handler in the chain. See
|
||||||
pub fn MixContexts(comptime context_tuple: anytype) type {
|
/// the `handle_other` function in this struct.
|
||||||
var fields: [context_tuple.len]std.builtin.Type.StructField = undefined;
|
|
||||||
for (context_tuple, 0..) |t, i| {
|
|
||||||
var fieldType: type = t.type;
|
|
||||||
var fieldName: []const u8 = t.name[0..];
|
|
||||||
var isOptional: bool = false;
|
|
||||||
if (fieldName[0] == '?') {
|
|
||||||
fieldType = @Type(.{ .Optional = .{ .child = fieldType } });
|
|
||||||
fieldName = fieldName[1..];
|
|
||||||
isOptional = true;
|
|
||||||
}
|
|
||||||
fields[i] = .{
|
|
||||||
.name = fieldName,
|
|
||||||
.type = fieldType,
|
|
||||||
.default_value = if (isOptional) &@as(fieldType, null) else null,
|
|
||||||
.is_comptime = false,
|
|
||||||
.alignment = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return @Type(.{
|
|
||||||
.Struct = .{
|
|
||||||
.layout = .Auto,
|
|
||||||
.fields = fields[0..],
|
|
||||||
.decls = &[_]std.builtin.Type.Declaration{},
|
|
||||||
.is_tuple = false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Your middleware components need to contain a handler
|
|
||||||
pub fn Handler(comptime ContextType: anytype) type {
|
pub fn Handler(comptime ContextType: anytype) type {
|
||||||
return struct {
|
return struct {
|
||||||
other_handler: ?*Self = null,
|
other_handler: ?*Self = null,
|
||||||
|
@ -46,7 +17,7 @@ pub fn Handler(comptime ContextType: anytype) type {
|
||||||
// will be set
|
// will be set
|
||||||
allocator: ?std.mem.Allocator = null,
|
allocator: ?std.mem.Allocator = null,
|
||||||
|
|
||||||
pub const RequestFn = *const fn (*Self, zap.SimpleRequest, *ContextType) bool;
|
pub const RequestFn = *const fn (*Self, zap.Request, *ContextType) bool;
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(on_request: RequestFn, other: ?*Self) Self {
|
pub fn init(on_request: RequestFn, other: ?*Self) Self {
|
||||||
|
@ -56,10 +27,10 @@ pub fn Handler(comptime ContextType: anytype) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// example for handling request
|
// example for handling a request request
|
||||||
// which you can use in your components, e.g.:
|
// which you can use in your components, e.g.:
|
||||||
// return self.handler.handleOther(r, context);
|
// return self.handler.handleOther(r, context);
|
||||||
pub fn handleOther(self: *Self, r: zap.SimpleRequest, context: *ContextType) bool {
|
pub fn handleOther(self: *Self, r: zap.Request, context: *ContextType) bool {
|
||||||
// in structs embedding a handler, we'd @fieldParentPtr the first
|
// in structs embedding a handler, we'd @fieldParentPtr the first
|
||||||
// param to get to the real self
|
// param to get to the real self
|
||||||
|
|
||||||
|
@ -80,16 +51,19 @@ pub fn Handler(comptime ContextType: anytype) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A convenience handler for artibrary zap.SimpleEndpoint
|
/// A convenience handler for artibrary zap.Endpoint
|
||||||
pub fn EndpointHandler(comptime HandlerType: anytype, comptime ContextType: anytype) type {
|
pub fn EndpointHandler(comptime HandlerType: anytype, comptime ContextType: anytype) type {
|
||||||
return struct {
|
return struct {
|
||||||
handler: HandlerType,
|
handler: HandlerType,
|
||||||
endpoint: *zap.SimpleEndpoint,
|
endpoint: *zap.Endpoint,
|
||||||
breakOnFinish: bool,
|
breakOnFinish: bool,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(endpoint: *zap.SimpleEndpoint, other: ?*HandlerType, breakOnFinish: bool) Self {
|
/// Create an endpointhandler from an endpoint and pass in the next (other) handler in the chain.
|
||||||
|
/// If `breakOnFinish` is `true`, the handler will stop handing requests down the chain if
|
||||||
|
/// the endpoint processed the request.
|
||||||
|
pub fn init(endpoint: *zap.Endpoint, other: ?*HandlerType, breakOnFinish: bool) Self {
|
||||||
return .{
|
return .{
|
||||||
.handler = HandlerType.init(onRequest, other),
|
.handler = HandlerType.init(onRequest, other),
|
||||||
.endpoint = endpoint,
|
.endpoint = endpoint,
|
||||||
|
@ -97,12 +71,17 @@ pub fn EndpointHandler(comptime HandlerType: anytype, comptime ContextType: anyt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need the handler as a common interface to chain stuff
|
/// Provides the handler as a common interface to chain stuff
|
||||||
pub fn getHandler(self: *Self) *HandlerType {
|
pub fn getHandler(self: *Self) *HandlerType {
|
||||||
return &self.handler;
|
return &self.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onRequest(handler: *HandlerType, r: zap.SimpleRequest, context: *ContextType) bool {
|
/// The Handler's request handling function. Gets called from the listener
|
||||||
|
/// with the request and a context instance. Calls the endpoint.
|
||||||
|
///
|
||||||
|
/// If `breakOnFinish` is `true`, the handler will stop handing requests down the chain if
|
||||||
|
/// the endpoint processed the request.
|
||||||
|
pub fn onRequest(handler: *HandlerType, r: zap.Request, context: *ContextType) bool {
|
||||||
var self = @fieldParentPtr(Self, "handler", handler);
|
var self = @fieldParentPtr(Self, "handler", handler);
|
||||||
r.setUserContext(context);
|
r.setUserContext(context);
|
||||||
self.endpoint.onRequest(r);
|
self.endpoint.onRequest(r);
|
||||||
|
@ -117,15 +96,18 @@ pub fn EndpointHandler(comptime HandlerType: anytype, comptime ContextType: anyt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Error = error{
|
pub const Error = error{
|
||||||
|
/// The listener could not be created because the settings provided to its
|
||||||
|
/// init() function contained an `on_request` callback that was not null.
|
||||||
InitOnRequestIsNotNull,
|
InitOnRequestIsNotNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RequestAllocatorFn = *const fn () std.mem.Allocator;
|
pub const RequestAllocatorFn = *const fn () std.mem.Allocator;
|
||||||
|
|
||||||
|
/// Special Listener that supports chaining request handlers.
|
||||||
pub fn Listener(comptime ContextType: anytype) type {
|
pub fn Listener(comptime ContextType: anytype) type {
|
||||||
return struct {
|
return struct {
|
||||||
listener: zap.SimpleHttpListener = undefined,
|
listener: zap.HttpListener = undefined,
|
||||||
settings: zap.SimpleHttpListenerSettings,
|
settings: zap.HttpListenerSettings,
|
||||||
|
|
||||||
// static initial handler
|
// static initial handler
|
||||||
var handler: ?*Handler(ContextType) = undefined;
|
var handler: ?*Handler(ContextType) = undefined;
|
||||||
|
@ -134,9 +116,10 @@ pub fn Listener(comptime ContextType: anytype) type {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
/// initialize the middleware handler
|
/// Construct and initialize a middleware handler.
|
||||||
/// the passed in settings must have on_request set to null
|
/// The passed in settings must have on_request set to null! If that is
|
||||||
pub fn init(settings: zap.SimpleHttpListenerSettings, initial_handler: *Handler(ContextType), request_alloc: ?RequestAllocatorFn) Error!Self {
|
/// not the case, an InitOnRequestIsNotNull error will be returned.
|
||||||
|
pub fn init(settings: zap.HttpListenerSettings, initial_handler: *Handler(ContextType), request_alloc: ?RequestAllocatorFn) Error!Self {
|
||||||
// override on_request with ourselves
|
// override on_request with ourselves
|
||||||
if (settings.on_request != null) {
|
if (settings.on_request != null) {
|
||||||
return Error.InitOnRequestIsNotNull;
|
return Error.InitOnRequestIsNotNull;
|
||||||
|
@ -147,21 +130,26 @@ pub fn Listener(comptime ContextType: anytype) type {
|
||||||
var ret: Self = .{
|
var ret: Self = .{
|
||||||
.settings = settings,
|
.settings = settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
ret.settings.on_request = onRequest;
|
ret.settings.on_request = onRequest;
|
||||||
ret.listener = zap.SimpleHttpListener.init(ret.settings);
|
ret.listener = zap.HttpListener.init(ret.settings);
|
||||||
handler = initial_handler;
|
handler = initial_handler;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start listening.
|
||||||
pub fn listen(self: *Self) !void {
|
pub fn listen(self: *Self) !void {
|
||||||
try self.listener.listen();
|
try self.listener.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is just a reference implementation
|
/// The listener's request handler, stepping through the chain of Handlers
|
||||||
// but it's actually used obviously. Create your own listener if you
|
/// by calling the initial one which takes it from there.
|
||||||
// want different behavior.
|
///
|
||||||
// Didn't want to make this a callback
|
/// This is just a reference implementation that you can use by default.
|
||||||
pub fn onRequest(r: zap.SimpleRequest) void {
|
/// Create your own listener if you want different behavior.
|
||||||
|
/// (Didn't want to make this a callback. Submit an issue if you really
|
||||||
|
/// think that's an issue).
|
||||||
|
pub fn onRequest(r: zap.Request) void {
|
||||||
// we are the 1st handler in the chain, so we create a context
|
// we are the 1st handler in the chain, so we create a context
|
||||||
var context: ContextType = .{};
|
var context: ContextType = .{};
|
||||||
|
|
||||||
|
@ -182,60 +170,3 @@ pub fn Listener(comptime ContextType: anytype) type {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test "it" {
|
|
||||||
|
|
||||||
// just some made-up struct
|
|
||||||
const User = struct {
|
|
||||||
name: []const u8,
|
|
||||||
email: []const u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
// just some made-up struct
|
|
||||||
const Session = struct {
|
|
||||||
sessionType: []const u8,
|
|
||||||
token: []const u8,
|
|
||||||
valid: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Mixed = MixContexts(
|
|
||||||
.{
|
|
||||||
.{ .name = "?user", .type = *User },
|
|
||||||
.{ .name = "?session", .type = *Session },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
std.debug.print("{any}\n", .{Mixed});
|
|
||||||
inline for (@typeInfo(Mixed).Struct.fields, 0..) |f, i| {
|
|
||||||
std.debug.print("field {} : name = {s} : type = {any}\n", .{ i, f.name, f.type });
|
|
||||||
}
|
|
||||||
|
|
||||||
const mixed: Mixed = .{
|
|
||||||
// it's all optionals which we made default to null in MixContexts
|
|
||||||
};
|
|
||||||
std.debug.print("mixed = {any}\n", .{mixed});
|
|
||||||
|
|
||||||
const NonOpts = MixContexts(
|
|
||||||
.{
|
|
||||||
.{ .name = "user", .type = *User },
|
|
||||||
.{ .name = "session", .type = *Session },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
var user: User = .{
|
|
||||||
.name = "renerocksai",
|
|
||||||
.email = "secret",
|
|
||||||
};
|
|
||||||
var session: Session = .{
|
|
||||||
.sessionType = "bearerToken",
|
|
||||||
.token = "ABCDEFG",
|
|
||||||
.valid = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// this will fail if we don't specify
|
|
||||||
const nonOpts: NonOpts = .{
|
|
||||||
.user = &user,
|
|
||||||
.session = &session,
|
|
||||||
};
|
|
||||||
std.debug.print("nonOpts = {any}\n", .{nonOpts});
|
|
||||||
}
|
|
||||||
|
|
|
@ -106,21 +106,21 @@ const HTTP_RESPONSE: []const u8 =
|
||||||
;
|
;
|
||||||
var received_response: []const u8 = "null";
|
var received_response: []const u8 = "null";
|
||||||
|
|
||||||
fn endpoint_http_get(e: *Endpoints.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn endpoint_http_get(e: *Endpoints.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
r.sendBody(HTTP_RESPONSE) catch return;
|
r.sendBody(HTTP_RESPONSE) catch return;
|
||||||
received_response = HTTP_RESPONSE;
|
received_response = HTTP_RESPONSE;
|
||||||
std.time.sleep(1 * std.time.ns_per_s);
|
std.time.sleep(1 * std.time.ns_per_s);
|
||||||
zap.fio_stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endpoint_http_unauthorized(e: *Endpoints.SimpleEndpoint, r: zap.SimpleRequest) void {
|
fn endpoint_http_unauthorized(e: *Endpoints.Endpoint, r: zap.Request) void {
|
||||||
_ = e;
|
_ = e;
|
||||||
r.setStatus(.unauthorized);
|
r.setStatus(.unauthorized);
|
||||||
r.sendBody("UNAUTHORIZED ACCESS") catch return;
|
r.sendBody("UNAUTHORIZED ACCESS") catch return;
|
||||||
received_response = "UNAUTHORIZED";
|
received_response = "UNAUTHORIZED";
|
||||||
std.time.sleep(1 * std.time.ns_per_s);
|
std.time.sleep(1 * std.time.ns_per_s);
|
||||||
zap.fio_stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -165,7 +165,7 @@ fn makeRequest(a: std.mem.Allocator, url: []const u8, auth: ?ClientAuthReqHeader
|
||||||
std.debug.print("RESPONSE:\n{s}\n", .{buffer[0..len]});
|
std.debug.print("RESPONSE:\n{s}\n", .{buffer[0..len]});
|
||||||
}
|
}
|
||||||
|
|
||||||
zap.fio_stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeRequestThread(a: std.mem.Allocator, url: []const u8, auth: ?ClientAuthReqHeaderFields) !std.Thread {
|
fn makeRequestThread(a: std.mem.Allocator, url: []const u8, auth: ?ClientAuthReqHeaderFields) !std.Thread {
|
||||||
|
@ -181,7 +181,7 @@ test "BearerAuthSingle authenticateRequest OK" {
|
||||||
const token = "ABCDEFG";
|
const token = "ABCDEFG";
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -194,7 +194,7 @@ test "BearerAuthSingle authenticateRequest OK" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -209,7 +209,7 @@ test "BearerAuthSingle authenticateRequest OK" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("\n\n*******************************************\n", .{});
|
// std.debug.print("\n\n*******************************************\n", .{});
|
||||||
|
@ -234,7 +234,7 @@ test "BearerAuthSingle authenticateRequest test-unauthorized" {
|
||||||
const token = "ABCDEFG";
|
const token = "ABCDEFG";
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -247,7 +247,7 @@ test "BearerAuthSingle authenticateRequest test-unauthorized" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -268,7 +268,7 @@ test "BearerAuthSingle authenticateRequest test-unauthorized" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
// std.debug.print("Waiting for the following:\n", .{});
|
||||||
|
@ -291,7 +291,7 @@ test "BearerAuthMulti authenticateRequest OK" {
|
||||||
const token = "ABCDEFG";
|
const token = "ABCDEFG";
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -304,7 +304,7 @@ test "BearerAuthMulti authenticateRequest OK" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -319,7 +319,7 @@ test "BearerAuthMulti authenticateRequest OK" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
// std.debug.print("Waiting for the following:\n", .{});
|
||||||
|
@ -342,7 +342,7 @@ test "BearerAuthMulti authenticateRequest test-unauthorized" {
|
||||||
const token = "invalid";
|
const token = "invalid";
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -355,7 +355,7 @@ test "BearerAuthMulti authenticateRequest test-unauthorized" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -370,7 +370,7 @@ test "BearerAuthMulti authenticateRequest test-unauthorized" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
// std.debug.print("Waiting for the following:\n", .{});
|
||||||
|
@ -393,7 +393,7 @@ test "BasicAuth Token68 authenticateRequest" {
|
||||||
const token = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
|
const token = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -406,7 +406,7 @@ test "BasicAuth Token68 authenticateRequest" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -426,7 +426,7 @@ test "BasicAuth Token68 authenticateRequest" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
// std.debug.print("Waiting for the following:\n", .{});
|
||||||
|
@ -449,7 +449,7 @@ test "BasicAuth Token68 authenticateRequest test-unauthorized" {
|
||||||
const token = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
|
const token = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -462,7 +462,7 @@ test "BasicAuth Token68 authenticateRequest test-unauthorized" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -482,7 +482,7 @@ test "BasicAuth Token68 authenticateRequest test-unauthorized" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
// std.debug.print("Waiting for the following:\n", .{});
|
||||||
|
@ -504,7 +504,7 @@ test "BasicAuth UserPass authenticateRequest" {
|
||||||
const a = std.testing.allocator;
|
const a = std.testing.allocator;
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -517,7 +517,7 @@ test "BasicAuth UserPass authenticateRequest" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -548,7 +548,7 @@ test "BasicAuth UserPass authenticateRequest" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
// std.debug.print("Waiting for the following:\n", .{});
|
||||||
|
@ -570,7 +570,7 @@ test "BasicAuth UserPass authenticateRequest test-unauthorized" {
|
||||||
const a = std.testing.allocator;
|
const a = std.testing.allocator;
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.EndpointListener.init(
|
||||||
a,
|
a,
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
@ -583,7 +583,7 @@ test "BasicAuth UserPass authenticateRequest test-unauthorized" {
|
||||||
defer listener.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep = Endpoints.SimpleEndpoint.init(.{
|
var ep = Endpoints.Endpoint.init(.{
|
||||||
.path = "/test",
|
.path = "/test",
|
||||||
.get = endpoint_http_get,
|
.get = endpoint_http_get,
|
||||||
.unauthorized = endpoint_http_unauthorized,
|
.unauthorized = endpoint_http_unauthorized,
|
||||||
|
@ -615,7 +615,7 @@ test "BasicAuth UserPass authenticateRequest test-unauthorized" {
|
||||||
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
const BearerAuthEndpoint = Endpoints.AuthenticatingEndpoint(Authenticator);
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
try listener.addEndpoint(auth_ep.getEndpoint());
|
try listener.register(auth_ep.endpoint());
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
// std.debug.print("Waiting for the following:\n", .{});
|
||||||
|
|
|
@ -15,7 +15,7 @@ fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
||||||
|
|
||||||
try req.send(.{});
|
try req.send(.{});
|
||||||
try req.wait();
|
try req.wait();
|
||||||
zap.fio_stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeRequestThread(a: std.mem.Allocator, url: []const u8) !std.Thread {
|
fn makeRequestThread(a: std.mem.Allocator, url: []const u8) !std.Thread {
|
||||||
|
@ -34,7 +34,7 @@ test "http parameters" {
|
||||||
var params: ?zap.HttpParamKVList = null;
|
var params: ?zap.HttpParamKVList = null;
|
||||||
var paramOneStr: ?zap.FreeOrNot = null;
|
var paramOneStr: ?zap.FreeOrNot = null;
|
||||||
|
|
||||||
pub fn on_request(r: zap.SimpleRequest) void {
|
pub fn on_request(r: zap.Request) void {
|
||||||
ran = true;
|
ran = true;
|
||||||
r.parseQuery();
|
r.parseQuery();
|
||||||
param_count = r.getParamCount();
|
param_count = r.getParamCount();
|
||||||
|
@ -45,7 +45,7 @@ test "http parameters" {
|
||||||
// true -> make copies of temp strings
|
// true -> make copies of temp strings
|
||||||
params = r.parametersToOwnedList(alloc, true) catch unreachable;
|
params = r.parametersToOwnedList(alloc, true) catch unreachable;
|
||||||
|
|
||||||
var maybe_str = r.getParamStr("one", alloc, true) catch unreachable;
|
var maybe_str = r.getParamStr(alloc, "one", true) catch unreachable;
|
||||||
if (maybe_str) |*s| {
|
if (maybe_str) |*s| {
|
||||||
paramOneStr = s.*;
|
paramOneStr = s.*;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ test "http parameters" {
|
||||||
Handler.alloc = allocator;
|
Handler.alloc = allocator;
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleHttpListener.init(
|
var listener = zap.HttpListener.init(
|
||||||
.{
|
.{
|
||||||
.port = 3001,
|
.port = 3001,
|
||||||
.on_request = Handler.on_request,
|
.on_request = Handler.on_request,
|
||||||
|
|
|
@ -22,13 +22,13 @@ fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
||||||
try req.wait();
|
try req.wait();
|
||||||
read_len = try req.readAll(&buffer);
|
read_len = try req.readAll(&buffer);
|
||||||
|
|
||||||
zap.fio_stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeRequestThread(a: std.mem.Allocator, url: []const u8) !std.Thread {
|
fn makeRequestThread(a: std.mem.Allocator, url: []const u8) !std.Thread {
|
||||||
return try std.Thread.spawn(.{}, makeRequest, .{ a, url });
|
return try std.Thread.spawn(.{}, makeRequest, .{ a, url });
|
||||||
}
|
}
|
||||||
pub fn on_request(r: zap.SimpleRequest) void {
|
pub fn on_request(r: zap.Request) void {
|
||||||
r.sendFile("src/tests/testfile.txt") catch unreachable;
|
r.sendFile("src/tests/testfile.txt") catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ test "send file" {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
|
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleHttpListener.init(
|
var listener = zap.HttpListener.init(
|
||||||
.{
|
.{
|
||||||
.port = 3002,
|
.port = 3002,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
|
|
64
src/util.zig
64
src/util.zig
|
@ -1,9 +1,10 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fio = @import("fio.zig");
|
const fio = @import("fio.zig");
|
||||||
|
|
||||||
|
/// Used internally: convert a FIO object into its string representation.
|
||||||
/// note: since this is called from within request functions, we don't make
|
/// note: since this is called from within request functions, we don't make
|
||||||
/// copies. Also, we return temp memory from fio. -> don't hold on to it outside
|
/// copies. Also, we return temp memory from fio. -> don't hold on to it outside
|
||||||
/// of a request function
|
/// of a request function. FIO temp memory strings do not need to be freed.
|
||||||
pub fn fio2str(o: fio.FIOBJ) ?[]const u8 {
|
pub fn fio2str(o: fio.FIOBJ) ?[]const u8 {
|
||||||
if (o == 0) return null;
|
if (o == 0) return null;
|
||||||
const x: fio.fio_str_info_s = fio.fiobj_obj2cstr(o);
|
const x: fio.fio_str_info_s = fio.fiobj_obj2cstr(o);
|
||||||
|
@ -12,6 +13,12 @@ pub fn fio2str(o: fio.FIOBJ) ?[]const u8 {
|
||||||
return x.data[0..x.len];
|
return x.data[0..x.len];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A "string" type used internally that carries a flag whether its buffer needs
|
||||||
|
/// to be freed or not - and honors it in `deinit()`. That way, it's always
|
||||||
|
/// safe to call deinit().
|
||||||
|
/// For instance, slices taken directly from the zap.Request need not be freed.
|
||||||
|
/// But the ad-hoc created string representation of a float parameter must be
|
||||||
|
/// freed after use.
|
||||||
pub const FreeOrNot = struct {
|
pub const FreeOrNot = struct {
|
||||||
str: []const u8,
|
str: []const u8,
|
||||||
freeme: bool,
|
freeme: bool,
|
||||||
|
@ -24,7 +31,10 @@ pub const FreeOrNot = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn fio2strAllocOrNot(o: fio.FIOBJ, a: std.mem.Allocator, always_alloc: bool) !FreeOrNot {
|
/// Used internally: convert a FIO object into its string representation.
|
||||||
|
/// Depending on the type of the object, a buffer will be created. Hence a
|
||||||
|
/// FreeOrNot type is used as the return type.
|
||||||
|
pub fn fio2strAllocOrNot(a: std.mem.Allocator, o: fio.FIOBJ, always_alloc: bool) !FreeOrNot {
|
||||||
if (o == 0) return .{ .str = "null", .freeme = false };
|
if (o == 0) return .{ .str = "null", .freeme = false };
|
||||||
if (o == fio.FIOBJ_INVALID) return .{ .str = "invalid", .freeme = false };
|
if (o == fio.FIOBJ_INVALID) return .{ .str = "invalid", .freeme = false };
|
||||||
return switch (fio.fiobj_type(o)) {
|
return switch (fio.fiobj_type(o)) {
|
||||||
|
@ -38,6 +48,8 @@ pub fn fio2strAllocOrNot(o: fio.FIOBJ, a: std.mem.Allocator, always_alloc: bool)
|
||||||
else => .{ .str = "unknown_type", .freeme = false },
|
else => .{ .str = "unknown_type", .freeme = false },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used internally: convert a zig slice into a FIO string.
|
||||||
pub fn str2fio(s: []const u8) fio.fio_str_info_s {
|
pub fn str2fio(s: []const u8) fio.fio_str_info_s {
|
||||||
return .{
|
return .{
|
||||||
.data = toCharPtr(s),
|
.data = toCharPtr(s),
|
||||||
|
@ -46,6 +58,7 @@ pub fn str2fio(s: []const u8) fio.fio_str_info_s {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used internally: convert a zig slice into a C pointer
|
||||||
pub fn toCharPtr(s: []const u8) [*c]u8 {
|
pub fn toCharPtr(s: []const u8) [*c]u8 {
|
||||||
return @as([*c]u8, @ptrFromInt(@intFromPtr(s.ptr)));
|
return @as([*c]u8, @ptrFromInt(@intFromPtr(s.ptr)));
|
||||||
}
|
}
|
||||||
|
@ -54,7 +67,8 @@ pub fn toCharPtr(s: []const u8) [*c]u8 {
|
||||||
// JSON helpers
|
// JSON helpers
|
||||||
//
|
//
|
||||||
|
|
||||||
/// provide your own buf, NOT mutex-protected!
|
/// Concenience: format an arbitrary value into a JSON string buffer.
|
||||||
|
/// Provide your own buf; this function is NOT mutex-protected!
|
||||||
pub fn stringifyBuf(
|
pub fn stringifyBuf(
|
||||||
buffer: []u8,
|
buffer: []u8,
|
||||||
value: anytype,
|
value: anytype,
|
||||||
|
@ -68,47 +82,3 @@ pub fn stringifyBuf(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// deprecated:
|
|
||||||
|
|
||||||
// 1MB JSON buffer
|
|
||||||
// var jsonbuf: [1024 * 1024]u8 = undefined;
|
|
||||||
// var mutex: std.Thread.Mutex = .{};
|
|
||||||
|
|
||||||
// use default 1MB buffer, mutex-protected
|
|
||||||
// pub fn stringify(
|
|
||||||
// value: anytype,
|
|
||||||
// options: std.json.StringifyOptions,
|
|
||||||
// ) ?[]const u8 {
|
|
||||||
// mutex.lock();
|
|
||||||
// defer mutex.unlock();
|
|
||||||
// var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
|
|
||||||
// var string = std.ArrayList(u8).init(fba.allocator());
|
|
||||||
// if (std.json.stringify(value, options, string.writer())) {
|
|
||||||
// return string.items;
|
|
||||||
// } else |_| { // error
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// use default 1MB buffer, mutex-protected
|
|
||||||
// pub fn stringifyArrayList(
|
|
||||||
// comptime T: anytype,
|
|
||||||
// list: *std.ArrayList(T),
|
|
||||||
// options: std.json.StringifyOptions,
|
|
||||||
// ) !?[]const u8 {
|
|
||||||
// mutex.lock();
|
|
||||||
// defer mutex.unlock();
|
|
||||||
// var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
|
|
||||||
// var string = std.ArrayList(u8).init(fba.allocator());
|
|
||||||
// var writer = string.writer();
|
|
||||||
// try writer.writeByte('[');
|
|
||||||
// var first: bool = true;
|
|
||||||
// for (list.items) |user| {
|
|
||||||
// if (!first) try writer.writeByte(',');
|
|
||||||
// first = false;
|
|
||||||
// try std.json.stringify(user, options, string.writer());
|
|
||||||
// }
|
|
||||||
// try writer.writeByte(']');
|
|
||||||
// return string.items;
|
|
||||||
// }
|
|
||||||
|
|
|
@ -3,10 +3,15 @@ const zap = @import("zap.zig");
|
||||||
const fio = @import("fio.zig");
|
const fio = @import("fio.zig");
|
||||||
const util = @import("util.zig");
|
const util = @import("util.zig");
|
||||||
|
|
||||||
|
/// The Handle type used for WebSocket connections. Do not mess with this.
|
||||||
pub const WsHandle = ?*fio.ws_s;
|
pub const WsHandle = ?*fio.ws_s;
|
||||||
|
|
||||||
|
/// WebSocket Handler. Pass in a Context type and it will give you a struct that
|
||||||
|
/// contains all the types and functions you need. See the websocket example
|
||||||
|
/// for more details.
|
||||||
pub fn Handler(comptime ContextType: type) type {
|
pub fn Handler(comptime ContextType: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
/// OnMessage Callback on a websocket
|
/// OnMessage Callback on a websocket, type.
|
||||||
pub const WsOnMessageFn = *const fn (
|
pub const WsOnMessageFn = *const fn (
|
||||||
/// user-provided context, passed in from websocketHttpUpgrade()
|
/// user-provided context, passed in from websocketHttpUpgrade()
|
||||||
context: ?*ContextType,
|
context: ?*ContextType,
|
||||||
|
@ -18,14 +23,16 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
is_text: bool,
|
is_text: bool,
|
||||||
) void;
|
) void;
|
||||||
|
|
||||||
/// Callback when websocket is closed. uuid is a connection identifier,
|
/// Callback (type) when websocket is closed. uuid is a connection identifier,
|
||||||
/// it is -1 if a connection could not be established
|
/// it is -1 if a connection could not be established
|
||||||
pub const WsOnCloseFn = *const fn (context: ?*ContextType, uuid: isize) void;
|
pub const WsOnCloseFn = *const fn (context: ?*ContextType, uuid: isize) void;
|
||||||
|
|
||||||
/// A websocket callback function. provides the context passed in at
|
/// A websocket callback function type. provides the context passed in at
|
||||||
/// websocketHttpUpgrade().
|
/// websocketHttpUpgrade().
|
||||||
pub const WsFn = *const fn (context: ?*ContextType, handle: WsHandle) void;
|
pub const WsFn = *const fn (context: ?*ContextType, handle: WsHandle) void;
|
||||||
|
|
||||||
|
/// Websocket connection handler creation settings. Provide the callbacks you need,
|
||||||
|
/// and an optional context.
|
||||||
pub const WebSocketSettings = struct {
|
pub const WebSocketSettings = struct {
|
||||||
/// on_message(context, handle, message, is_text)
|
/// on_message(context, handle, message, is_text)
|
||||||
on_message: ?WsOnMessageFn = null,
|
on_message: ?WsOnMessageFn = null,
|
||||||
|
@ -102,12 +109,13 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const WebSocketError = error{
|
pub const WebSocketError = error{
|
||||||
WriteError,
|
WriteError,
|
||||||
UpgradeError,
|
UpgradeError,
|
||||||
SubscribeError,
|
SubscribeError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Write to the websocket identified by the handle.
|
||||||
pub inline fn write(handle: WsHandle, message: []const u8, is_text: bool) WebSocketError!void {
|
pub inline fn write(handle: WsHandle, message: []const u8, is_text: bool) WebSocketError!void {
|
||||||
if (fio.websocket_write(
|
if (fio.websocket_write(
|
||||||
handle,
|
handle,
|
||||||
|
@ -118,21 +126,26 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The context pointer is stored in facilio's udata pointer. Use
|
||||||
|
/// this function to turn that pointer into a pointer to your
|
||||||
|
/// ContextType.
|
||||||
pub fn udataToContext(udata: *anyopaque) *ContextType {
|
pub fn udataToContext(udata: *anyopaque) *ContextType {
|
||||||
return @as(*ContextType, @ptrCast(@alignCast(udata)));
|
return @as(*ContextType, @ptrCast(@alignCast(udata)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Close the websocket connection.
|
||||||
pub inline fn close(handle: WsHandle) void {
|
pub inline fn close(handle: WsHandle) void {
|
||||||
fio.websocket_close(handle);
|
fio.websocket_close(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Settings for publishing a message in a channel.
|
||||||
const PublishArgs = struct {
|
const PublishArgs = struct {
|
||||||
channel: []const u8,
|
channel: []const u8,
|
||||||
message: []const u8,
|
message: []const u8,
|
||||||
is_json: bool = false,
|
is_json: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// publish a message in a channel
|
/// Publish a message in a channel.
|
||||||
pub inline fn publish(args: PublishArgs) void {
|
pub inline fn publish(args: PublishArgs) void {
|
||||||
fio.fio_publish(.{
|
fio.fio_publish(.{
|
||||||
.channel = util.str2fio(args.channel),
|
.channel = util.str2fio(args.channel),
|
||||||
|
@ -141,12 +154,19 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type for callback on subscription message.
|
||||||
pub const SubscriptionOnMessageFn = *const fn (context: ?*ContextType, handle: WsHandle, channel: []const u8, message: []const u8) void;
|
pub const SubscriptionOnMessageFn = *const fn (context: ?*ContextType, handle: WsHandle, channel: []const u8, message: []const u8) void;
|
||||||
|
|
||||||
|
/// Type for callback on unsubscribe message.
|
||||||
pub const SubscriptionOnUnsubscribeFn = *const fn (context: ?*ContextType) void;
|
pub const SubscriptionOnUnsubscribeFn = *const fn (context: ?*ContextType) void;
|
||||||
|
|
||||||
|
/// Settings for subscribing to a channel.
|
||||||
pub const SubscribeArgs = struct {
|
pub const SubscribeArgs = struct {
|
||||||
|
/// channel name
|
||||||
channel: []const u8,
|
channel: []const u8,
|
||||||
|
/// on message callback
|
||||||
on_message: ?SubscriptionOnMessageFn = null,
|
on_message: ?SubscriptionOnMessageFn = null,
|
||||||
|
/// on unsubscribe callback
|
||||||
on_unsubscribe: ?SubscriptionOnUnsubscribeFn = null,
|
on_unsubscribe: ?SubscriptionOnUnsubscribeFn = null,
|
||||||
/// this is not wrapped nicely yet
|
/// this is not wrapped nicely yet
|
||||||
match: fio.fio_match_fn = null,
|
match: fio.fio_match_fn = null,
|
||||||
|
@ -162,9 +182,11 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
/// above ~32Kb might be assumed to be binary rather than tested. force_binary has
|
/// above ~32Kb might be assumed to be binary rather than tested. force_binary has
|
||||||
/// precedence over force_text.
|
/// precedence over force_text.
|
||||||
force_text: bool = false,
|
force_text: bool = false,
|
||||||
|
/// your provided arbitrary context
|
||||||
context: ?*ContextType = null,
|
context: ?*ContextType = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Subscribe to a channel.
|
||||||
/// Returns a subscription ID on success and 0 on failure.
|
/// Returns a subscription ID on success and 0 on failure.
|
||||||
/// we copy the pointer so make sure the struct stays valid.
|
/// we copy the pointer so make sure the struct stays valid.
|
||||||
/// we need it to look up the ziggified callbacks.
|
/// we need it to look up the ziggified callbacks.
|
||||||
|
|
297
src/zap.zig
297
src/zap.zig
|
@ -2,18 +2,28 @@
|
||||||
// or maybe let's just make it zap directly...
|
// or maybe let's just make it zap directly...
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fio = @import("fio.zig");
|
|
||||||
|
/// The facilio C API. No need to use this.
|
||||||
|
pub const fio = @import("fio.zig");
|
||||||
|
|
||||||
/// Server-Side TLS function wrapper
|
/// Server-Side TLS function wrapper
|
||||||
pub const Tls = @import("tls.zig");
|
pub const Tls = @import("tls.zig");
|
||||||
|
|
||||||
pub usingnamespace @import("fio.zig");
|
// pub usingnamespace @import("fio.zig");
|
||||||
pub usingnamespace @import("endpoint.zig");
|
pub usingnamespace @import("endpoint.zig");
|
||||||
pub usingnamespace @import("util.zig");
|
pub usingnamespace @import("util.zig");
|
||||||
pub usingnamespace @import("http.zig");
|
pub usingnamespace @import("http.zig");
|
||||||
pub usingnamespace @import("mustache.zig");
|
pub usingnamespace @import("mustache.zig");
|
||||||
pub usingnamespace @import("http_auth.zig");
|
pub usingnamespace @import("http_auth.zig");
|
||||||
|
|
||||||
|
/// Middleware support.
|
||||||
|
/// Contains a special Listener and a Handler struct that support chaining
|
||||||
|
/// requests handlers, with an optional stop once a handler indicates it
|
||||||
|
/// processed the request. Also sports an EndpointHandler for using regular zap
|
||||||
|
/// Endpoints as Handlers.
|
||||||
pub const Middleware = @import("middleware.zig");
|
pub const Middleware = @import("middleware.zig");
|
||||||
|
|
||||||
|
/// Websocket API
|
||||||
pub const WebSockets = @import("websockets.zig");
|
pub const WebSockets = @import("websockets.zig");
|
||||||
|
|
||||||
pub const Log = @import("log.zig");
|
pub const Log = @import("log.zig");
|
||||||
|
@ -40,16 +50,20 @@ pub fn stop() void {
|
||||||
fio.fio_stop();
|
fio.fio_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extremely simplistic zap debug function.
|
||||||
|
/// TODO: re-wwrite logging or use std.log
|
||||||
pub fn debug(comptime fmt: []const u8, args: anytype) void {
|
pub fn debug(comptime fmt: []const u8, args: anytype) void {
|
||||||
if (_debug) {
|
if (_debug) {
|
||||||
std.debug.print("[zap] - " ++ fmt, args);
|
std.debug.print("[zap] - " ++ fmt, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable zap debug logging
|
||||||
pub fn enableDebugLog() void {
|
pub fn enableDebugLog() void {
|
||||||
_debug = true;
|
_debug = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// start Zap with debug logging on
|
||||||
pub fn startWithLogging(args: fio.fio_start_args) void {
|
pub fn startWithLogging(args: fio.fio_start_args) void {
|
||||||
debug = true;
|
debug = true;
|
||||||
fio.fio_start(args);
|
fio.fio_start(args);
|
||||||
|
@ -70,13 +84,17 @@ pub const HttpError = error{
|
||||||
SendFile,
|
SendFile,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Http Content Type enum.
|
||||||
|
/// Needs some love.
|
||||||
pub const ContentType = enum {
|
pub const ContentType = enum {
|
||||||
TEXT,
|
TEXT,
|
||||||
HTML,
|
HTML,
|
||||||
JSON,
|
JSON,
|
||||||
|
// TODO: more content types
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SimpleRequest = struct {
|
/// HttpRequest passed to request callback functions.
|
||||||
|
pub const Request = struct {
|
||||||
path: ?[]const u8,
|
path: ?[]const u8,
|
||||||
query: ?[]const u8,
|
query: ?[]const u8,
|
||||||
body: ?[]const u8,
|
body: ?[]const u8,
|
||||||
|
@ -90,7 +108,7 @@ pub const SimpleRequest = struct {
|
||||||
/// NEVER touch this field!!!!
|
/// NEVER touch this field!!!!
|
||||||
/// use markAsFinished() and isFinished() instead
|
/// use markAsFinished() and isFinished() instead
|
||||||
/// this is a hack: the listener will put a pointer to this into the udata
|
/// this is a hack: the listener will put a pointer to this into the udata
|
||||||
/// field of `h`. So copies of the SimpleRequest will all have way to the
|
/// field of `h`. So copies of the Request will all have way to the
|
||||||
/// same instance of this field.
|
/// same instance of this field.
|
||||||
_is_finished_request_global: bool,
|
_is_finished_request_global: bool,
|
||||||
/// NEVER touch this field!!!!
|
/// NEVER touch this field!!!!
|
||||||
|
@ -103,22 +121,27 @@ pub const SimpleRequest = struct {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
/// mark the current request as finished. Important for middleware-style
|
||||||
|
/// request handler chaining. Called when sending a body, redirecting, etc.
|
||||||
pub fn markAsFinished(self: *const Self, finished: bool) void {
|
pub fn markAsFinished(self: *const Self, finished: bool) void {
|
||||||
// we might be a copy
|
// we might be a copy
|
||||||
self._is_finished.* = finished;
|
self._is_finished.* = finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// tell whether request processing has finished. (e.g. response sent,
|
||||||
|
/// redirected, ...)
|
||||||
pub fn isFinished(self: *const Self) bool {
|
pub fn isFinished(self: *const Self) bool {
|
||||||
// we might be a copy
|
// we might be a copy
|
||||||
return self._is_finished.*;
|
return self._is_finished.*;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if you absolutely must, you can set any context here
|
/// if you absolutely must, you can set any context on the request here
|
||||||
// (note, this line is linked to from the readme)
|
// (note, this line is linked to from the readme) -- TODO: sync
|
||||||
pub fn setUserContext(self: *const Self, context: *anyopaque) void {
|
pub fn setUserContext(self: *const Self, context: *anyopaque) void {
|
||||||
self._user_context.*.user_context = context;
|
self._user_context.*.user_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get the associated user context of the request.
|
||||||
pub fn getUserContext(self: *const Self, comptime Context: type) ?*Context {
|
pub fn getUserContext(self: *const Self, comptime Context: type) ?*Context {
|
||||||
if (self._user_context.*.user_context) |ptr| {
|
if (self._user_context.*.user_context) |ptr| {
|
||||||
return @as(*Context, @ptrCast(@alignCast(ptr)));
|
return @as(*Context, @ptrCast(@alignCast(ptr)));
|
||||||
|
@ -127,6 +150,7 @@ pub const SimpleRequest = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to send an error stack trace.
|
||||||
pub fn sendError(self: *const Self, err: anyerror, errorcode_num: usize) void {
|
pub fn sendError(self: *const Self, err: anyerror, errorcode_num: usize) void {
|
||||||
// TODO: query accept headers
|
// TODO: query accept headers
|
||||||
if (self._internal_sendError(err, errorcode_num)) {
|
if (self._internal_sendError(err, errorcode_num)) {
|
||||||
|
@ -135,6 +159,8 @@ pub const SimpleRequest = struct {
|
||||||
self.sendBody(@errorName(err)) catch return;
|
self.sendBody(@errorName(err)) catch return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used internally. Probably does not need to be public.
|
||||||
pub fn _internal_sendError(self: *const Self, err: anyerror, errorcode_num: usize) !void {
|
pub fn _internal_sendError(self: *const Self, err: anyerror, errorcode_num: usize) !void {
|
||||||
// TODO: query accept headers
|
// TODO: query accept headers
|
||||||
// TODO: let's hope 20k is enough. Maybe just really allocate here
|
// TODO: let's hope 20k is enough. Maybe just really allocate here
|
||||||
|
@ -151,16 +177,18 @@ pub const SimpleRequest = struct {
|
||||||
try self.sendBody(string.items);
|
try self.sendBody(string.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send body.
|
||||||
pub fn sendBody(self: *const Self, body: []const u8) HttpError!void {
|
pub fn sendBody(self: *const Self, body: []const u8) HttpError!void {
|
||||||
const ret = fio.http_send_body(self.h, @as(
|
const ret = fio.http_send_body(self.h, @as(
|
||||||
*anyopaque,
|
*anyopaque,
|
||||||
@ptrFromInt(@intFromPtr(body.ptr)),
|
@ptrFromInt(@intFromPtr(body.ptr)),
|
||||||
), body.len);
|
), body.len);
|
||||||
debug("SimpleRequest.sendBody(): ret = {}\n", .{ret});
|
debug("Request.sendBody(): ret = {}\n", .{ret});
|
||||||
if (ret == -1) return error.HttpSendBody;
|
if (ret == -1) return error.HttpSendBody;
|
||||||
self.markAsFinished(true);
|
self.markAsFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set content type and send json buffer.
|
||||||
pub fn sendJson(self: *const Self, json: []const u8) HttpError!void {
|
pub fn sendJson(self: *const Self, json: []const u8) HttpError!void {
|
||||||
if (self.setContentType(.JSON)) {
|
if (self.setContentType(.JSON)) {
|
||||||
if (fio.http_send_body(self.h, @as(
|
if (fio.http_send_body(self.h, @as(
|
||||||
|
@ -171,6 +199,7 @@ pub const SimpleRequest = struct {
|
||||||
} else |err| return err;
|
} else |err| return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set content type.
|
||||||
pub fn setContentType(self: *const Self, c: ContentType) HttpError!void {
|
pub fn setContentType(self: *const Self, c: ContentType) HttpError!void {
|
||||||
const s = switch (c) {
|
const s = switch (c) {
|
||||||
.TEXT => "text/plain",
|
.TEXT => "text/plain",
|
||||||
|
@ -181,7 +210,7 @@ pub const SimpleRequest = struct {
|
||||||
return self.setHeader("content-type", s);
|
return self.setHeader("content-type", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect to path with status code 302 by default
|
/// redirect to path with status code 302 by default
|
||||||
pub fn redirectTo(self: *const Self, path: []const u8, code: ?http.StatusCode) HttpError!void {
|
pub fn redirectTo(self: *const Self, path: []const u8, code: ?http.StatusCode) HttpError!void {
|
||||||
self.setStatus(if (code) |status| status else .found);
|
self.setStatus(if (code) |status| status else .found);
|
||||||
try self.setHeader("Location", path);
|
try self.setHeader("Location", path);
|
||||||
|
@ -242,6 +271,7 @@ pub const SimpleRequest = struct {
|
||||||
return util.fio2str(fio.fiobj_hash_get(self.h.*.headers, hname));
|
return util.fio2str(fio.fiobj_hash_get(self.h.*.headers, hname));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set header.
|
||||||
pub fn setHeader(self: *const Self, name: []const u8, value: []const u8) HttpError!void {
|
pub fn setHeader(self: *const Self, name: []const u8, value: []const u8) HttpError!void {
|
||||||
const hname: fio.fio_str_info_s = .{
|
const hname: fio.fio_str_info_s = .{
|
||||||
.data = util.toCharPtr(name),
|
.data = util.toCharPtr(name),
|
||||||
|
@ -261,7 +291,7 @@ pub const SimpleRequest = struct {
|
||||||
// FIXME without the following if, we get errors in release builds
|
// FIXME without the following if, we get errors in release builds
|
||||||
// at least we don't have to log unconditionally
|
// at least we don't have to log unconditionally
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
std.debug.print("***************** zap.zig:145\n", .{});
|
std.debug.print("***************** zap.zig:274\n", .{});
|
||||||
}
|
}
|
||||||
debug("setHeader: ret = {}\n", .{ret});
|
debug("setHeader: ret = {}\n", .{ret});
|
||||||
|
|
||||||
|
@ -269,10 +299,12 @@ pub const SimpleRequest = struct {
|
||||||
return error.HttpSetHeader;
|
return error.HttpSetHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set status by numeric value.
|
||||||
pub fn setStatusNumeric(self: *const Self, status: usize) void {
|
pub fn setStatusNumeric(self: *const Self, status: usize) void {
|
||||||
self.h.*.status = status;
|
self.h.*.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set status by enum.
|
||||||
pub fn setStatus(self: *const Self, status: http.StatusCode) void {
|
pub fn setStatus(self: *const Self, status: http.StatusCode) void {
|
||||||
self.h.*.status = @as(usize, @intCast(@intFromEnum(status)));
|
self.h.*.status = @as(usize, @intCast(@intFromEnum(status)));
|
||||||
}
|
}
|
||||||
|
@ -315,11 +347,12 @@ pub const SimpleRequest = struct {
|
||||||
fio.http_parse_query(self.h);
|
fio.http_parse_query(self.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse received cookie headers
|
||||||
pub fn parseCookies(self: *const Self, url_encoded: bool) void {
|
pub fn parseCookies(self: *const Self, url_encoded: bool) void {
|
||||||
fio.http_parse_cookies(self.h, if (url_encoded) 1 else 0);
|
fio.http_parse_cookies(self.h, if (url_encoded) 1 else 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a response cookie
|
/// Set a response cookie
|
||||||
pub fn setCookie(self: *const Self, args: CookieArgs) HttpError!void {
|
pub fn setCookie(self: *const Self, args: CookieArgs) HttpError!void {
|
||||||
const c: fio.http_cookie_args_s = .{
|
const c: fio.http_cookie_args_s = .{
|
||||||
.name = util.toCharPtr(args.name),
|
.name = util.toCharPtr(args.name),
|
||||||
|
@ -340,6 +373,7 @@ pub const SimpleRequest = struct {
|
||||||
// if(fio.http_set_cookie(...) == -1)
|
// if(fio.http_set_cookie(...) == -1)
|
||||||
// instead of capturing it in `ret` first and then checking it,
|
// instead of capturing it in `ret` first and then checking it,
|
||||||
// all ReleaseXXX builds return an error!
|
// all ReleaseXXX builds return an error!
|
||||||
|
// TODO: still happening?
|
||||||
const ret = fio.http_set_cookie(self.h, c);
|
const ret = fio.http_set_cookie(self.h, c);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
std.log.err("fio.http_set_cookie returned: {}\n", .{ret});
|
std.log.err("fio.http_set_cookie returned: {}\n", .{ret});
|
||||||
|
@ -347,8 +381,8 @@ pub const SimpleRequest = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns named cookie. Works like getParamStr()
|
/// Returns named cookie. Works like getParamStr().
|
||||||
pub fn getCookieStr(self: *const Self, name: []const u8, a: std.mem.Allocator, always_alloc: bool) !?util.FreeOrNot {
|
pub fn getCookieStr(self: *const Self, a: std.mem.Allocator, name: []const u8, always_alloc: bool) !?util.FreeOrNot {
|
||||||
if (self.h.*.cookies == 0) return null;
|
if (self.h.*.cookies == 0) return null;
|
||||||
const key = fio.fiobj_str_new(name.ptr, name.len);
|
const key = fio.fiobj_str_new(name.ptr, name.len);
|
||||||
defer fio.fiobj_free_wrapped(key);
|
defer fio.fiobj_free_wrapped(key);
|
||||||
|
@ -356,10 +390,10 @@ pub const SimpleRequest = struct {
|
||||||
if (value == fio.FIOBJ_INVALID) {
|
if (value == fio.FIOBJ_INVALID) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return try util.fio2strAllocOrNot(value, a, always_alloc);
|
return try util.fio2strAllocOrNot(a, value, always_alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of parameters after parsing.
|
/// Returns the number of cookies after parsing.
|
||||||
///
|
///
|
||||||
/// Parse with parseCookies()
|
/// Parse with parseCookies()
|
||||||
pub fn getCookiesCount(self: *const Self) isize {
|
pub fn getCookiesCount(self: *const Self) isize {
|
||||||
|
@ -440,11 +474,11 @@ pub const SimpleRequest = struct {
|
||||||
// this is thread-safe, guaranteed by fio
|
// this is thread-safe, guaranteed by fio
|
||||||
const fiobj_key: fio.FIOBJ = fio.fiobj_hash_key_in_loop();
|
const fiobj_key: fio.FIOBJ = fio.fiobj_hash_key_in_loop();
|
||||||
ctx.params.append(.{
|
ctx.params.append(.{
|
||||||
.key = util.fio2strAllocOrNot(fiobj_key, ctx.allocator, ctx.always_alloc) catch |err| {
|
.key = util.fio2strAllocOrNot(ctx.allocator, fiobj_key, ctx.always_alloc) catch |err| {
|
||||||
ctx.last_error = err;
|
ctx.last_error = err;
|
||||||
return -1;
|
return -1;
|
||||||
},
|
},
|
||||||
.value = util.fio2strAllocOrNot(fiobj_value, ctx.allocator, ctx.always_alloc) catch |err| {
|
.value = util.fio2strAllocOrNot(ctx.allocator, fiobj_value, ctx.always_alloc) catch |err| {
|
||||||
ctx.last_error = err;
|
ctx.last_error = err;
|
||||||
return -1;
|
return -1;
|
||||||
},
|
},
|
||||||
|
@ -493,11 +527,11 @@ pub const SimpleRequest = struct {
|
||||||
// this is thread-safe, guaranteed by fio
|
// this is thread-safe, guaranteed by fio
|
||||||
const fiobj_key: fio.FIOBJ = fio.fiobj_hash_key_in_loop();
|
const fiobj_key: fio.FIOBJ = fio.fiobj_hash_key_in_loop();
|
||||||
ctx.params.append(.{
|
ctx.params.append(.{
|
||||||
.key = util.fio2strAllocOrNot(fiobj_key, ctx.allocator, ctx.dupe_strings) catch |err| {
|
.key = util.fio2strAllocOrNot(ctx.allocator, fiobj_key, ctx.dupe_strings) catch |err| {
|
||||||
ctx.last_error = err;
|
ctx.last_error = err;
|
||||||
return -1;
|
return -1;
|
||||||
},
|
},
|
||||||
.value = Fiobj2HttpParam(fiobj_value, ctx.allocator, ctx.dupe_strings) catch |err| {
|
.value = Fiobj2HttpParam(ctx.allocator, fiobj_value, ctx.dupe_strings) catch |err| {
|
||||||
ctx.last_error = err;
|
ctx.last_error = err;
|
||||||
return -1;
|
return -1;
|
||||||
},
|
},
|
||||||
|
@ -524,7 +558,7 @@ pub const SimpleRequest = struct {
|
||||||
///
|
///
|
||||||
/// Requires parseBody() and/or parseQuery() have been called.
|
/// Requires parseBody() and/or parseQuery() have been called.
|
||||||
/// The returned string needs to be deinited with .deinit()
|
/// The returned string needs to be deinited with .deinit()
|
||||||
pub fn getParamStr(self: *const Self, name: []const u8, a: std.mem.Allocator, always_alloc: bool) !?util.FreeOrNot {
|
pub fn getParamStr(self: *const Self, a: std.mem.Allocator, name: []const u8, always_alloc: bool) !?util.FreeOrNot {
|
||||||
if (self.h.*.params == 0) return null;
|
if (self.h.*.params == 0) return null;
|
||||||
const key = fio.fiobj_str_new(name.ptr, name.len);
|
const key = fio.fiobj_str_new(name.ptr, name.len);
|
||||||
defer fio.fiobj_free_wrapped(key);
|
defer fio.fiobj_free_wrapped(key);
|
||||||
|
@ -532,7 +566,7 @@ pub const SimpleRequest = struct {
|
||||||
if (value == fio.FIOBJ_INVALID) {
|
if (value == fio.FIOBJ_INVALID) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return try util.fio2strAllocOrNot(value, a, always_alloc);
|
return try util.fio2strAllocOrNot(a, value, always_alloc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -546,6 +580,7 @@ pub const HttpParamStrKV = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// List of key value pairs of Http param strings.
|
||||||
pub const HttpParamStrKVList = struct {
|
pub const HttpParamStrKVList = struct {
|
||||||
items: []HttpParamStrKV,
|
items: []HttpParamStrKV,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
@ -557,6 +592,7 @@ pub const HttpParamStrKVList = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// List of key value pairs of Http params (might be of different types).
|
||||||
pub const HttpParamKVList = struct {
|
pub const HttpParamKVList = struct {
|
||||||
items: []HttpParamKV,
|
items: []HttpParamKV,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
@ -568,6 +604,7 @@ pub const HttpParamKVList = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Enum for HttpParam tagged union
|
||||||
pub const HttpParamValueType = enum {
|
pub const HttpParamValueType = enum {
|
||||||
// Null,
|
// Null,
|
||||||
Bool,
|
Bool,
|
||||||
|
@ -579,6 +616,7 @@ pub const HttpParamValueType = enum {
|
||||||
Array_Binfile,
|
Array_Binfile,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Tagged union holding a typed Http param
|
||||||
pub const HttpParam = union(HttpParamValueType) {
|
pub const HttpParam = union(HttpParamValueType) {
|
||||||
Bool: bool,
|
Bool: bool,
|
||||||
Int: isize,
|
Int: isize,
|
||||||
|
@ -593,6 +631,7 @@ pub const HttpParam = union(HttpParamValueType) {
|
||||||
Array_Binfile: std.ArrayList(HttpParamBinaryFile),
|
Array_Binfile: std.ArrayList(HttpParamBinaryFile),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Key value pair of one typed Http param
|
||||||
pub const HttpParamKV = struct {
|
pub const HttpParamKV = struct {
|
||||||
key: util.FreeOrNot,
|
key: util.FreeOrNot,
|
||||||
value: ?HttpParam,
|
value: ?HttpParam,
|
||||||
|
@ -607,6 +646,7 @@ pub const HttpParamKV = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Struct representing an uploaded file.
|
||||||
pub const HttpParamBinaryFile = struct {
|
pub const HttpParamBinaryFile = struct {
|
||||||
/// file contents
|
/// file contents
|
||||||
data: ?[]const u8 = null,
|
data: ?[]const u8 = null,
|
||||||
|
@ -615,6 +655,7 @@ pub const HttpParamBinaryFile = struct {
|
||||||
/// filename
|
/// filename
|
||||||
filename: ?[]const u8 = null,
|
filename: ?[]const u8 = null,
|
||||||
|
|
||||||
|
/// format function for printing file upload data
|
||||||
pub fn format(value: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) std.os.WriteError!void {
|
pub fn format(value: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) std.os.WriteError!void {
|
||||||
const d = value.data orelse "\\0";
|
const d = value.data orelse "\\0";
|
||||||
const m = value.mimetype orelse "null";
|
const m = value.mimetype orelse "null";
|
||||||
|
@ -735,14 +776,15 @@ fn parseBinfilesFrom(a: std.mem.Allocator, o: fio.FIOBJ) !HttpParam {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Fiobj2HttpParam(o: fio.FIOBJ, a: std.mem.Allocator, dupe_string: bool) !?HttpParam {
|
/// Parse FIO object into a typed Http param. Supports file uploads.
|
||||||
|
pub fn Fiobj2HttpParam(a: std.mem.Allocator, o: fio.FIOBJ, dupe_string: bool) !?HttpParam {
|
||||||
return switch (fio.fiobj_type(o)) {
|
return switch (fio.fiobj_type(o)) {
|
||||||
fio.FIOBJ_T_NULL => null,
|
fio.FIOBJ_T_NULL => null,
|
||||||
fio.FIOBJ_T_TRUE => .{ .Bool = true },
|
fio.FIOBJ_T_TRUE => .{ .Bool = true },
|
||||||
fio.FIOBJ_T_FALSE => .{ .Bool = false },
|
fio.FIOBJ_T_FALSE => .{ .Bool = false },
|
||||||
fio.FIOBJ_T_NUMBER => .{ .Int = fio.fiobj_obj2num(o) },
|
fio.FIOBJ_T_NUMBER => .{ .Int = fio.fiobj_obj2num(o) },
|
||||||
fio.FIOBJ_T_FLOAT => .{ .Float = fio.fiobj_obj2float(o) },
|
fio.FIOBJ_T_FLOAT => .{ .Float = fio.fiobj_obj2float(o) },
|
||||||
fio.FIOBJ_T_STRING => .{ .String = try util.fio2strAllocOrNot(o, a, dupe_string) },
|
fio.FIOBJ_T_STRING => .{ .String = try util.fio2strAllocOrNot(a, o, dupe_string) },
|
||||||
fio.FIOBJ_T_ARRAY => {
|
fio.FIOBJ_T_ARRAY => {
|
||||||
return .{ .Unsupported = null };
|
return .{ .Unsupported = null };
|
||||||
},
|
},
|
||||||
|
@ -754,6 +796,7 @@ pub fn Fiobj2HttpParam(o: fio.FIOBJ, a: std.mem.Allocator, dupe_string: bool) !?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Args for setting a cookie
|
||||||
pub const CookieArgs = struct {
|
pub const CookieArgs = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
value: []const u8,
|
value: []const u8,
|
||||||
|
@ -765,25 +808,31 @@ pub const CookieArgs = struct {
|
||||||
http_only: bool = true,
|
http_only: bool = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const HttpRequestFn = *const fn (r: [*c]fio.http_s) callconv(.C) void;
|
/// Used internally: facilio Http request callback function type
|
||||||
pub const SimpleHttpRequestFn = *const fn (SimpleRequest) void;
|
pub const FioHttpRequestFn = *const fn (r: [*c]fio.http_s) callconv(.C) void;
|
||||||
|
|
||||||
/// websocket connection upgrade
|
/// Zap Http request callback function type.
|
||||||
|
pub const HttpRequestFn = *const fn (Request) void;
|
||||||
|
|
||||||
|
/// websocket connection upgrade callback type
|
||||||
/// fn(request, targetstring)
|
/// fn(request, targetstring)
|
||||||
pub const SimpleHttpUpgradeFn = *const fn (r: SimpleRequest, target_protocol: []const u8) void;
|
pub const HttpUpgradeFn = *const fn (r: Request, target_protocol: []const u8) void;
|
||||||
|
|
||||||
/// http finish, called when zap finishes. You get your udata back in the
|
/// http finish, called when zap finishes. You get your udata back in the
|
||||||
/// struct.
|
/// HttpFinishSetting struct.
|
||||||
pub const SimpleHttpFinishSettings = [*c]fio.struct_http_settings_s;
|
pub const HttpFinishSettings = [*c]fio.struct_http_settings_s;
|
||||||
pub const SimpleHttpFinishFn = *const fn (SimpleHttpFinishSettings) void;
|
|
||||||
|
|
||||||
pub const SimpleHttpListenerSettings = struct {
|
/// Http finish callback type
|
||||||
|
pub const HttpFinishFn = *const fn (HttpFinishSettings) void;
|
||||||
|
|
||||||
|
/// Listener settings
|
||||||
|
pub const HttpListenerSettings = struct {
|
||||||
port: usize,
|
port: usize,
|
||||||
interface: [*c]const u8 = null,
|
interface: [*c]const u8 = null,
|
||||||
on_request: ?SimpleHttpRequestFn,
|
on_request: ?HttpRequestFn,
|
||||||
on_response: ?SimpleHttpRequestFn = null,
|
on_response: ?HttpRequestFn = null,
|
||||||
on_upgrade: ?SimpleHttpUpgradeFn = null,
|
on_upgrade: ?HttpUpgradeFn = null,
|
||||||
on_finish: ?SimpleHttpFinishFn = null,
|
on_finish: ?HttpFinishFn = null,
|
||||||
// provide any pointer in there for "user data". it will be passed pack in
|
// provide any pointer in there for "user data". it will be passed pack in
|
||||||
// on_finish()'s copy of the struct_http_settings_s
|
// on_finish()'s copy of the struct_http_settings_s
|
||||||
udata: ?*anyopaque = null,
|
udata: ?*anyopaque = null,
|
||||||
|
@ -797,26 +846,26 @@ pub const SimpleHttpListenerSettings = struct {
|
||||||
tls: ?Tls = null,
|
tls: ?Tls = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SimpleHttpListener = struct {
|
/// Http listener
|
||||||
settings: SimpleHttpListenerSettings,
|
pub const HttpListener = struct {
|
||||||
|
settings: HttpListenerSettings,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
var the_one_and_only_listener: ?*SimpleHttpListener = null;
|
var the_one_and_only_listener: ?*HttpListener = null;
|
||||||
|
|
||||||
pub fn init(settings: SimpleHttpListenerSettings) Self {
|
/// Create a listener
|
||||||
|
pub fn init(settings: HttpListenerSettings) Self {
|
||||||
std.debug.assert(settings.on_request != null);
|
std.debug.assert(settings.on_request != null);
|
||||||
return .{
|
return .{
|
||||||
.settings = settings,
|
.settings = settings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// on_upgrade: ?*const fn ([*c]fio.http_s, [*c]u8, usize) callconv(.C) void = null,
|
// we could make it dynamic by passing a HttpListener via udata
|
||||||
// on_finish: ?*const fn ([*c]fio.struct_http_settings_s) callconv(.C) void = null,
|
/// Used internally: the listener's facilio request callback
|
||||||
|
|
||||||
// we could make it dynamic by passing a SimpleHttpListener via udata
|
|
||||||
pub fn theOneAndOnlyRequestCallBack(r: [*c]fio.http_s) callconv(.C) void {
|
pub fn theOneAndOnlyRequestCallBack(r: [*c]fio.http_s) callconv(.C) void {
|
||||||
if (the_one_and_only_listener) |l| {
|
if (the_one_and_only_listener) |l| {
|
||||||
var req: SimpleRequest = .{
|
var req: Request = .{
|
||||||
.path = util.fio2str(r.*.path),
|
.path = util.fio2str(r.*.path),
|
||||||
.query = util.fio2str(r.*.query),
|
.query = util.fio2str(r.*.query),
|
||||||
.body = util.fio2str(r.*.body),
|
.body = util.fio2str(r.*.body),
|
||||||
|
@ -827,7 +876,7 @@ pub const SimpleHttpListener = struct {
|
||||||
};
|
};
|
||||||
req._is_finished = &req._is_finished_request_global;
|
req._is_finished = &req._is_finished_request_global;
|
||||||
|
|
||||||
var user_context: SimpleRequest.UserContext = .{};
|
var user_context: Request.UserContext = .{};
|
||||||
req._user_context = &user_context;
|
req._user_context = &user_context;
|
||||||
|
|
||||||
req.markAsFinished(false);
|
req.markAsFinished(false);
|
||||||
|
@ -839,9 +888,10 @@ pub const SimpleHttpListener = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used internally: the listener's facilio response callback
|
||||||
pub fn theOneAndOnlyResponseCallBack(r: [*c]fio.http_s) callconv(.C) void {
|
pub fn theOneAndOnlyResponseCallBack(r: [*c]fio.http_s) callconv(.C) void {
|
||||||
if (the_one_and_only_listener) |l| {
|
if (the_one_and_only_listener) |l| {
|
||||||
var req: SimpleRequest = .{
|
var req: Request = .{
|
||||||
.path = util.fio2str(r.*.path),
|
.path = util.fio2str(r.*.path),
|
||||||
.query = util.fio2str(r.*.query),
|
.query = util.fio2str(r.*.query),
|
||||||
.body = util.fio2str(r.*.body),
|
.body = util.fio2str(r.*.body),
|
||||||
|
@ -852,16 +902,17 @@ pub const SimpleHttpListener = struct {
|
||||||
};
|
};
|
||||||
req._is_finished = &req._is_finished_request_global;
|
req._is_finished = &req._is_finished_request_global;
|
||||||
|
|
||||||
var user_context: SimpleRequest.UserContext = .{};
|
var user_context: Request.UserContext = .{};
|
||||||
req._user_context = &user_context;
|
req._user_context = &user_context;
|
||||||
|
|
||||||
l.settings.on_response.?(req);
|
l.settings.on_response.?(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used internally: the listener's facilio upgrade callback
|
||||||
pub fn theOneAndOnlyUpgradeCallBack(r: [*c]fio.http_s, target: [*c]u8, target_len: usize) callconv(.C) void {
|
pub fn theOneAndOnlyUpgradeCallBack(r: [*c]fio.http_s, target: [*c]u8, target_len: usize) callconv(.C) void {
|
||||||
if (the_one_and_only_listener) |l| {
|
if (the_one_and_only_listener) |l| {
|
||||||
var req: SimpleRequest = .{
|
var req: Request = .{
|
||||||
.path = util.fio2str(r.*.path),
|
.path = util.fio2str(r.*.path),
|
||||||
.query = util.fio2str(r.*.query),
|
.query = util.fio2str(r.*.query),
|
||||||
.body = util.fio2str(r.*.body),
|
.body = util.fio2str(r.*.body),
|
||||||
|
@ -873,25 +924,27 @@ pub const SimpleHttpListener = struct {
|
||||||
const zigtarget: []u8 = target[0..target_len];
|
const zigtarget: []u8 = target[0..target_len];
|
||||||
req._is_finished = &req._is_finished_request_global;
|
req._is_finished = &req._is_finished_request_global;
|
||||||
|
|
||||||
var user_context: SimpleRequest.UserContext = .{};
|
var user_context: Request.UserContext = .{};
|
||||||
req._user_context = &user_context;
|
req._user_context = &user_context;
|
||||||
|
|
||||||
l.settings.on_upgrade.?(req, zigtarget);
|
l.settings.on_upgrade.?(req, zigtarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used internally: the listener's facilio finish callback
|
||||||
pub fn theOneAndOnlyFinishCallBack(s: [*c]fio.struct_http_settings_s) callconv(.C) void {
|
pub fn theOneAndOnlyFinishCallBack(s: [*c]fio.struct_http_settings_s) callconv(.C) void {
|
||||||
if (the_one_and_only_listener) |l| {
|
if (the_one_and_only_listener) |l| {
|
||||||
l.settings.on_finish.?(s);
|
l.settings.on_finish.?(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start listening
|
||||||
pub fn listen(self: *Self) !void {
|
pub fn listen(self: *Self) !void {
|
||||||
var pfolder: [*c]const u8 = null;
|
var pfolder: [*c]const u8 = null;
|
||||||
var pfolder_len: usize = 0;
|
var pfolder_len: usize = 0;
|
||||||
|
|
||||||
if (self.settings.public_folder) |pf| {
|
if (self.settings.public_folder) |pf| {
|
||||||
debug("SimpleHttpListener.listen(): public folder is {s}\n", .{pf});
|
debug("HttpListener.listen(): public folder is {s}\n", .{pf});
|
||||||
pfolder_len = pf.len;
|
pfolder_len = pf.len;
|
||||||
pfolder = pf.ptr;
|
pfolder = pf.ptr;
|
||||||
}
|
}
|
||||||
|
@ -921,15 +974,12 @@ pub const SimpleHttpListener = struct {
|
||||||
// TODO: BUG: without this print/sleep statement, -Drelease* loop forever
|
// TODO: BUG: without this print/sleep statement, -Drelease* loop forever
|
||||||
// in debug2 and debug3 of hello example
|
// in debug2 and debug3 of hello example
|
||||||
// std.debug.print("X\n", .{});
|
// std.debug.print("X\n", .{});
|
||||||
std.time.sleep(500 * 1000 * 1000);
|
// TODO: still happening?
|
||||||
|
std.time.sleep(500 * std.time.ns_per_ms);
|
||||||
|
|
||||||
var portbuf: [100]u8 = undefined;
|
var portbuf: [100]u8 = undefined;
|
||||||
const printed_port = try std.fmt.bufPrintZ(&portbuf, "{d}", .{self.settings.port});
|
const printed_port = try std.fmt.bufPrintZ(&portbuf, "{d}", .{self.settings.port});
|
||||||
|
|
||||||
// pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![:0]u8 {
|
|
||||||
// const result = try bufPrint(buf, fmt ++ "\x00", args);
|
|
||||||
// return result[0 .. result.len - 1 :0];
|
|
||||||
// }
|
|
||||||
const ret = fio.http_listen(printed_port.ptr, self.settings.interface, x);
|
const ret = fio.http_listen(printed_port.ptr, self.settings.interface, x);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
return error.ListenError;
|
return error.ListenError;
|
||||||
|
@ -937,7 +987,7 @@ pub const SimpleHttpListener = struct {
|
||||||
|
|
||||||
// set ourselves up to handle requests:
|
// set ourselves up to handle requests:
|
||||||
// TODO: do we mind the race condition?
|
// TODO: do we mind the race condition?
|
||||||
// the SimpleHttpRequestFn will check if this is null and not process
|
// the HttpRequestFn will check if this is null and not process
|
||||||
// the request if it isn't set. hence, if started under full load, the
|
// the request if it isn't set. hence, if started under full load, the
|
||||||
// first request(s) might not be serviced, as long as it takes from
|
// first request(s) might not be serviced, as long as it takes from
|
||||||
// fio.http_listen() to here
|
// fio.http_listen() to here
|
||||||
|
@ -945,73 +995,78 @@ pub const SimpleHttpListener = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
/// Low-level API
|
||||||
// lower level listening
|
pub const LowLevel = struct {
|
||||||
//
|
/// lower level listening, if you don't want to use a listener but rather use
|
||||||
pub const ListenSettings = struct {
|
/// the listen() function.
|
||||||
on_request: ?*const fn ([*c]fio.http_s) callconv(.C) void = null,
|
pub const ListenSettings = struct {
|
||||||
on_upgrade: ?*const fn ([*c]fio.http_s, [*c]u8, usize) callconv(.C) void = null,
|
on_request: ?FioHttpRequestFn = null,
|
||||||
on_response: ?*const fn ([*c]fio.http_s) callconv(.C) void = null,
|
on_upgrade: ?FioHttpRequestFn = null,
|
||||||
on_finish: ?*const fn ([*c]fio.struct_http_settings_s) callconv(.C) void = null,
|
on_response: ?FioHttpRequestFn = null,
|
||||||
public_folder: ?[]const u8 = null,
|
on_finish: ?FioHttpRequestFn = null,
|
||||||
max_header_size: usize = 32 * 1024,
|
public_folder: ?[]const u8 = null,
|
||||||
max_body_size: usize = 50 * 1024 * 1024,
|
max_header_size: usize = 32 * 1024,
|
||||||
max_clients: isize = 100,
|
max_body_size: usize = 50 * 1024 * 1024,
|
||||||
keepalive_timeout_s: u8 = 5,
|
max_clients: isize = 100,
|
||||||
log: bool = false,
|
keepalive_timeout_s: u8 = 5,
|
||||||
|
log: bool = false,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init() Self {
|
/// Create settings with defaults
|
||||||
return .{};
|
pub fn init() Self {
|
||||||
|
return .{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Low level listen function
|
||||||
|
pub fn listen(port: [*c]const u8, interface: [*c]const u8, settings: ListenSettings) ListenError!void {
|
||||||
|
var pfolder: [*c]const u8 = null;
|
||||||
|
var pfolder_len: usize = 0;
|
||||||
|
|
||||||
|
if (settings.public_folder) |pf| {
|
||||||
|
pfolder_len = pf.len;
|
||||||
|
pfolder = pf.ptr;
|
||||||
|
}
|
||||||
|
const x: fio.http_settings_s = .{
|
||||||
|
.on_request = settings.on_request,
|
||||||
|
.on_upgrade = settings.on_upgrade,
|
||||||
|
.on_response = settings.on_response,
|
||||||
|
.on_finish = settings.on_finish,
|
||||||
|
.udata = null,
|
||||||
|
.public_folder = pfolder,
|
||||||
|
.public_folder_length = pfolder_len,
|
||||||
|
.max_header_size = settings.max_header_size,
|
||||||
|
.max_body_size = settings.max_body_size,
|
||||||
|
.max_clients = settings.max_clients,
|
||||||
|
.tls = null,
|
||||||
|
.reserved1 = 0,
|
||||||
|
.reserved2 = 0,
|
||||||
|
.reserved3 = 0,
|
||||||
|
.ws_max_msg_size = settings.ws_max_msg_size,
|
||||||
|
.timeout = settings.keepalive_timeout_s,
|
||||||
|
.ws_timeout = 0,
|
||||||
|
.log = if (settings.log) 1 else 0,
|
||||||
|
.is_client = 0,
|
||||||
|
};
|
||||||
|
// TODO: BUG: without this print/sleep statement, -Drelease* loop forever
|
||||||
|
// in debug2 and debug3 of hello example
|
||||||
|
// std.debug.print("X\n", .{});
|
||||||
|
// TODO: still happening?
|
||||||
|
std.time.sleep(500 * std.time.ns_per_ms);
|
||||||
|
|
||||||
|
if (fio.http_listen(port, interface, x) == -1) {
|
||||||
|
return error.ListenError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// lower level sendBody
|
||||||
|
pub fn sendBody(request: [*c]fio.http_s, body: []const u8) HttpError!void {
|
||||||
|
const ret = fio.http_send_body(request, @as(
|
||||||
|
*anyopaque,
|
||||||
|
@ptrFromInt(@intFromPtr(body.ptr)),
|
||||||
|
), body.len);
|
||||||
|
debug("sendBody(): ret = {}\n", .{ret});
|
||||||
|
if (ret != -1) return error.HttpSendBody;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn listen(port: [*c]const u8, interface: [*c]const u8, settings: ListenSettings) ListenError!void {
|
|
||||||
var pfolder: [*c]const u8 = null;
|
|
||||||
var pfolder_len: usize = 0;
|
|
||||||
|
|
||||||
if (settings.public_folder) |pf| {
|
|
||||||
pfolder_len = pf.len;
|
|
||||||
pfolder = pf.ptr;
|
|
||||||
}
|
|
||||||
const x: fio.http_settings_s = .{
|
|
||||||
.on_request = settings.on_request,
|
|
||||||
.on_upgrade = settings.on_upgrade,
|
|
||||||
.on_response = settings.on_response,
|
|
||||||
.on_finish = settings.on_finish,
|
|
||||||
.udata = null,
|
|
||||||
.public_folder = pfolder,
|
|
||||||
.public_folder_length = pfolder_len,
|
|
||||||
.max_header_size = settings.max_header_size,
|
|
||||||
.max_body_size = settings.max_body_size,
|
|
||||||
.max_clients = settings.max_clients,
|
|
||||||
.tls = null,
|
|
||||||
.reserved1 = 0,
|
|
||||||
.reserved2 = 0,
|
|
||||||
.reserved3 = 0,
|
|
||||||
.ws_max_msg_size = settings.ws_max_msg_size,
|
|
||||||
.timeout = settings.keepalive_timeout_s,
|
|
||||||
.ws_timeout = 0,
|
|
||||||
.log = if (settings.log) 1 else 0,
|
|
||||||
.is_client = 0,
|
|
||||||
};
|
|
||||||
// TODO: BUG: without this print/sleep statement, -Drelease* loop forever
|
|
||||||
// in debug2 and debug3 of hello example
|
|
||||||
// std.debug.print("X\n", .{});
|
|
||||||
std.time.sleep(500 * 1000 * 1000);
|
|
||||||
|
|
||||||
if (fio.http_listen(port, interface, x) == -1) {
|
|
||||||
return error.ListenError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lower level sendBody
|
|
||||||
pub fn sendBody(request: [*c]fio.http_s, body: []const u8) HttpError!void {
|
|
||||||
const ret = fio.http_send_body(request, @as(
|
|
||||||
*anyopaque,
|
|
||||||
@ptrFromInt(@intFromPtr(body.ptr)),
|
|
||||||
), body.len);
|
|
||||||
debug("sendBody(): ret = {}\n", .{ret});
|
|
||||||
if (ret != -1) return error.HttpSendBody;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request(r: zap.SimpleRequest) void {
|
fn on_request(r: zap.Request) void {
|
||||||
r.setStatus(.not_found);
|
r.setStatus(.not_found);
|
||||||
r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return;
|
r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = port,
|
.port = port,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.public_folder = docs_dir,
|
.public_folder = docs_dir,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request_minimal(r: zap.SimpleRequest) void {
|
fn on_request_minimal(r: zap.Request) void {
|
||||||
r.sendBody("Hello from ZAP!!!") catch return;
|
r.sendBody("Hello from ZAP!!!") catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var listener = zap.SimpleHttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request_minimal,
|
.on_request = on_request_minimal,
|
||||||
.log = false,
|
.log = false,
|
||||||
|
|
Loading…
Add table
Reference in a new issue