mirror of
https://github.com/zigzap/zap.git
synced 2025-10-21 15:44:10 +00:00
Merge branch 'zigzap:master' into master
This commit is contained in:
commit
d75d3906c7
12 changed files with 409 additions and 20 deletions
47
.github/workflows/mastercheck-localhost.yml
vendored
Normal file
47
.github/workflows/mastercheck-localhost.yml
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
name: Works with Zig master (localhost patch)
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches:
|
||||||
|
# - master
|
||||||
|
# pull_request:
|
||||||
|
# branches:
|
||||||
|
# - master
|
||||||
|
# schedule:
|
||||||
|
# - cron: "0 0 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# platform: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
platform: [ubuntu-latest]
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: goto-bus-stop/setup-zig@v2
|
||||||
|
with:
|
||||||
|
version: master
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.10'
|
||||||
|
- name: wget
|
||||||
|
uses: wei/wget@v1
|
||||||
|
with:
|
||||||
|
args: https://github.com/zigzap/facil.io/archive/refs/tags/zap-0.0.8.tar.gz
|
||||||
|
- name: Check zig version
|
||||||
|
run: zig version
|
||||||
|
- name: hack build.zig.zon
|
||||||
|
run: |
|
||||||
|
mv build.zig.zon build.zig.zon.moved && mv build.zig.zon.localhost build.zig.zon && python -m http.server &
|
||||||
|
sleep 3
|
||||||
|
zig build
|
||||||
|
- name: Build all examples
|
||||||
|
run: ./build_all.sh
|
||||||
|
- name: Run authentication tests
|
||||||
|
run: zig build test-authentication
|
||||||
|
- name: Run http parameter tests
|
||||||
|
run: zig build test-httpparams
|
||||||
|
- name: Run sendfile tests
|
||||||
|
run: zig build test-sendfile
|
||||||
|
|
12
.github/workflows/mastercheck.yml
vendored
12
.github/workflows/mastercheck.yml
vendored
|
@ -22,20 +22,8 @@ jobs:
|
||||||
- uses: goto-bus-stop/setup-zig@v2
|
- uses: goto-bus-stop/setup-zig@v2
|
||||||
with:
|
with:
|
||||||
version: master
|
version: master
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
- name: wget
|
|
||||||
uses: wei/wget@v1
|
|
||||||
with:
|
|
||||||
args: https://github.com/zigzap/facil.io/archive/refs/tags/zap-0.0.8.tar.gz
|
|
||||||
- name: Check zig version
|
- name: Check zig version
|
||||||
run: zig version
|
run: zig version
|
||||||
- name: hack build.zig.zon
|
|
||||||
run: |
|
|
||||||
mv build.zig.zon build.zig.zon.moved && mv build.zig.zon.localhost build.zig.zon && python -m http.server &
|
|
||||||
sleep 3
|
|
||||||
zig build
|
|
||||||
- name: Build all examples
|
- name: Build all examples
|
||||||
run: ./build_all.sh
|
run: ./build_all.sh
|
||||||
- name: Run authentication tests
|
- name: Run authentication tests
|
||||||
|
|
|
@ -58,6 +58,8 @@ Here's what works:
|
||||||
request handlers in middleware style. Provide custom context structs, totally
|
request handlers in middleware style. Provide custom context structs, totally
|
||||||
type-safe, using **[ZIG-CEPTION](doc/zig-ception.md)**. If you come from GO
|
type-safe, using **[ZIG-CEPTION](doc/zig-ception.md)**. If you come from GO
|
||||||
this might appeal to you.
|
this might appeal to you.
|
||||||
|
- **[MIDDLEWARE with endpoint support](examples/middleware_with_endpoint/middleware_with_endpoint.zig)**: Same as the example above, but this time we use an endpoint at the end of the chain, by wrapping it via `zap.Middleware.EndpointHandler`. Mixing endpoints in your middleware chain allows for usage of Zap's authenticated endpoints and your custom endpoints. Since Endpoints use a simpler API, you have to use `r.setUserContext()` and `r.getUserContext()` with the request if you want to access the middleware context from a wrapped endpoint. Since this mechanism uses an `*anyopaque` pointer underneath (to not break the Endpoint API), it is less type-safe than `zap.Middleware`'s use of contexts.
|
||||||
|
- **Per Request Contexts** : With the introduction of `setContext()` and `getContext()`, you can, of course use those two in projects that don't use `zap.SimpleEndpoint` or `zap.Middleware`, too, if you really, really, absolutely don't find another way to solve your context problem. **We recommend using a `zap.SimpleEndpoint`** inside of a struct that can provide all the context you need **instead**. You get access to your struct in the callbacks via the `@fieldParentPtr()` trick that is used extensively in Zap's examples, like the [endpoint example](examples/endpoint/endpoint.zig).
|
||||||
|
|
||||||
|
|
||||||
I'll continue wrapping more of facil.io's functionality and adding stuff to zap
|
I'll continue wrapping more of facil.io's functionality and adding stuff to zap
|
||||||
|
@ -147,10 +149,10 @@ To add zap to `build.zig.zon`:
|
||||||
.version = "0.0.1",
|
.version = "0.0.1",
|
||||||
|
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
// zap release-0.0.22
|
// zap release-0.0.23
|
||||||
.zap = .{
|
.zap = .{
|
||||||
.url = "https://github.com/zigzap/zap/archive/refs/tags/release-0.0.22.tar.gz",
|
.url = "https://github.com/zigzap/zap/archive/refs/tags/release-0.0.23.tar.gz",
|
||||||
.hash = "12204761c4f94997c3bd26f420cf9060541c0c09514370dc129e04b35e58d9f3ae71",
|
.hash = "1220175a7495f41889208349fedd6a35e96d8e413e5cd23b9b875e40d176bad459e1",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ pub fn build(b: *std.build.Builder) !void {
|
||||||
.{ .name = "userpass_session", .src = "examples/userpass_session_auth/userpass_session_auth.zig" },
|
.{ .name = "userpass_session", .src = "examples/userpass_session_auth/userpass_session_auth.zig" },
|
||||||
.{ .name = "sendfile", .src = "examples/sendfile/sendfile.zig" },
|
.{ .name = "sendfile", .src = "examples/sendfile/sendfile.zig" },
|
||||||
.{ .name = "middleware", .src = "examples/middleware/middleware.zig" },
|
.{ .name = "middleware", .src = "examples/middleware/middleware.zig" },
|
||||||
|
.{ .name = "middleware_with_endpoint", .src = "examples/middleware_with_endpoint/middleware_with_endpoint.zig" },
|
||||||
}) |excfg| {
|
}) |excfg| {
|
||||||
const ex_name = excfg.name;
|
const ex_name = excfg.name;
|
||||||
const ex_src = excfg.src;
|
const ex_src = excfg.src;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.{
|
.{
|
||||||
.name = "zap",
|
.name = "zap",
|
||||||
.version = "0.0.22",
|
.version = "0.0.23",
|
||||||
|
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.@"facil.io" = .{
|
.@"facil.io" = .{
|
||||||
|
|
|
@ -9,9 +9,7 @@ authentication, see the [UserPassSessionAuth](../src/http_auth.zig#L319) and its
|
||||||
|
|
||||||
For convenience, Authenticator types exist that can authenticate requests.
|
For convenience, Authenticator types exist that can authenticate requests.
|
||||||
|
|
||||||
Zap also provides an `AuthenticatingEndpoint` endpoint-wrapper.
|
Zap also provides an `AuthenticatingEndpoint` endpoint-wrapper. Have a look at the [example](../examples/endpoint_auth) and the [tests](../src/tests/test_auth.zig).
|
||||||
|
|
||||||
Have a look at the tests: [here](../src/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.SimpleRequest` and returns
|
||||||
|
|
|
@ -164,6 +164,7 @@ const HtmlMiddleWare = struct {
|
||||||
if (context.session) |session| {
|
if (context.session) |session| {
|
||||||
sessionFound = true;
|
sessionFound = true;
|
||||||
|
|
||||||
|
std.debug.assert(r.isFinished() == false);
|
||||||
const message = std.fmt.bufPrint(&buf, "User: {s} / {s}, Session: {s} / {s}", .{
|
const message = std.fmt.bufPrint(&buf, "User: {s} / {s}, Session: {s} / {s}", .{
|
||||||
user.name,
|
user.name,
|
||||||
user.email,
|
user.email,
|
||||||
|
@ -172,6 +173,7 @@ const HtmlMiddleWare = struct {
|
||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
r.setContentType(.TEXT) catch unreachable;
|
r.setContentType(.TEXT) catch unreachable;
|
||||||
r.sendBody(message) catch unreachable;
|
r.sendBody(message) catch unreachable;
|
||||||
|
std.debug.assert(r.isFinished() == true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
246
examples/middleware_with_endpoint/middleware_with_endpoint.zig
Normal file
246
examples/middleware_with_endpoint/middleware_with_endpoint.zig
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// just a way to share our allocator via callback
|
||||||
|
const SharedAllocator = struct {
|
||||||
|
// static
|
||||||
|
var allocator: std.mem.Allocator = undefined;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
// just a convenience function
|
||||||
|
pub fn init(a: std.mem.Allocator) void {
|
||||||
|
allocator = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static function we can pass to the listener later
|
||||||
|
pub fn getAllocator() std.mem.Allocator {
|
||||||
|
return allocator;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create a combined context struct
|
||||||
|
const Context = zap.Middleware.MixContexts(.{
|
||||||
|
.{ .name = "?user", .type = UserMiddleWare.User },
|
||||||
|
.{ .name = "?session", .type = SessionMiddleWare.Session },
|
||||||
|
});
|
||||||
|
|
||||||
|
// we create a Handler type based on our Context
|
||||||
|
const Handler = zap.Middleware.Handler(Context);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Example user middleware: puts user info into the context
|
||||||
|
//
|
||||||
|
const UserMiddleWare = struct {
|
||||||
|
handler: Handler,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
// Just some arbitrary struct we want in the per-request context
|
||||||
|
// This is so that it can be constructed via .{}
|
||||||
|
// as we can't expect the listener to know how to initialize our context structs
|
||||||
|
const User = struct {
|
||||||
|
name: []const u8 = undefined,
|
||||||
|
email: []const u8 = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(other: ?*Handler) Self {
|
||||||
|
return .{
|
||||||
|
.handler = Handler.init(onRequest, other),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need the handler as a common interface to chain stuff
|
||||||
|
pub fn getHandler(self: *Self) *Handler {
|
||||||
|
return &self.handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note that the first parameter is of type *Handler, not *Self !!!
|
||||||
|
pub fn onRequest(handler: *Handler, r: zap.SimpleRequest, context: *Context) bool {
|
||||||
|
|
||||||
|
// this is how we would get our self pointer
|
||||||
|
var self = @fieldParentPtr(Self, "handler", handler);
|
||||||
|
_ = self;
|
||||||
|
|
||||||
|
// do our work: fill in the user field of the context
|
||||||
|
context.user = User{
|
||||||
|
.name = "renerocksai",
|
||||||
|
.email = "supa@secret.org",
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.print("\n\nUser Middleware: set user in context {any}\n\n", .{context.user});
|
||||||
|
|
||||||
|
// continue in the chain
|
||||||
|
return handler.handleOther(r, context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Example session middleware: puts session info into the context
|
||||||
|
//
|
||||||
|
const SessionMiddleWare = struct {
|
||||||
|
handler: Handler,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
// Just some arbitrary struct we want in the per-request context
|
||||||
|
// note: it MUST have all default values!!!
|
||||||
|
const Session = struct {
|
||||||
|
info: []const u8 = undefined,
|
||||||
|
token: []const u8 = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(other: ?*Handler) Self {
|
||||||
|
return .{
|
||||||
|
.handler = Handler.init(onRequest, other),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need the handler as a common interface to chain stuff
|
||||||
|
pub fn getHandler(self: *Self) *Handler {
|
||||||
|
return &self.handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note that the first parameter is of type *Handler, not *Self !!!
|
||||||
|
pub fn onRequest(handler: *Handler, r: zap.SimpleRequest, context: *Context) bool {
|
||||||
|
// this is how we would get our self pointer
|
||||||
|
var self = @fieldParentPtr(Self, "handler", handler);
|
||||||
|
_ = self;
|
||||||
|
|
||||||
|
context.session = Session{
|
||||||
|
.info = "secret session",
|
||||||
|
.token = "rot47-asdlkfjsaklfdj",
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.print("\n\nSessionMiddleware: set session in context {any}\n\n", .{context.session});
|
||||||
|
|
||||||
|
// continue in the chain
|
||||||
|
return handler.handleOther(r, context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// !!!! ENDPOINT !!!
|
||||||
|
//
|
||||||
|
// We define an endpoint as we usually would.
|
||||||
|
// NO ROUTING IS PERFORMED
|
||||||
|
// as we are just going to wrap it in a bunch of Middleware components
|
||||||
|
// and therefore NOT using an endpoint listener that would the routing for us
|
||||||
|
//
|
||||||
|
// Hence, the endpoint should check r.path in its on_request to check wether
|
||||||
|
// it is adressed or not.
|
||||||
|
//
|
||||||
|
// N.B. the EndpointHandler checks if the endpoint turned the request into
|
||||||
|
// "finished" state, e.g. by sending anything. If the endpoint didn't finish the
|
||||||
|
// request, the EndpointHandler will pass the request on to the next handler in
|
||||||
|
// the chain if there is one. See also the EndpointHandler's `breakOnFinish`
|
||||||
|
// parameter.
|
||||||
|
//
|
||||||
|
const HtmlEndpoint = struct {
|
||||||
|
endpoint: zap.SimpleEndpoint = undefined,
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init() Self {
|
||||||
|
return .{
|
||||||
|
.endpoint = zap.SimpleEndpoint.init(.{
|
||||||
|
.path = "/doesn'tmatter",
|
||||||
|
.get = get,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getEndpoint(self: *Self) *zap.SimpleEndpoint {
|
||||||
|
return &self.endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(ep: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
|
var self = @fieldParentPtr(Self, "endpoint", ep);
|
||||||
|
_ = self;
|
||||||
|
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
var userFound: bool = false;
|
||||||
|
var sessionFound: bool = false;
|
||||||
|
|
||||||
|
// the EndpointHandler set this for us!
|
||||||
|
// we got middleware context!!!
|
||||||
|
const maybe_context: ?*Context = r.getUserContext(Context);
|
||||||
|
if (maybe_context) |context| {
|
||||||
|
std.debug.print("\n\nHtmlEndpoint: handling request with context: {any}\n\n", .{context});
|
||||||
|
|
||||||
|
if (context.user) |user| {
|
||||||
|
userFound = true;
|
||||||
|
if (context.session) |session| {
|
||||||
|
sessionFound = true;
|
||||||
|
|
||||||
|
std.debug.assert(r.isFinished() == false);
|
||||||
|
const message = std.fmt.bufPrint(&buf, "User: {s} / {s}, Session: {s} / {s}", .{
|
||||||
|
user.name,
|
||||||
|
user.email,
|
||||||
|
session.info,
|
||||||
|
session.token,
|
||||||
|
}) catch unreachable;
|
||||||
|
r.setContentType(.TEXT) catch unreachable;
|
||||||
|
r.sendBody(message) catch unreachable;
|
||||||
|
std.debug.assert(r.isFinished() == true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = std.fmt.bufPrint(&buf, "User info found: {}, session info found: {}", .{ userFound, sessionFound }) catch unreachable;
|
||||||
|
|
||||||
|
r.setContentType(.TEXT) catch unreachable;
|
||||||
|
r.sendBody(message) catch unreachable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{
|
||||||
|
.thread_safe = true,
|
||||||
|
}){};
|
||||||
|
var allocator = gpa.allocator();
|
||||||
|
SharedAllocator.init(allocator);
|
||||||
|
|
||||||
|
// create the endpoint
|
||||||
|
var htmlEndpoint = HtmlEndpoint.init();
|
||||||
|
|
||||||
|
// we wrap the endpoint with a middleware handler
|
||||||
|
var htmlHandler = zap.Middleware.EndpointHandler(Handler, Context).init(
|
||||||
|
htmlEndpoint.getEndpoint(), // the endpoint
|
||||||
|
null, // no other handler (we are the last in the chain)
|
||||||
|
true, // break on finish. See EndpointHandler for this. Not applicable here.
|
||||||
|
);
|
||||||
|
|
||||||
|
// we wrap it in the session Middleware component
|
||||||
|
var sessionHandler = SessionMiddleWare.init(htmlHandler.getHandler());
|
||||||
|
|
||||||
|
// we wrap that in the user Middleware component
|
||||||
|
var userHandler = UserMiddleWare.init(sessionHandler.getHandler());
|
||||||
|
|
||||||
|
// we create a listener with our combined context
|
||||||
|
// and pass it the initial handler: the user handler
|
||||||
|
var listener = try zap.Middleware.Listener(Context).init(
|
||||||
|
.{
|
||||||
|
.on_request = null, // must be null
|
||||||
|
.port = 3000,
|
||||||
|
.log = true,
|
||||||
|
.max_clients = 100000,
|
||||||
|
},
|
||||||
|
userHandler.getHandler(),
|
||||||
|
SharedAllocator.getAllocator,
|
||||||
|
);
|
||||||
|
zap.enableDebugLog();
|
||||||
|
listener.listen() catch |err| {
|
||||||
|
std.debug.print("\nLISTEN ERROR: {any}\n", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.print("Visit me on http://127.0.0.1:3000\n", .{});
|
||||||
|
|
||||||
|
// start worker threads
|
||||||
|
zap.start(.{
|
||||||
|
.threads = 2,
|
||||||
|
.workers = 1,
|
||||||
|
});
|
||||||
|
}
|
|
@ -66,7 +66,9 @@ pub const http_s = extern struct {
|
||||||
params: FIOBJ,
|
params: FIOBJ,
|
||||||
body: FIOBJ,
|
body: FIOBJ,
|
||||||
udata: ?*anyopaque,
|
udata: ?*anyopaque,
|
||||||
}; // zig-cache/i/e0c8a6e617497ade13de512cbe191f23/include/http.h:153:12: warning: struct demoted to opaque type - has bitfield
|
};
|
||||||
|
|
||||||
|
// zig-cache/i/e0c8a6e617497ade13de512cbe191f23/include/http.h:153:12: warning: struct demoted to opaque type - has bitfield
|
||||||
|
|
||||||
// typedef struct {
|
// typedef struct {
|
||||||
// /** The cookie's name (Symbol). */
|
// /** The cookie's name (Symbol). */
|
||||||
|
|
|
@ -80,6 +80,42 @@ pub fn Handler(comptime ContextType: anytype) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A convenience handler for artibrary zap.SimpleEndpoint
|
||||||
|
pub fn EndpointHandler(comptime HandlerType: anytype, comptime ContextType: anytype) type {
|
||||||
|
return struct {
|
||||||
|
handler: HandlerType,
|
||||||
|
endpoint: *zap.SimpleEndpoint,
|
||||||
|
breakOnFinish: bool,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init(endpoint: *zap.SimpleEndpoint, other: ?*HandlerType, breakOnFinish: bool) Self {
|
||||||
|
return .{
|
||||||
|
.handler = HandlerType.init(onRequest, other),
|
||||||
|
.endpoint = endpoint,
|
||||||
|
.breakOnFinish = breakOnFinish,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need the handler as a common interface to chain stuff
|
||||||
|
pub fn getHandler(self: *Self) *HandlerType {
|
||||||
|
return &self.handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onRequest(handler: *HandlerType, r: zap.SimpleRequest, context: *ContextType) bool {
|
||||||
|
var self = @fieldParentPtr(Self, "handler", handler);
|
||||||
|
r.setUserContext(context);
|
||||||
|
self.endpoint.onRequest(r);
|
||||||
|
|
||||||
|
// if the request was handled by the endpoint, we may break the chain here
|
||||||
|
if (r.isFinished() and self.breakOnFinish) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return self.handler.handleOther(r, context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub const Error = error{
|
pub const Error = error{
|
||||||
InitOnRequestIsNotNull,
|
InitOnRequestIsNotNull,
|
||||||
};
|
};
|
||||||
|
|
66
src/zap.zig
66
src/zap.zig
|
@ -69,8 +69,48 @@ pub const SimpleRequest = struct {
|
||||||
method: ?[]const u8,
|
method: ?[]const u8,
|
||||||
h: [*c]fio.http_s,
|
h: [*c]fio.http_s,
|
||||||
|
|
||||||
|
/// NEVER touch this field!!!!
|
||||||
|
/// if you absolutely MUST, then you may provide context here
|
||||||
|
/// via setUserContext and getUserContext
|
||||||
|
_user_context: *UserContext,
|
||||||
|
/// NEVER touch this field!!!!
|
||||||
|
/// use markAsFinished() and isFinished() instead
|
||||||
|
/// 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
|
||||||
|
/// same instance of this field.
|
||||||
|
_is_finished_request_global: bool,
|
||||||
|
/// NEVER touch this field!!!!
|
||||||
|
/// this is part of the hack.
|
||||||
|
_is_finished: *bool = undefined,
|
||||||
|
|
||||||
|
const UserContext = struct {
|
||||||
|
user_context: ?*anyopaque = null,
|
||||||
|
};
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn markAsFinished(self: *const Self, finished: bool) void {
|
||||||
|
// we might be a copy
|
||||||
|
self._is_finished.* = finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isFinished(self: *const Self) bool {
|
||||||
|
// we might be a copy
|
||||||
|
return self._is_finished.*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setUserContext(self: *const Self, context: *anyopaque) void {
|
||||||
|
self._user_context.*.user_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getUserContext(self: *const Self, comptime Context: type) ?*Context {
|
||||||
|
if (self._user_context.*.user_context) |ptr| {
|
||||||
|
return @ptrCast(*Context, @alignCast(@alignOf(*Context), ptr));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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, @intToPtr(
|
const ret = fio.http_send_body(self.h, @intToPtr(
|
||||||
*anyopaque,
|
*anyopaque,
|
||||||
|
@ -78,6 +118,7 @@ pub const SimpleRequest = struct {
|
||||||
), body.len);
|
), body.len);
|
||||||
debug("SimpleRequest.sendBody(): ret = {}\n", .{ret});
|
debug("SimpleRequest.sendBody(): ret = {}\n", .{ret});
|
||||||
if (ret == -1) return error.HttpSendBody;
|
if (ret == -1) return error.HttpSendBody;
|
||||||
|
self.markAsFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sendJson(self: *const Self, json: []const u8) HttpError!void {
|
pub fn sendJson(self: *const Self, json: []const u8) HttpError!void {
|
||||||
|
@ -86,6 +127,7 @@ pub const SimpleRequest = struct {
|
||||||
*anyopaque,
|
*anyopaque,
|
||||||
@ptrToInt(json.ptr),
|
@ptrToInt(json.ptr),
|
||||||
), json.len) != 0) return error.HttpSendBody;
|
), json.len) != 0) return error.HttpSendBody;
|
||||||
|
self.markAsFinished(true);
|
||||||
} else |err| return err;
|
} else |err| return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +146,7 @@ pub const SimpleRequest = struct {
|
||||||
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);
|
||||||
try self.sendBody("moved");
|
try self.sendBody("moved");
|
||||||
|
self.markAsFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// shows how to use the logger
|
/// shows how to use the logger
|
||||||
|
@ -187,6 +230,7 @@ pub const SimpleRequest = struct {
|
||||||
pub fn sendFile(self: *const Self, file_path: []const u8) !void {
|
pub fn sendFile(self: *const Self, file_path: []const u8) !void {
|
||||||
if (fio.http_sendfile2(self.h, util.toCharPtr(file_path), file_path.len, null, 0) != 0)
|
if (fio.http_sendfile2(self.h, util.toCharPtr(file_path), file_path.len, null, 0) != 0)
|
||||||
return error.SendFile;
|
return error.SendFile;
|
||||||
|
self.markAsFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to decode the request's body.
|
/// Attempts to decode the request's body.
|
||||||
|
@ -575,7 +619,15 @@ pub const SimpleHttpListener = struct {
|
||||||
.body = util.fio2str(r.*.body),
|
.body = util.fio2str(r.*.body),
|
||||||
.method = util.fio2str(r.*.method),
|
.method = util.fio2str(r.*.method),
|
||||||
.h = r,
|
.h = r,
|
||||||
|
._is_finished_request_global = false,
|
||||||
|
._user_context = undefined,
|
||||||
};
|
};
|
||||||
|
req._is_finished = &req._is_finished_request_global;
|
||||||
|
|
||||||
|
var user_context: SimpleRequest.UserContext = .{};
|
||||||
|
req._user_context = &user_context;
|
||||||
|
|
||||||
|
req.markAsFinished(false);
|
||||||
std.debug.assert(l.settings.on_request != null);
|
std.debug.assert(l.settings.on_request != null);
|
||||||
if (l.settings.on_request) |on_request| {
|
if (l.settings.on_request) |on_request| {
|
||||||
// l.settings.on_request.?(req);
|
// l.settings.on_request.?(req);
|
||||||
|
@ -592,7 +644,14 @@ pub const SimpleHttpListener = struct {
|
||||||
.body = util.fio2str(r.*.body),
|
.body = util.fio2str(r.*.body),
|
||||||
.method = util.fio2str(r.*.method),
|
.method = util.fio2str(r.*.method),
|
||||||
.h = r,
|
.h = r,
|
||||||
|
._is_finished_request_global = false,
|
||||||
|
._user_context = undefined,
|
||||||
};
|
};
|
||||||
|
req._is_finished = &req._is_finished_request_global;
|
||||||
|
|
||||||
|
var user_context: SimpleRequest.UserContext = .{};
|
||||||
|
req._user_context = &user_context;
|
||||||
|
|
||||||
l.settings.on_response.?(req);
|
l.settings.on_response.?(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -605,8 +664,15 @@ pub const SimpleHttpListener = struct {
|
||||||
.body = util.fio2str(r.*.body),
|
.body = util.fio2str(r.*.body),
|
||||||
.method = util.fio2str(r.*.method),
|
.method = util.fio2str(r.*.method),
|
||||||
.h = r,
|
.h = r,
|
||||||
|
._is_finished_request_global = false,
|
||||||
|
._user_context = undefined,
|
||||||
};
|
};
|
||||||
var zigtarget: []u8 = target[0..target_len];
|
var zigtarget: []u8 = target[0..target_len];
|
||||||
|
req._is_finished = &req._is_finished_request_global;
|
||||||
|
|
||||||
|
var user_context: SimpleRequest.UserContext = .{};
|
||||||
|
req._user_context = &user_context;
|
||||||
|
|
||||||
l.settings.on_upgrade.?(req, zigtarget);
|
l.settings.on_upgrade.?(req, zigtarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,3 +15,4 @@ websockets
|
||||||
userpass_session
|
userpass_session
|
||||||
sendfile
|
sendfile
|
||||||
middleware
|
middleware
|
||||||
|
middleware_with_endpoint
|
||||||
|
|
Loading…
Add table
Reference in a new issue