mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Introduce static option in Bun.serve() (#13540)
This commit is contained in:
@@ -70,6 +70,28 @@ const server = Bun.serve({
|
||||
});
|
||||
```
|
||||
|
||||
### `static` responses
|
||||
|
||||
Serve static responses by route with the `static` option
|
||||
|
||||
```ts
|
||||
Bun.serve({
|
||||
static: {
|
||||
"/api/health-check": new Response("All good!"),
|
||||
"/old-link": Response.redirect("/new-link", 301),
|
||||
"/": new Response("Hello World"),
|
||||
},
|
||||
|
||||
fetch(req) {
|
||||
return new Response("404!");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
{% note %}
|
||||
`static` is experimental and may change in the future.
|
||||
{% /note %}
|
||||
|
||||
### Changing the `port` and `hostname`
|
||||
|
||||
To configure which port and hostname the server will listen on, set `port` and `hostname` in the options object.
|
||||
|
||||
20
packages/bun-types/bun.d.ts
vendored
20
packages/bun-types/bun.d.ts
vendored
@@ -2298,6 +2298,26 @@ declare module "bun" {
|
||||
* This string will currently do nothing. But in the future it could be useful for logs or metrics.
|
||||
*/
|
||||
id?: string | null;
|
||||
|
||||
/**
|
||||
* Server static Response objects by route.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* Bun.serve({
|
||||
* static: {
|
||||
* "/": new Response("Hello World"),
|
||||
* "/about": new Response("About"),
|
||||
* },
|
||||
* fetch(req) {
|
||||
* return new Response("Fallback response");
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
static?: Record<`/${string}`, Response>;
|
||||
}
|
||||
|
||||
interface ServeOptions extends GenericServeOptions {
|
||||
|
||||
@@ -2,7 +2,7 @@ const Bun = @This();
|
||||
const default_allocator = bun.default_allocator;
|
||||
const bun = @import("root").bun;
|
||||
const Environment = bun.Environment;
|
||||
|
||||
const AnyBlob = bun.JSC.WebCore.AnyBlob;
|
||||
const Global = bun.Global;
|
||||
const strings = bun.strings;
|
||||
const string = bun.string;
|
||||
@@ -90,7 +90,7 @@ const SendfileContext = struct {
|
||||
const linux = std.os.linux;
|
||||
const Async = bun.Async;
|
||||
const httplog = Output.scoped(.Server, false);
|
||||
|
||||
const ctxLog = Output.scoped(.RequestContext, false);
|
||||
const BlobFileContentResult = struct {
|
||||
data: [:0]const u8,
|
||||
fn init(comptime fieldname: []const u8, js_obj: JSC.JSValue, global: *JSC.JSGlobalObject, exception: JSC.C.ExceptionRef) ?BlobFileContentResult {
|
||||
@@ -119,6 +119,296 @@ const BlobFileContentResult = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn getContentType(headers: ?*JSC.FetchHeaders, blob: *const JSC.WebCore.AnyBlob, allocator: std.mem.Allocator) struct { MimeType, bool, bool } {
|
||||
var needs_content_type = true;
|
||||
var content_type_needs_free = false;
|
||||
|
||||
const content_type: MimeType = brk: {
|
||||
if (headers) |headers_| {
|
||||
if (headers_.fastGet(.ContentType)) |content| {
|
||||
needs_content_type = false;
|
||||
|
||||
var content_slice = content.toSlice(allocator);
|
||||
defer content_slice.deinit();
|
||||
|
||||
const content_type_allocator = if (content_slice.allocator.isNull()) null else allocator;
|
||||
break :brk MimeType.init(content_slice.slice(), content_type_allocator, &content_type_needs_free);
|
||||
}
|
||||
}
|
||||
|
||||
break :brk if (blob.contentType().len > 0)
|
||||
MimeType.byName(blob.contentType())
|
||||
else if (MimeType.sniff(blob.slice())) |content|
|
||||
content
|
||||
else if (blob.wasString())
|
||||
MimeType.text
|
||||
// TODO: should we get the mime type off of the Blob.Store if it exists?
|
||||
// A little wary of doing this right now due to causing some breaking change
|
||||
else
|
||||
MimeType.other;
|
||||
};
|
||||
|
||||
return .{ content_type, needs_content_type, content_type_needs_free };
|
||||
}
|
||||
|
||||
fn writeHeaders(
|
||||
headers: *JSC.FetchHeaders,
|
||||
comptime ssl: bool,
|
||||
resp_ptr: ?*uws.NewApp(ssl).Response,
|
||||
) void {
|
||||
ctxLog("writeHeaders", .{});
|
||||
headers.fastRemove(.ContentLength);
|
||||
headers.fastRemove(.TransferEncoding);
|
||||
if (!ssl) headers.fastRemove(.StrictTransportSecurity);
|
||||
if (resp_ptr) |resp| {
|
||||
headers.toUWSResponse(ssl, resp);
|
||||
}
|
||||
}
|
||||
|
||||
fn writeStatus(comptime ssl: bool, resp_ptr: ?*uws.NewApp(ssl).Response, status: u16) void {
|
||||
if (resp_ptr) |resp| {
|
||||
if (HTTPStatusText.get(status)) |text| {
|
||||
resp.writeStatus(text);
|
||||
} else {
|
||||
var status_text_buf: [48]u8 = undefined;
|
||||
resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{status}) catch unreachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const StaticRoute = struct {
|
||||
server: ?AnyServer = null,
|
||||
status_code: u16,
|
||||
blob: AnyBlob,
|
||||
cached_blob_size: u64 = 0,
|
||||
has_content_disposition: bool = false,
|
||||
headers: Headers = .{
|
||||
.allocator = bun.default_allocator,
|
||||
},
|
||||
ref_count: u32 = 1,
|
||||
|
||||
const HTTPResponse = uws.AnyResponse;
|
||||
const Route = @This();
|
||||
|
||||
pub usingnamespace bun.NewRefCounted(@This(), deinit);
|
||||
|
||||
fn deinit(this: *Route) void {
|
||||
this.blob.detach();
|
||||
this.headers.deinit();
|
||||
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
pub fn fromJS(globalThis: *JSC.JSGlobalObject, argument: JSC.JSValue) ?*Route {
|
||||
if (argument.as(JSC.WebCore.Response)) |response| {
|
||||
|
||||
// The user may want to pass in the same Response object multiple endpoints
|
||||
// Let's let them do that.
|
||||
response.body.value.toBlobIfPossible();
|
||||
|
||||
var blob: AnyBlob = brk: {
|
||||
switch (response.body.value) {
|
||||
.Used => {
|
||||
globalThis.throwInvalidArguments("Response body has already been used", .{});
|
||||
return null;
|
||||
},
|
||||
|
||||
else => {
|
||||
globalThis.throwInvalidArguments("Body must be fully buffered before it can be used in a static route. Consider calling new Response(await response.blob()) to buffer the body.", .{});
|
||||
return null;
|
||||
},
|
||||
.Null, .Empty => {
|
||||
break :brk AnyBlob{
|
||||
.InternalBlob = JSC.WebCore.InternalBlob{
|
||||
.bytes = std.ArrayList(u8).init(bun.default_allocator),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
.Blob, .InternalBlob, .WTFStringImpl => {
|
||||
if (response.body.value == .Blob and response.body.value.Blob.needsToReadFile()) {
|
||||
globalThis.throwTODO("TODO: support Bun.file(path) in static routes");
|
||||
return null;
|
||||
}
|
||||
var blob = response.body.value.use();
|
||||
blob.globalThis = globalThis;
|
||||
blob.allocator = null;
|
||||
response.body.value = .{ .Blob = blob.dupe() };
|
||||
|
||||
break :brk .{ .Blob = blob };
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var has_content_disposition = false;
|
||||
|
||||
if (response.init.headers) |headers| {
|
||||
has_content_disposition = headers.fastHas(.ContentDisposition);
|
||||
headers.fastRemove(.TransferEncoding);
|
||||
headers.fastRemove(.ContentLength);
|
||||
}
|
||||
|
||||
const headers: Headers = if (response.init.headers) |headers|
|
||||
Headers.from(headers, bun.default_allocator, .{
|
||||
.body = &blob,
|
||||
}) catch {
|
||||
blob.detach();
|
||||
globalThis.throwOutOfMemory();
|
||||
return null;
|
||||
}
|
||||
else
|
||||
.{
|
||||
.allocator = bun.default_allocator,
|
||||
};
|
||||
|
||||
return Route.new(.{
|
||||
.blob = blob,
|
||||
.cached_blob_size = blob.size(),
|
||||
.has_content_disposition = has_content_disposition,
|
||||
.headers = headers,
|
||||
.server = null,
|
||||
.status_code = response.statusCode(),
|
||||
});
|
||||
}
|
||||
|
||||
globalThis.throwInvalidArguments("Expected a Response object", .{});
|
||||
return null;
|
||||
}
|
||||
|
||||
// HEAD requests have no body.
|
||||
pub fn onHEADRequest(this: *Route, req: *uws.Request, resp: HTTPResponse) void {
|
||||
req.setYield(false);
|
||||
this.ref();
|
||||
if (this.server) |server| {
|
||||
server.onPendingRequest();
|
||||
resp.timeout(server.config().idleTimeout);
|
||||
}
|
||||
resp.corked(renderMetadata, .{ this, resp });
|
||||
resp.end("", resp.shouldCloseConnection());
|
||||
this.onResponseComplete(resp);
|
||||
}
|
||||
|
||||
pub fn onRequest(this: *Route, req: *uws.Request, resp: HTTPResponse) void {
|
||||
req.setYield(false);
|
||||
this.ref();
|
||||
if (this.server) |server| {
|
||||
server.onPendingRequest();
|
||||
resp.timeout(server.config().idleTimeout);
|
||||
}
|
||||
var finished = false;
|
||||
this.doRenderBlob(resp, &finished);
|
||||
if (finished) {
|
||||
this.onResponseComplete(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
this.toAsync(resp);
|
||||
}
|
||||
|
||||
fn toAsync(this: *Route, resp: HTTPResponse) void {
|
||||
resp.onAborted(*Route, onAborted, this);
|
||||
resp.onWritable(*Route, onWritableBytes, this);
|
||||
}
|
||||
|
||||
fn onAborted(this: *Route, resp: HTTPResponse) void {
|
||||
this.onResponseComplete(resp);
|
||||
}
|
||||
|
||||
fn onResponseComplete(this: *Route, resp: HTTPResponse) void {
|
||||
resp.clearAborted();
|
||||
resp.clearOnWritable();
|
||||
|
||||
if (this.server) |server| {
|
||||
server.onStaticRequestComplete();
|
||||
}
|
||||
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn doRenderBlob(this: *Route, resp: HTTPResponse, did_finish: *bool) void {
|
||||
// We are not corked
|
||||
// The body is small
|
||||
// Faster to do the memcpy than to do the two network calls
|
||||
// We are not streaming
|
||||
// This is an important performance optimization
|
||||
if (this.blob.fastSize() < 16384 - 1024) {
|
||||
resp.corked(doRenderBlobCorked, .{ this, resp, did_finish });
|
||||
} else {
|
||||
this.doRenderBlobCorked(resp, did_finish);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doRenderBlobCorked(this: *Route, resp: HTTPResponse, did_finish: *bool) void {
|
||||
this.renderMetadata(resp);
|
||||
this.renderBytes(resp, did_finish);
|
||||
}
|
||||
|
||||
fn onWritable(this: *Route, write_offset: u64, resp: HTTPResponse) void {
|
||||
if (!this.onWritableBytes(write_offset, resp)) {
|
||||
this.toAsync(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
this.onResponseComplete(resp);
|
||||
}
|
||||
|
||||
fn onWritableBytes(this: *Route, write_offset: u64, resp: HTTPResponse) bool {
|
||||
const blob = this.blob;
|
||||
const all_bytes = blob.slice();
|
||||
|
||||
const bytes = all_bytes[@min(all_bytes.len, @as(usize, @truncate(write_offset)))..];
|
||||
|
||||
if (!resp.tryEnd(
|
||||
bytes,
|
||||
all_bytes.len,
|
||||
resp.shouldCloseConnection(),
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn doWriteStatus(_: *StaticRoute, status: u16, resp: HTTPResponse) void {
|
||||
switch (resp) {
|
||||
.SSL => |r| writeStatus(true, r, status),
|
||||
.TCP => |r| writeStatus(false, r, status),
|
||||
}
|
||||
}
|
||||
|
||||
fn doWriteHeaders(this: *StaticRoute, resp: HTTPResponse) void {
|
||||
switch (resp) {
|
||||
inline .SSL, .TCP => |s| {
|
||||
const entries = this.headers.entries.slice();
|
||||
const names: []const Api.StringPointer = entries.items(.name);
|
||||
const values: []const Api.StringPointer = entries.items(.value);
|
||||
const buf = this.headers.buf.items;
|
||||
|
||||
for (names, values) |name, value| {
|
||||
s.writeHeader(name.slice(buf), value.slice(buf));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn renderBytes(this: *Route, resp: HTTPResponse, did_finish: *bool) void {
|
||||
did_finish.* = this.onWritableBytes(0, resp);
|
||||
}
|
||||
|
||||
fn renderMetadata(this: *Route, resp: HTTPResponse) void {
|
||||
var status = this.status_code;
|
||||
const size = this.cached_blob_size;
|
||||
|
||||
status = if (status == 200 and size == 0 and !this.blob.isDetached())
|
||||
204
|
||||
else
|
||||
status;
|
||||
|
||||
this.doWriteStatus(status, resp);
|
||||
this.doWriteHeaders(resp);
|
||||
}
|
||||
};
|
||||
|
||||
pub const ServerConfig = struct {
|
||||
address: union(enum) {
|
||||
tcp: struct {
|
||||
@@ -163,6 +453,41 @@ pub const ServerConfig = struct {
|
||||
id: []const u8 = "",
|
||||
allow_hot: bool = true,
|
||||
|
||||
static_routes: std.ArrayList(StaticRouteEntry) = std.ArrayList(StaticRouteEntry).init(bun.default_allocator),
|
||||
|
||||
pub const StaticRouteEntry = struct {
|
||||
path: []const u8,
|
||||
route: *StaticRoute,
|
||||
|
||||
pub fn deinit(this: *StaticRouteEntry) void {
|
||||
bun.default_allocator.free(this.path);
|
||||
this.route.deref();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn applyStaticRoutes(this: *ServerConfig, comptime ssl: bool, server: AnyServer, app: *uws.NewApp(ssl)) void {
|
||||
for (this.static_routes.items) |entry| {
|
||||
entry.route.server = server;
|
||||
const handler_wrap = struct {
|
||||
pub fn handler(route: *StaticRoute, req: *uws.Request, resp: *uws.NewApp(ssl).Response) void {
|
||||
route.onRequest(req, switch (comptime ssl) {
|
||||
true => .{ .SSL = resp },
|
||||
false => .{ .TCP = resp },
|
||||
});
|
||||
}
|
||||
|
||||
pub fn HEAD(route: *StaticRoute, req: *uws.Request, resp: *uws.NewApp(ssl).Response) void {
|
||||
route.onHEADRequest(req, switch (comptime ssl) {
|
||||
true => .{ .SSL = resp },
|
||||
false => .{ .TCP = resp },
|
||||
});
|
||||
}
|
||||
};
|
||||
app.head(entry.path, *StaticRoute, entry.route, handler_wrap.HEAD);
|
||||
app.any(entry.path, *StaticRoute, entry.route, handler_wrap.handler);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(this: *ServerConfig) void {
|
||||
this.address.deinit(bun.default_allocator);
|
||||
|
||||
@@ -181,6 +506,11 @@ pub const ServerConfig = struct {
|
||||
this.sni.?.deinitWithAllocator(bun.default_allocator);
|
||||
this.sni = null;
|
||||
}
|
||||
|
||||
for (this.static_routes.items) |*entry| {
|
||||
entry.deinit();
|
||||
}
|
||||
this.static_routes.clearAndFree();
|
||||
}
|
||||
|
||||
pub fn computeID(this: *const ServerConfig, allocator: std.mem.Allocator) []const u8 {
|
||||
@@ -886,14 +1216,66 @@ pub const ServerConfig = struct {
|
||||
args.base_uri = origin;
|
||||
}
|
||||
|
||||
if (arguments.next()) |arg| {
|
||||
defer if (global.hasException()) if (args.ssl_config) |*conf| conf.deinit();
|
||||
defer {
|
||||
if (global.hasException() or exception.* != null) {
|
||||
if (args.ssl_config) |*conf| {
|
||||
conf.deinit();
|
||||
args.ssl_config = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (arguments.next()) |arg| {
|
||||
if (!arg.isObject()) {
|
||||
JSC.throwInvalidArguments("Bun.serve expects an object", .{}, global, exception);
|
||||
return args;
|
||||
}
|
||||
|
||||
if (arg.get(global, "static")) |static| {
|
||||
if (!static.isObject()) {
|
||||
JSC.throwInvalidArguments("Bun.serve expects 'static' to be an object shaped like { [pathname: string]: Response }", .{}, global, exception);
|
||||
return args;
|
||||
}
|
||||
|
||||
var iter = JSC.JSPropertyIterator(.{
|
||||
.skip_empty_name = true,
|
||||
.include_value = true,
|
||||
}).init(global, static);
|
||||
defer iter.deinit();
|
||||
|
||||
while (iter.next()) |key| {
|
||||
const path, const is_ascii = key.toOwnedSliceReturningAllASCII(bun.default_allocator) catch bun.outOfMemory();
|
||||
|
||||
const value = iter.value;
|
||||
|
||||
if (path.len == 0 or path[0] != '/') {
|
||||
bun.default_allocator.free(path);
|
||||
JSC.throwInvalidArguments("Invalid static route \"{s}\". path must start with '/'", .{path}, global, exception);
|
||||
return args;
|
||||
}
|
||||
|
||||
if (!is_ascii) {
|
||||
bun.default_allocator.free(path);
|
||||
JSC.throwInvalidArguments("Invalid static route \"{s}\". Please encode all non-ASCII characters in the path.", .{path}, global, exception);
|
||||
return args;
|
||||
}
|
||||
|
||||
if (StaticRoute.fromJS(global, value)) |route| {
|
||||
args.static_routes.append(.{
|
||||
.path = path,
|
||||
.route = route,
|
||||
}) catch bun.outOfMemory();
|
||||
} else if (global.hasException()) {
|
||||
bun.default_allocator.free(path);
|
||||
return args;
|
||||
} else {
|
||||
Output.panic("Internal error: expected exception or static route", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (global.hasException()) return args;
|
||||
|
||||
if (arg.get(global, "idleTimeout")) |value| {
|
||||
if (!value.isUndefinedOrNull()) {
|
||||
if (!value.isAnyInt()) {
|
||||
@@ -1420,7 +1802,7 @@ pub const AnyRequestContext = struct {
|
||||
fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comptime ThisServer: type) type {
|
||||
return struct {
|
||||
const RequestContext = @This();
|
||||
const ctxLog = Output.scoped(.RequestContext, false);
|
||||
|
||||
const App = uws.NewApp(ssl_enabled);
|
||||
pub threadlocal var pool: ?*RequestContext.RequestContextStackAllocator = null;
|
||||
pub const ResponseStream = JSC.WebCore.HTTPServerWritable(ssl_enabled);
|
||||
@@ -1861,7 +2243,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onWritableResponseBuffer(this: *RequestContext, _: u64, resp: *App.Response) callconv(.C) bool {
|
||||
pub fn onWritableResponseBuffer(this: *RequestContext, _: u64, resp: *App.Response) bool {
|
||||
ctxLog("onWritableResponseBuffer", .{});
|
||||
|
||||
assert(this.resp == resp);
|
||||
@@ -1873,7 +2255,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
|
||||
// TODO: should we cork?
|
||||
pub fn onWritableCompleteResponseBufferAndMetadata(this: *RequestContext, write_offset: u64, resp: *App.Response) callconv(.C) bool {
|
||||
pub fn onWritableCompleteResponseBufferAndMetadata(this: *RequestContext, write_offset: u64, resp: *App.Response) bool {
|
||||
ctxLog("onWritableCompleteResponseBufferAndMetadata", .{});
|
||||
assert(this.resp == resp);
|
||||
|
||||
@@ -1893,7 +2275,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
return this.sendWritableBytesForCompleteResponseBuffer(this.response_buf_owned.items, write_offset, resp);
|
||||
}
|
||||
|
||||
pub fn onWritableCompleteResponseBuffer(this: *RequestContext, write_offset: u64, resp: *App.Response) callconv(.C) bool {
|
||||
pub fn onWritableCompleteResponseBuffer(this: *RequestContext, write_offset: u64, resp: *App.Response) bool {
|
||||
ctxLog("onWritableCompleteResponseBuffer", .{});
|
||||
assert(this.resp == resp);
|
||||
if (this.isAbortedOrEnded()) {
|
||||
@@ -2027,33 +2409,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
}
|
||||
|
||||
fn writeHeaders(
|
||||
this: *RequestContext,
|
||||
headers: *JSC.FetchHeaders,
|
||||
) void {
|
||||
ctxLog("writeHeaders", .{});
|
||||
headers.fastRemove(.ContentLength);
|
||||
headers.fastRemove(.TransferEncoding);
|
||||
if (!ssl_enabled) headers.fastRemove(.StrictTransportSecurity);
|
||||
if (this.resp) |resp| {
|
||||
headers.toUWSResponse(ssl_enabled, resp);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeStatus(this: *RequestContext, status: u16) void {
|
||||
var status_text_buf: [48]u8 = undefined;
|
||||
assert(!this.flags.has_written_status);
|
||||
this.flags.has_written_status = true;
|
||||
|
||||
if (this.resp) |resp| {
|
||||
if (HTTPStatusText.get(status)) |text| {
|
||||
resp.writeStatus(text);
|
||||
} else {
|
||||
resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{status}) catch unreachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn endSendFile(this: *RequestContext, writeOffSet: usize, closeConnection: bool) void {
|
||||
if (this.resp) |resp| {
|
||||
defer this.deref();
|
||||
@@ -2142,7 +2497,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn onWritableBytes(this: *RequestContext, write_offset: u64, resp: *App.Response) callconv(.C) bool {
|
||||
pub fn onWritableBytes(this: *RequestContext, write_offset: u64, resp: *App.Response) bool {
|
||||
ctxLog("onWritableBytes", .{});
|
||||
assert(this.resp == resp);
|
||||
if (this.isAbortedOrEnded()) {
|
||||
@@ -2192,7 +2547,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn onWritableSendfile(this: *RequestContext, _: u64, _: *App.Response) callconv(.C) bool {
|
||||
pub fn onWritableSendfile(this: *RequestContext, _: u64, _: *App.Response) bool {
|
||||
ctxLog("onWritableSendfile", .{});
|
||||
return this.onSendfile();
|
||||
}
|
||||
@@ -3158,7 +3513,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
|
||||
pub inline fn shouldCloseConnection(this: *const RequestContext) bool {
|
||||
if (this.resp) |resp| {
|
||||
return resp.state().isHttpConnectionClose();
|
||||
return resp.shouldCloseConnection();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -3324,33 +3679,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
else
|
||||
status;
|
||||
|
||||
var needs_content_type = true;
|
||||
var content_type_needs_free = false;
|
||||
|
||||
const content_type: MimeType = brk: {
|
||||
if (response.init.headers) |headers_| {
|
||||
if (headers_.fastGet(.ContentType)) |content| {
|
||||
needs_content_type = false;
|
||||
|
||||
var content_slice = content.toSlice(this.allocator);
|
||||
defer content_slice.deinit();
|
||||
|
||||
const content_type_allocator = if (content_slice.allocator.isNull()) null else this.allocator;
|
||||
break :brk MimeType.init(content_slice.slice(), content_type_allocator, &content_type_needs_free);
|
||||
}
|
||||
}
|
||||
|
||||
break :brk if (this.blob.contentType().len > 0)
|
||||
MimeType.byName(this.blob.contentType())
|
||||
else if (MimeType.sniff(this.blob.slice())) |content|
|
||||
content
|
||||
else if (this.blob.wasString())
|
||||
MimeType.text
|
||||
// TODO: should we get the mime type off of the Blob.Store if it exists?
|
||||
// A little wary of doing this right now due to causing some breaking change
|
||||
else
|
||||
MimeType.other;
|
||||
};
|
||||
const content_type, const needs_content_type, const content_type_needs_free = getContentType(
|
||||
response.init.headers,
|
||||
&this.blob,
|
||||
this.allocator,
|
||||
);
|
||||
defer if (content_type_needs_free) content_type.deinit(this.allocator);
|
||||
var has_content_disposition = false;
|
||||
var has_content_range = false;
|
||||
@@ -3362,15 +3695,15 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
status = 206;
|
||||
}
|
||||
|
||||
this.writeStatus(status);
|
||||
this.writeHeaders(headers_);
|
||||
this.doWriteStatus(status);
|
||||
this.doWriteHeaders(headers_);
|
||||
response.init.headers = null;
|
||||
headers_.deref();
|
||||
} else if (needs_content_range) {
|
||||
status = 206;
|
||||
this.writeStatus(status);
|
||||
this.doWriteStatus(status);
|
||||
} else {
|
||||
this.writeStatus(status);
|
||||
this.doWriteStatus(status);
|
||||
}
|
||||
|
||||
if (needs_content_type and
|
||||
@@ -3421,6 +3754,17 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
}
|
||||
|
||||
fn doWriteStatus(this: *RequestContext, status: u16) void {
|
||||
assert(!this.flags.has_written_status);
|
||||
this.flags.has_written_status = true;
|
||||
|
||||
writeStatus(ssl_enabled, this.resp, status);
|
||||
}
|
||||
|
||||
fn doWriteHeaders(this: *RequestContext, headers: *JSC.FetchHeaders) void {
|
||||
writeHeaders(headers, ssl_enabled, this.resp);
|
||||
}
|
||||
|
||||
pub fn renderBytes(this: *RequestContext) void {
|
||||
// copy it to stack memory to prevent aliasing issues in release builds
|
||||
const blob = this.blob;
|
||||
@@ -4328,7 +4672,7 @@ pub const ServerWebSocket = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn finalize(this: *ServerWebSocket) callconv(.C) void {
|
||||
pub fn finalize(this: *ServerWebSocket) void {
|
||||
log("finalize", .{});
|
||||
this.destroy();
|
||||
}
|
||||
@@ -5634,6 +5978,18 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
this.config.websocket = ws.*;
|
||||
} // we don't remove it
|
||||
}
|
||||
|
||||
if (this.config.static_routes.items.len > 0) {
|
||||
// TODO: clear old static routes
|
||||
}
|
||||
|
||||
if (new_config.static_routes.items.len > 0) {
|
||||
new_config.applyStaticRoutes(
|
||||
ssl_enabled,
|
||||
AnyServer.from(this),
|
||||
this.app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onReload(
|
||||
@@ -5964,6 +6320,11 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
return JSC.JSValue.jsBoolean(debug_mode);
|
||||
}
|
||||
|
||||
pub fn onStaticRequestComplete(this: *ThisServer) void {
|
||||
this.pending_requests -= 1;
|
||||
this.deinitIfWeCan();
|
||||
}
|
||||
|
||||
pub fn onRequestComplete(this: *ThisServer) void {
|
||||
this.vm.eventLoop().processGCTimer();
|
||||
|
||||
@@ -5971,7 +6332,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
this.deinitIfWeCan();
|
||||
}
|
||||
|
||||
pub fn finalize(this: *ThisServer) callconv(.C) void {
|
||||
pub fn finalize(this: *ThisServer) void {
|
||||
httplog("finalize", .{});
|
||||
this.flags.has_js_deinited = true;
|
||||
this.deinitIfWeCan();
|
||||
@@ -6311,13 +6672,17 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onPendingRequest(this: *ThisServer) void {
|
||||
this.pending_requests += 1;
|
||||
}
|
||||
|
||||
pub fn onRequest(
|
||||
this: *ThisServer,
|
||||
req: *uws.Request,
|
||||
resp: *App.Response,
|
||||
) void {
|
||||
JSC.markBinding(@src());
|
||||
this.pending_requests += 1;
|
||||
this.onPendingRequest();
|
||||
if (comptime Environment.isDebug) {
|
||||
this.vm.eventLoop().debug.enter();
|
||||
}
|
||||
@@ -6505,6 +6870,14 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
}
|
||||
|
||||
fn setRoutes(this: *ThisServer) void {
|
||||
if (this.config.static_routes.items.len > 0) {
|
||||
this.config.applyStaticRoutes(
|
||||
ssl_enabled,
|
||||
AnyServer.from(this),
|
||||
this.app,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.config.websocket) |*websocket| {
|
||||
websocket.globalObject = this.globalThis;
|
||||
websocket.handler.app = this.app;
|
||||
@@ -6641,7 +7014,46 @@ pub const HTTPServer = NewServer(JSC.Codegen.JSHTTPServer, false, false);
|
||||
pub const HTTPSServer = NewServer(JSC.Codegen.JSHTTPSServer, true, false);
|
||||
pub const DebugHTTPServer = NewServer(JSC.Codegen.JSDebugHTTPServer, false, true);
|
||||
pub const DebugHTTPSServer = NewServer(JSC.Codegen.JSDebugHTTPSServer, true, true);
|
||||
const AnyServer = union(enum) {
|
||||
HTTPServer: *HTTPServer,
|
||||
HTTPSServer: *HTTPSServer,
|
||||
DebugHTTPServer: *DebugHTTPServer,
|
||||
DebugHTTPSServer: *DebugHTTPSServer,
|
||||
|
||||
pub fn config(this: AnyServer) *const ServerConfig {
|
||||
return switch (this) {
|
||||
inline else => |server| &server.config,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn from(server: anytype) AnyServer {
|
||||
return switch (@TypeOf(server)) {
|
||||
*HTTPServer => AnyServer{ .HTTPServer = server },
|
||||
*HTTPSServer => AnyServer{ .HTTPSServer = server },
|
||||
*DebugHTTPServer => AnyServer{ .DebugHTTPServer = server },
|
||||
*DebugHTTPSServer => AnyServer{ .DebugHTTPSServer = server },
|
||||
else => @compileError("Invalid server type"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn onPendingRequest(this: AnyServer) void {
|
||||
switch (this) {
|
||||
inline else => |server| server.onPendingRequest(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onRequestComplete(this: AnyServer) void {
|
||||
switch (this) {
|
||||
inline else => |server| server.onRequestComplete(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onStaticRequestComplete(this: AnyServer) void {
|
||||
switch (this) {
|
||||
inline else => |server| server.onStaticRequestComplete(),
|
||||
}
|
||||
}
|
||||
};
|
||||
const welcome_page_html_gz = @embedFile("welcome-page.html.gz");
|
||||
|
||||
extern fn Bun__addInspector(bool, *anyopaque, *JSC.JSGlobalObject) void;
|
||||
|
||||
@@ -2997,6 +2997,11 @@ pub const Headers = struct {
|
||||
buf: std.ArrayListUnmanaged(u8) = .{},
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
pub fn deinit(this: *Headers) void {
|
||||
this.entries.deinit(this.allocator);
|
||||
this.buf.clearAndFree(this.allocator);
|
||||
}
|
||||
|
||||
pub fn asStr(this: *const Headers, ptr: Api.StringPointer) []const u8 {
|
||||
return if (ptr.offset + ptr.length <= this.buf.items.len)
|
||||
this.buf.items[ptr.offset..][0..ptr.length]
|
||||
|
||||
@@ -2120,7 +2120,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return this.buffer.ptr[this.offset..this.buffer.len];
|
||||
}
|
||||
|
||||
pub fn onWritable(this: *@This(), write_offset: u64, _: *UWSResponse) callconv(.C) bool {
|
||||
pub fn onWritable(this: *@This(), write_offset: u64, _: *UWSResponse) bool {
|
||||
// write_offset is the amount of data that was written not how much we need to write
|
||||
log("onWritable ({d})", .{write_offset});
|
||||
// onWritable reset backpressure state to allow flushing
|
||||
|
||||
@@ -183,8 +183,9 @@ extern "C"
|
||||
}
|
||||
}
|
||||
|
||||
void uws_app_head(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data)
|
||||
void uws_app_head(int ssl, uws_app_t *app, const char *pattern_ptr, size_t pattern_len, uws_method_handler handler, void *user_data)
|
||||
{
|
||||
std::string pattern = std::string(pattern_ptr, pattern_len);
|
||||
if (ssl)
|
||||
{
|
||||
uWS::SSLApp *uwsApp = (uWS::SSLApp *)app;
|
||||
@@ -194,7 +195,7 @@ extern "C"
|
||||
return;
|
||||
}
|
||||
uwsApp->head(pattern, [handler, user_data](auto *res, auto *req)
|
||||
{ handler((uws_res_t *)res, (uws_req_t *)req, user_data); });
|
||||
{ handler((uws_res_t *)res, (uws_req_t *)req, user_data); });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -205,10 +206,9 @@ extern "C"
|
||||
return;
|
||||
}
|
||||
uwsApp->head(pattern, [handler, user_data](auto *res, auto *req)
|
||||
{ handler((uws_res_t *)res, (uws_req_t *)req, user_data); });
|
||||
{ handler((uws_res_t *)res, (uws_req_t *)req, user_data); });
|
||||
}
|
||||
}
|
||||
|
||||
void uws_app_connect(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data)
|
||||
{
|
||||
if (ssl)
|
||||
@@ -261,8 +261,9 @@ extern "C"
|
||||
}
|
||||
}
|
||||
|
||||
void uws_app_any(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data)
|
||||
void uws_app_any(int ssl, uws_app_t *app, const char *pattern_ptr, size_t pattern_len, uws_method_handler handler, void *user_data)
|
||||
{
|
||||
std::string pattern = std::string(pattern_ptr, pattern_len);
|
||||
if (ssl)
|
||||
{
|
||||
uWS::SSLApp *uwsApp = (uWS::SSLApp *)app;
|
||||
|
||||
188
src/deps/uws.zig
188
src/deps/uws.zig
@@ -1894,6 +1894,153 @@ pub const SocketAddress = struct {
|
||||
is_ipv6: bool,
|
||||
};
|
||||
|
||||
pub const AnyResponse = union(enum) {
|
||||
SSL: *NewApp(true).Response,
|
||||
TCP: *NewApp(false).Response,
|
||||
|
||||
pub fn timeout(this: AnyResponse, seconds: u8) void {
|
||||
switch (this) {
|
||||
.SSL => |resp| resp.timeout(seconds),
|
||||
.TCP => |resp| resp.timeout(seconds),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeStatus(this: AnyResponse, status: []const u8) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.writeStatus(status),
|
||||
.TCP => |resp| resp.writeStatus(status),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeHeader(this: AnyResponse, key: []const u8, value: []const u8) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.writeHeader(key, value),
|
||||
.TCP => |resp| resp.writeHeader(key, value),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn write(this: AnyResponse, data: []const u8) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.write(data),
|
||||
.TCP => |resp| resp.write(data),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn end(this: AnyResponse, data: []const u8, close_connection: bool) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.end(data, close_connection),
|
||||
.TCP => |resp| resp.end(data, close_connection),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn shouldCloseConnection(this: AnyResponse) bool {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.shouldCloseConnection(),
|
||||
.TCP => |resp| resp.shouldCloseConnection(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn tryEnd(this: AnyResponse, data: []const u8, total_size: usize, close_connection: bool) bool {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.tryEnd(data, total_size, close_connection),
|
||||
.TCP => |resp| resp.tryEnd(data, total_size, close_connection),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn pause(this: AnyResponse) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.pause(),
|
||||
.TCP => |resp| resp.pause(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn @"resume"(this: AnyResponse) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.@"resume"(),
|
||||
.TCP => |resp| resp.@"resume"(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeOrEndWithoutBody(this: AnyResponse, data: []const u8) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.writeOrEndWithoutBody(data),
|
||||
.TCP => |resp| resp.writeOrEndWithoutBody(data),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn onWritable(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType, u64, AnyResponse) bool, opcional_data: UserDataType) void {
|
||||
const wrapper = struct {
|
||||
pub fn ssl_handler(user_data: UserDataType, offset: u64, resp: *NewApp(true).Response) bool {
|
||||
return handler(user_data, offset, .{ .SSL = resp });
|
||||
}
|
||||
|
||||
pub fn tcp_handler(user_data: UserDataType, offset: u64, resp: *NewApp(false).Response) bool {
|
||||
return handler(user_data, offset, .{ .TCP = resp });
|
||||
}
|
||||
};
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.onWritable(UserDataType, wrapper.ssl_handler, opcional_data),
|
||||
.TCP => |resp| resp.onWritable(UserDataType, wrapper.tcp_handler, opcional_data),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn onAborted(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType, AnyResponse) void, opcional_data: UserDataType) void {
|
||||
const wrapper = struct {
|
||||
pub fn ssl_handler(user_data: UserDataType, resp: *NewApp(true).Response) void {
|
||||
handler(user_data, .{ .SSL = resp });
|
||||
}
|
||||
pub fn tcp_handler(user_data: UserDataType, resp: *NewApp(false).Response) void {
|
||||
handler(user_data, .{ .TCP = resp });
|
||||
}
|
||||
};
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.onAborted(UserDataType, wrapper.ssl_handler, opcional_data),
|
||||
.TCP => |resp| resp.onAborted(UserDataType, wrapper.tcp_handler, opcional_data),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn clearAborted(this: AnyResponse) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.clearAborted(),
|
||||
.TCP => |resp| resp.clearAborted(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn clearOnWritable(this: AnyResponse) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.clearOnWritable(),
|
||||
.TCP => |resp| resp.clearOnWritable(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn clearOnData(this: AnyResponse) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.clearOnData(),
|
||||
.TCP => |resp| resp.clearOnData(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn endStream(this: AnyResponse, close_connection: bool) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.endStream(close_connection),
|
||||
.TCP => |resp| resp.endStream(close_connection),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn corked(this: AnyResponse, comptime handler: anytype, args_tuple: anytype) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.corked(handler, args_tuple),
|
||||
.TCP => |resp| resp.corked(handler, args_tuple),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn runCorkedWithType(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType) void, opcional_data: UserDataType) void {
|
||||
return switch (this) {
|
||||
.SSL => |resp| resp.runCorkedWithType(UserDataType, handler, opcional_data),
|
||||
.TCP => |resp| resp.runCorkedWithType(UserDataType, handler, opcional_data),
|
||||
};
|
||||
}
|
||||
};
|
||||
pub fn NewApp(comptime ssl: bool) type {
|
||||
return opaque {
|
||||
const ssl_flag = @as(i32, @intFromBool(ssl));
|
||||
@@ -2046,7 +2193,7 @@ pub fn NewApp(comptime ssl: bool) type {
|
||||
}
|
||||
pub fn head(
|
||||
app: *ThisApp,
|
||||
pattern: [:0]const u8,
|
||||
pattern: []const u8,
|
||||
comptime UserDataType: type,
|
||||
user_data: UserDataType,
|
||||
comptime handler: (fn (UserDataType, *Request, *Response) void),
|
||||
@@ -2054,7 +2201,7 @@ pub fn NewApp(comptime ssl: bool) type {
|
||||
if (comptime is_bindgen) {
|
||||
unreachable;
|
||||
}
|
||||
uws_app_head(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data);
|
||||
uws_app_head(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern.ptr, pattern.len, RouteHandler(UserDataType, handler).handle, user_data);
|
||||
}
|
||||
pub fn connect(
|
||||
app: *ThisApp,
|
||||
@@ -2082,7 +2229,7 @@ pub fn NewApp(comptime ssl: bool) type {
|
||||
}
|
||||
pub fn any(
|
||||
app: *ThisApp,
|
||||
pattern: [:0]const u8,
|
||||
pattern: []const u8,
|
||||
comptime UserDataType: type,
|
||||
user_data: UserDataType,
|
||||
comptime handler: (fn (UserDataType, *Request, *Response) void),
|
||||
@@ -2090,7 +2237,7 @@ pub fn NewApp(comptime ssl: bool) type {
|
||||
if (comptime is_bindgen) {
|
||||
unreachable;
|
||||
}
|
||||
uws_app_any(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern, RouteHandler(UserDataType, handler).handle, user_data);
|
||||
uws_app_any(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern.ptr, pattern.len, RouteHandler(UserDataType, handler).handle, user_data);
|
||||
}
|
||||
pub fn domain(app: *ThisApp, pattern: [:0]const u8) void {
|
||||
uws_app_domain(ssl_flag, @as(*uws_app_t, @ptrCast(app)), pattern);
|
||||
@@ -2233,6 +2380,10 @@ pub fn NewApp(comptime ssl: bool) type {
|
||||
return uws_res_state(ssl_flag, @as(*const uws_res, @ptrCast(@alignCast(res))));
|
||||
}
|
||||
|
||||
pub fn shouldCloseConnection(this: *const Response) bool {
|
||||
return this.state().isHttpConnectionClose();
|
||||
}
|
||||
|
||||
pub fn prepareForSendfile(res: *Response) void {
|
||||
return uws_res_prepare_for_sendfile(ssl_flag, res.downcast());
|
||||
}
|
||||
@@ -2324,7 +2475,7 @@ pub fn NewApp(comptime ssl: bool) type {
|
||||
pub fn onWritable(
|
||||
res: *Response,
|
||||
comptime UserDataType: type,
|
||||
comptime handler: fn (UserDataType, u64, *Response) callconv(.C) bool,
|
||||
comptime handler: fn (UserDataType, u64, *Response) bool,
|
||||
user_data: UserDataType,
|
||||
) void {
|
||||
const Wrapper = struct {
|
||||
@@ -2407,22 +2558,19 @@ pub fn NewApp(comptime ssl: bool) type {
|
||||
|
||||
pub fn corked(
|
||||
res: *Response,
|
||||
comptime Function: anytype,
|
||||
args: anytype,
|
||||
) @typeInfo(@TypeOf(Function)).Fn.return_type.? {
|
||||
comptime handler: anytype,
|
||||
args_tuple: anytype,
|
||||
) void {
|
||||
const Wrapper = struct {
|
||||
opts: @TypeOf(args),
|
||||
result: @typeInfo(@TypeOf(Function)).Fn.return_type.? = undefined,
|
||||
pub fn run(this: *@This()) void {
|
||||
this.result = Function(this.opts);
|
||||
const handler_fn = handler;
|
||||
const Args = *@TypeOf(args_tuple);
|
||||
pub fn handle(user_data: ?*anyopaque) callconv(.C) void {
|
||||
const args: Args = @alignCast(@ptrCast(user_data.?));
|
||||
@call(.always_inline, handler_fn, args.*);
|
||||
}
|
||||
};
|
||||
var wrapped = Wrapper{
|
||||
.opts = args,
|
||||
.result = undefined,
|
||||
};
|
||||
runCorkedWithType(res, *Wrapper, Wrapper.run, &wrapped);
|
||||
return wrapped.result;
|
||||
|
||||
uws_res_cork(ssl_flag, res.downcast(), @constCast(@ptrCast(&args_tuple)), Wrapper.handle);
|
||||
}
|
||||
|
||||
pub fn runCorkedWithType(
|
||||
@@ -2618,10 +2766,10 @@ extern fn uws_app_options(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, hand
|
||||
extern fn uws_app_delete(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_patch(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_put(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_head(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_head(ssl: i32, app: *uws_app_t, pattern: [*]const u8, pattern_len: usize, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_connect(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_trace(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_any(ssl: i32, app: *uws_app_t, pattern: [*c]const u8, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_any(ssl: i32, app: *uws_app_t, pattern: [*]const u8, pattern_len: usize, handler: uws_method_handler, user_data: ?*anyopaque) void;
|
||||
extern fn uws_app_run(ssl: i32, *uws_app_t) void;
|
||||
extern fn uws_app_domain(ssl: i32, app: *uws_app_t, domain: [*c]const u8) void;
|
||||
extern fn uws_app_listen(ssl: i32, app: *uws_app_t, port: i32, handler: uws_listen_handler, user_data: ?*anyopaque) void;
|
||||
|
||||
Reference in New Issue
Block a user