mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 22:16:08 +00:00
WIP
This commit is contained in:
parent
1693f24ce2
commit
35c5f26de9
@ -23,8 +23,8 @@
|
||||
.hash = "1220d4f1c2472769b0d689ea878f41f0a66cb07f28569a138aea2c0a648a5c90dd4e",
|
||||
},
|
||||
.httpz = .{
|
||||
.url = "https://github.com/karlseguin/http.zig/archive/0d4a5cd520a54eaf800438e0b9093c77c90dcf11.tar.gz",
|
||||
.hash = "12209b8216a80f21be12d43e588811150bdbbb53d35eac6a2a61c460f197350e19ad",
|
||||
.url = "https://github.com/karlseguin/http.zig/archive/206a34c0ee35a07b89d000f630b2f1e0f7c98119.tar.gz",
|
||||
.hash = "1220768b5925b4e13f73c036f1ca18b4a7d987ffaf5e825af6443d5d4ed8e37e7dfd",
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -2,10 +2,7 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub const Server = @import("http/Server.zig");
|
||||
pub const Request = if (builtin.os.tag == .windows)
|
||||
@import("windows/Request.zig")
|
||||
else
|
||||
@import("http/Request.zig");
|
||||
pub const Request = @import("http/Request.zig");
|
||||
pub const StaticRequest = @import("http/StaticRequest.zig");
|
||||
pub const Response = @import("http/Response.zig");
|
||||
pub const Session = @import("http/Session.zig");
|
||||
|
@ -72,8 +72,6 @@ const Dispatcher = struct {
|
||||
};
|
||||
|
||||
pub fn listen(self: *Server) !void {
|
||||
if (builtin.os.tag == .windows) return try @import("../windows.zig").listen(self);
|
||||
|
||||
var httpz_server = try httpz.ServerCtx(Dispatcher, Dispatcher).init(
|
||||
self.allocator,
|
||||
.{
|
||||
@ -150,8 +148,7 @@ fn processNextRequest(
|
||||
try self.logger.logRequest(&request);
|
||||
}
|
||||
|
||||
// TODO: Make private when http.zig Windows is working
|
||||
pub fn renderResponse(self: *Server, request: *jetzig.http.Request) !void {
|
||||
fn renderResponse(self: *Server, request: *jetzig.http.Request) !void {
|
||||
const static_resource = self.matchStaticResource(request) catch |err| {
|
||||
if (isUnhandledError(err)) return err;
|
||||
|
||||
@ -332,8 +329,7 @@ fn isUnhandledError(err: anyerror) bool {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Make private when http.zig Windows is working
|
||||
pub fn isBadHttpError(err: anyerror) bool {
|
||||
fn isBadHttpError(err: anyerror) bool {
|
||||
return switch (err) {
|
||||
error.JetzigParseHeadError,
|
||||
error.UnknownHttpMethod,
|
||||
|
@ -1,546 +0,0 @@
|
||||
const std = @import("std");
|
||||
|
||||
const jetzig = @import("../../jetzig.zig");
|
||||
|
||||
const Request = @This();
|
||||
const default_content_type = "text/html";
|
||||
|
||||
pub const Method = enum { DELETE, GET, PATCH, POST, HEAD, PUT, CONNECT, OPTIONS, TRACE };
|
||||
pub const Modifier = enum { edit, new };
|
||||
pub const Format = enum { HTML, JSON, UNKNOWN };
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
path: jetzig.http.Path,
|
||||
method: Method,
|
||||
headers: jetzig.http.Headers,
|
||||
server: *jetzig.http.Server,
|
||||
std_http_request: std.http.Server.Request,
|
||||
response: *jetzig.http.Response,
|
||||
status_code: jetzig.http.status_codes.StatusCode = .not_found,
|
||||
response_data: *jetzig.data.Data,
|
||||
query_params: ?*jetzig.http.Query = null,
|
||||
query_body: ?*jetzig.http.Query = null,
|
||||
cookies: *jetzig.http.Cookies = undefined,
|
||||
session: *jetzig.http.Session = undefined,
|
||||
body: []const u8 = undefined,
|
||||
processed: bool = false,
|
||||
layout: ?[]const u8 = null,
|
||||
layout_disabled: bool = false,
|
||||
rendered: bool = false,
|
||||
redirected: bool = false,
|
||||
rendered_multiple: bool = false,
|
||||
rendered_view: ?jetzig.views.View = null,
|
||||
start_time: i128,
|
||||
store: RequestStore,
|
||||
cache: RequestStore,
|
||||
|
||||
/// Wrapper for KV store that uses the request's arena allocator for fetching values.
|
||||
pub const RequestStore = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
store: *jetzig.kv.Store,
|
||||
|
||||
/// Put a String or into the key-value store.
|
||||
pub fn get(self: RequestStore, key: []const u8) !?*jetzig.data.Value {
|
||||
return try self.store.get(try self.data(), key);
|
||||
}
|
||||
|
||||
/// Get a String from the store.
|
||||
pub fn put(self: RequestStore, key: []const u8, value: *jetzig.data.Value) !void {
|
||||
try self.store.put(key, value);
|
||||
}
|
||||
|
||||
/// Remove a String to from the key-value store and return it if found.
|
||||
pub fn fetchRemove(self: RequestStore, key: []const u8) !?*jetzig.data.Value {
|
||||
return try self.store.fetchRemove(try self.data(), key);
|
||||
}
|
||||
|
||||
/// Remove a String to from the key-value store.
|
||||
pub fn remove(self: RequestStore, key: []const u8) !void {
|
||||
try self.store.remove(key);
|
||||
}
|
||||
|
||||
/// Append a Value to the end of an Array in the key-value store.
|
||||
pub fn append(self: RequestStore, key: []const u8, value: *jetzig.data.Value) !void {
|
||||
try self.store.append(key, value);
|
||||
}
|
||||
|
||||
/// Prepend a Value to the start of an Array in the key-value store.
|
||||
pub fn prepend(self: RequestStore, key: []const u8, value: *jetzig.data.Value) !void {
|
||||
try self.store.prepend(key, value);
|
||||
}
|
||||
|
||||
/// Pop a String from an Array in the key-value store.
|
||||
pub fn pop(self: RequestStore, key: []const u8) !?*jetzig.data.Value {
|
||||
return try self.store.pop(try self.data(), key);
|
||||
}
|
||||
|
||||
/// Left-pop a String from an Array in the key-value store.
|
||||
pub fn popFirst(self: RequestStore, key: []const u8) !?*jetzig.data.Value {
|
||||
return try self.store.popFirst(try self.data(), key);
|
||||
}
|
||||
|
||||
fn data(self: RequestStore) !*jetzig.data.Data {
|
||||
const arena_data = try self.allocator.create(jetzig.data.Data);
|
||||
arena_data.* = jetzig.data.Data.init(self.allocator);
|
||||
return arena_data;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(
|
||||
allocator: std.mem.Allocator,
|
||||
server: *jetzig.http.Server,
|
||||
start_time: i128,
|
||||
std_http_request: std.http.Server.Request,
|
||||
response: *jetzig.http.Response,
|
||||
) !Request {
|
||||
const method = switch (std_http_request.head.method) {
|
||||
.DELETE => Method.DELETE,
|
||||
.GET => Method.GET,
|
||||
.PATCH => Method.PATCH,
|
||||
.POST => Method.POST,
|
||||
.HEAD => Method.HEAD,
|
||||
.PUT => Method.PUT,
|
||||
.CONNECT => Method.CONNECT,
|
||||
.OPTIONS => Method.OPTIONS,
|
||||
.TRACE => Method.TRACE,
|
||||
_ => return error.JetzigUnsupportedHttpMethod,
|
||||
};
|
||||
|
||||
const response_data = try allocator.create(jetzig.data.Data);
|
||||
response_data.* = jetzig.data.Data.init(allocator);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.path = jetzig.http.Path.init(std_http_request.head.target),
|
||||
.method = method,
|
||||
.headers = jetzig.http.Headers.init(allocator),
|
||||
.server = server,
|
||||
.response = response,
|
||||
.response_data = response_data,
|
||||
.std_http_request = std_http_request,
|
||||
.start_time = start_time,
|
||||
.store = .{ .store = server.store, .allocator = allocator },
|
||||
.cache = .{ .store = server.cache, .allocator = allocator },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Request) void {
|
||||
self.session.deinit();
|
||||
self.cookies.deinit();
|
||||
self.allocator.destroy(self.cookies);
|
||||
self.allocator.destroy(self.session);
|
||||
if (self.processed) self.allocator.free(self.body);
|
||||
}
|
||||
|
||||
/// Process request, read body if present, parse headers (TODO)
|
||||
pub fn process(self: *Request) !void {
|
||||
var headers_it = self.std_http_request.iterateHeaders();
|
||||
var cookie: ?[]const u8 = null;
|
||||
|
||||
while (headers_it.next()) |header| {
|
||||
try self.headers.append(header.name, header.value);
|
||||
if (std.mem.eql(u8, header.name, "Cookie")) cookie = header.value;
|
||||
}
|
||||
|
||||
self.cookies = try self.allocator.create(jetzig.http.Cookies);
|
||||
self.cookies.* = jetzig.http.Cookies.init(
|
||||
self.allocator,
|
||||
cookie orelse "",
|
||||
);
|
||||
try self.cookies.parse();
|
||||
|
||||
self.session = try self.allocator.create(jetzig.http.Session);
|
||||
self.session.* = jetzig.http.Session.init(self.allocator, self.cookies, self.server.options.secret);
|
||||
self.session.parse() catch |err| {
|
||||
switch (err) {
|
||||
error.JetzigInvalidSessionCookie => {
|
||||
try self.server.logger.DEBUG("Invalid session cookie detected. Resetting session.", .{});
|
||||
try self.session.reset();
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
|
||||
const reader = try self.std_http_request.reader();
|
||||
self.body = try reader.readAllAlloc(self.allocator, jetzig.config.get(usize, "max_bytes_request_body"));
|
||||
self.processed = true;
|
||||
}
|
||||
|
||||
/// Set response headers, write response payload, and finalize the response.
|
||||
pub fn respond(self: *Request) !void {
|
||||
if (!self.processed) unreachable;
|
||||
|
||||
var cookie_it = self.cookies.headerIterator();
|
||||
while (try cookie_it.next()) |header| {
|
||||
// FIXME: Skip setting cookies that are already present ?
|
||||
try self.response.headers.append("Set-Cookie", header);
|
||||
}
|
||||
|
||||
var std_response_headers = try self.response.headers.stdHeaders();
|
||||
defer std_response_headers.deinit(self.allocator);
|
||||
|
||||
try self.std_http_request.respond(
|
||||
self.response.content,
|
||||
.{
|
||||
.keep_alive = false,
|
||||
.status = switch (self.response.status_code) {
|
||||
inline else => |tag| @field(std.http.Status, @tagName(tag)),
|
||||
},
|
||||
.extra_headers = std_response_headers.items,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Render a response. This function can only be called once per request (repeat calls will
|
||||
/// trigger an error).
|
||||
pub fn render(self: *Request, status_code: jetzig.http.status_codes.StatusCode) jetzig.views.View {
|
||||
if (self.rendered) self.rendered_multiple = true;
|
||||
|
||||
self.rendered = true;
|
||||
self.rendered_view = .{ .data = self.response_data, .status_code = status_code };
|
||||
return self.rendered_view.?;
|
||||
}
|
||||
|
||||
/// Issue a redirect to a new location.
|
||||
/// ```zig
|
||||
/// return request.redirect("https://www.example.com/", .moved_permanently);
|
||||
/// ```
|
||||
/// ```zig
|
||||
/// return request.redirect("https://www.example.com/", .found);
|
||||
/// ```
|
||||
/// The second argument must be `moved_permanently` or `found`.
|
||||
pub fn redirect(
|
||||
self: *Request,
|
||||
location: []const u8,
|
||||
redirect_status: enum { moved_permanently, found },
|
||||
) jetzig.views.View {
|
||||
if (self.rendered) self.rendered_multiple = true;
|
||||
|
||||
self.rendered = true;
|
||||
self.redirected = true;
|
||||
|
||||
const status_code = switch (redirect_status) {
|
||||
.moved_permanently => jetzig.http.status_codes.StatusCode.moved_permanently,
|
||||
.found => jetzig.http.status_codes.StatusCode.found,
|
||||
};
|
||||
|
||||
self.response_data.reset();
|
||||
|
||||
self.response.headers.remove("Location");
|
||||
self.response.headers.append("Location", location) catch @panic("OOM");
|
||||
|
||||
self.rendered_view = .{ .data = self.response_data, .status_code = status_code };
|
||||
return self.rendered_view.?;
|
||||
}
|
||||
|
||||
/// Infer the current format (JSON or HTML) from the request in this order:
|
||||
/// * Extension (path ends in `.json` or `.html`)
|
||||
/// * `Accept` header (`application/json` or `text/html`)
|
||||
/// * `Content-Type` header (`application/json` or `text/html`)
|
||||
/// * Fall back to default: HTML
|
||||
pub fn requestFormat(self: *const Request) jetzig.http.Request.Format {
|
||||
return self.extensionFormat() orelse
|
||||
self.acceptHeaderFormat() orelse
|
||||
self.contentTypeHeaderFormat() orelse
|
||||
.UNKNOWN;
|
||||
}
|
||||
|
||||
/// Set the layout for the current request/response. Use this to override a `pub const layout`
|
||||
/// declaration in a view, either in middleware or in a view function itself.
|
||||
pub fn setLayout(self: *Request, layout: ?[]const u8) void {
|
||||
if (layout) |layout_name| {
|
||||
self.layout = layout_name;
|
||||
self.layout_disabled = false;
|
||||
} else {
|
||||
self.layout_disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive a layout name from the current request if defined, otherwise from the route (if
|
||||
/// defined).
|
||||
pub fn getLayout(self: *Request, route: *jetzig.views.Route) ?[]const u8 {
|
||||
if (self.layout_disabled) return null;
|
||||
if (self.layout) |capture| return capture;
|
||||
if (route.layout) |capture| return capture;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Shortcut for `request.headers.getFirstValue`. Returns the first matching value for a given
|
||||
/// header name or `null` if not found. Header names are case-insensitive.
|
||||
pub fn getHeader(self: *const Request, key: []const u8) ?[]const u8 {
|
||||
return self.headers.getFirstValue(key);
|
||||
}
|
||||
|
||||
/// Return a `Value` representing request parameters. Parameters are normalized, meaning that
|
||||
/// both the JSON request body and query parameters are accessed via the same interface.
|
||||
/// Note that query parameters are supported for JSON requests if no request body is present,
|
||||
/// otherwise the parsed JSON request body will take precedence and query parameters will be
|
||||
/// ignored.
|
||||
pub fn params(self: *Request) !*jetzig.data.Value {
|
||||
if (!self.processed) unreachable;
|
||||
|
||||
switch (self.requestFormat()) {
|
||||
.JSON => {
|
||||
if (self.body.len == 0) return self.queryParams();
|
||||
|
||||
var data = try self.allocator.create(jetzig.data.Data);
|
||||
data.* = jetzig.data.Data.init(self.allocator);
|
||||
data.fromJson(self.body) catch |err| {
|
||||
switch (err) {
|
||||
error.SyntaxError, error.UnexpectedEndOfInput => return error.JetzigBodyParseError,
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
return data.value.?;
|
||||
},
|
||||
.HTML, .UNKNOWN => return self.parseQuery(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `*Value` representing request parameters. This function **always** returns the
|
||||
/// parsed query string and never the request body.
|
||||
pub fn queryParams(self: *Request) !*jetzig.data.Value {
|
||||
if (self.query_params) |parsed| return parsed.data.value.?;
|
||||
|
||||
const data = try self.allocator.create(jetzig.data.Data);
|
||||
data.* = jetzig.data.Data.init(self.allocator);
|
||||
self.query_params = try self.allocator.create(jetzig.http.Query);
|
||||
self.query_params.?.* = jetzig.http.Query.init(
|
||||
self.allocator,
|
||||
self.path.query orelse "",
|
||||
data,
|
||||
);
|
||||
try self.query_params.?.parse();
|
||||
return self.query_params.?.data.value.?;
|
||||
}
|
||||
|
||||
// Parses request body as params if present, otherwise delegates to `queryParams`.
|
||||
fn parseQuery(self: *Request) !*jetzig.data.Value {
|
||||
if (self.body.len == 0) return try self.queryParams();
|
||||
if (self.query_body) |parsed| return parsed.data.value.?;
|
||||
|
||||
const data = try self.allocator.create(jetzig.data.Data);
|
||||
data.* = jetzig.data.Data.init(self.allocator);
|
||||
self.query_body = try self.allocator.create(jetzig.http.Query);
|
||||
self.query_body.?.* = jetzig.http.Query.init(
|
||||
self.allocator,
|
||||
self.body,
|
||||
data,
|
||||
);
|
||||
try self.query_body.?.parse();
|
||||
return self.query_body.?.data.value.?;
|
||||
}
|
||||
|
||||
/// Creates a new Job. Receives a job name which must resolve to `src/app/jobs/<name>.zig`
|
||||
/// Call `Job.put(...)` to set job params.
|
||||
/// Call `Job.background()` to run the job outside of the request/response flow.
|
||||
/// e.g.:
|
||||
/// ```
|
||||
/// pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
||||
/// var job = try request.job("foo"); // Will invoke `process()` in `src/app/jobs/foo.zig`
|
||||
/// try job.put("foo", data.string("bar"));
|
||||
/// try job.background(); // Job added to queue and processed by job worker.
|
||||
/// return request.render(.ok);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn job(self: *Request, job_name: []const u8) !*jetzig.Job {
|
||||
const background_job = try self.allocator.create(jetzig.Job);
|
||||
background_job.* = jetzig.Job.init(
|
||||
self.allocator,
|
||||
self.server.store,
|
||||
self.server.job_queue,
|
||||
self.server.cache,
|
||||
self.server.logger,
|
||||
self.server.job_definitions,
|
||||
job_name,
|
||||
);
|
||||
return background_job;
|
||||
}
|
||||
|
||||
const RequestMail = struct {
|
||||
request: *Request,
|
||||
mail_params: jetzig.mail.MailParams,
|
||||
name: []const u8,
|
||||
|
||||
// Will allow scheduling when strategy is `.later` (e.g.).
|
||||
const DeliveryOptions = struct {};
|
||||
|
||||
pub fn deliver(self: RequestMail, strategy: enum { background, now }, options: DeliveryOptions) !void {
|
||||
_ = options;
|
||||
var mail_job = try self.request.job("__jetzig_mail");
|
||||
|
||||
try mail_job.params.put("mailer_name", mail_job.data.string(self.name));
|
||||
|
||||
const from = if (self.mail_params.from) |from| mail_job.data.string(from) else null;
|
||||
try mail_job.params.put("from", from);
|
||||
|
||||
var to_array = try mail_job.data.array();
|
||||
if (self.mail_params.to) |capture| {
|
||||
for (capture) |to| try to_array.append(mail_job.data.string(to));
|
||||
}
|
||||
try mail_job.params.put("to", to_array);
|
||||
|
||||
const subject = if (self.mail_params.subject) |subject| mail_job.data.string(subject) else null;
|
||||
try mail_job.params.put("subject", subject);
|
||||
|
||||
const html = if (self.mail_params.html) |html| mail_job.data.string(html) else null;
|
||||
try mail_job.params.put("html", html);
|
||||
|
||||
const text = if (self.mail_params.text) |text| mail_job.data.string(text) else null;
|
||||
try mail_job.params.put("text", text);
|
||||
|
||||
if (self.request.response_data.value) |value| try mail_job.params.put(
|
||||
"params",
|
||||
if (strategy == .now) try value.clone(self.request.allocator) else value,
|
||||
);
|
||||
|
||||
switch (strategy) {
|
||||
.background => try mail_job.schedule(),
|
||||
.now => try mail_job.definition.?.runFn(
|
||||
self.request.allocator,
|
||||
mail_job.params,
|
||||
jetzig.jobs.JobEnv{
|
||||
.environment = self.request.server.options.environment,
|
||||
.logger = self.request.server.logger,
|
||||
.routes = self.request.server.routes,
|
||||
.mailers = self.request.server.mailer_definitions,
|
||||
.jobs = self.request.server.job_definitions,
|
||||
.store = self.request.server.store,
|
||||
.cache = self.request.server.cache,
|
||||
.mutex = undefined,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn mail(self: *Request, name: []const u8, mail_params: jetzig.mail.MailParams) RequestMail {
|
||||
return .{
|
||||
.request = self,
|
||||
.name = name,
|
||||
.mail_params = mail_params,
|
||||
};
|
||||
}
|
||||
|
||||
fn extensionFormat(self: *const Request) ?jetzig.http.Request.Format {
|
||||
const extension = self.path.extension orelse return null;
|
||||
if (std.mem.eql(u8, extension, ".html")) {
|
||||
return .HTML;
|
||||
} else if (std.mem.eql(u8, extension, ".json")) {
|
||||
return .JSON;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acceptHeaderFormat(self: *const Request) ?jetzig.http.Request.Format {
|
||||
const acceptHeader = self.getHeader("Accept");
|
||||
|
||||
if (acceptHeader) |item| {
|
||||
if (std.mem.eql(u8, item, "text/html")) return .HTML;
|
||||
if (std.mem.eql(u8, item, "application/json")) return .JSON;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn contentTypeHeaderFormat(self: *const Request) ?jetzig.http.Request.Format {
|
||||
const acceptHeader = self.getHeader("content-type");
|
||||
|
||||
if (acceptHeader) |item| {
|
||||
if (std.mem.eql(u8, item, "text/html")) return .HTML;
|
||||
if (std.mem.eql(u8, item, "application/json")) return .JSON;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn hash(self: *Request) ![]const u8 {
|
||||
return try std.fmt.allocPrint(
|
||||
self.allocator,
|
||||
"{s}-{s}-{s}",
|
||||
.{ @tagName(self.method), self.path, @tagName(self.requestFormat()) },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn fmtMethod(self: *const Request, colorized: bool) []const u8 {
|
||||
if (!colorized) return @tagName(self.method);
|
||||
|
||||
return switch (self.method) {
|
||||
.GET => jetzig.colors.cyan("GET"),
|
||||
.PUT => jetzig.colors.yellow("PUT"),
|
||||
.PATCH => jetzig.colors.yellow("PATCH"),
|
||||
.HEAD => jetzig.colors.white("HEAD"),
|
||||
.POST => jetzig.colors.green("POST"),
|
||||
.DELETE => jetzig.colors.red("DELETE"),
|
||||
inline else => |method| jetzig.colors.white(@tagName(method)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Format a status code appropriately for the current request format.
|
||||
/// e.g. `.HTML` => `404 Not Found`
|
||||
/// `.JSON` => `{ "message": "Not Found", "status": "404" }`
|
||||
pub fn formatStatus(self: *const Request, status_code: jetzig.http.StatusCode) ![]const u8 {
|
||||
const status = jetzig.http.status_codes.get(status_code);
|
||||
|
||||
return switch (self.requestFormat()) {
|
||||
.JSON => try std.json.stringifyAlloc(self.allocator, .{
|
||||
.@"error" = .{
|
||||
.message = status.getMessage(),
|
||||
.code = status.getCode(),
|
||||
},
|
||||
}, .{}),
|
||||
.HTML, .UNKNOWN => status.getFormatted(.{ .linebreak = true }),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setResponse(
|
||||
self: *Request,
|
||||
rendered_view: jetzig.http.Server.RenderedView,
|
||||
options: struct { content_type: ?[]const u8 = null },
|
||||
) void {
|
||||
self.response.content = rendered_view.content;
|
||||
self.response.status_code = rendered_view.view.status_code;
|
||||
self.response.content_type = options.content_type orelse switch (self.requestFormat()) {
|
||||
.HTML, .UNKNOWN => "text/html",
|
||||
.JSON => "application/json",
|
||||
};
|
||||
}
|
||||
|
||||
// Determine if a given route matches the current request.
|
||||
pub fn match(self: *Request, route: jetzig.views.Route) !bool {
|
||||
return switch (self.method) {
|
||||
.GET => switch (route.action) {
|
||||
.index => self.isMatch(.exact, route),
|
||||
.get => self.isMatch(.resource_id, route),
|
||||
else => false,
|
||||
},
|
||||
.POST => switch (route.action) {
|
||||
.post => self.isMatch(.exact, route),
|
||||
else => false,
|
||||
},
|
||||
.PUT => switch (route.action) {
|
||||
.put => self.isMatch(.resource_id, route),
|
||||
else => false,
|
||||
},
|
||||
.PATCH => switch (route.action) {
|
||||
.patch => self.isMatch(.resource_id, route),
|
||||
else => false,
|
||||
},
|
||||
.DELETE => switch (route.action) {
|
||||
.delete => self.isMatch(.resource_id, route),
|
||||
else => false,
|
||||
},
|
||||
.HEAD, .CONNECT, .OPTIONS, .TRACE => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn isMatch(self: *Request, match_type: enum { exact, resource_id }, route: jetzig.views.Route) bool {
|
||||
const path = switch (match_type) {
|
||||
.exact => self.path.base_path,
|
||||
.resource_id => self.path.directory,
|
||||
};
|
||||
|
||||
return std.mem.eql(u8, path, route.uri_path);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user