mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
first cut of sendError
This commit is contained in:
parent
52551e0970
commit
e5e657870e
5 changed files with 102 additions and 25 deletions
71
README.md
71
README.md
|
@ -25,42 +25,63 @@ juicy, and alpha._
|
|||
|
||||
Here's what works:
|
||||
|
||||
- **Super easy build process**: zap's `build.zig` now uses the up-and-coming
|
||||
zig package manager for its C-dependencies, no git submodules anymore.
|
||||
- **Super easy build process**: zap's `build.zig` now uses the up-and-coming zig
|
||||
package manager for its C-dependencies, no git submodules anymore.
|
||||
- _tested on Linux and macOS (arm, M1)_
|
||||
- **[hello](examples/hello/hello.zig)**: welcomes you with some static
|
||||
HTML
|
||||
- **[routes](examples/routes/routes.zig)**: a super easy example
|
||||
dispatching on the HTTP path
|
||||
- **[serve](examples/serve/serve.zig)**: the traditional static web
|
||||
server with optional dynamic request handling
|
||||
- **[hello](examples/hello/hello.zig)**: welcomes you with some static HTML
|
||||
- **[routes](examples/routes/routes.zig)**: a super easy example dispatching on
|
||||
the HTTP path
|
||||
- **[serve](examples/serve/serve.zig)**: the traditional static web server with
|
||||
optional dynamic request handling
|
||||
- **[sendfile](examples/sendfile/sendfile.zig)**: simple example of how to send
|
||||
a file, honoring compression headers, etc.
|
||||
- **[hello_json](examples/hello_json/hello_json.zig)**: serves you json
|
||||
dependent on HTTP path
|
||||
- **[endpoint](examples/endpoint/)**: a simple JSON REST API example featuring
|
||||
a `/users` endpoint for PUTting/DELETE-ing/GET-ting/POST-ing and listing
|
||||
users, together with a static HTML and JavaScript frontend to play with.
|
||||
- **[endpoint](examples/endpoint/)**: a simple JSON REST API example featuring a
|
||||
`/users` endpoint for PUTting/DELETE-ing/GET-ting/POST-ing and listing users,
|
||||
together with a static HTML and JavaScript frontend to play with.
|
||||
- **[mustache](examples/mustache/mustache.zig)**: a simple example using
|
||||
[mustache](https://mustache.github.io/) templating.
|
||||
- **[endpoint authentication](examples/endpoint_auth/endpoint_auth.zig)**: a simple authenticated
|
||||
endpoint. Read more about authentication [here](./doc/authentication.md).
|
||||
- **[http parameters](examples/http_params/http_params.zig)**: a simple example sending
|
||||
itself query parameters of all supported types.
|
||||
- **[cookies](examples/cookies/cookies.zig)**: a simple example sending
|
||||
itself a cookie and responding with a session cookie.
|
||||
- **[websockets](examples/websockets/)**: a simple websockets chat for the browser.
|
||||
- **[Username/Password Session Authentication](./examples/userpass_session_auth/)**:
|
||||
A convenience authenticator that redirects un-authenticated requests to a
|
||||
login page and sends cookies containing session tokens based on
|
||||
username/password pairs transmitted via POST request.
|
||||
- **[endpoint authentication](examples/endpoint_auth/endpoint_auth.zig)**: a
|
||||
simple authenticated endpoint. Read more about authentication
|
||||
[here](./doc/authentication.md).
|
||||
- **[http parameters](examples/http_params/http_params.zig)**: a simple example
|
||||
sending itself query parameters of all supported types.
|
||||
- **[cookies](examples/cookies/cookies.zig)**: a simple example sending itself a
|
||||
cookie and responding with a session cookie.
|
||||
- **[websockets](examples/websockets/)**: a simple websockets chat for the
|
||||
browser.
|
||||
- **[Username/Password Session
|
||||
Authentication](./examples/userpass_session_auth/)**: A convenience
|
||||
authenticator that redirects un-authenticated requests to a login page and
|
||||
sends cookies containing session tokens based on username/password pairs
|
||||
transmitted via POST request.
|
||||
- **[MIDDLEWARE support](examples/middleware/middleware.zig)**: chain together
|
||||
request handlers in middleware style. Provide custom context structs, totally
|
||||
type-safe, using **[ZIG-CEPTION](doc/zig-ception.md)**. If you come from GO
|
||||
this might appeal to you.
|
||||
- **[MIDDLEWARE with endpoint support](examples/middleware_with_endpoint/middleware_with_endpoint.zig)**: Same as the example above, but this time we use an endpoint at the end of the chain, by wrapping it via `zap.Middleware.EndpointHandler`. Mixing endpoints in your middleware chain allows for usage of Zap's authenticated endpoints and your custom endpoints. Since Endpoints use a simpler API, you have to use `r.setUserContext()` and `r.getUserContext()` with the request if you want to access the middleware context from a wrapped endpoint. Since this mechanism uses an `*anyopaque` pointer underneath (to not break the Endpoint API), it is less type-safe than `zap.Middleware`'s use of contexts.
|
||||
- **Per Request Contexts** : With the introduction of `setContext()` and `getContext()`, you can, of course use those two in projects that don't use `zap.SimpleEndpoint` or `zap.Middleware`, too, if you really, really, absolutely don't find another way to solve your context problem. **We recommend using a `zap.SimpleEndpoint`** inside of a struct that can provide all the context you need **instead**. You get access to your struct in the callbacks via the `@fieldParentPtr()` trick that is used extensively in Zap's examples, like the [endpoint example](examples/endpoint/endpoint.zig).
|
||||
|
||||
- **[MIDDLEWARE with endpoint
|
||||
support](examples/middleware_with_endpoint/middleware_with_endpoint.zig)**:
|
||||
Same as the example above, but this time we use an endpoint at the end of the
|
||||
chain, by wrapping it via `zap.Middleware.EndpointHandler`. Mixing endpoints
|
||||
in your middleware chain allows for usage of Zap's authenticated endpoints and
|
||||
your custom endpoints. Since Endpoints use a simpler API, you have to use
|
||||
`r.setUserContext()` and `r.getUserContext()` with the request if you want to
|
||||
access the middleware context from a wrapped endpoint. Since this mechanism
|
||||
uses an `*anyopaque` pointer underneath (to not break the Endpoint API), it is
|
||||
less type-safe than `zap.Middleware`'s use of contexts.
|
||||
- [**Per Request Contexts**](./src/zap.zig#L102) : With the introduction of
|
||||
`setUserContext()` and `getUserContext()`, you can, of course use those two in
|
||||
projects that don't use `zap.SimpleEndpoint` or `zap.Middleware`, too, if you
|
||||
really, really, absolutely don't find another way to solve your context
|
||||
problem. **We recommend using a `zap.SimpleEndpoint`** inside of a struct that
|
||||
can provide all the context you need **instead**. You get access to your
|
||||
struct in the callbacks via the `@fieldParentPtr()` trick that is used
|
||||
extensively in Zap's examples, like the [endpoint
|
||||
example](examples/endpoint/endpoint.zig).
|
||||
- [**Error Trace Responses**](./examples/senderror/senderror.zig): You can now
|
||||
call `r.sendError(err, status_code)` when you catch an error and a stack trace
|
||||
will be returned to the client / browser.
|
||||
|
||||
I'll continue wrapping more of facil.io's functionality and adding stuff to zap
|
||||
to a point where I can use it as the JSON REST API backend for real research
|
||||
|
|
|
@ -57,6 +57,7 @@ pub fn build(b: *std.build.Builder) !void {
|
|||
.{ .name = "sendfile", .src = "examples/sendfile/sendfile.zig" },
|
||||
.{ .name = "middleware", .src = "examples/middleware/middleware.zig" },
|
||||
.{ .name = "middleware_with_endpoint", .src = "examples/middleware_with_endpoint/middleware_with_endpoint.zig" },
|
||||
.{ .name = "senderror", .src = "examples/senderror/senderror.zig" },
|
||||
}) |excfg| {
|
||||
const ex_name = excfg.name;
|
||||
const ex_src = excfg.src;
|
||||
|
|
28
examples/senderror/senderror.zig
Normal file
28
examples/senderror/senderror.zig
Normal file
|
@ -0,0 +1,28 @@
|
|||
const std = @import("std");
|
||||
const zap = @import("zap");
|
||||
|
||||
fn MAKE_MEGA_ERROR() !void {
|
||||
return error.MEGA_ERROR;
|
||||
}
|
||||
|
||||
fn MY_REQUEST_HANDLER(r: zap.SimpleRequest) void {
|
||||
MAKE_MEGA_ERROR() catch |err| {
|
||||
r.sendError(err, 505);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var listener = zap.SimpleHttpListener.init(.{
|
||||
.port = 3000,
|
||||
.on_request = MY_REQUEST_HANDLER,
|
||||
.log = true,
|
||||
});
|
||||
try listener.listen();
|
||||
|
||||
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
|
||||
|
||||
zap.start(.{
|
||||
.threads = 2,
|
||||
.workers = 2,
|
||||
});
|
||||
}
|
26
src/zap.zig
26
src/zap.zig
|
@ -99,6 +99,8 @@ pub const SimpleRequest = struct {
|
|||
return self._is_finished.*;
|
||||
}
|
||||
|
||||
/// if you absolutely must, you can set any context here
|
||||
// (note, this line is linked to from the readme)
|
||||
pub fn setUserContext(self: *const Self, context: *anyopaque) void {
|
||||
self._user_context.*.user_context = context;
|
||||
}
|
||||
|
@ -111,6 +113,30 @@ pub const SimpleRequest = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sendError(self: *const Self, err: anyerror, errorcode_num: usize) void {
|
||||
// TODO: query accept headers
|
||||
if (self._internal_sendError(err, errorcode_num)) {
|
||||
return;
|
||||
} else |_| {
|
||||
self.sendBody(@errorName(err)) catch return;
|
||||
}
|
||||
}
|
||||
pub fn _internal_sendError(self: *const Self, err: anyerror, errorcode_num: usize) !void {
|
||||
// TODO: query accept headers
|
||||
// TODO: let's hope 20k is enough. Maybe just really allocate here
|
||||
self.h.*.status = errorcode_num;
|
||||
var buf: [20 * 1024]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
||||
var string = std.ArrayList(u8).init(fba.allocator());
|
||||
var writer = string.writer();
|
||||
try writer.print("ERROR: {any}\n\n", .{err});
|
||||
|
||||
var debugInfo = try std.debug.getSelfDebugInfo();
|
||||
var ttyConfig: std.debug.TTY.Config = .no_color;
|
||||
try std.debug.writeCurrentStackTrace(writer, debugInfo, ttyConfig, null);
|
||||
try self.sendBody(string.items);
|
||||
}
|
||||
|
||||
pub fn sendBody(self: *const Self, body: []const u8) HttpError!void {
|
||||
const ret = fio.http_send_body(self.h, @intToPtr(
|
||||
*anyopaque,
|
||||
|
|
|
@ -16,3 +16,4 @@ userpass_session
|
|||
sendfile
|
||||
middleware
|
||||
middleware_with_endpoint
|
||||
senderror
|
||||
|
|
Loading…
Add table
Reference in a new issue