mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
wip, handle all supported http methods in endpoint.zig
This commit is contained in:
parent
8078b96d3f
commit
e17107db3b
6 changed files with 97 additions and 17 deletions
|
@ -14,9 +14,11 @@ pub fn get(_: *ErrorEndpoint, _: zap.Request) !void {
|
||||||
return error.@"Oh-no!";
|
return error.@"Oh-no!";
|
||||||
}
|
}
|
||||||
|
|
||||||
// unused:
|
// not implemented, don't care
|
||||||
|
pub fn custom_method(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn delete(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn options(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
pub fn post(_: *ErrorEndpoint, _: zap.Request) !void {}
|
pub fn post(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
pub fn put(_: *ErrorEndpoint, _: zap.Request) !void {}
|
pub fn put(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
pub fn delete(_: *ErrorEndpoint, _: zap.Request) !void {}
|
|
||||||
pub fn patch(_: *ErrorEndpoint, _: zap.Request) !void {}
|
pub fn patch(_: *ErrorEndpoint, _: zap.Request) !void {}
|
||||||
pub fn options(_: *ErrorEndpoint, _: zap.Request) !void {}
|
|
||||||
|
|
|
@ -18,8 +18,11 @@ pub fn get(_: *StopEndpoint, _: zap.Request) !void {
|
||||||
zap.stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// not implemented:
|
||||||
|
pub fn custom_method(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn delete(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn options(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
|
pub fn patch(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
pub fn post(_: *StopEndpoint, _: zap.Request) !void {}
|
pub fn post(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
pub fn put(_: *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 {}
|
|
||||||
|
|
|
@ -131,3 +131,7 @@ pub fn options(_: *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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// not implemented:
|
||||||
|
pub fn custom_method(_: *UserWeb, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *UserWeb, _: zap.Request) !void {}
|
||||||
|
|
|
@ -33,11 +33,13 @@ const Endpoint = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// not implemented, don't care
|
// not implemented, don't care
|
||||||
|
pub fn custom_method(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn delete(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn options(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn post(_: *Endpoint, _: zap.Request) !void {}
|
pub fn post(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn put(_: *Endpoint, _: zap.Request) !void {}
|
pub fn put(_: *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 main() !void {
|
pub fn main() !void {
|
||||||
|
|
|
@ -14,11 +14,13 @@
|
||||||
//!
|
//!
|
||||||
//! /// Handlers by request method:
|
//! /// Handlers by request method:
|
||||||
//! pub fn get(_: *Self, _: zap.Request) !void {}
|
//! pub fn get(_: *Self, _: zap.Request) !void {}
|
||||||
|
//! pub fn head(_: *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 custom_method(_: *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 {}
|
||||||
|
@ -44,11 +46,13 @@
|
||||||
//! zap.stop();
|
//! zap.stop();
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
//! pub fn head(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
//! pub fn post(_: *StopEndpoint, _: zap.Request) !void {}
|
//! pub fn post(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
//! pub fn put(_: *StopEndpoint, _: zap.Request) !void {}
|
//! 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 custom_method(_: *StopEndpoint, _: zap.Request) !void {}
|
||||||
//! };
|
//! };
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
@ -73,7 +77,20 @@ const Request = zap.Request;
|
||||||
const ListenerSettings = zap.HttpListenerSettings;
|
const ListenerSettings = zap.HttpListenerSettings;
|
||||||
const HttpListener = zap.HttpListener;
|
const HttpListener = zap.HttpListener;
|
||||||
|
|
||||||
pub fn checkEndpointType(T: type) void {
|
const ImplementedMethods = struct {
|
||||||
|
get: bool = false,
|
||||||
|
head: bool = false,
|
||||||
|
post: bool = false,
|
||||||
|
put: bool = false,
|
||||||
|
delete: bool = false,
|
||||||
|
patch: bool = false,
|
||||||
|
options: bool = false,
|
||||||
|
custom_method: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn checkEndpointType(T: type) ImplementedMethods {
|
||||||
|
var implemented_methods: ImplementedMethods = .{};
|
||||||
|
|
||||||
if (@hasField(T, "path")) {
|
if (@hasField(T, "path")) {
|
||||||
if (@FieldType(T, "path") != []const u8) {
|
if (@FieldType(T, "path") != []const u8) {
|
||||||
@compileError(@typeName(@FieldType(T, "path")) ++ " has wrong type, expected: []const u8");
|
@compileError(@typeName(@FieldType(T, "path")) ++ " has wrong type, expected: []const u8");
|
||||||
|
@ -90,13 +107,16 @@ pub fn checkEndpointType(T: type) void {
|
||||||
@compileError(@typeName(T) ++ " has no error_strategy field");
|
@compileError(@typeName(T) ++ " has no error_strategy field");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use field names of ImplementedMethods
|
||||||
const methods_to_check = [_][]const u8{
|
const methods_to_check = [_][]const u8{
|
||||||
"get",
|
"get",
|
||||||
|
"head",
|
||||||
"post",
|
"post",
|
||||||
"put",
|
"put",
|
||||||
"delete",
|
"delete",
|
||||||
"patch",
|
"patch",
|
||||||
"options",
|
"options",
|
||||||
|
"custom_method",
|
||||||
};
|
};
|
||||||
|
|
||||||
const params_to_check = [_]type{
|
const params_to_check = [_]type{
|
||||||
|
@ -150,10 +170,16 @@ pub fn checkEndpointType(T: type) void {
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
@field(implemented_methods, method) = true;
|
||||||
} else {
|
} else {
|
||||||
@compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`");
|
@compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`");
|
||||||
|
// TODO: shall we warn?
|
||||||
|
// No, we should provide a default implementation that calls
|
||||||
|
// "unhandled request" callback, and if that's not defined, log the
|
||||||
|
// request as being unhandled.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return implemented_methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Binder = struct {
|
pub const Binder = struct {
|
||||||
|
@ -161,6 +187,7 @@ pub const Binder = struct {
|
||||||
call: *const fn (*Interface, zap.Request) anyerror!void = undefined,
|
call: *const fn (*Interface, zap.Request) anyerror!void = undefined,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
destroy: *const fn (*Interface, std.mem.Allocator) void = undefined,
|
destroy: *const fn (*Interface, std.mem.Allocator) void = undefined,
|
||||||
|
implemented_methods: ImplementedMethods = undefined,
|
||||||
};
|
};
|
||||||
pub fn Bind(ArbitraryEndpoint: type) type {
|
pub fn Bind(ArbitraryEndpoint: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
|
@ -187,12 +214,13 @@ pub const Binder = struct {
|
||||||
pub fn onRequest(self: *Bound, r: zap.Request) !void {
|
pub fn onRequest(self: *Bound, r: zap.Request) !void {
|
||||||
const ret = switch (r.methodAsEnum()) {
|
const ret = switch (r.methodAsEnum()) {
|
||||||
.GET => self.endpoint.*.get(r),
|
.GET => self.endpoint.*.get(r),
|
||||||
|
.HEAD => self.endpoint.*.head(r),
|
||||||
.POST => self.endpoint.*.post(r),
|
.POST => self.endpoint.*.post(r),
|
||||||
.PUT => self.endpoint.*.put(r),
|
.PUT => self.endpoint.*.put(r),
|
||||||
.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),
|
||||||
else => error.UnsupportedHtmlRequestMethod,
|
.UNKNOWN => self.endpoint.*.custom_method(r),
|
||||||
};
|
};
|
||||||
if (ret) {
|
if (ret) {
|
||||||
// handled without error
|
// handled without error
|
||||||
|
@ -208,7 +236,7 @@ pub const Binder = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(ArbitraryEndpoint: type, value: *ArbitraryEndpoint) Binder.Bind(ArbitraryEndpoint) {
|
pub fn init(ArbitraryEndpoint: type, value: *ArbitraryEndpoint) Binder.Bind(ArbitraryEndpoint) {
|
||||||
checkEndpointType(ArbitraryEndpoint);
|
const implemented_methods = checkEndpointType(ArbitraryEndpoint);
|
||||||
const BoundEp = Binder.Bind(ArbitraryEndpoint);
|
const BoundEp = Binder.Bind(ArbitraryEndpoint);
|
||||||
return .{
|
return .{
|
||||||
.endpoint = value,
|
.endpoint = value,
|
||||||
|
@ -216,6 +244,7 @@ pub const Binder = struct {
|
||||||
.path = value.path,
|
.path = value.path,
|
||||||
.call = BoundEp.onRequestInterface,
|
.call = BoundEp.onRequestInterface,
|
||||||
.destroy = BoundEp.destroy,
|
.destroy = BoundEp.destroy,
|
||||||
|
.implemented_methods = implemented_methods,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -242,6 +271,15 @@ pub fn Authenticating(EndpointType: type, Authenticator: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Authenticates all other requests using the Authenticator.
|
||||||
|
pub fn custom_method(self: *AuthenticatingEndpoint, r: zap.Request) anyerror!void {
|
||||||
|
try switch (self.authenticator.authenticateRequest(&r)) {
|
||||||
|
.AuthFailed => return self.ep.*.unauthorized(r),
|
||||||
|
.AuthOK => self.ep.*.custom_method(r),
|
||||||
|
.Handled => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Authenticates GET requests using the Authenticator.
|
/// Authenticates GET requests using the Authenticator.
|
||||||
pub fn get(self: *AuthenticatingEndpoint, r: zap.Request) anyerror!void {
|
pub fn get(self: *AuthenticatingEndpoint, r: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&r)) {
|
try switch (self.authenticator.authenticateRequest(&r)) {
|
||||||
|
@ -251,6 +289,15 @@ pub fn Authenticating(EndpointType: type, Authenticator: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Authenticates POST requests using the Authenticator.
|
/// Authenticates POST requests using the Authenticator.
|
||||||
pub fn post(self: *AuthenticatingEndpoint, r: zap.Request) anyerror!void {
|
pub fn post(self: *AuthenticatingEndpoint, r: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&r)) {
|
try switch (self.authenticator.authenticateRequest(&r)) {
|
||||||
|
@ -320,6 +367,7 @@ pub const Listener = struct {
|
||||||
/// User-defined request callback that only gets called if no endpoints
|
/// User-defined request callback that only gets called if no endpoints
|
||||||
/// match a request.
|
/// match a request.
|
||||||
on_request: ?zap.HttpRequestFn,
|
on_request: ?zap.HttpRequestFn,
|
||||||
|
|
||||||
on_response: ?zap.HttpRequestFn = null,
|
on_response: ?zap.HttpRequestFn = null,
|
||||||
on_upgrade: ?zap.HttpUpgradeFn = null,
|
on_upgrade: ?zap.HttpUpgradeFn = null,
|
||||||
on_finish: ?zap.HttpFinishFn = null,
|
on_finish: ?zap.HttpFinishFn = null,
|
||||||
|
@ -346,7 +394,7 @@ pub const Listener = struct {
|
||||||
/// Internal, static request handler callback. Will be set to the optional,
|
/// Internal, static request handler callback. Will be set to the optional,
|
||||||
/// user-defined request callback that only gets called if no endpoints match
|
/// user-defined request callback that only gets called if no endpoints match
|
||||||
/// a request.
|
/// a request.
|
||||||
var on_request: ?zap.HttpRequestFn = null;
|
var on_unhandled_request: ?zap.HttpRequestFn = null;
|
||||||
|
|
||||||
/// Callback, called if an error is raised and not caught by the ErrorStrategy
|
/// Callback, called if an error is raised and not caught by the ErrorStrategy
|
||||||
var on_error: ?*const fn (Request, anyerror) void = null;
|
var on_error: ?*const fn (Request, anyerror) void = null;
|
||||||
|
@ -385,7 +433,7 @@ pub const Listener = struct {
|
||||||
ls.on_request = Listener.onRequest;
|
ls.on_request = Listener.onRequest;
|
||||||
|
|
||||||
// store the settings-provided request callbacks for later use
|
// store the settings-provided request callbacks for later use
|
||||||
on_request = settings.on_request;
|
on_unhandled_request = settings.on_request;
|
||||||
on_error = settings.on_error;
|
on_error = settings.on_error;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
@ -429,7 +477,6 @@ pub const Listener = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const EndpointType = @typeInfo(@TypeOf(e)).pointer.child;
|
const EndpointType = @typeInfo(@TypeOf(e)).pointer.child;
|
||||||
checkEndpointType(EndpointType);
|
|
||||||
const bound = try self.allocator.create(Binder.Bind(EndpointType));
|
const bound = try self.allocator.create(Binder.Bind(EndpointType));
|
||||||
bound.* = Binder.init(EndpointType, e);
|
bound.* = Binder.init(EndpointType, e);
|
||||||
try endpoints.append(self.allocator, &bound.interface);
|
try endpoints.append(self.allocator, &bound.interface);
|
||||||
|
@ -440,6 +487,25 @@ pub const Listener = struct {
|
||||||
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 interface.call(interface, r) catch |err| {
|
return interface.call(interface, r) catch |err| {
|
||||||
|
if (err == error.NotImplemented) {
|
||||||
|
// we can try the on_unhandled_request
|
||||||
|
if (on_unhandled_request) |callback| {
|
||||||
|
// perform the callback and catch the error
|
||||||
|
callback(r) catch |cb_err| {
|
||||||
|
// if an error happened in the callback
|
||||||
|
// AND we have an on_error callback:
|
||||||
|
if (on_error) |error_cb| {
|
||||||
|
error_cb(r, cb_err);
|
||||||
|
} else {
|
||||||
|
zap.log.err(
|
||||||
|
"Endpoint onRequest error {} in endpoint interface {}\n",
|
||||||
|
.{ err, interface },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
// if error is not dealt with in the entpoint, e.g.
|
// if error is not dealt with in the entpoint, e.g.
|
||||||
// if error strategy is .raise:
|
// if error strategy is .raise:
|
||||||
if (on_error) |error_cb| {
|
if (on_error) |error_cb| {
|
||||||
|
@ -455,7 +521,7 @@ pub const Listener = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if set, call the user-provided default callback
|
// if set, call the user-provided default callback
|
||||||
if (on_request) |foo| {
|
if (on_unhandled_request) |foo| {
|
||||||
foo(r) catch |err| {
|
foo(r) catch |err| {
|
||||||
// if error is not dealt with in the entpoint, e.g.
|
// if error is not dealt with in the entpoint, e.g.
|
||||||
// if error strategy is .raise:
|
// if error strategy is .raise:
|
||||||
|
|
|
@ -165,11 +165,14 @@ pub const Endpoint = struct {
|
||||||
std.time.sleep(1 * std.time.ns_per_s);
|
std.time.sleep(1 * std.time.ns_per_s);
|
||||||
zap.stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
// not implemented, don't care
|
||||||
|
pub fn custom_method(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn delete(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn head(_: *Endpoint, _: zap.Request) !void {}
|
||||||
|
pub fn options(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn post(_: *Endpoint, _: zap.Request) !void {}
|
pub fn post(_: *Endpoint, _: zap.Request) !void {}
|
||||||
pub fn put(_: *Endpoint, _: zap.Request) !void {}
|
pub fn put(_: *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 {}
|
|
||||||
};
|
};
|
||||||
//
|
//
|
||||||
// end of http client code
|
// end of http client code
|
||||||
|
|
Loading…
Add table
Reference in a new issue