mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
Compare commits
23 commits
24dfcbaeaa
...
905b376c21
Author | SHA1 | Date | |
---|---|---|---|
![]() |
905b376c21 | ||
![]() |
87415e1686 | ||
![]() |
6105c2d4ed | ||
![]() |
ddcf3899c1 | ||
![]() |
0fc425a95d | ||
![]() |
ef523d7767 | ||
![]() |
29d339892e | ||
![]() |
35634a97cd | ||
![]() |
041ca3e301 | ||
![]() |
c0cc025eda | ||
![]() |
fd567f3c29 | ||
![]() |
283e0d60d0 | ||
![]() |
a7a904aea4 | ||
![]() |
4e1b50aca8 | ||
![]() |
ec7cac6f6a | ||
![]() |
f7cf3dd39f | ||
![]() |
b134f969f3 | ||
![]() |
47ecb13d7b | ||
![]() |
8078b96d3f | ||
![]() |
4591f4048b | ||
![]() |
295c525ec2 | ||
![]() |
8d187310c7 | ||
![]() |
e93e45de42 |
35 changed files with 907 additions and 296 deletions
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -39,7 +39,7 @@ jobs:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
release_name: Release ${{ steps.tag.outputs.version }} # Remove 'refs/tags/' from the tag name
|
release_name: Release ${{ steps.tag.outputs.version }} # Remove 'refs/tags/' from the tag name
|
||||||
body_path: relnotes.md
|
body_path: relnotes.md
|
||||||
prerelease: true
|
prerelease: false
|
||||||
|
|
||||||
- name: Announce Release
|
- name: Announce Release
|
||||||
env:
|
env:
|
||||||
|
|
10
README.md
10
README.md
|
@ -215,7 +215,7 @@ really promising.
|
||||||
|
|
||||||
### 📣 Shout-Outs
|
### 📣 Shout-Outs
|
||||||
|
|
||||||
- [httpz](https://github.com/karlseguin/http.zig) : Pure Zig! Close to Zap's
|
- [http.zig](https://github.com/karlseguin/http.zig) : Pure Zig! Close to Zap's
|
||||||
model. Performance = good!
|
model. Performance = good!
|
||||||
- [jetzig](https://github.com/jetzig-framework/jetzig) : Comfortably develop
|
- [jetzig](https://github.com/jetzig-framework/jetzig) : Comfortably develop
|
||||||
modern web applications quickly, using http.zig under the hood
|
modern web applications quickly, using http.zig under the hood
|
||||||
|
@ -298,7 +298,7 @@ In your zig project folder (where `build.zig` is located), run:
|
||||||
|
|
||||||
<!-- INSERT_DEP_BEGIN -->
|
<!-- INSERT_DEP_BEGIN -->
|
||||||
```
|
```
|
||||||
zig fetch --save "git+https://github.com/zigzap/zap#v0.10.0"
|
zig fetch --save "git+https://github.com/zigzap/zap#v0.10.4"
|
||||||
```
|
```
|
||||||
<!-- INSERT_DEP_END -->
|
<!-- INSERT_DEP_END -->
|
||||||
|
|
||||||
|
@ -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.Request) 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});
|
||||||
}
|
}
|
||||||
|
@ -408,3 +408,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
33
build.zig
33
build.zig
|
@ -52,6 +52,7 @@ pub fn build(b: *std.Build) !void {
|
||||||
}{
|
}{
|
||||||
.{ .name = "app_basic", .src = "examples/app/basic.zig" },
|
.{ .name = "app_basic", .src = "examples/app/basic.zig" },
|
||||||
.{ .name = "app_auth", .src = "examples/app/auth.zig" },
|
.{ .name = "app_auth", .src = "examples/app/auth.zig" },
|
||||||
|
.{ .name = "app_errors", .src = "examples/app/errors.zig" },
|
||||||
.{ .name = "hello", .src = "examples/hello/hello.zig" },
|
.{ .name = "hello", .src = "examples/hello/hello.zig" },
|
||||||
.{ .name = "https", .src = "examples/https/https.zig" },
|
.{ .name = "https", .src = "examples/https/https.zig" },
|
||||||
.{ .name = "hello2", .src = "examples/hello2/hello2.zig" },
|
.{ .name = "hello2", .src = "examples/hello2/hello2.zig" },
|
||||||
|
@ -185,6 +186,28 @@ pub fn build(b: *std.Build) !void {
|
||||||
const run_sendfile_tests = b.addRunArtifact(sendfile_tests);
|
const run_sendfile_tests = b.addRunArtifact(sendfile_tests);
|
||||||
const install_sendfile_tests = b.addInstallArtifact(sendfile_tests, .{});
|
const install_sendfile_tests = b.addInstallArtifact(sendfile_tests, .{});
|
||||||
|
|
||||||
|
const recvfile_tests = b.addTest(.{
|
||||||
|
.name = "recv_tests",
|
||||||
|
.root_source_file = b.path("src/tests/test_recvfile.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
recvfile_tests.root_module.addImport("zap", zap_module);
|
||||||
|
const run_recvfile_tests = b.addRunArtifact(recvfile_tests);
|
||||||
|
const install_recvfile_tests = b.addInstallArtifact(recvfile_tests, .{});
|
||||||
|
|
||||||
|
const recvfile_notype_tests = b.addTest(.{
|
||||||
|
.name = "recv_tests",
|
||||||
|
.root_source_file = b.path("src/tests/test_recvfile_notype.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
recvfile_notype_tests.root_module.addImport("zap", zap_module);
|
||||||
|
const run_recvfile_notype_tests = b.addRunArtifact(recvfile_notype_tests);
|
||||||
|
const install_recvfile_notype_tests = b.addInstallArtifact(recvfile_notype_tests, .{});
|
||||||
|
|
||||||
// test commands
|
// test commands
|
||||||
const run_auth_test_step = b.step("test-authentication", "Run auth unit tests [REMOVE zig-cache!]");
|
const run_auth_test_step = b.step("test-authentication", "Run auth unit tests [REMOVE zig-cache!]");
|
||||||
run_auth_test_step.dependOn(&run_auth_tests.step);
|
run_auth_test_step.dependOn(&run_auth_tests.step);
|
||||||
|
@ -202,6 +225,14 @@ pub fn build(b: *std.Build) !void {
|
||||||
run_sendfile_test_step.dependOn(&run_sendfile_tests.step);
|
run_sendfile_test_step.dependOn(&run_sendfile_tests.step);
|
||||||
run_sendfile_test_step.dependOn(&install_sendfile_tests.step);
|
run_sendfile_test_step.dependOn(&install_sendfile_tests.step);
|
||||||
|
|
||||||
|
const run_recvfile_test_step = b.step("test-recvfile", "Run http param unit tests [REMOVE zig-cache!]");
|
||||||
|
run_recvfile_test_step.dependOn(&run_recvfile_tests.step);
|
||||||
|
run_recvfile_test_step.dependOn(&install_recvfile_tests.step);
|
||||||
|
|
||||||
|
const run_recvfile_notype_test_step = b.step("test-recvfile_notype", "Run http param unit tests [REMOVE zig-cache!]");
|
||||||
|
run_recvfile_notype_test_step.dependOn(&run_recvfile_notype_tests.step);
|
||||||
|
run_recvfile_notype_test_step.dependOn(&install_recvfile_notype_tests.step);
|
||||||
|
|
||||||
// Similar to creating the run step earlier, this exposes a `test` step to
|
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||||
// the `zig build --help` menu, providing a way for the participant to request
|
// the `zig build --help` menu, providing a way for the participant to request
|
||||||
// running the unit tests.
|
// running the unit tests.
|
||||||
|
@ -210,6 +241,8 @@ pub fn build(b: *std.Build) !void {
|
||||||
test_step.dependOn(&run_mustache_tests.step);
|
test_step.dependOn(&run_mustache_tests.step);
|
||||||
test_step.dependOn(&run_httpparams_tests.step);
|
test_step.dependOn(&run_httpparams_tests.step);
|
||||||
test_step.dependOn(&run_sendfile_tests.step);
|
test_step.dependOn(&run_sendfile_tests.step);
|
||||||
|
test_step.dependOn(&run_recvfile_tests.step);
|
||||||
|
test_step.dependOn(&run_recvfile_notype_tests.step);
|
||||||
|
|
||||||
//
|
//
|
||||||
// docserver
|
// docserver
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.{
|
.{
|
||||||
.name = .zap,
|
.name = .zap,
|
||||||
.version = "0.9.1",
|
.version = "0.10.4",
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"build.zig",
|
"build.zig",
|
||||||
"build.zig.zon",
|
"build.zig.zon",
|
||||||
|
|
|
@ -58,13 +58,6 @@ const MyEndpoint = struct {
|
||||||
);
|
);
|
||||||
try r.sendBody(response);
|
try r.sendBody(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// not implemented, don't care
|
|
||||||
pub fn post(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn put(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn delete(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn patch(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn options(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
@ -82,8 +75,8 @@ pub fn main() !void {
|
||||||
// App is the type
|
// App is the type
|
||||||
// app is the instance
|
// app is the instance
|
||||||
const App = zap.App.Create(MyContext);
|
const App = zap.App.Create(MyContext);
|
||||||
var app = try App.init(allocator, &my_context, .{});
|
try App.init(allocator, &my_context, .{});
|
||||||
defer app.deinit();
|
defer App.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep: MyEndpoint = .{
|
var ep: MyEndpoint = .{
|
||||||
|
@ -100,10 +93,10 @@ pub fn main() !void {
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
// make the authenticating endpoint known to the app
|
// make the authenticating endpoint known to the app
|
||||||
try app.register(&auth_ep);
|
try App.register(&auth_ep);
|
||||||
|
|
||||||
// listen
|
// listen
|
||||||
try app.listen(.{
|
try App.listen(.{
|
||||||
.interface = "0.0.0.0",
|
.interface = "0.0.0.0",
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
});
|
});
|
||||||
|
|
|
@ -59,14 +59,7 @@ const SimpleEndpoint = struct {
|
||||||
try r.sendBody(response_text);
|
try r.sendBody(response_text);
|
||||||
std.time.sleep(std.time.ns_per_ms * 300);
|
std.time.sleep(std.time.ns_per_ms * 300);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// empty stubs for all other request methods
|
|
||||||
pub fn post(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn put(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn delete(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn patch(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn options(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const StopEndpoint = struct {
|
const StopEndpoint = struct {
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
|
@ -81,12 +74,6 @@ const StopEndpoint = struct {
|
||||||
, .{context.*.db_connection});
|
, .{context.*.db_connection});
|
||||||
zap.stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn put(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn delete(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn patch(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn options(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
@ -103,19 +90,19 @@ pub fn main() !void {
|
||||||
|
|
||||||
// create an App instance
|
// create an App instance
|
||||||
const App = zap.App.Create(MyContext);
|
const App = zap.App.Create(MyContext);
|
||||||
var app = try App.init(allocator, &my_context, .{});
|
try App.init(allocator, &my_context, .{});
|
||||||
defer app.deinit();
|
defer App.deinit();
|
||||||
|
|
||||||
// create the endpoints
|
// create the endpoints
|
||||||
var my_endpoint = SimpleEndpoint.init("/test", "some endpoint specific data");
|
var my_endpoint = SimpleEndpoint.init("/test", "some endpoint specific data");
|
||||||
var stop_endpoint: StopEndpoint = .{ .path = "/stop" };
|
var stop_endpoint: StopEndpoint = .{ .path = "/stop" };
|
||||||
//
|
//
|
||||||
// register the endpoints with the app
|
// register the endpoints with the App
|
||||||
try app.register(&my_endpoint);
|
try App.register(&my_endpoint);
|
||||||
try app.register(&stop_endpoint);
|
try App.register(&stop_endpoint);
|
||||||
|
|
||||||
// listen on the network
|
// listen on the network
|
||||||
try app.listen(.{
|
try App.listen(.{
|
||||||
.interface = "0.0.0.0",
|
.interface = "0.0.0.0",
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
});
|
});
|
||||||
|
|
125
examples/app/errors.zig
Normal file
125
examples/app/errors.zig
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
//!
|
||||||
|
//! Part of the Zap examples.
|
||||||
|
//!
|
||||||
|
//! Build me with `zig build app_errors`.
|
||||||
|
//! Run me with `zig build run-app_errors`.
|
||||||
|
//!
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// The global Application Context
|
||||||
|
const MyContext = struct {
|
||||||
|
db_connection: []const u8,
|
||||||
|
|
||||||
|
// we don't use this
|
||||||
|
pub fn unhandledRequest(_: *MyContext, _: Allocator, _: zap.Request) anyerror!void {}
|
||||||
|
|
||||||
|
pub fn unhandledError(_: *MyContext, _: zap.Request, err: anyerror) void {
|
||||||
|
std.debug.print("\n\n\nUNHANDLED ERROR: {} !!! \n\n\n", .{err});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A very simple endpoint handling only GET requests
|
||||||
|
const ErrorEndpoint = struct {
|
||||||
|
|
||||||
|
// zap.App.Endpoint Interface part
|
||||||
|
path: []const u8,
|
||||||
|
error_strategy: zap.Endpoint.ErrorStrategy = .raise,
|
||||||
|
|
||||||
|
// data specific for this endpoint
|
||||||
|
some_data: []const u8,
|
||||||
|
|
||||||
|
pub fn init(path: []const u8, data: []const u8) ErrorEndpoint {
|
||||||
|
return .{
|
||||||
|
.path = path,
|
||||||
|
.some_data = data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle GET requests
|
||||||
|
pub fn get(_: *ErrorEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {
|
||||||
|
|
||||||
|
// we just return an error
|
||||||
|
// our error_strategy = .raise
|
||||||
|
// -> error will be raised and dispatched to MyContext.unhandledError
|
||||||
|
return error.@"Oh-No!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty stubs for all other request methods
|
||||||
|
pub fn post(_: *ErrorEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn put(_: *ErrorEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn delete(_: *ErrorEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn patch(_: *ErrorEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn options(_: *ErrorEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *ErrorEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const StopEndpoint = struct {
|
||||||
|
path: []const u8,
|
||||||
|
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
|
||||||
|
|
||||||
|
pub fn get(_: *StopEndpoint, _: Allocator, context: *MyContext, _: zap.Request) !void {
|
||||||
|
std.debug.print(
|
||||||
|
\\Before I stop, let me dump the app context:
|
||||||
|
\\db_connection='{s}'
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{context.*.db_connection});
|
||||||
|
zap.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn put(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn delete(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn patch(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
pub fn options(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
// setup allocations
|
||||||
|
var gpa: std.heap.GeneralPurposeAllocator(.{
|
||||||
|
// just to be explicit
|
||||||
|
.thread_safe = true,
|
||||||
|
}) = .{};
|
||||||
|
defer std.debug.print("\n\nLeaks detected: {}\n\n", .{gpa.deinit() != .ok});
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
// create an App context
|
||||||
|
var my_context: MyContext = .{ .db_connection = "db connection established!" };
|
||||||
|
|
||||||
|
// create an App instance
|
||||||
|
const App = zap.App.Create(MyContext);
|
||||||
|
try App.init(allocator, &my_context, .{});
|
||||||
|
defer App.deinit();
|
||||||
|
|
||||||
|
// create the endpoints
|
||||||
|
var my_endpoint = ErrorEndpoint.init("/error", "some endpoint specific data");
|
||||||
|
var stop_endpoint: StopEndpoint = .{ .path = "/stop" };
|
||||||
|
//
|
||||||
|
// register the endpoints with the App
|
||||||
|
try App.register(&my_endpoint);
|
||||||
|
try App.register(&stop_endpoint);
|
||||||
|
|
||||||
|
// listen on the network
|
||||||
|
try App.listen(.{
|
||||||
|
.interface = "0.0.0.0",
|
||||||
|
.port = 3000,
|
||||||
|
});
|
||||||
|
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
|
||||||
|
|
||||||
|
std.debug.print(
|
||||||
|
\\ Try me via:
|
||||||
|
\\ curl http://localhost:3000/error
|
||||||
|
\\ Stop me via:
|
||||||
|
\\ curl http://localhost:3000/stop
|
||||||
|
\\
|
||||||
|
, .{});
|
||||||
|
|
||||||
|
// start worker threads -- only 1 process!!!
|
||||||
|
zap.start(.{
|
||||||
|
.threads = 2,
|
||||||
|
.workers = 1,
|
||||||
|
});
|
||||||
|
}
|
|
@ -7,6 +7,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const Handler = struct {
|
const Handler = struct {
|
||||||
var alloc: std.mem.Allocator = undefined;
|
var alloc: std.mem.Allocator = undefined;
|
||||||
|
|
||||||
|
@ -17,7 +25,7 @@ const Handler = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (r.body) |body| {
|
if (r.body) |body| {
|
||||||
std.log.info("Body length is {any}\n", .{body.len});
|
std.log.info("Body length is {any}", .{body.len});
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse potential query params (for ?terminate=true)
|
// parse potential query params (for ?terminate=true)
|
||||||
|
@ -35,7 +43,7 @@ const Handler = struct {
|
||||||
for (params.items) |kv| {
|
for (params.items) |kv| {
|
||||||
if (kv.value) |v| {
|
if (kv.value) |v| {
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
std.log.info("Param `{s}` in owned list is {any}\n", .{ kv.key, v });
|
std.log.info("Param `{s}` in owned list is {any}", .{ kv.key, v });
|
||||||
switch (v) {
|
switch (v) {
|
||||||
// single-file upload
|
// single-file upload
|
||||||
zap.Request.HttpParam.Hash_Binfile => |*file| {
|
zap.Request.HttpParam.Hash_Binfile => |*file| {
|
||||||
|
@ -43,9 +51,9 @@ const Handler = struct {
|
||||||
const mimetype = file.mimetype orelse "(no mimetype)";
|
const mimetype = file.mimetype orelse "(no mimetype)";
|
||||||
const data = file.data orelse "";
|
const data = file.data orelse "";
|
||||||
|
|
||||||
std.log.debug(" filename: `{s}`\n", .{filename});
|
std.log.debug(" filename: `{s}`", .{filename});
|
||||||
std.log.debug(" mimetype: {s}\n", .{mimetype});
|
std.log.debug(" mimetype: {s}", .{mimetype});
|
||||||
std.log.debug(" contents: {any}\n", .{data});
|
std.log.debug(" contents: {any}", .{data});
|
||||||
},
|
},
|
||||||
// multi-file upload
|
// multi-file upload
|
||||||
zap.Request.HttpParam.Array_Binfile => |*files| {
|
zap.Request.HttpParam.Array_Binfile => |*files| {
|
||||||
|
@ -54,9 +62,9 @@ const Handler = struct {
|
||||||
const mimetype = file.mimetype orelse "(no mimetype)";
|
const mimetype = file.mimetype orelse "(no mimetype)";
|
||||||
const data = file.data orelse "";
|
const data = file.data orelse "";
|
||||||
|
|
||||||
std.log.debug(" filename: `{s}`\n", .{filename});
|
std.log.debug(" filename: `{s}`", .{filename});
|
||||||
std.log.debug(" mimetype: {s}\n", .{mimetype});
|
std.log.debug(" mimetype: {s}", .{mimetype});
|
||||||
std.log.debug(" contents: {any}\n", .{data});
|
std.log.debug(" contents: {any}", .{data});
|
||||||
}
|
}
|
||||||
files.*.deinit();
|
files.*.deinit();
|
||||||
},
|
},
|
||||||
|
@ -71,7 +79,7 @@ const Handler = struct {
|
||||||
|
|
||||||
// check if we received a terminate=true parameter
|
// check if we received a terminate=true parameter
|
||||||
if (r.getParamSlice("terminate")) |str| {
|
if (r.getParamSlice("terminate")) |str| {
|
||||||
std.log.info("?terminate={s}\n", .{str});
|
std.log.info("?terminate={s}", .{str});
|
||||||
if (std.mem.eql(u8, str, "true")) {
|
if (std.mem.eql(u8, str, "true")) {
|
||||||
zap.stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
@ -100,11 +108,11 @@ pub fn main() !void {
|
||||||
.public_folder = ".",
|
.public_folder = ".",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
zap.enableDebugLog();
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
std.log.info("\n\nURL is http://localhost:3000\n", .{});
|
std.log.info("\n\nURL is http://localhost:3000", .{});
|
||||||
std.log.info("\ncurl -v --request POST -F img=@test012345.bin http://127.0.0.1:3000\n", .{});
|
std.log.info("\ncurl -v --request POST -F img=@test012345.bin http://127.0.0.1:3000", .{});
|
||||||
std.log.info("\n\nTerminate with CTRL+C or by sending query param terminate=true\n", .{});
|
std.log.info("\n\nTerminate with CTRL+C or by sending query param terminate=true", .{});
|
||||||
|
|
||||||
zap.start(.{
|
zap.start(.{
|
||||||
.threads = 1,
|
.threads = 1,
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// We send ourselves a request with a cookie
|
// We send ourselves a request with a cookie
|
||||||
fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
||||||
const uri = try std.Uri.parse(url);
|
const uri = try std.Uri.parse(url);
|
||||||
|
@ -79,8 +87,8 @@ pub fn main() !void {
|
||||||
std.log.info("Cookie ZIG_ZAP not found!", .{});
|
std.log.info("Cookie ZIG_ZAP not found!", .{});
|
||||||
}
|
}
|
||||||
} else |err| {
|
} else |err| {
|
||||||
std.log.err("ERROR!\n", .{});
|
std.log.err("ERROR!", .{});
|
||||||
std.log.err("cannot check for `ZIG_ZAP` cookie: {any}\n", .{err});
|
std.log.err("cannot check for `ZIG_ZAP` cookie: {any}", .{err});
|
||||||
}
|
}
|
||||||
|
|
||||||
r.setCookie(.{
|
r.setCookie(.{
|
||||||
|
@ -93,8 +101,8 @@ pub fn main() !void {
|
||||||
//
|
//
|
||||||
// check out other params: domain, path, secure, http_only
|
// check out other params: domain, path, secure, http_only
|
||||||
}) catch |err| {
|
}) catch |err| {
|
||||||
std.log.err("ERROR!\n", .{});
|
std.log.err("ERROR!", .{});
|
||||||
std.log.err("cannot set cookie: {any}\n", .{err});
|
std.log.err("cannot set cookie: {any}", .{err});
|
||||||
};
|
};
|
||||||
|
|
||||||
r.sendBody("Hello") catch unreachable;
|
r.sendBody("Hello") catch unreachable;
|
||||||
|
@ -113,7 +121,6 @@ pub fn main() !void {
|
||||||
.max_body_size = 1 * 1024,
|
.max_body_size = 1 * 1024,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
zap.enableDebugLog();
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
std.log.info("\n\nTerminate with CTRL+C", .{});
|
std.log.info("\n\nTerminate with CTRL+C", .{});
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ path: []const u8 = "/error",
|
||||||
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
|
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
|
||||||
|
|
||||||
pub fn get(_: *ErrorEndpoint, _: zap.Request) !void {
|
pub fn get(_: *ErrorEndpoint, _: zap.Request) !void {
|
||||||
|
// error_strategy is set to .log_to_response
|
||||||
|
// --> this error will be shown in the browser, with a nice error trace
|
||||||
return error.@"Oh-no!";
|
return error.@"Oh-no!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,3 +20,4 @@ pub fn put(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
pub fn delete(_: *ErrorEndpoint, _: zap.Request) !void {}
|
pub fn delete(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
pub fn patch(_: *ErrorEndpoint, _: zap.Request) !void {}
|
pub fn patch(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
pub fn options(_: *ErrorEndpoint, _: zap.Request) !void {}
|
pub fn options(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
|
|
|
@ -19,6 +19,11 @@ fn on_request(r: zap.Request) !void {
|
||||||
try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
|
try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is just to demo that we could catch arbitrary errors as fallback
|
||||||
|
fn on_error(_: zap.Request, err: anyerror) void {
|
||||||
|
std.debug.print("\n\n\nOh no!!! We didn't chatch this error: {}\n\n\n", .{err});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{
|
var gpa = std.heap.GeneralPurposeAllocator(.{
|
||||||
.thread_safe = true,
|
.thread_safe = true,
|
||||||
|
@ -33,6 +38,8 @@ pub fn main() !void {
|
||||||
.{
|
.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
|
// optional
|
||||||
|
.on_error = on_error,
|
||||||
.log = true,
|
.log = true,
|
||||||
.public_folder = "examples/endpoint/html",
|
.public_folder = "examples/endpoint/html",
|
||||||
.max_clients = 100000,
|
.max_clients = 100000,
|
||||||
|
@ -47,11 +54,13 @@ pub fn main() !void {
|
||||||
|
|
||||||
var stopEp = StopEndpoint.init("/stop");
|
var stopEp = StopEndpoint.init("/stop");
|
||||||
var errorEp: ErrorEndpoint = .{};
|
var errorEp: ErrorEndpoint = .{};
|
||||||
|
var unhandledErrorEp: ErrorEndpoint = .{ .error_strategy = .raise, .path = "/unhandled" };
|
||||||
|
|
||||||
// register endpoints with the listener
|
// register endpoints with the listener
|
||||||
try listener.register(&userWeb);
|
try listener.register(&userWeb);
|
||||||
try listener.register(&stopEp);
|
try listener.register(&stopEp);
|
||||||
try listener.register(&errorEp);
|
try listener.register(&errorEp);
|
||||||
|
try listener.register(&unhandledErrorEp);
|
||||||
|
|
||||||
// fake some users
|
// fake some users
|
||||||
var uid: usize = undefined;
|
var uid: usize = undefined;
|
||||||
|
@ -77,5 +86,5 @@ pub fn main() !void {
|
||||||
|
|
||||||
// show potential memory leaks when ZAP is shut down
|
// show potential memory leaks when ZAP is shut down
|
||||||
const has_leaked = gpa.detectLeaks();
|
const has_leaked = gpa.detectLeaks();
|
||||||
std.log.debug("Has leaked: {}\n", .{has_leaked});
|
std.log.debug("Has leaked: {}", .{has_leaked});
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,3 +23,4 @@ pub fn put(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
pub fn delete(_: *StopEndpoint, _: zap.Request) !void {}
|
pub fn delete(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
pub fn patch(_: *StopEndpoint, _: zap.Request) !void {}
|
pub fn patch(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
pub fn options(_: *StopEndpoint, _: zap.Request) !void {}
|
pub fn options(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
|
|
@ -127,7 +127,12 @@ pub fn delete(self: *UserWeb, r: zap.Request) !void {
|
||||||
|
|
||||||
pub fn options(_: *UserWeb, r: zap.Request) !void {
|
pub fn options(_: *UserWeb, r: zap.Request) !void {
|
||||||
try r.setHeader("Access-Control-Allow-Origin", "*");
|
try r.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
try r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
try r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD");
|
||||||
|
r.setStatus(zap.http.StatusCode.no_content);
|
||||||
|
r.markAsFinished(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn head(_: *UserWeb, r: zap.Request) !void {
|
||||||
r.setStatus(zap.http.StatusCode.no_content);
|
r.setStatus(zap.http.StatusCode.no_content);
|
||||||
r.markAsFinished(true);
|
r.markAsFinished(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ const Endpoint = struct {
|
||||||
pub fn delete(_: *Endpoint, _: zap.Request) !void {}
|
pub fn delete(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn patch(_: *Endpoint, _: zap.Request) !void {}
|
pub fn patch(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn options(_: *Endpoint, _: zap.Request) !void {}
|
pub fn options(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *Endpoint, _: zap.Request) !void {}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// We send ourselves a request
|
// We send ourselves a request
|
||||||
fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
||||||
const uri = try std.Uri.parse(url);
|
const uri = try std.Uri.parse(url);
|
||||||
|
@ -103,7 +111,7 @@ pub fn main() !void {
|
||||||
std.log.info("Param one not found!", .{});
|
std.log.info("Param one not found!", .{});
|
||||||
}
|
}
|
||||||
} else |err| {
|
} else |err| {
|
||||||
std.log.err("cannot check for `one` param: {any}\n", .{err});
|
std.log.err("cannot check for `one` param: {any}", .{err});
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we received a terminate=true parameter
|
// check if we received a terminate=true parameter
|
||||||
|
@ -127,9 +135,9 @@ pub fn main() !void {
|
||||||
.max_body_size = 1 * 1024,
|
.max_body_size = 1 * 1024,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
zap.enableDebugLog();
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
std.log.info("\n\nTerminate with CTRL+C or by sending query param terminate=true\n", .{});
|
std.log.info("\n\nTerminate with CTRL+C or by sending query param terminate=true", .{});
|
||||||
|
|
||||||
const thread = try makeRequestThread(allocator, "http://127.0.0.1:3000/?one=1&two=2&string=hello+world&float=6.28&bool=true");
|
const thread = try makeRequestThread(allocator, "http://127.0.0.1:3000/?one=1&two=2&string=hello+world&float=6.28&bool=true");
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// just a way to share our allocator via callback
|
// just a way to share our allocator via callback
|
||||||
const SharedAllocator = struct {
|
const SharedAllocator = struct {
|
||||||
// static
|
// static
|
||||||
|
@ -212,7 +220,7 @@ pub fn main() !void {
|
||||||
userHandler.getHandler(),
|
userHandler.getHandler(),
|
||||||
SharedAllocator.getAllocator,
|
SharedAllocator.getAllocator,
|
||||||
);
|
);
|
||||||
zap.enableDebugLog();
|
|
||||||
listener.listen() catch |err| {
|
listener.listen() catch |err| {
|
||||||
std.debug.print("\nLISTEN ERROR: {any}\n", .{err});
|
std.debug.print("\nLISTEN ERROR: {any}\n", .{err});
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// just a way to share our allocator via callback
|
// just a way to share our allocator via callback
|
||||||
const SharedAllocator = struct {
|
const SharedAllocator = struct {
|
||||||
// static
|
// static
|
||||||
|
@ -149,6 +157,7 @@ const HtmlEndpoint = struct {
|
||||||
pub fn delete(_: *HtmlEndpoint, _: zap.Request) !void {}
|
pub fn delete(_: *HtmlEndpoint, _: zap.Request) !void {}
|
||||||
pub fn patch(_: *HtmlEndpoint, _: zap.Request) !void {}
|
pub fn patch(_: *HtmlEndpoint, _: zap.Request) !void {}
|
||||||
pub fn options(_: *HtmlEndpoint, _: zap.Request) !void {}
|
pub fn options(_: *HtmlEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *HtmlEndpoint, _: zap.Request) !void {}
|
||||||
|
|
||||||
pub fn get(_: *HtmlEndpoint, r: zap.Request) !void {
|
pub fn get(_: *HtmlEndpoint, r: zap.Request) !void {
|
||||||
var buf: [1024]u8 = undefined;
|
var buf: [1024]u8 = undefined;
|
||||||
|
@ -224,7 +233,7 @@ pub fn main() !void {
|
||||||
userHandler.getHandler(),
|
userHandler.getHandler(),
|
||||||
SharedAllocator.getAllocator,
|
SharedAllocator.getAllocator,
|
||||||
);
|
);
|
||||||
zap.enableDebugLog();
|
|
||||||
listener.listen() catch |err| {
|
listener.listen() catch |err| {
|
||||||
std.debug.print("\nLISTEN ERROR: {any}\n", .{err});
|
std.debug.print("\nLISTEN ERROR: {any}\n", .{err});
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,6 +8,14 @@ const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
const Mustache = @import("zap").Mustache;
|
const Mustache = @import("zap").Mustache;
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
fn on_request(r: zap.Request) !void {
|
fn on_request(r: zap.Request) !void {
|
||||||
const template =
|
const template =
|
||||||
\\ {{=<< >>=}}
|
\\ {{=<< >>=}}
|
||||||
|
@ -63,12 +71,9 @@ pub fn main() !void {
|
||||||
});
|
});
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
|
|
||||||
// zap.enableDebugLog();
|
|
||||||
// zap.debug("ZAP debug logging is on\n", .{});
|
|
||||||
|
|
||||||
// we can also use facilio logging
|
// we can also use facilio logging
|
||||||
// zap.Log.fio_set_log_level(zap.Log.fio_log_level_debug);
|
// zap.Logging.fio_set_log_level(zap.Log.fio_log_level_debug);
|
||||||
// zap.Log.fio_log_debug("hello from fio\n");
|
// zap.Logging.fio_log_debug("hello from fio\n");
|
||||||
|
|
||||||
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
|
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
var buffer: [1024]u8 = undefined;
|
var buffer: [1024]u8 = undefined;
|
||||||
var read_len: ?usize = null;
|
var read_len: ?usize = null;
|
||||||
|
|
||||||
|
@ -43,7 +51,6 @@ pub fn main() !void {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
zap.enableDebugLog();
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
|
|
||||||
std.debug.print("Visit me on http://127.0.0.1:3000\n", .{});
|
std.debug.print("Visit me on http://127.0.0.1:3000\n", .{});
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const Lookup = std.StringHashMap([]const u8);
|
const Lookup = std.StringHashMap([]const u8);
|
||||||
const auth_lock_pw_table = false;
|
const auth_lock_pw_table = false;
|
||||||
|
|
||||||
|
@ -140,8 +148,6 @@ pub fn main() !void {
|
||||||
});
|
});
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
|
|
||||||
zap.enableDebugLog();
|
|
||||||
|
|
||||||
// Usernames -> Passwords for the /login page
|
// Usernames -> Passwords for the /login page
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
var userpass = Lookup.init(allocator);
|
var userpass = Lookup.init(allocator);
|
||||||
|
|
|
@ -81,20 +81,17 @@ const ContextManager = struct {
|
||||||
//
|
//
|
||||||
// Websocket Callbacks
|
// Websocket Callbacks
|
||||||
//
|
//
|
||||||
fn on_open_websocket(context: ?*Context, handle: WebSockets.WsHandle) void {
|
fn on_open_websocket(context: ?*Context, handle: WebSockets.WsHandle) !void {
|
||||||
if (context) |ctx| {
|
if (context) |ctx| {
|
||||||
_ = WebsocketHandler.subscribe(handle, &ctx.subscribeArgs) catch |err| {
|
_ = try WebsocketHandler.subscribe(handle, &ctx.subscribeArgs);
|
||||||
std.log.err("Error opening websocket: {any}", .{err});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// say hello
|
// say hello
|
||||||
var buf: [128]u8 = undefined;
|
var buf: [128]u8 = undefined;
|
||||||
const message = std.fmt.bufPrint(
|
const message = try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"{s} joined the chat.",
|
"{s} joined the chat.",
|
||||||
.{ctx.userName},
|
.{ctx.userName},
|
||||||
) catch unreachable;
|
);
|
||||||
|
|
||||||
// send notification to all others
|
// send notification to all others
|
||||||
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message });
|
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message });
|
||||||
|
@ -102,16 +99,16 @@ fn on_open_websocket(context: ?*Context, handle: WebSockets.WsHandle) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_close_websocket(context: ?*Context, uuid: isize) void {
|
fn on_close_websocket(context: ?*Context, uuid: isize) !void {
|
||||||
_ = uuid;
|
_ = uuid;
|
||||||
if (context) |ctx| {
|
if (context) |ctx| {
|
||||||
// say goodbye
|
// say goodbye
|
||||||
var buf: [128]u8 = undefined;
|
var buf: [128]u8 = undefined;
|
||||||
const message = std.fmt.bufPrint(
|
const message = try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"{s} left the chat.",
|
"{s} left the chat.",
|
||||||
.{ctx.userName},
|
.{ctx.userName},
|
||||||
) catch unreachable;
|
);
|
||||||
|
|
||||||
// send notification to all others
|
// send notification to all others
|
||||||
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message });
|
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message });
|
||||||
|
@ -124,7 +121,7 @@ fn handle_websocket_message(
|
||||||
handle: WebSockets.WsHandle,
|
handle: WebSockets.WsHandle,
|
||||||
message: []const u8,
|
message: []const u8,
|
||||||
is_text: bool,
|
is_text: bool,
|
||||||
) void {
|
) !void {
|
||||||
_ = handle;
|
_ = handle;
|
||||||
_ = is_text;
|
_ = is_text;
|
||||||
|
|
||||||
|
@ -145,11 +142,11 @@ fn handle_websocket_message(
|
||||||
if (message.len > max_msg_len) {
|
if (message.len > max_msg_len) {
|
||||||
trimmed_message = message[0..max_msg_len];
|
trimmed_message = message[0..max_msg_len];
|
||||||
}
|
}
|
||||||
const chat_message = std.fmt.bufPrint(
|
const chat_message = try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
format_string,
|
format_string,
|
||||||
.{ ctx.userName, trimmed_message },
|
.{ ctx.userName, trimmed_message },
|
||||||
) catch unreachable;
|
);
|
||||||
|
|
||||||
// send notification to all others
|
// send notification to all others
|
||||||
WebsocketHandler.publish(
|
WebsocketHandler.publish(
|
||||||
|
@ -169,7 +166,7 @@ fn handle_websocket_message(
|
||||||
// HTTP stuff
|
// HTTP stuff
|
||||||
//
|
//
|
||||||
fn on_request(r: zap.Request) !void {
|
fn on_request(r: zap.Request) !void {
|
||||||
r.setHeader("Server", "zap.example") catch unreachable;
|
try r.setHeader("Server", "zap.example");
|
||||||
try r.sendBody(
|
try r.sendBody(
|
||||||
\\ <html><body>
|
\\ <html><body>
|
||||||
\\ <h1>This is a simple Websocket chatroom example</h1>
|
\\ <h1>This is a simple Websocket chatroom example</h1>
|
||||||
|
@ -177,7 +174,7 @@ fn on_request(r: zap.Request) !void {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_upgrade(r: zap.Request, 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});
|
||||||
|
@ -190,10 +187,7 @@ fn on_upgrade(r: zap.Request, target_protocol: []const u8) void {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebsocketHandler.upgrade(r.h, &context.settings) catch |err| {
|
try WebsocketHandler.upgrade(r.h, &context.settings);
|
||||||
std.log.err("Error in websocketUpgrade(): {any}", .{err});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
std.log.info("connection upgrade OK", .{});
|
std.log.info("connection upgrade OK", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
173
src/App.zig
173
src/App.zig
|
@ -1,3 +1,16 @@
|
||||||
|
//! zap.App takes the zap.Endpoint concept one step further: instead of having
|
||||||
|
//! only per-endpoint instance data (fields of your Endpoint struct), endpoints
|
||||||
|
//! in a zap.App easily share a global 'App Context'.
|
||||||
|
//!
|
||||||
|
//! In addition to the global App Context, all Endpoint request handlers also
|
||||||
|
//! receive an arena allocator for easy, care-free allocations. There is one
|
||||||
|
//! arena allocator per thread, and arenas are reset after each request.
|
||||||
|
//!
|
||||||
|
//! Just like regular / legacy zap.Endpoints, returning errors from request
|
||||||
|
//! handlers is OK. It's decided on a per-endpoint basis how errors are dealt
|
||||||
|
//! with, via the ErrorStrategy enum field.
|
||||||
|
//!
|
||||||
|
//! See `App.Create()`.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
@ -16,7 +29,20 @@ pub const AppOpts = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// creates an App with custom app context
|
/// creates an App with custom app context
|
||||||
pub fn Create(comptime Context: type) type {
|
///
|
||||||
|
/// About App Contexts:
|
||||||
|
///
|
||||||
|
/// ```zig
|
||||||
|
/// const MyContext = struct {
|
||||||
|
/// // You may (optionally) define the following global handlers:
|
||||||
|
/// pub fn unhandledRequest(_: *MyContext, _: Allocator, _: Request) anyerror!void {}
|
||||||
|
/// pub fn unhandledError(_: *MyContext, _: Request, _: anyerror) void {}
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
pub fn Create(
|
||||||
|
/// Your user-defined "Global App Context" type
|
||||||
|
comptime Context: type,
|
||||||
|
) type {
|
||||||
return struct {
|
return struct {
|
||||||
const App = @This();
|
const App = @This();
|
||||||
|
|
||||||
|
@ -35,19 +61,21 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// the internal http listener
|
/// the internal http listener
|
||||||
listener: HttpListener = undefined,
|
listener: HttpListener = undefined,
|
||||||
|
|
||||||
/// function pointer to handler for otherwise unhandled requests
|
/// function pointer to handler for otherwise unhandled requests.
|
||||||
/// Will automatically be set if your Context provides an unhandled
|
/// Will automatically be set if your Context provides an
|
||||||
/// function of type `fn(*Context, Allocator, Request)`
|
/// `unhandledRequest` function of type `fn(*Context, Allocator,
|
||||||
///
|
/// Request) !void`.
|
||||||
unhandled: ?*const fn (*Context, Allocator, Request) anyerror!void = null,
|
unhandled_request: ?*const fn (*Context, Allocator, Request) anyerror!void = null,
|
||||||
|
|
||||||
|
/// function pointer to handler for unhandled errors.
|
||||||
|
/// Errors are unhandled if they are not logged but raised by the
|
||||||
|
/// ErrorStrategy. Will automatically be set if your Context
|
||||||
|
/// provides an `unhandledError` function of type `fn(*Context,
|
||||||
|
/// Allocator, Request, anyerror) void`.
|
||||||
|
unhandled_error: ?*const fn (*Context, Request, anyerror) void = null,
|
||||||
};
|
};
|
||||||
var _static: InstanceData = .{};
|
var _static: InstanceData = .{};
|
||||||
|
|
||||||
/// 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: ?*const fn (Allocator, *Context, Request) anyerror!void = null;
|
|
||||||
|
|
||||||
pub const Endpoint = struct {
|
pub const Endpoint = struct {
|
||||||
pub const Interface = struct {
|
pub const Interface = struct {
|
||||||
call: *const fn (*Interface, Request) anyerror!void = undefined,
|
call: *const fn (*Interface, Request) anyerror!void = undefined,
|
||||||
|
@ -82,13 +110,15 @@ pub fn Create(comptime Context: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onRequest(self: *Bound, arena: Allocator, app_context: *Context, r: Request) !void {
|
pub fn onRequest(self: *Bound, arena: Allocator, app_context: *Context, r: Request) !void {
|
||||||
|
// TODO: simplitfy this with @tagName?
|
||||||
const ret = switch (r.methodAsEnum()) {
|
const ret = switch (r.methodAsEnum()) {
|
||||||
.GET => self.endpoint.*.get(arena, app_context, r),
|
.GET => callHandlerIfExist("get", self.endpoint, arena, app_context, r),
|
||||||
.POST => self.endpoint.*.post(arena, app_context, r),
|
.POST => callHandlerIfExist("post", self.endpoint, arena, app_context, r),
|
||||||
.PUT => self.endpoint.*.put(arena, app_context, r),
|
.PUT => callHandlerIfExist("put", self.endpoint, arena, app_context, r),
|
||||||
.DELETE => self.endpoint.*.delete(arena, app_context, r),
|
.DELETE => callHandlerIfExist("delete", self.endpoint, arena, app_context, r),
|
||||||
.PATCH => self.endpoint.*.patch(arena, app_context, r),
|
.PATCH => callHandlerIfExist("patch", self.endpoint, arena, app_context, r),
|
||||||
.OPTIONS => self.endpoint.*.options(arena, app_context, r),
|
.OPTIONS => callHandlerIfExist("options", self.endpoint, arena, app_context, r),
|
||||||
|
.HEAD => callHandlerIfExist("head", self.endpoint, arena, app_context, r),
|
||||||
else => error.UnsupportedHtmlRequestMethod,
|
else => error.UnsupportedHtmlRequestMethod,
|
||||||
};
|
};
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -97,7 +127,7 @@ pub fn Create(comptime Context: type) type {
|
||||||
switch (self.endpoint.*.error_strategy) {
|
switch (self.endpoint.*.error_strategy) {
|
||||||
.raise => return err,
|
.raise => return err,
|
||||||
.log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505),
|
.log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505),
|
||||||
.log_to_console => zap.debug(
|
.log_to_console => zap.log.err(
|
||||||
"Error in {} {s} : {}",
|
"Error in {} {s} : {}",
|
||||||
.{ Bound, r.method orelse "(no method)", err },
|
.{ Bound, r.method orelse "(no method)", err },
|
||||||
),
|
),
|
||||||
|
@ -145,6 +175,7 @@ pub fn Create(comptime Context: type) type {
|
||||||
"delete",
|
"delete",
|
||||||
"patch",
|
"patch",
|
||||||
"options",
|
"options",
|
||||||
|
"head",
|
||||||
};
|
};
|
||||||
const params_to_check = [_]type{
|
const params_to_check = [_]type{
|
||||||
*T,
|
*T,
|
||||||
|
@ -197,8 +228,6 @@ pub fn Create(comptime Context: type) type {
|
||||||
if (ret_info.error_union.payload != void) {
|
if (ret_info.error_union.payload != void) {
|
||||||
@compileError("Expected return type of method `" ++ @typeName(T) ++ "." ++ method ++ "` to be !void, got: !" ++ @typeName(ret_info.error_union.payload));
|
@compileError("Expected return type of method `" ++ @typeName(T) ++ "." ++ method ++ "` to be !void, got: !" ++ @typeName(ret_info.error_union.payload));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
@compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,8 +256,8 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// Authenticates GET requests using the Authenticator.
|
/// Authenticates GET requests using the Authenticator.
|
||||||
pub fn get(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
pub fn get(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => callHandlerIfExist("unauthorized", self.ep, arena, context, request),
|
||||||
.AuthOK => self.ep.*.get(arena, context, request),
|
.AuthOK => callHandlerIfExist("get", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -236,8 +265,8 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// Authenticates POST requests using the Authenticator.
|
/// Authenticates POST requests using the Authenticator.
|
||||||
pub fn post(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
pub fn post(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => callHandlerIfExist("unauthorized", self.ep, arena, context, request),
|
||||||
.AuthOK => self.ep.*.post(arena, context, request),
|
.AuthOK => callHandlerIfExist("post", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -245,8 +274,8 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// Authenticates PUT requests using the Authenticator.
|
/// Authenticates PUT requests using the Authenticator.
|
||||||
pub fn put(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn put(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => callHandlerIfExist("unauthorized", self.ep, arena, context, request),
|
||||||
.AuthOK => self.ep.*.put(arena, context, request),
|
.AuthOK => callHandlerIfExist("put", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -254,8 +283,8 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// Authenticates DELETE requests using the Authenticator.
|
/// Authenticates DELETE requests using the Authenticator.
|
||||||
pub fn delete(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn delete(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => callHandlerIfExist("unauthorized", self.ep, arena, context, request),
|
||||||
.AuthOK => self.ep.*.delete(arena, context, request),
|
.AuthOK => callHandlerIfExist("delete", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -263,8 +292,8 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// Authenticates PATCH requests using the Authenticator.
|
/// Authenticates PATCH requests using the Authenticator.
|
||||||
pub fn patch(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn patch(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => callHandlerIfExist("unauthorized", self.ep, arena, context, request),
|
||||||
.AuthOK => self.ep.*.patch(arena, context, request),
|
.AuthOK => callHandlerIfExist("patch", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -272,8 +301,17 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// Authenticates OPTIONS requests using the Authenticator.
|
/// Authenticates OPTIONS requests using the Authenticator.
|
||||||
pub fn options(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn options(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => callHandlerIfExist("unauthorized", self.ep, arena, context, request),
|
||||||
.AuthOK => self.ep.*.put(arena, context, request),
|
.AuthOK => callHandlerIfExist("options", self.ep, arena, context, request),
|
||||||
|
.Handled => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Authenticates HEAD requests using the Authenticator.
|
||||||
|
pub fn head(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
|
.AuthFailed => callHandlerIfExist("unauthorized", self.ep, arena, context, request),
|
||||||
|
.AuthOK => callHandlerIfExist("head", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -293,7 +331,7 @@ pub fn Create(comptime Context: type) type {
|
||||||
tls: ?zap.Tls = null,
|
tls: ?zap.Tls = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(gpa_: Allocator, context_: *Context, opts_: AppOpts) !App {
|
pub fn init(gpa_: Allocator, context_: *Context, opts_: AppOpts) !void {
|
||||||
if (_static.there_can_be_only_one) {
|
if (_static.there_can_be_only_one) {
|
||||||
return error.OnlyOneAppAllowed;
|
return error.OnlyOneAppAllowed;
|
||||||
}
|
}
|
||||||
|
@ -302,20 +340,28 @@ pub fn Create(comptime Context: type) type {
|
||||||
_static.opts = opts_;
|
_static.opts = opts_;
|
||||||
_static.there_can_be_only_one = true;
|
_static.there_can_be_only_one = true;
|
||||||
|
|
||||||
// set unhandled callback if provided by Context
|
// set unhandled_request callback if provided by Context
|
||||||
if (@hasDecl(Context, "unhandled")) {
|
if (@hasDecl(Context, "unhandledRequest")) {
|
||||||
// try if we can use it
|
// try if we can use it
|
||||||
const Unhandled = @TypeOf(@field(Context, "unhandled"));
|
const Unhandled = @TypeOf(@field(Context, "unhandledRequest"));
|
||||||
const Expected = fn (_: *Context, _: Allocator, _: Request) anyerror!void;
|
const Expected = fn (_: *Context, _: Allocator, _: Request) anyerror!void;
|
||||||
if (Unhandled != Expected) {
|
if (Unhandled != Expected) {
|
||||||
@compileError("`unhandled` method of " ++ @typeName(Context) ++ " has wrong type:\n" ++ @typeName(Unhandled) ++ "\nexpected:\n" ++ @typeName(Expected));
|
@compileError("`unhandledRequest` method of " ++ @typeName(Context) ++ " has wrong type:\n" ++ @typeName(Unhandled) ++ "\nexpected:\n" ++ @typeName(Expected));
|
||||||
}
|
}
|
||||||
_static.unhandled = Context.unhandled;
|
_static.unhandled_request = Context.unhandledRequest;
|
||||||
|
}
|
||||||
|
if (@hasDecl(Context, "unhandledError")) {
|
||||||
|
// try if we can use it
|
||||||
|
const Unhandled = @TypeOf(@field(Context, "unhandledError"));
|
||||||
|
const Expected = fn (_: *Context, _: Request, _: anyerror) void;
|
||||||
|
if (Unhandled != Expected) {
|
||||||
|
@compileError("`unhandledError` method of " ++ @typeName(Context) ++ " has wrong type:\n" ++ @typeName(Unhandled) ++ "\nexpected:\n" ++ @typeName(Expected));
|
||||||
|
}
|
||||||
|
_static.unhandled_error = Context.unhandledError;
|
||||||
}
|
}
|
||||||
return .{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(_: *App) void {
|
pub fn deinit() void {
|
||||||
// we created endpoint wrappers but only tracked their interfaces
|
// we created endpoint wrappers but only tracked their interfaces
|
||||||
// hence, we need to destroy the wrappers through their interfaces
|
// hence, we need to destroy the wrappers through their interfaces
|
||||||
if (false) {
|
if (false) {
|
||||||
|
@ -336,12 +382,26 @@ pub fn Create(comptime Context: type) type {
|
||||||
|
|
||||||
var it = _static.track_arenas.valueIterator();
|
var it = _static.track_arenas.valueIterator();
|
||||||
while (it.next()) |arena| {
|
while (it.next()) |arena| {
|
||||||
// std.debug.print("deiniting arena: {*}\n", .{arena});
|
|
||||||
arena.deinit();
|
arena.deinit();
|
||||||
}
|
}
|
||||||
_static.track_arenas.deinit(_static.gpa);
|
_static.track_arenas.deinit(_static.gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This can be resolved at comptime so *perhaps it does affect optimiazation
|
||||||
|
pub fn callHandlerIfExist(comptime fn_name: []const u8, e: anytype, arena: Allocator, ctx: *Context, r: Request) anyerror!void {
|
||||||
|
const EndPoint = @TypeOf(e.*);
|
||||||
|
if (@hasDecl(EndPoint, fn_name)) {
|
||||||
|
return @field(EndPoint, fn_name)(e, arena, ctx, r);
|
||||||
|
}
|
||||||
|
zap.log.debug(
|
||||||
|
"Unhandled `{s}` {s} request ({s} not implemented in {s})",
|
||||||
|
.{ r.method orelse "<unknown>", r.path orelse "", fn_name, @typeName(Endpoint) },
|
||||||
|
);
|
||||||
|
r.setStatus(.method_not_allowed);
|
||||||
|
try r.sendBody("405 - method not allowed\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_arena() !*ArenaAllocator {
|
pub fn get_arena() !*ArenaAllocator {
|
||||||
const thread_id = std.Thread.getCurrentId();
|
const thread_id = std.Thread.getCurrentId();
|
||||||
_static.track_arena_lock.lockShared();
|
_static.track_arena_lock.lockShared();
|
||||||
|
@ -364,7 +424,7 @@ pub fn Create(comptime Context: type) type {
|
||||||
/// If you try to register an endpoint whose path would shadow an
|
/// If you try to register an endpoint whose path would shadow an
|
||||||
/// already registered one, you will receive an
|
/// already registered one, you will receive an
|
||||||
/// EndpointPathShadowError.
|
/// EndpointPathShadowError.
|
||||||
pub fn register(_: *App, endpoint: anytype) !void {
|
pub fn register(endpoint: anytype) !void {
|
||||||
for (_static.endpoints.items) |other| {
|
for (_static.endpoints.items) |other| {
|
||||||
if (std.mem.startsWith(
|
if (std.mem.startsWith(
|
||||||
u8,
|
u8,
|
||||||
|
@ -385,7 +445,7 @@ pub fn Create(comptime Context: type) type {
|
||||||
try _static.endpoints.append(_static.gpa, &bound.interface);
|
try _static.endpoints.append(_static.gpa, &bound.interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen(_: *App, l: ListenerSettings) !void {
|
pub fn listen(l: ListenerSettings) !void {
|
||||||
_static.listener = HttpListener.init(.{
|
_static.listener = HttpListener.init(.{
|
||||||
.interface = l.interface,
|
.interface = l.interface,
|
||||||
.port = l.port,
|
.port = l.port,
|
||||||
|
@ -404,17 +464,34 @@ pub fn Create(comptime Context: type) type {
|
||||||
if (r.path) |p| {
|
if (r.path) |p| {
|
||||||
for (_static.endpoints.items) |interface| {
|
for (_static.endpoints.items) |interface| {
|
||||||
if (std.mem.startsWith(u8, p, interface.path)) {
|
if (std.mem.startsWith(u8, p, interface.path)) {
|
||||||
return try interface.call(interface, r);
|
return interface.call(interface, r) catch |err| {
|
||||||
|
// if error is not dealt with in the interface, e.g.
|
||||||
|
// if error strategy is .raise:
|
||||||
|
if (_static.unhandled_error) |error_cb| {
|
||||||
|
error_cb(_static.context, r, err);
|
||||||
|
} else {
|
||||||
|
zap.log.err(
|
||||||
|
"App.Endpoint onRequest error {} in endpoint interface {}\n",
|
||||||
|
.{ err, interface },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (on_request) |foo| {
|
|
||||||
|
// this is basically the "not found" handler
|
||||||
|
if (_static.unhandled_request) |foo| {
|
||||||
var arena = try get_arena();
|
var arena = try get_arena();
|
||||||
foo(arena.allocator(), _static.context, r) catch |err| {
|
foo(_static.context, arena.allocator(), r) catch |err| {
|
||||||
switch (_static.opts.default_error_strategy) {
|
switch (_static.opts.default_error_strategy) {
|
||||||
.raise => return err,
|
.raise => if (_static.unhandled_error) |error_cb| {
|
||||||
|
error_cb(_static.context, r, err);
|
||||||
|
} else {
|
||||||
|
zap.Logging.on_uncaught_error("App on_request", err);
|
||||||
|
},
|
||||||
.log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505),
|
.log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505),
|
||||||
.log_to_console => zap.debug("Error in {} {s} : {}", .{ App, r.method orelse "(no method)", err }),
|
.log_to_console => zap.log.err("Error in {} {s} : {}", .{ App, r.method orelse "(no method)", err }),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,18 @@
|
||||||
const std = @import("std");
|
//! Access to facil.io's logging facilities
|
||||||
|
//!
|
||||||
// TODO: rework logging in zap
|
//! Zap uses Zig's standard logging facilities, which you can control like this:
|
||||||
|
//!
|
||||||
debugOn: bool,
|
//! ```zig
|
||||||
|
//! pub const std_options: std.Options = .{
|
||||||
/// Access to facil.io's logging facilities
|
//! // general log level
|
||||||
const Log = @This();
|
//! .log_level = .info,
|
||||||
|
//! .log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
pub fn init(comptime debug: bool) Log {
|
//! // log level specific to zap
|
||||||
return .{
|
//! .{ .scope = .zap, .level = .debug },
|
||||||
.debugOn = debug,
|
//! },
|
||||||
};
|
//! };
|
||||||
}
|
//! ```
|
||||||
|
const Logging = @This();
|
||||||
pub fn log(self: *const Log, comptime fmt: []const u8, args: anytype) void {
|
|
||||||
if (self.debugOn) {
|
|
||||||
std.debug.print("[zap] - " ++ fmt, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern const fio_log_level_none: c_int;
|
pub extern const fio_log_level_none: c_int;
|
||||||
pub extern const fio_log_level_fatal: c_int;
|
pub extern const fio_log_level_fatal: c_int;
|
||||||
|
@ -34,12 +29,9 @@ pub extern fn fio_log_error(msg: [*c]const u8) void;
|
||||||
pub extern fn fio_log_fatal(msg: [*c]const u8) void;
|
pub extern fn fio_log_fatal(msg: [*c]const u8) void;
|
||||||
pub extern fn fio_log_debug(msg: [*c]const u8) void;
|
pub extern fn fio_log_debug(msg: [*c]const u8) void;
|
||||||
|
|
||||||
// pub fn getDebugLogger(comptime debug: bool) type {
|
/// Error reporting of last resort
|
||||||
// return struct {
|
pub fn on_uncaught_error(comptime domain: []const u8, err: anyerror) void {
|
||||||
// pub fn log(comptime fmt: []const u8, args: anytype) void {
|
const std = @import("std");
|
||||||
// if (debug) {
|
const log = std.log.scoped(.zap);
|
||||||
// std.debug.print("[zap] - " ++ fmt, args);
|
log.err(domain ++ " : {}", .{err});
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
137
src/endpoint.zig
137
src/endpoint.zig
|
@ -10,17 +10,19 @@
|
||||||
//! ```zig
|
//! ```zig
|
||||||
//! /// The http request path / slug of the endpoint
|
//! /// The http request path / slug of the endpoint
|
||||||
//! path: []const u8,
|
//! path: []const u8,
|
||||||
|
//! error_strategy: zap.Endpoint.ErrorStrategy,
|
||||||
//!
|
//!
|
||||||
//! /// Handlers by request method:
|
//! /// Handlers by request method:
|
||||||
//! pub fn get(_: *Self, _: zap.Request) void {}
|
//! pub fn get(_: *Self, _: zap.Request) !void {}
|
||||||
//! pub fn post(_: *Self, _: zap.Request) void {}
|
//! pub fn post(_: *Self, _: zap.Request) !void {}
|
||||||
//! pub fn put(_: *Self, _: zap.Request) void {}
|
//! pub fn put(_: *Self, _: zap.Request) !void {}
|
||||||
//! pub fn delete(_: *Self, _: zap.Request) void {}
|
//! pub fn delete(_: *Self, _: zap.Request) !void {}
|
||||||
//! pub fn patch(_: *Self, _: zap.Request) void {}
|
//! pub fn patch(_: *Self, _: zap.Request) !void {}
|
||||||
//! pub fn options(_: *Self, _: zap.Request) void {}
|
//! pub fn options(_: *Self, _: zap.Request) !void {}
|
||||||
|
//! pub fn head(_: *Self, _: zap.Request) !void {}
|
||||||
//!
|
//!
|
||||||
//! // optional, if auth stuff is used:
|
//! // optional, if auth stuff is used:
|
||||||
//! pub fn unauthorized(_: *Self, _: zap.Request) void {}
|
//! pub fn unauthorized(_: *Self, _: zap.Request) !void {}
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Example:
|
//! Example:
|
||||||
|
@ -30,22 +32,25 @@
|
||||||
//!
|
//!
|
||||||
//! ```zig
|
//! ```zig
|
||||||
//! const StopEndpoint = struct {
|
//! const StopEndpoint = struct {
|
||||||
|
//! path: []const u8,
|
||||||
|
//! error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
|
||||||
//!
|
//!
|
||||||
//! pub fn init( path: []const u8,) StopEndpoint {
|
//! pub fn init(path: []const u8) StopEndpoint {
|
||||||
//! return .{
|
//! return .{
|
||||||
//! .path = path,
|
//! .path = path,
|
||||||
//! };
|
//! };
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! pub fn post(_: *StopEndpoint, _: zap.Request) void {}
|
//! pub fn get(_: *StopEndpoint, _: zap.Request) !void {
|
||||||
//! pub fn put(_: *StopEndpoint, _: zap.Request) void {}
|
|
||||||
//! pub fn delete(_: *StopEndpoint, _: zap.Request) void {}
|
|
||||||
//! pub fn patch(_: *StopEndpoint, _: zap.Request) void {}
|
|
||||||
//! pub fn options(_: *StopEndpoint, _: zap.Request) void {}
|
|
||||||
//!
|
|
||||||
//! pub fn get(_: *StopEndpoint, _: zap.Request) void {
|
|
||||||
//! zap.stop();
|
//! zap.stop();
|
||||||
//! }
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub fn post(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
//! pub fn put(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
//! pub fn delete(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
//! pub fn patch(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
//! pub fn options(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
//! pub fn head(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
//! };
|
//! };
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
@ -57,9 +62,11 @@ const auth = @import("http_auth.zig");
|
||||||
pub const ErrorStrategy = enum {
|
pub const ErrorStrategy = enum {
|
||||||
/// log errors to console
|
/// log errors to console
|
||||||
log_to_console,
|
log_to_console,
|
||||||
/// log errors to console AND generate a HTML response
|
/// send an HTML response containing an error trace
|
||||||
log_to_response,
|
log_to_response,
|
||||||
/// raise errors -> TODO: clarify: where can they be caught? in App.run()
|
/// raise errors.
|
||||||
|
/// raised errors, if kept unhandled, will ultimately be logged by
|
||||||
|
/// zap.Logging.on_uncaught_error()
|
||||||
raise,
|
raise,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,6 +99,7 @@ pub fn checkEndpointType(T: type) void {
|
||||||
"delete",
|
"delete",
|
||||||
"patch",
|
"patch",
|
||||||
"options",
|
"options",
|
||||||
|
"head",
|
||||||
};
|
};
|
||||||
|
|
||||||
const params_to_check = [_]type{
|
const params_to_check = [_]type{
|
||||||
|
@ -187,6 +195,7 @@ pub const Binder = struct {
|
||||||
.DELETE => self.endpoint.*.delete(r),
|
.DELETE => self.endpoint.*.delete(r),
|
||||||
.PATCH => self.endpoint.*.patch(r),
|
.PATCH => self.endpoint.*.patch(r),
|
||||||
.OPTIONS => self.endpoint.*.options(r),
|
.OPTIONS => self.endpoint.*.options(r),
|
||||||
|
.HEAD => self.endpoint.*.head(r),
|
||||||
else => error.UnsupportedHtmlRequestMethod,
|
else => error.UnsupportedHtmlRequestMethod,
|
||||||
};
|
};
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -195,7 +204,7 @@ pub const Binder = struct {
|
||||||
switch (self.endpoint.*.error_strategy) {
|
switch (self.endpoint.*.error_strategy) {
|
||||||
.raise => return err,
|
.raise => return err,
|
||||||
.log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505),
|
.log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505),
|
||||||
.log_to_console => zap.debug("Error in {} {s} : {}", .{ Bound, r.method orelse "(no method)", err }),
|
.log_to_console => zap.log.err("Error in {} {s} : {}", .{ Bound, r.method orelse "(no method)", err }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,6 +299,15 @@ pub fn Authenticating(EndpointType: type, Authenticator: type) type {
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Authenticates HEAD requests using the Authenticator.
|
||||||
|
pub fn head(self: *AuthenticatingEndpoint, r: zap.Request) anyerror!void {
|
||||||
|
try switch (self.authenticator.authenticateRequest(&r)) {
|
||||||
|
.AuthFailed => return self.ep.*.unauthorized(r),
|
||||||
|
.AuthOK => self.ep.*.head(r),
|
||||||
|
.Handled => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +326,33 @@ pub const Listener = struct {
|
||||||
listener: HttpListener,
|
listener: HttpListener,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
pub const Settings = struct {
|
||||||
|
port: usize,
|
||||||
|
interface: [*c]const u8 = null,
|
||||||
|
|
||||||
|
/// User-defined request callback that only gets called if no endpoints
|
||||||
|
/// match a request.
|
||||||
|
on_request: ?zap.HttpRequestFn,
|
||||||
|
on_response: ?zap.HttpRequestFn = null,
|
||||||
|
on_upgrade: ?zap.HttpUpgradeFn = null,
|
||||||
|
on_finish: ?zap.HttpFinishFn = null,
|
||||||
|
|
||||||
|
/// Callback, called if an error is raised and not caught by the
|
||||||
|
/// ErrorStrategy
|
||||||
|
on_error: ?*const fn (Request, anyerror) void = null,
|
||||||
|
|
||||||
|
// provide any pointer in there for "user data". it will be passed pack in
|
||||||
|
// on_finish()'s copy of the struct_http_settings_s
|
||||||
|
udata: ?*anyopaque = null,
|
||||||
|
public_folder: ?[]const u8 = null,
|
||||||
|
max_clients: ?isize = null,
|
||||||
|
max_body_size: ?usize = null,
|
||||||
|
timeout: ?u8 = null,
|
||||||
|
log: bool = false,
|
||||||
|
ws_timeout: u8 = 40,
|
||||||
|
ws_max_msg_size: usize = 262144,
|
||||||
|
tls: ?zap.Tls = null,
|
||||||
|
};
|
||||||
/// Internal static interface struct of member endpoints
|
/// Internal static interface struct of member endpoints
|
||||||
var endpoints: std.ArrayListUnmanaged(*Binder.Interface) = .empty;
|
var endpoints: std.ArrayListUnmanaged(*Binder.Interface) = .empty;
|
||||||
|
|
||||||
|
@ -316,23 +361,46 @@ pub const Listener = struct {
|
||||||
/// a request.
|
/// a request.
|
||||||
var on_request: ?zap.HttpRequestFn = null;
|
var on_request: ?zap.HttpRequestFn = null;
|
||||||
|
|
||||||
|
/// Callback, called if an error is raised and not caught by the ErrorStrategy
|
||||||
|
var on_error: ?*const fn (Request, anyerror) void = null;
|
||||||
|
|
||||||
/// Initialize a new endpoint listener. Note, if you pass an `on_request`
|
/// Initialize a new endpoint listener. Note, if you pass an `on_request`
|
||||||
/// callback in the provided ListenerSettings, this request callback will be
|
/// callback in the provided ListenerSettings, this request callback will be
|
||||||
/// called every time a request arrives that no endpoint matches.
|
/// called every time a request arrives that no endpoint matches.
|
||||||
pub fn init(a: std.mem.Allocator, l: ListenerSettings) Listener {
|
pub fn init(a: std.mem.Allocator, settings: Settings) Listener {
|
||||||
// reset the global in case init is called multiple times, as is the
|
// reset the global in case init is called multiple times, as is the
|
||||||
// case in the authentication tests
|
// case in the authentication tests
|
||||||
endpoints = .empty;
|
endpoints = .empty;
|
||||||
|
|
||||||
// take copy of listener settings before modifying the callback field
|
var ls: zap.HttpListenerSettings = .{
|
||||||
var ls = l;
|
.port = settings.port,
|
||||||
|
.interface = settings.interface,
|
||||||
|
|
||||||
|
// we set to our own handler
|
||||||
|
.on_request = onRequest,
|
||||||
|
|
||||||
|
.on_response = settings.on_response,
|
||||||
|
.on_upgrade = settings.on_upgrade,
|
||||||
|
.on_finish = settings.on_finish,
|
||||||
|
.udata = settings.udata,
|
||||||
|
.public_folder = settings.public_folder,
|
||||||
|
.max_clients = settings.max_clients,
|
||||||
|
.max_body_size = settings.max_body_size,
|
||||||
|
.timeout = settings.timeout,
|
||||||
|
.log = settings.log,
|
||||||
|
.ws_timeout = settings.ws_timeout,
|
||||||
|
.ws_max_msg_size = settings.ws_max_msg_size,
|
||||||
|
.tls = settings.tls,
|
||||||
|
};
|
||||||
|
|
||||||
// override the settings with our internal, actual callback function
|
// override the settings with our internal, actual callback function
|
||||||
// so that "we" will be called on request
|
// so that "we" will be called on request
|
||||||
ls.on_request = Listener.onRequest;
|
ls.on_request = Listener.onRequest;
|
||||||
|
|
||||||
// store the settings-provided request callback for later use
|
// store the settings-provided request callbacks for later use
|
||||||
on_request = l.on_request;
|
on_request = settings.on_request;
|
||||||
|
on_error = settings.on_error;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.listener = HttpListener.init(ls),
|
.listener = HttpListener.init(ls),
|
||||||
.allocator = a,
|
.allocator = a,
|
||||||
|
@ -384,13 +452,32 @@ pub const Listener = struct {
|
||||||
if (r.path) |p| {
|
if (r.path) |p| {
|
||||||
for (endpoints.items) |interface| {
|
for (endpoints.items) |interface| {
|
||||||
if (std.mem.startsWith(u8, p, interface.path)) {
|
if (std.mem.startsWith(u8, p, interface.path)) {
|
||||||
return try interface.call(interface, r);
|
return interface.call(interface, r) catch |err| {
|
||||||
|
// if error is not dealt with in the entpoint, e.g.
|
||||||
|
// if error strategy is .raise:
|
||||||
|
if (on_error) |error_cb| {
|
||||||
|
error_cb(r, err);
|
||||||
|
} else {
|
||||||
|
zap.log.err(
|
||||||
|
"Endpoint onRequest error {} in endpoint interface {}\n",
|
||||||
|
.{ err, interface },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if set, call the user-provided default callback
|
// if set, call the user-provided default callback
|
||||||
if (on_request) |foo| {
|
if (on_request) |foo| {
|
||||||
try foo(r);
|
foo(r) catch |err| {
|
||||||
|
// if error is not dealt with in the entpoint, e.g.
|
||||||
|
// if error strategy is .raise:
|
||||||
|
if (on_error) |error_cb| {
|
||||||
|
error_cb(r, err);
|
||||||
|
} else {
|
||||||
|
zap.Logging.on_uncaught_error("Endpoint on_request", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -440,9 +440,9 @@ pub fn UserPassSession(comptime Lookup: type, comptime lockedPwLookups: bool) ty
|
||||||
.value = "invalid",
|
.value = "invalid",
|
||||||
.max_age_s = -1,
|
.max_age_s = -1,
|
||||||
})) {
|
})) {
|
||||||
zap.debug("logout ok\n", .{});
|
zap.debug("logout ok", .{});
|
||||||
} else |err| {
|
} else |err| {
|
||||||
zap.debug("logout cookie setting failed: {any}\n", .{err});
|
zap.debug("logout cookie setting failed: {any}", .{err});
|
||||||
}
|
}
|
||||||
|
|
||||||
r.parseCookies(false);
|
r.parseCookies(false);
|
||||||
|
|
|
@ -109,6 +109,7 @@ pub fn EndpointHandler(comptime HandlerType: anytype, comptime EndpointType: any
|
||||||
.DELETE => try self.endpoint.*.delete(r),
|
.DELETE => try self.endpoint.*.delete(r),
|
||||||
.PATCH => try self.endpoint.*.patch(r),
|
.PATCH => try self.endpoint.*.patch(r),
|
||||||
.OPTIONS => try self.endpoint.*.options(r),
|
.OPTIONS => try self.endpoint.*.options(r),
|
||||||
|
.HEAD => try self.endpoint.*.head(r),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,18 +194,13 @@ fn fiobjectify(
|
||||||
.@"struct" => |S| {
|
.@"struct" => |S| {
|
||||||
// create a new fio hashmap
|
// create a new fio hashmap
|
||||||
const m = fio.fiobj_hash_new();
|
const m = fio.fiobj_hash_new();
|
||||||
// std.debug.print("new struct\n", .{});
|
|
||||||
inline for (S.fields) |Field| {
|
inline for (S.fields) |Field| {
|
||||||
// don't include void fields
|
// don't include void fields
|
||||||
if (Field.type == void) continue;
|
if (Field.type == void) continue;
|
||||||
|
|
||||||
// std.debug.print(" new field: {s}\n", .{Field.name});
|
|
||||||
const fname = fio.fiobj_str_new(util.toCharPtr(Field.name), Field.name.len);
|
const fname = fio.fiobj_str_new(util.toCharPtr(Field.name), Field.name.len);
|
||||||
// std.debug.print(" fiobj name : {any}\n", .{fname});
|
|
||||||
const v = @field(value, Field.name);
|
const v = @field(value, Field.name);
|
||||||
// std.debug.print(" value: {any}\n", .{v});
|
|
||||||
const fvalue = fiobjectify(v);
|
const fvalue = fiobjectify(v);
|
||||||
// std.debug.print(" fiobj value: {any}\n", .{fvalue});
|
|
||||||
_ = fio.fiobj_hash_set(m, fname, fvalue);
|
_ = fio.fiobj_hash_set(m, fname, fvalue);
|
||||||
fio.fiobj_free_wrapped(fname);
|
fio.fiobj_free_wrapped(fname);
|
||||||
}
|
}
|
||||||
|
@ -225,7 +220,6 @@ fn fiobjectify(
|
||||||
},
|
},
|
||||||
// TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
|
// TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
|
||||||
.slice => {
|
.slice => {
|
||||||
// std.debug.print("new slice\n", .{});
|
|
||||||
if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {
|
if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {
|
||||||
return fio.fiobj_str_new(util.toCharPtr(value), value.len);
|
return fio.fiobj_str_new(util.toCharPtr(value), value.len);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const Log = @import("log.zig");
|
|
||||||
const http = @import("http.zig");
|
const http = @import("http.zig");
|
||||||
const fio = @import("fio.zig");
|
const fio = @import("fio.zig");
|
||||||
|
|
||||||
|
@ -126,36 +125,43 @@ fn parseBinfilesFrom(a: Allocator, o: fio.FIOBJ) !HttpParam {
|
||||||
fio.fiobj_free_wrapped(key_name);
|
fio.fiobj_free_wrapped(key_name);
|
||||||
fio.fiobj_free_wrapped(key_data);
|
fio.fiobj_free_wrapped(key_data);
|
||||||
fio.fiobj_free_wrapped(key_type);
|
fio.fiobj_free_wrapped(key_type);
|
||||||
} // files: they should have "data", "type", and "filename" keys
|
} // files: they should have "data" and "filename" keys
|
||||||
if (fio.fiobj_hash_haskey(o, key_data) == 1 and fio.fiobj_hash_haskey(o, key_type) == 1 and fio.fiobj_hash_haskey(o, key_name) == 1) {
|
if (fio.fiobj_hash_haskey(o, key_data) == 1 and fio.fiobj_hash_haskey(o, key_name) == 1) {
|
||||||
const filename = fio.fiobj_obj2cstr(fio.fiobj_hash_get(o, key_name));
|
const filename = fio.fiobj_obj2cstr(fio.fiobj_hash_get(o, key_name));
|
||||||
const mimetype = fio.fiobj_obj2cstr(fio.fiobj_hash_get(o, key_type));
|
|
||||||
const data = fio.fiobj_hash_get(o, key_data);
|
const data = fio.fiobj_hash_get(o, key_data);
|
||||||
|
|
||||||
|
var mimetype: []const u8 = undefined;
|
||||||
|
if (fio.fiobj_hash_haskey(o, key_type) == 1) {
|
||||||
|
const mt = fio.fiobj_obj2cstr(fio.fiobj_hash_get(o, key_type));
|
||||||
|
mimetype = mt.data[0..mt.len];
|
||||||
|
} else {
|
||||||
|
mimetype = &"application/octet-stream".*;
|
||||||
|
}
|
||||||
|
|
||||||
var data_slice: ?[]const u8 = null;
|
var data_slice: ?[]const u8 = null;
|
||||||
|
|
||||||
switch (fio.fiobj_type(data)) {
|
switch (fio.fiobj_type(data)) {
|
||||||
fio.FIOBJ_T_DATA => {
|
fio.FIOBJ_T_DATA => {
|
||||||
if (fio.is_invalid(data) == 1) {
|
if (fio.is_invalid(data) == 1) {
|
||||||
data_slice = "(zap: invalid data)";
|
data_slice = "(zap: invalid data)";
|
||||||
std.log.warn("WARNING: HTTP param binary file is not a data object\n", .{});
|
zap.log.warn("HTTP param binary file is not a data object", .{});
|
||||||
} else {
|
} else {
|
||||||
// the data
|
// the data
|
||||||
const data_len = fio.fiobj_data_len(data);
|
const data_len = fio.fiobj_data_len(data);
|
||||||
var data_buf = fio.fiobj_data_read(data, data_len);
|
var data_buf = fio.fiobj_data_read(data, data_len);
|
||||||
|
|
||||||
if (data_len < 0) {
|
if (data_len < 0) {
|
||||||
std.log.warn("WARNING: HTTP param binary file size negative: {d}\n", .{data_len});
|
zap.log.warn("HTTP param binary file size negative: {d}", .{data_len});
|
||||||
std.log.warn("FIOBJ_TYPE of data is: {d}\n", .{fio.fiobj_type(data)});
|
zap.log.warn("FIOBJ_TYPE of data is: {d}", .{fio.fiobj_type(data)});
|
||||||
} else {
|
} else {
|
||||||
if (data_buf.len != data_len) {
|
if (data_buf.len != data_len) {
|
||||||
std.log.warn("WARNING: HTTP param binary file size mismatch: should {d}, is: {d}\n", .{ data_len, data_buf.len });
|
zap.log.warn("HTTP param binary file size mismatch: should {d}, is: {d}", .{ data_len, data_buf.len });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_buf.len > 0) {
|
if (data_buf.len > 0) {
|
||||||
data_slice = data_buf.data[0..data_buf.len];
|
data_slice = data_buf.data[0..data_buf.len];
|
||||||
} else {
|
} else {
|
||||||
std.log.warn("WARNING: HTTP param binary file buffer size negative: {d}\n", .{data_buf.len});
|
zap.log.warn("HTTP param binary file buffer size negative: {d}", .{data_buf.len});
|
||||||
data_slice = "(zap: invalid data: negative BUFFER size)";
|
data_slice = "(zap: invalid data: negative BUFFER size)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +171,7 @@ fn parseBinfilesFrom(a: Allocator, o: fio.FIOBJ) !HttpParam {
|
||||||
const fiostr = fio.fiobj_obj2cstr(data);
|
const fiostr = fio.fiobj_obj2cstr(data);
|
||||||
if (fiostr.len == 0) {
|
if (fiostr.len == 0) {
|
||||||
data_slice = "(zap: empty string data)";
|
data_slice = "(zap: empty string data)";
|
||||||
std.log.warn("WARNING: HTTP param binary file has empty string object\n", .{});
|
zap.log.warn("WARNING: HTTP param binary file has empty string object\n", .{});
|
||||||
} else {
|
} else {
|
||||||
data_slice = fiostr.data[0..fiostr.len];
|
data_slice = fiostr.data[0..fiostr.len];
|
||||||
}
|
}
|
||||||
|
@ -185,15 +191,15 @@ fn parseBinfilesFrom(a: Allocator, o: fio.FIOBJ) !HttpParam {
|
||||||
const file_mimetype_obj = fio.fiobj_ary_entry(mt_ary, i);
|
const file_mimetype_obj = fio.fiobj_ary_entry(mt_ary, i);
|
||||||
var has_error: bool = false;
|
var has_error: bool = false;
|
||||||
if (fio.is_invalid(file_data_obj) == 1) {
|
if (fio.is_invalid(file_data_obj) == 1) {
|
||||||
std.log.debug("file data invalid in array", .{});
|
zap.log.debug("file data invalid in array", .{});
|
||||||
has_error = true;
|
has_error = true;
|
||||||
}
|
}
|
||||||
if (fio.is_invalid(file_name_obj) == 1) {
|
if (fio.is_invalid(file_name_obj) == 1) {
|
||||||
std.log.debug("file name invalid in array", .{});
|
zap.log.debug("file name invalid in array", .{});
|
||||||
has_error = true;
|
has_error = true;
|
||||||
}
|
}
|
||||||
if (fio.is_invalid(file_mimetype_obj) == 1) {
|
if (fio.is_invalid(file_mimetype_obj) == 1) {
|
||||||
std.log.debug("file mimetype invalid in array", .{});
|
zap.log.debug("file mimetype invalid in array", .{});
|
||||||
has_error = true;
|
has_error = true;
|
||||||
}
|
}
|
||||||
if (has_error) {
|
if (has_error) {
|
||||||
|
@ -222,7 +228,7 @@ fn parseBinfilesFrom(a: Allocator, o: fio.FIOBJ) !HttpParam {
|
||||||
|
|
||||||
return .{ .Hash_Binfile = .{
|
return .{ .Hash_Binfile = .{
|
||||||
.filename = filename.data[0..filename.len],
|
.filename = filename.data[0..filename.len],
|
||||||
.mimetype = mimetype.data[0..mimetype.len],
|
.mimetype = mimetype,
|
||||||
.data = data_slice,
|
.data = data_slice,
|
||||||
} };
|
} };
|
||||||
} else {
|
} else {
|
||||||
|
@ -358,7 +364,6 @@ pub fn sendBody(self: *const Request, body: []const u8) HttpError!void {
|
||||||
*anyopaque,
|
*anyopaque,
|
||||||
@ptrFromInt(@intFromPtr(body.ptr)),
|
@ptrFromInt(@intFromPtr(body.ptr)),
|
||||||
), body.len);
|
), body.len);
|
||||||
zap.debug("Request.sendBody(): ret = {}\n", .{ret});
|
|
||||||
if (ret == -1) return error.HttpSendBody;
|
if (ret == -1) return error.HttpSendBody;
|
||||||
self.markAsFinished(true);
|
self.markAsFinished(true);
|
||||||
}
|
}
|
||||||
|
@ -381,7 +386,7 @@ pub fn setContentType(self: *const Request, c: ContentType) HttpError!void {
|
||||||
.JSON => "application/json",
|
.JSON => "application/json",
|
||||||
else => "text/html",
|
else => "text/html",
|
||||||
};
|
};
|
||||||
zap.debug("setting content-type to {s}\n", .{s});
|
zap.log.debug("setting content-type to {s}", .{s});
|
||||||
return self.setHeader("content-type", s);
|
return self.setHeader("content-type", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,21 +398,6 @@ pub fn redirectTo(self: *const Request, path: []const u8, code: ?http.StatusCode
|
||||||
self.markAsFinished(true);
|
self.markAsFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// shows how to use the logger
|
|
||||||
pub fn setContentTypeWithLogger(
|
|
||||||
self: *const Request,
|
|
||||||
c: ContentType,
|
|
||||||
logger: *const Log,
|
|
||||||
) HttpError!void {
|
|
||||||
const s = switch (c) {
|
|
||||||
.TEXT => "text/plain",
|
|
||||||
.JSON => "application/json",
|
|
||||||
else => "text/html",
|
|
||||||
};
|
|
||||||
logger.log("setting content-type to {s}\n", .{s});
|
|
||||||
return self.setHeader("content-type", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to determine the content type by file extension of request path, and sets it.
|
/// Tries to determine the content type by file extension of request path, and sets it.
|
||||||
pub fn setContentTypeFromPath(self: *const Request) !void {
|
pub fn setContentTypeFromPath(self: *const Request) !void {
|
||||||
const t = fio.http_mimetype_find2(self.h.*.path);
|
const t = fio.http_mimetype_find2(self.h.*.path);
|
||||||
|
@ -518,21 +508,18 @@ pub fn setHeader(self: *const Request, name: []const u8, value: []const u8) Http
|
||||||
.capa = name.len,
|
.capa = name.len,
|
||||||
};
|
};
|
||||||
|
|
||||||
zap.debug("setHeader: hname = {s}\n", .{name});
|
|
||||||
const vname: fio.fio_str_info_s = .{
|
const vname: fio.fio_str_info_s = .{
|
||||||
.data = util.toCharPtr(value),
|
.data = util.toCharPtr(value),
|
||||||
.len = value.len,
|
.len = value.len,
|
||||||
.capa = value.len,
|
.capa = value.len,
|
||||||
};
|
};
|
||||||
zap.debug("setHeader: vname = {s}\n", .{value});
|
|
||||||
const ret = fio.http_set_header2(self.h, hname, vname);
|
const ret = fio.http_set_header2(self.h, hname, vname);
|
||||||
|
|
||||||
// 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:274\n", .{});
|
zap.log.debug("***************** zap.zig:274", .{});
|
||||||
}
|
}
|
||||||
zap.debug("setHeader: ret = {}\n", .{ret});
|
|
||||||
|
|
||||||
if (ret == 0) return;
|
if (ret == 0) return;
|
||||||
return error.HttpSetHeader;
|
return error.HttpSetHeader;
|
||||||
|
@ -700,7 +687,7 @@ pub fn setCookie(self: *const Request, args: CookieArgs) HttpError!void {
|
||||||
// TODO: still happening?
|
// 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});
|
zap.log.err("fio.http_set_cookie returned: {}", .{ret});
|
||||||
return error.SetCookie;
|
return error.SetCookie;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,7 @@ pub const Endpoint = struct {
|
||||||
pub fn delete(_: *Endpoint, _: zap.Request) !void {}
|
pub fn delete(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn patch(_: *Endpoint, _: zap.Request) !void {}
|
pub fn patch(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn options(_: *Endpoint, _: zap.Request) !void {}
|
pub fn options(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *Endpoint, _: zap.Request) !void {}
|
||||||
};
|
};
|
||||||
//
|
//
|
||||||
// end of http client code
|
// end of http client code
|
||||||
|
@ -209,10 +210,6 @@ test "BearerAuthSingle authenticateRequest OK" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("\n\n*******************************************\n", .{});
|
|
||||||
// std.debug.print("\n\nPlease run the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client_runner\n", .{});
|
|
||||||
// std.debug.print("\n\n*******************************************\n", .{});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = token });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = token });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
@ -266,8 +263,6 @@ test "BearerAuthSingle authenticateRequest test-unauthorized" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client http://127.0.0.1:3000/test Bearer invalid\r", .{});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = "invalid" });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = "invalid" });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
@ -279,7 +274,6 @@ test "BearerAuthSingle authenticateRequest test-unauthorized" {
|
||||||
});
|
});
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("UNAUTHORIZED", received_response);
|
try std.testing.expectEqualStrings("UNAUTHORIZED", received_response);
|
||||||
std.debug.print("\nI made it\n", .{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "BearerAuthMulti authenticateRequest OK" {
|
test "BearerAuthMulti authenticateRequest OK" {
|
||||||
|
@ -316,8 +310,6 @@ test "BearerAuthMulti authenticateRequest OK" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client_runner http://127.0.0.1:3000/test Bearer " ++ token ++ "\r", .{});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = token });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = token });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
@ -365,8 +357,6 @@ test "BearerAuthMulti authenticateRequest test-unauthorized" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client_runner http://127.0.0.1:3000/test Bearer invalid\r", .{});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = "invalid" });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Bearer, .token = "invalid" });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
@ -419,8 +409,6 @@ test "BasicAuth Token68 authenticateRequest" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client http://127.0.0.1:3000/test Basic " ++ token ++ "\r", .{});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = token });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = token });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
@ -473,8 +461,6 @@ test "BasicAuth Token68 authenticateRequest test-unauthorized" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client http://127.0.0.1:3000/test Basic " ++ "invalid\r", .{});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = "invalid" });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = "invalid" });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
@ -537,8 +523,6 @@ test "BasicAuth UserPass authenticateRequest" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client http://127.0.0.1:3000/test Basic {s}\r", .{encoded});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = encoded });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = encoded });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
@ -604,8 +588,6 @@ test "BasicAuth UserPass authenticateRequest test-unauthorized" {
|
||||||
try listener.register(&auth_ep);
|
try listener.register(&auth_ep);
|
||||||
|
|
||||||
listener.listen() catch {};
|
listener.listen() catch {};
|
||||||
// std.debug.print("Waiting for the following:\n", .{});
|
|
||||||
// std.debug.print("./zig-out/bin/http_client http://127.0.0.1:3000/test Basic invalid\r", .{});
|
|
||||||
|
|
||||||
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = "invalid" });
|
const thread = try makeRequestThread(a, "http://127.0.0.1:3000/test", .{ .auth = .Basic, .token = "invalid" });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
fn makeRequest(a: std.mem.Allocator, url: []const u8) !void {
|
||||||
var http_client: std.http.Client = .{ .allocator = a };
|
var http_client: std.http.Client = .{ .allocator = a };
|
||||||
defer http_client.deinit();
|
defer http_client.deinit();
|
||||||
|
@ -64,7 +72,6 @@ test "http parameters" {
|
||||||
.max_body_size = 1 * 1024,
|
.max_body_size = 1 * 1024,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
zap.enableDebugLog();
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
|
|
||||||
const thread = try makeRequestThread(allocator, "http://127.0.0.1:3001/?one=1&two=2&string=hello+world&float=6.28&bool=true");
|
const thread = try makeRequestThread(allocator, "http://127.0.0.1:3001/?one=1&two=2&string=hello+world&float=6.28&bool=true");
|
||||||
|
|
133
src/tests/test_recvfile.zig
Normal file
133
src/tests/test_recvfile.zig
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const BOUNDARY = "---XcPmntPm3EGd9NaxNUPFFL";
|
||||||
|
const PARAM_NAME = "file";
|
||||||
|
|
||||||
|
const EXPECTED_CONTENT = "Hello, this is a test file.";
|
||||||
|
const EXPECTED_MIMETYPE = "text/plain";
|
||||||
|
const EXPECTED_FILENAME = "myfile.txt";
|
||||||
|
|
||||||
|
var test_error: ?anyerror = null;
|
||||||
|
|
||||||
|
fn makeRequest(allocator: std.mem.Allocator, url: []const u8) !void {
|
||||||
|
var http_client: std.http.Client = .{ .allocator = allocator };
|
||||||
|
defer http_client.deinit();
|
||||||
|
|
||||||
|
const payload_wrong_line_ending = try std.fmt.allocPrint(allocator,
|
||||||
|
\\--{s}
|
||||||
|
\\Content-Disposition: form-data; name="{s}"; filename="{s}"
|
||||||
|
\\Content-Type: {s}
|
||||||
|
\\
|
||||||
|
\\{s}
|
||||||
|
\\--{s}--
|
||||||
|
\\
|
||||||
|
, .{ BOUNDARY, PARAM_NAME, EXPECTED_FILENAME, EXPECTED_MIMETYPE, EXPECTED_CONTENT.*, BOUNDARY });
|
||||||
|
defer allocator.free(payload_wrong_line_ending);
|
||||||
|
|
||||||
|
const payload = try std.mem.replaceOwned(u8, allocator, payload_wrong_line_ending, "\n", "\r\n");
|
||||||
|
defer allocator.free(payload);
|
||||||
|
|
||||||
|
const request_content_type = try std.fmt.allocPrint(
|
||||||
|
allocator,
|
||||||
|
"multipart/form-data; boundary={s}",
|
||||||
|
.{BOUNDARY},
|
||||||
|
);
|
||||||
|
defer allocator.free(request_content_type);
|
||||||
|
|
||||||
|
// Allocate a buffer for server headers
|
||||||
|
var buf: [4096]u8 = undefined;
|
||||||
|
_ = try http_client.fetch(.{
|
||||||
|
.method = .POST,
|
||||||
|
.location = .{ .url = url },
|
||||||
|
.headers = .{
|
||||||
|
.content_type = .{ .override = request_content_type },
|
||||||
|
},
|
||||||
|
.payload = payload,
|
||||||
|
.server_header_buffer = &buf,
|
||||||
|
});
|
||||||
|
|
||||||
|
zap.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_request(r: zap.Request) !void {
|
||||||
|
on_request_inner(r) catch |err| {
|
||||||
|
test_error = err;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_request_inner(r: zap.Request) !void {
|
||||||
|
try r.parseBody();
|
||||||
|
const params = try r.parametersToOwnedList(std.testing.allocator);
|
||||||
|
defer params.deinit();
|
||||||
|
|
||||||
|
std.testing.expect(params.items.len == 1) catch |err| {
|
||||||
|
std.debug.print("Expected exactly one parameter, got {}\n", .{params.items.len});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
const param = params.items[0];
|
||||||
|
std.testing.expect(param.value != null) catch |err| {
|
||||||
|
std.debug.print("Expected parameter value to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
const value = param.value.?;
|
||||||
|
|
||||||
|
std.testing.expect(value == .Hash_Binfile) catch |err| {
|
||||||
|
std.debug.print("Expected parameter type to be Hash_Binfile, got {}\n", .{value});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
const file = value.Hash_Binfile;
|
||||||
|
std.testing.expect(file.data != null) catch |err| {
|
||||||
|
std.debug.print("Expected file data to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
std.testing.expect(file.mimetype != null) catch |err| {
|
||||||
|
std.debug.print("Expected file mimetype to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
std.testing.expect(file.filename != null) catch |err| {
|
||||||
|
std.debug.print("Expected file filename to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
|
// These will print the error if the test fails
|
||||||
|
try std.testing.expectEqualStrings(file.data.?, &EXPECTED_CONTENT.*);
|
||||||
|
try std.testing.expectEqualStrings(file.mimetype.?, &EXPECTED_MIMETYPE.*);
|
||||||
|
try std.testing.expectEqualStrings(file.filename.?, &EXPECTED_FILENAME.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "recv file" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
var listener = zap.HttpListener.init(
|
||||||
|
.{
|
||||||
|
.port = 3003,
|
||||||
|
.on_request = on_request,
|
||||||
|
.log = false,
|
||||||
|
.max_clients = 10,
|
||||||
|
.max_body_size = 1 * 1024,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
try listener.listen();
|
||||||
|
|
||||||
|
const t1 = try std.Thread.spawn(.{}, makeRequest, .{ allocator, "http://127.0.0.1:3003" });
|
||||||
|
defer t1.join();
|
||||||
|
|
||||||
|
zap.start(.{
|
||||||
|
.threads = 1,
|
||||||
|
.workers = 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (test_error) |err| {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
131
src/tests/test_recvfile_notype.zig
Normal file
131
src/tests/test_recvfile_notype.zig
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const BOUNDARY = "---XcPmntPm3EGd9NaxNUPFFL";
|
||||||
|
const PARAM_NAME = "file";
|
||||||
|
const EXPECTED_CONTENT = "Hello, this is a test file.";
|
||||||
|
const EXPECTED_MIMETYPE = "application/octet-stream";
|
||||||
|
const EXPECTED_FILENAME = "myfile.txt";
|
||||||
|
|
||||||
|
var test_error: ?anyerror = null;
|
||||||
|
|
||||||
|
fn makeRequest(allocator: std.mem.Allocator, url: []const u8) !void {
|
||||||
|
var http_client: std.http.Client = .{ .allocator = allocator };
|
||||||
|
defer http_client.deinit();
|
||||||
|
|
||||||
|
const payload_wrong_line_ending = try std.fmt.allocPrint(allocator,
|
||||||
|
\\--{s}
|
||||||
|
\\Content-Disposition: form-data; name={s}"; filename="{s}"
|
||||||
|
\\
|
||||||
|
\\{s}
|
||||||
|
\\--{s}--
|
||||||
|
\\
|
||||||
|
, .{ BOUNDARY, PARAM_NAME, EXPECTED_FILENAME, EXPECTED_CONTENT.*, BOUNDARY });
|
||||||
|
defer allocator.free(payload_wrong_line_ending);
|
||||||
|
|
||||||
|
const payload = try std.mem.replaceOwned(u8, allocator, payload_wrong_line_ending, "\n", "\r\n");
|
||||||
|
defer allocator.free(payload);
|
||||||
|
|
||||||
|
const request_content_type = try std.fmt.allocPrint(
|
||||||
|
allocator,
|
||||||
|
"multipart/form-data; boundary={s}",
|
||||||
|
.{BOUNDARY},
|
||||||
|
);
|
||||||
|
defer allocator.free(request_content_type);
|
||||||
|
|
||||||
|
// Allocate a buffer for server headers
|
||||||
|
var buf: [4096]u8 = undefined;
|
||||||
|
_ = try http_client.fetch(.{
|
||||||
|
.method = .POST,
|
||||||
|
.location = .{ .url = url },
|
||||||
|
.headers = .{
|
||||||
|
.content_type = .{ .override = request_content_type },
|
||||||
|
},
|
||||||
|
.payload = payload,
|
||||||
|
.server_header_buffer = &buf,
|
||||||
|
});
|
||||||
|
|
||||||
|
zap.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_request(r: zap.Request) !void {
|
||||||
|
on_request_inner(r) catch |err| {
|
||||||
|
test_error = err;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_request_inner(r: zap.Request) !void {
|
||||||
|
try r.parseBody();
|
||||||
|
const params = try r.parametersToOwnedList(std.testing.allocator);
|
||||||
|
defer params.deinit();
|
||||||
|
|
||||||
|
std.testing.expect(params.items.len == 1) catch |err| {
|
||||||
|
std.debug.print("Expected exactly one parameter, got {}\n", .{params.items.len});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
const param = params.items[0];
|
||||||
|
std.testing.expect(param.value != null) catch |err| {
|
||||||
|
std.debug.print("Expected parameter value to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
const value = param.value.?;
|
||||||
|
|
||||||
|
std.testing.expect(value == .Hash_Binfile) catch |err| {
|
||||||
|
std.debug.print("Expected parameter type to be Hash_Binfile, got {}\n", .{value});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
const file = value.Hash_Binfile;
|
||||||
|
std.testing.expect(file.data != null) catch |err| {
|
||||||
|
std.debug.print("Expected file data to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
std.testing.expect(file.mimetype != null) catch |err| {
|
||||||
|
std.debug.print("Expected file mimetype to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
std.testing.expect(file.filename != null) catch |err| {
|
||||||
|
std.debug.print("Expected file filename to be non-null, got null\n", .{});
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
|
// These will print the error if the test fails
|
||||||
|
try std.testing.expectEqualStrings(file.data.?, &EXPECTED_CONTENT.*);
|
||||||
|
try std.testing.expectEqualStrings(file.mimetype.?, &EXPECTED_MIMETYPE.*);
|
||||||
|
try std.testing.expectEqualStrings(file.filename.?, &EXPECTED_FILENAME.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "recv file" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
var listener = zap.HttpListener.init(
|
||||||
|
.{
|
||||||
|
.port = 3003,
|
||||||
|
.on_request = on_request,
|
||||||
|
.log = false,
|
||||||
|
.max_clients = 10,
|
||||||
|
.max_body_size = 1 * 1024,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
try listener.listen();
|
||||||
|
|
||||||
|
const t1 = try std.Thread.spawn(.{}, makeRequest, .{ allocator, "http://127.0.0.1:3003" });
|
||||||
|
defer t1.join();
|
||||||
|
|
||||||
|
zap.start(.{
|
||||||
|
.threads = 1,
|
||||||
|
.workers = 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (test_error) |err| {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// set default log level to .info and ZAP log level to .debug
|
||||||
|
pub const std_options: std.Options = .{
|
||||||
|
.log_level = .info,
|
||||||
|
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||||
|
.{ .scope = .zap, .level = .debug },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
var buffer: [1024]u8 = undefined;
|
var buffer: [1024]u8 = undefined;
|
||||||
var read_len: ?usize = null;
|
var read_len: ?usize = null;
|
||||||
|
|
||||||
|
@ -41,7 +49,6 @@ test "send file" {
|
||||||
.max_body_size = 1 * 1024,
|
.max_body_size = 1 * 1024,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
zap.enableDebugLog();
|
|
||||||
try listener.listen();
|
try listener.listen();
|
||||||
|
|
||||||
const thread = try makeRequestThread(allocator, "http://127.0.0.1:3002/?file=src/tests/testfile.txt");
|
const thread = try makeRequestThread(allocator, "http://127.0.0.1:3002/?file=src/tests/testfile.txt");
|
||||||
|
|
|
@ -21,15 +21,15 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
message: []const u8,
|
message: []const u8,
|
||||||
/// indicator if message is text or binary
|
/// indicator if message is text or binary
|
||||||
is_text: bool,
|
is_text: bool,
|
||||||
) void;
|
) anyerror!void;
|
||||||
|
|
||||||
/// Callback (type) 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) anyerror!void;
|
||||||
|
|
||||||
/// A websocket callback function type. 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) anyerror!void;
|
||||||
|
|
||||||
/// Websocket connection handler creation settings. Provide the callbacks you need,
|
/// Websocket connection handler creation settings. Provide the callbacks you need,
|
||||||
/// and an optional context.
|
/// and an optional context.
|
||||||
|
@ -68,7 +68,9 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
const message = msg.data[0..msg.len];
|
const message = msg.data[0..msg.len];
|
||||||
if (user_provided_settings) |settings| {
|
if (user_provided_settings) |settings| {
|
||||||
if (settings.on_message) |on_message| {
|
if (settings.on_message) |on_message| {
|
||||||
on_message(settings.context, handle, message, is_text == 1);
|
on_message(settings.context, handle, message, is_text == 1) catch |err| {
|
||||||
|
zap.Logging.on_uncaught_error("WebSocket Handler on_message", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +79,9 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
|
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
|
||||||
if (user_provided_settings) |settings| {
|
if (user_provided_settings) |settings| {
|
||||||
if (settings.on_open) |on_open| {
|
if (settings.on_open) |on_open| {
|
||||||
on_open(settings.context, handle);
|
on_open(settings.context, handle) catch |err| {
|
||||||
|
zap.Logging.on_uncaught_error("WebSocket Handler on_open", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +90,9 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
|
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
|
||||||
if (user_provided_settings) |settings| {
|
if (user_provided_settings) |settings| {
|
||||||
if (settings.on_ready) |on_ready| {
|
if (settings.on_ready) |on_ready| {
|
||||||
on_ready(settings.context, handle);
|
on_ready(settings.context, handle) catch |err| {
|
||||||
|
zap.Logging.on_uncaught_error("WebSocket Handler on_ready", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +101,9 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
|
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
|
||||||
if (user_provided_settings) |settings| {
|
if (user_provided_settings) |settings| {
|
||||||
if (settings.on_shutdown) |on_shutdown| {
|
if (settings.on_shutdown) |on_shutdown| {
|
||||||
on_shutdown(settings.context, handle);
|
on_shutdown(settings.context, handle) catch |err| {
|
||||||
|
zap.Logging.on_uncaught_error("WebSocket Handler on_shutdown", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +112,9 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(udata)));
|
const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(udata)));
|
||||||
if (user_provided_settings) |settings| {
|
if (user_provided_settings) |settings| {
|
||||||
if (settings.on_close) |on_close| {
|
if (settings.on_close) |on_close| {
|
||||||
on_close(settings.context, uuid);
|
on_close(settings.context, uuid) catch |err| {
|
||||||
|
zap.Logging.on_uncaught_error("WebSocket Handler on_close", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,10 +165,10 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for callback on subscription message.
|
/// 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) anyerror!void;
|
||||||
|
|
||||||
/// Type for callback on unsubscribe message.
|
/// Type for callback on unsubscribe message.
|
||||||
pub const SubscriptionOnUnsubscribeFn = *const fn (context: ?*ContextType) void;
|
pub const SubscriptionOnUnsubscribeFn = *const fn (context: ?*ContextType) anyerror!void;
|
||||||
|
|
||||||
/// Settings for subscribing to a channel.
|
/// Settings for subscribing to a channel.
|
||||||
pub const SubscribeArgs = struct {
|
pub const SubscribeArgs = struct {
|
||||||
|
@ -213,7 +223,9 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
if (udata) |p| {
|
if (udata) |p| {
|
||||||
const args = @as(*SubscribeArgs, @ptrCast(@alignCast(p)));
|
const args = @as(*SubscribeArgs, @ptrCast(@alignCast(p)));
|
||||||
if (args.on_message) |on_message| {
|
if (args.on_message) |on_message| {
|
||||||
on_message(args.context, handle, channel.data[0..channel.len], message.data[0..message.len]);
|
on_message(args.context, handle, channel.data[0..channel.len], message.data[0..message.len]) catch |err| {
|
||||||
|
zap.Logging.on_uncaught_error("WebSocket Subscription on_message", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,7 +233,9 @@ pub fn Handler(comptime ContextType: type) type {
|
||||||
if (udata) |p| {
|
if (udata) |p| {
|
||||||
const args = @as(*SubscribeArgs, @ptrCast(@alignCast(p)));
|
const args = @as(*SubscribeArgs, @ptrCast(@alignCast(p)));
|
||||||
if (args.on_unsubscribe) |on_unsubscribe| {
|
if (args.on_unsubscribe) |on_unsubscribe| {
|
||||||
on_unsubscribe(args.context);
|
on_unsubscribe(args.context) catch |err| {
|
||||||
|
zap.Logging.on_uncaught_error("WebSocket Subscription on_unsubscribe", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
src/zap.zig
44
src/zap.zig
|
@ -37,13 +37,12 @@ pub const Middleware = @import("middleware.zig");
|
||||||
/// Websocket API
|
/// Websocket API
|
||||||
pub const WebSockets = @import("websockets.zig");
|
pub const WebSockets = @import("websockets.zig");
|
||||||
|
|
||||||
pub const Log = @import("log.zig");
|
pub const Logging = @import("Logging.zig");
|
||||||
|
pub const log = std.log.scoped(.zap);
|
||||||
|
|
||||||
pub const http = @import("http.zig");
|
pub const http = @import("http.zig");
|
||||||
pub const util = @import("util.zig");
|
pub const util = @import("util.zig");
|
||||||
|
|
||||||
// TODO: replace with comptime debug logger like in log.zig
|
|
||||||
var _debug: bool = false;
|
|
||||||
|
|
||||||
/// Start the IO reactor
|
/// Start the IO reactor
|
||||||
///
|
///
|
||||||
/// Will start listeners etc.
|
/// Will start listeners etc.
|
||||||
|
@ -60,23 +59,9 @@ pub fn stop() void {
|
||||||
fio.fio_stop();
|
fio.fio_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extremely simplistic zap debug function.
|
/// Extremely simplistic zap debug function, to save a few keystrokes
|
||||||
/// 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) {
|
log.debug("[zap] - " ++ fmt, args);
|
||||||
std.debug.print("[zap] - " ++ fmt, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enable zap debug logging
|
|
||||||
pub fn enableDebugLog() void {
|
|
||||||
_debug = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// start Zap with debug logging on
|
|
||||||
pub fn startWithLogging(args: fio.fio_start_args) void {
|
|
||||||
_debug = true;
|
|
||||||
fio.fio_start(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a new mimetype to be used for files ending with the given extension.
|
/// Registers a new mimetype to be used for files ending with the given extension.
|
||||||
|
@ -138,14 +123,14 @@ pub const HttpRequestFn = *const fn (Request) anyerror!void;
|
||||||
|
|
||||||
/// websocket connection upgrade callback type
|
/// websocket connection upgrade callback type
|
||||||
/// fn(request, targetstring)
|
/// fn(request, targetstring)
|
||||||
pub const HttpUpgradeFn = *const fn (r: Request, target_protocol: []const u8) void;
|
pub const HttpUpgradeFn = *const fn (r: Request, target_protocol: []const u8) anyerror!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
|
||||||
/// HttpFinishSetting struct.
|
/// HttpFinishSetting struct.
|
||||||
pub const HttpFinishSettings = [*c]fio.struct_http_settings_s;
|
pub const HttpFinishSettings = [*c]fio.struct_http_settings_s;
|
||||||
|
|
||||||
/// Http finish callback type
|
/// Http finish callback type
|
||||||
pub const HttpFinishFn = *const fn (HttpFinishSettings) void;
|
pub const HttpFinishFn = *const fn (HttpFinishSettings) anyerror!void;
|
||||||
|
|
||||||
/// Listener settings
|
/// Listener settings
|
||||||
pub const HttpListenerSettings = struct {
|
pub const HttpListenerSettings = struct {
|
||||||
|
@ -204,8 +189,7 @@ pub const HttpListener = struct {
|
||||||
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| {
|
||||||
on_request(req) catch |err| {
|
on_request(req) catch |err| {
|
||||||
// TODO: log / handle the error in a better way
|
Logging.on_uncaught_error("HttpListener on_request", err);
|
||||||
std.debug.print("zap on_request error: {}", .{err});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,8 +213,7 @@ pub const HttpListener = struct {
|
||||||
req._user_context = &user_context;
|
req._user_context = &user_context;
|
||||||
|
|
||||||
l.settings.on_response.?(req) catch |err| {
|
l.settings.on_response.?(req) catch |err| {
|
||||||
// TODO: log / handle the error in a better way
|
Logging.on_uncaught_error("HttpListener on_response", err);
|
||||||
std.debug.print("zap on_response error: {}", .{err});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,14 +236,18 @@ pub const HttpListener = struct {
|
||||||
var user_context: Request.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) catch |err| {
|
||||||
|
Logging.on_uncaught_error("HttpListener on_upgrade", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used internally: the listener's facilio finish callback
|
/// 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) catch |err| {
|
||||||
|
Logging.on_uncaught_error("HttpListener on_finish", err);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +377,6 @@ pub const LowLevel = struct {
|
||||||
*anyopaque,
|
*anyopaque,
|
||||||
@ptrFromInt(@intFromPtr(body.ptr)),
|
@ptrFromInt(@intFromPtr(body.ptr)),
|
||||||
), body.len);
|
), body.len);
|
||||||
debug("sendBody(): ret = {}\n", .{ret});
|
|
||||||
if (ret != -1) return error.HttpSendBody;
|
if (ret != -1) return error.HttpSendBody;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue