diff options
Diffstat (limited to 'src/bun.js/api/server.zig')
-rw-r--r-- | src/bun.js/api/server.zig | 272 |
1 files changed, 144 insertions, 128 deletions
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 39e9a8046..b79e6c7ab 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -549,38 +549,29 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // TODO: support builtin compression const can_sendfile = !ssl_enabled; - pub const thenables = shim.thenables(.{ - PromiseHandler, - }); - - pub const lazy_static_functions = thenables; - pub const Export = lazy_static_functions; - - const PromiseHandler = JSC.Thenable(RequestContext, onResolve, onReject); - pub fn setAbortHandler(this: *RequestContext) void { if (this.has_abort_handler) return; this.has_abort_handler = true; this.resp.onAborted(*RequestContext, RequestContext.onAbort, this); } - pub fn onResolve( - ctx: *RequestContext, - _: *JSC.JSGlobalObject, - arguments: []const JSC.JSValue, - ) void { + pub fn onResolve(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + const arguments = callframe.arguments(2); + var ctx = arguments.ptr[1].asPromisePtr(@This()); + const result = arguments.ptr[0]; ctx.pending_promises_for_abort -|= 1; if (ctx.aborted) { ctx.finalizeForAbort(); - return; + return JSValue.jsUndefined(); } - if (arguments.len == 0) { + if (result.isEmptyOrUndefinedOrNull()) { ctx.renderMissing(); - return; + return JSValue.jsUndefined(); } - handleResolve(ctx, arguments[0]); + handleResolve(ctx, result); + return JSValue.jsUndefined(); } fn handleResolve(ctx: *RequestContext, value: JSC.JSValue) void { @@ -606,18 +597,19 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (this.pending_promises_for_abort == 0) this.finalize(); } - pub fn onReject( - ctx: *RequestContext, - _: *JSC.JSGlobalObject, - arguments: []const JSC.JSValue, - ) void { + pub fn onReject(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + const arguments = callframe.arguments(2); + var ctx = arguments.ptr[1].asPromisePtr(@This()); + const err = arguments.ptr[0]; + ctx.pending_promises_for_abort -|= 1; if (ctx.aborted) { ctx.finalizeForAbort(); - return; + return JSValue.jsUndefined(); } - handleReject(ctx, if (arguments.len > 0) arguments[0] else JSC.JSValue.jsUndefined()); + handleReject(ctx, if (!err.isEmptyOrUndefinedOrNull()) err else JSC.JSValue.jsUndefined()); + return JSValue.jsUndefined(); } fn handleReject(ctx: *RequestContext, value: JSC.JSValue) void { @@ -1247,101 +1239,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp assignment_result.ensureStillAlive(); // it returns a Promise when it goes through ReadableStreamDefaultReader if (assignment_result.asPromise()) |promise| { - const AwaitPromise = struct { - pub fn onResolve(req: *RequestContext, _: *JSGlobalObject, _: []const JSC.JSValue) void { - streamLog("onResolve", .{}); - var wrote_anything = false; - - if (req.sink) |wrapper| { - wrapper.sink.pending_flush = null; - wrapper.sink.done = true; - req.aborted = req.aborted or wrapper.sink.aborted; - wrote_anything = wrapper.sink.wrote > 0; - wrapper.sink.finalize(); - wrapper.detach(); - req.sink = null; - wrapper.sink.destroy(); - } - - if (req.response_ptr) |resp| { - if (resp.body.value == .Locked) { - resp.body.value.Locked.readable.?.done(); - resp.body.value = .{ .Used = {} }; - } - } - - if (req.aborted) { - req.finalizeForAbort(); - return; - } - - const responded = req.resp.hasResponded(); - - if (!responded and !wrote_anything) { - req.resp.clearAborted(); - req.renderMissing(); - return; - } else if (!responded and wrote_anything and !req.aborted) { - req.resp.clearAborted(); - req.resp.endStream(false); - } - - req.finalize(); - } - pub fn onReject(req: *RequestContext, globalThis: *JSGlobalObject, args: []const JSC.JSValue) void { - var wrote_anything = req.has_written_status; - - if (req.sink) |wrapper| { - wrapper.sink.pending_flush = null; - wrapper.sink.done = true; - wrote_anything = wrote_anything or wrapper.sink.wrote > 0; - req.aborted = req.aborted or wrapper.sink.aborted; - wrapper.sink.finalize(); - wrapper.detach(); - req.sink = null; - wrapper.sink.destroy(); - } - - if (req.response_ptr) |resp| { - if (resp.body.value == .Locked) { - resp.body.value.Locked.readable.?.done(); - resp.body.value = .{ .Used = {} }; - } - } - - streamLog("onReject({s})", .{wrote_anything}); - - if (req.aborted) { - req.finalizeForAbort(); - return; - } - - if (args.len > 0 and !wrote_anything) { - req.response_jsvalue.unprotect(); - req.response_jsvalue = JSValue.zero; - req.handleReject(args[0]); - return; - } else if (wrote_anything) { - req.resp.endStream(true); - if (comptime debug_mode) { - if (args.len > 0) { - var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(req.allocator); - defer exception_list.deinit(); - req.server.vm.runErrorHandler(args[0], &exception_list); - } - } - req.finalize(); - return; - } - - const fallback = JSC.SystemError{ - .code = ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_UNHANDLED_ERROR))), - .message = ZigString.init("Unhandled error in ReadableStream"), - }; - req.handleReject(fallback.toErrorInstance(globalThis)); - } - }; - streamLog("returned a promise", .{}); switch (promise.status(this.server.globalThis.vm())) { .Pending => { @@ -1355,19 +1252,18 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp }; assignment_result.then( this.server.globalThis, - RequestContext, this, - AwaitPromise.onResolve, - AwaitPromise.onReject, + onResolveStream, + onRejectStream, ); // the response_stream should be GC'd }, .Fulfilled => { - AwaitPromise.onResolve(this, this.server.globalThis, &.{promise.result(this.server.globalThis.vm())}); + this.handleResolveStream(); }, .Rejected => { - AwaitPromise.onReject(this, this.server.globalThis, &.{promise.result(this.server.globalThis.vm())}); + this.handleRejectStream(this.server.globalThis, promise.result(this.server.globalThis.vm())); }, } return; @@ -1401,6 +1297,113 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp const streamLog = Output.scoped(.ReadableStream, false); + pub fn handleResolveStream(req: *RequestContext) void { + streamLog("onResolve", .{}); + var wrote_anything = false; + if (req.sink) |wrapper| { + wrapper.sink.pending_flush = null; + wrapper.sink.done = true; + req.aborted = req.aborted or wrapper.sink.aborted; + wrote_anything = wrapper.sink.wrote > 0; + wrapper.sink.finalize(); + wrapper.detach(); + req.sink = null; + wrapper.sink.destroy(); + } + + if (req.response_ptr) |resp| { + if (resp.body.value == .Locked) { + resp.body.value.Locked.readable.?.done(); + resp.body.value = .{ .Used = {} }; + } + } + + if (req.aborted) { + req.finalizeForAbort(); + return; + } + + const responded = req.resp.hasResponded(); + + if (!responded and !wrote_anything) { + req.resp.clearAborted(); + req.renderMissing(); + return; + } else if (!responded and wrote_anything and !req.aborted) { + req.resp.clearAborted(); + req.resp.endStream(false); + } + + req.finalize(); + } + + pub fn onResolveStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + var args = callframe.arguments(2); + var req: *@This() = args.ptr[args.len - 1].asPromisePtr(@This()); + req.handleResolveStream(); + return JSValue.jsUndefined(); + } + pub fn onRejectStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + const args = callframe.arguments(2); + var req = args.ptr[args.len - 1].asPromisePtr(@This()); + var err = args.ptr[0]; + req.handleRejectStream(globalThis, err); + return JSValue.jsUndefined(); + } + + pub fn handleRejectStream(req: *@This(), globalThis: *JSC.JSGlobalObject, err: JSValue) void { + var wrote_anything = req.has_written_status; + + if (req.sink) |wrapper| { + wrapper.sink.pending_flush = null; + wrapper.sink.done = true; + wrote_anything = wrote_anything or wrapper.sink.wrote > 0; + req.aborted = req.aborted or wrapper.sink.aborted; + wrapper.sink.finalize(); + wrapper.detach(); + req.sink = null; + wrapper.sink.destroy(); + } + + if (req.response_ptr) |resp| { + if (resp.body.value == .Locked) { + resp.body.value.Locked.readable.?.done(); + resp.body.value = .{ .Used = {} }; + } + } + + streamLog("onReject({s})", .{wrote_anything}); + + if (req.aborted) { + req.finalizeForAbort(); + return; + } + + if (!err.isEmptyOrUndefinedOrNull() and !wrote_anything) { + req.response_jsvalue.unprotect(); + req.response_jsvalue = JSValue.zero; + req.handleReject(err); + return; + } else if (wrote_anything) { + req.resp.endStream(true); + if (comptime debug_mode) { + if (!err.isEmptyOrUndefinedOrNull()) { + var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(req.allocator); + defer exception_list.deinit(); + req.server.vm.runErrorHandler(err, &exception_list); + } + } + req.finalize(); + return; + } + + const fallback = JSC.SystemError{ + .code = ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_UNHANDLED_ERROR))), + .message = ZigString.init("Unhandled error in ReadableStream"), + }; + req.handleReject(fallback.toErrorInstance(globalThis)); + } + pub fn doRenderWithBody(this: *RequestContext, value: *JSC.WebCore.Body.Value) void { switch (value.*) { .Error => { @@ -1799,14 +1802,27 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp onPull(bun.cast(*RequestContext, this)); } + pub const Export = shim.exportFunctions(.{ + .onResolve = onResolve, + .onReject = onReject, + .onResolveStream = onResolveStream, + .onRejectStream = onRejectStream, + }); + comptime { if (!JSC.is_bindgen) { - @export(PromiseHandler.resolve, .{ + @export(onResolve, .{ .name = Export[0].symbol_name, }); - @export(PromiseHandler.reject, .{ + @export(onReject, .{ .name = Export[1].symbol_name, }); + @export(onResolveStream, .{ + .name = Export[2].symbol_name, + }); + @export(onRejectStream, .{ + .name = Export[3].symbol_name, + }); } } }; @@ -2235,7 +2251,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { request_object.headers = JSC.FetchHeaders.createFromUWS(this.globalThis, req); } - RequestContext.PromiseHandler.then(ctx, response_value, this.globalThis); + response_value.then(this.globalThis, ctx, RequestContext.onResolve, RequestContext.onReject); return; } |