diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | src/deps/_libusockets.h | 2 | ||||
-rw-r--r-- | src/deps/libuwsockets.cpp | 45 | ||||
m--------- | src/deps/uws | 0 | ||||
-rw-r--r-- | src/deps/uws.zig | 5 | ||||
-rw-r--r-- | src/javascript/jsc/api/server.zig | 120 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 9 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 134 |
8 files changed, 122 insertions, 197 deletions
@@ -302,7 +302,7 @@ ARCHIVE_FILES_WITHOUT_LIBCRYPTO = $(MIMALLOC_FILE_PATH) \ ARCHIVE_FILES = $(ARCHIVE_FILES_WITHOUT_LIBCRYPTO) -lcrypto ifeq ($(OS_NAME), darwin) - ARCHIVE_FILES += $(wildcard $(BUN_DEPS_DIR)/uws/uSockets/src/*.o) $(wildcard $(BUN_DEPS_DIR)/uws/uSockets/src/**/*.o) $(BUN_DEPS_OUT_DIR)/libuwsockets.o + ARCHIVE_FILES += $(wildcard $(BUN_DEPS_DIR)/uws/uSockets/src/**/*.bc) $(wildcard $(BUN_DEPS_DIR)/uws/uSockets/src/*.bc) $(BUN_DEPS_OUT_DIR)/libuwsockets.o else ARCHIVE_FILES += -lusockets $(BUN_DEPS_OUT_DIR)/libuwsockets.o endif @@ -485,7 +485,7 @@ usockets: $(CXX) -fPIC $(CXXFLAGS) $(UWS_CXX_FLAGS) -emit-llvm -Isrc $(UWS_LDFLAGS) -g $(DEFAULT_LINKER_FLAGS) $(PLATFORM_LINKER_FLAGS) $(OPTIMIZATION_LEVEL) -g -c *.cpp; cd $(BUN_DEPS_DIR)/uws/uSockets && \ - $(AR) rcvs $(BUN_DEPS_OUT_DIR)/libusockets.a src/*.o src/eventing/*.o src/crypto/*.o + $(AR) rcvs $(BUN_DEPS_OUT_DIR)/libusockets.a $(wildcard $(BUN_DEPS_DIR)/uws/uSockets/src/**/*.bc) $(wildcard $(BUN_DEPS_DIR)/uws/uSockets/src/*.bc) uws: usockets $(CXX) -emit-llvm -fPIC -I$(BUN_DEPS_DIR)/uws/uSockets/src $(CLANG_FLAGS) $(CFLAGS) $(UWS_CXX_FLAGS) $(UWS_LDFLAGS) $(PLATFORM_LINKER_FLAGS) -c -I$(BUN_DEPS_DIR) $(BUN_DEPS_OUT_DIR)/libusockets.a $(BUN_DEPS_DIR)/libuwsockets.cpp -o $(BUN_DEPS_OUT_DIR)/libuwsockets.o diff --git a/src/deps/_libusockets.h b/src/deps/_libusockets.h index f6b8e2158..08410de0b 100644 --- a/src/deps/_libusockets.h +++ b/src/deps/_libusockets.h @@ -302,6 +302,8 @@ void uws_res_set_write_offset(int ssl, uws_res_t *res, size_t off); void us_socket_mark_needs_more_not_ssl(uws_res_t *res); bool uws_res_try_end(int ssl, uws_res_t *res, const char *bytes, size_t len, size_t total_len); + +void uws_res_prepare_for_sendfile(int ssl, uws_res_t *res); #ifdef __cplusplus } #endif diff --git a/src/deps/libuwsockets.cpp b/src/deps/libuwsockets.cpp index 7eba32d44..f854cafdb 100644 --- a/src/deps/libuwsockets.cpp +++ b/src/deps/libuwsockets.cpp @@ -793,11 +793,17 @@ void uws_res_write_header_int(int ssl, uws_res_t *res, const char *key, void uws_res_end_without_body(int ssl, uws_res_t *res) { if (ssl) { uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; - uwsRes->endWithoutBody(0); + uwsRes->getHttpResponseData()->state |= + uWS::HttpResponseData<true>::HTTP_END_CALLED; + uwsRes->markDone(uwsRes->getHttpResponseData()); + us_socket_timeout(true, (us_socket_t *)uwsRes, uWS::HTTP_TIMEOUT_S); } else { uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; - uwsRes->endWithoutBody(0); + uwsRes->getHttpResponseData()->state |= + uWS::HttpResponseData<false>::HTTP_END_CALLED; + uwsRes->markDone(uwsRes->getHttpResponseData()); + us_socket_timeout(false, (us_socket_t *)uwsRes, uWS::HTTP_TIMEOUT_S); } } @@ -1002,15 +1008,13 @@ void uws_res_write_headers(int ssl, uws_res_t *res, const StringPointer *names, } void uws_res_uncork(int ssl, uws_res_t *res) { - // if (ssl) { - // uWS::HttpResponse<true> *uwsRes = - // (uWS::HttpResponse<true> *)res; - // uwsRes->uncork(); - // } else { - // uWS::HttpResponse<false> *uwsRes = - // (uWS::HttpResponse<false> *)res; - // uwsRes->uncork(); - // } + if (ssl) { + uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; + uwsRes->uncork(); + } else { + uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; + uwsRes->uncork(); + } } void us_socket_mark_needs_more_not_ssl(uws_res_t *res) { @@ -1035,12 +1039,31 @@ void uws_res_cork(int ssl, uws_res_t *res, void *ctx, if (ssl) { uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; uwsRes->cork([ctx, corker]() { corker(ctx); }); + } else { uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; uwsRes->cork([ctx, corker]() { corker(ctx); }); } } +void uws_res_prepare_for_sendfile(int ssl, uws_res_t *res) { + if (ssl) { + uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; + auto pair = uwsRes->getSendBuffer(2); + char *ptr = pair.first; + ptr[0] = '\r'; + ptr[1] = '\n'; + uwsRes->uncork(); + } else { + uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; + auto pair = uwsRes->getSendBuffer(2); + char *ptr = pair.first; + ptr[0] = '\r'; + ptr[1] = '\n'; + uwsRes->uncork(); + } +} + bool uws_res_try_end(int ssl, uws_res_t *res, const char *bytes, size_t len, size_t total_len) { if (ssl) { diff --git a/src/deps/uws b/src/deps/uws -Subproject ec7fec7ae264000b86ac308b7844043acc9e309 +Subproject f0ea7574ab3c642bc1c0ee4991e771c1ee164f4 diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 6ec3e6855..101bac88e 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -443,6 +443,10 @@ pub fn NewApp(comptime ssl: bool) type { return uws_res_try_end(ssl_flag, res.downcast(), data.ptr, data.len, total); } + pub fn prepareForSendfile(res: *Response) void { + return uws_res_prepare_for_sendfile(ssl_flag, res.downcast()); + } + pub fn uncork(_: *Response) void { // uws_res_uncork( // ssl_flag, @@ -639,6 +643,7 @@ pub fn NewApp(comptime ssl: bool) type { }; }; } +extern fn uws_res_prepare_for_sendfile(ssl: c_int, res: *uws_res) void; extern fn uws_res_get_native_handle(ssl: c_int, res: *uws_res) *us_socket_t; extern fn uws_create_app(ssl: c_int, options: us_socket_context_options_t) *uws_app_t; extern fn uws_app_destroy(ssl: c_int, app: *uws_app_t) void; diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig index 2ebd30ee1..9f38045f0 100644 --- a/src/javascript/jsc/api/server.zig +++ b/src/javascript/jsc/api/server.zig @@ -89,7 +89,9 @@ const SendfileContext = struct { offset: Blob.SizeType = 0, has_listener: bool = false, has_set_on_writable: bool = false, + auto_close: bool = false, }; +const DateTime = @import("datetime"); const linux = std.os.linux; pub const ServerConfig = struct { @@ -769,7 +771,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.resp.setWriteOffset(this.sendfile.offset); this.resp.endWithoutBody(); // use node syscall so that we don't segfault on BADF - _ = JSC.Node.Syscall.close(this.sendfile.fd); + if (this.sendfile.auto_close) + _ = JSC.Node.Syscall.close(this.sendfile.fd); this.sendfile = undefined; this.finalize(); } @@ -802,7 +805,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.sendfile.remain -= @intCast(Blob.SizeType, this.sendfile.offset - start); if (errcode != .SUCCESS or this.aborted or this.sendfile.remain == 0 or val == 0) { - if (errcode != .SUCCESS) { + if (errcode != .AGAIN and errcode != .SUCCESS and errcode != .PIPE) { Output.prettyErrorln("Error: {s}", .{@tagName(errcode)}); Output.flush(); } @@ -826,7 +829,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.sendfile.offset += wrote; this.sendfile.remain -= wrote; if (errcode != .AGAIN or this.aborted or this.sendfile.remain == 0 or sbytes == 0) { - if (errcode != .AGAIN and errcode != .SUCCESS) { + if (errcode != .AGAIN and errcode != .SUCCESS and errcode != .PIPE) { Output.prettyErrorln("Error: {s}", .{@tagName(errcode)}); Output.flush(); } @@ -840,6 +843,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.resp.onWritable(*RequestContext, onWritableSendfile, this); } + this.setAbortHandler(); this.resp.markNeedsMore(); return true; @@ -870,64 +874,99 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return this.onSendfile(); } - pub fn onPrepareSendfileWrap(this: *anyopaque, fd: i32, size: Blob.SizeType, err: ?JSC.SystemError, globalThis: *JSGlobalObject) void { - onPrepareSendfile(bun.cast(*RequestContext, this), fd, size, err, globalThis); - } + // We tried open() in another thread for this + // it was not faster due to the mountain of syscalls + pub fn renderSendFile(this: *RequestContext, blob: JSC.WebCore.Blob) void { + this.blob = blob; + const file = &this.blob.store.?.data.file; + var file_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const auto_close = file.pathlike != .fd; + const fd = if (!auto_close) + file.pathlike.fd + else switch (JSC.Node.Syscall.open(file.pathlike.path.sliceZ(&file_buf), std.os.O.RDONLY | std.os.O.NONBLOCK | std.os.O.CLOEXEC, 0)) { + .result => |_fd| _fd, + .err => |err| return this.runErrorHandler(err.withPath(file.pathlike.path.slice()).toSystemError().toErrorInstance( + this.server.globalThis, + )), + }; - fn onPrepareSendfile(this: *RequestContext, fd: i32, size: Blob.SizeType, err: ?JSC.SystemError, globalThis: *JSGlobalObject) void { - if (err) |system_error| { - if (this.aborted) { - this.finalize(); + // stat only blocks if the target is a file descriptor + const stat: std.os.Stat = switch (JSC.Node.Syscall.fstat(fd)) { + .result => |result| result, + .err => |err| { + this.runErrorHandler(err.withPath(file.pathlike.path.slice()).toSystemError().toErrorInstance( + this.server.globalThis, + )); + if (auto_close) { + _ = JSC.Node.Syscall.close(fd); + } return; - } + }, + }; - if (system_error.errno == @enumToInt(std.os.E.NOENT)) { - this.runErrorHandlerWithStatusCode(system_error.toErrorInstance(globalThis), 404); - } else { - this.runErrorHandlerWithStatusCode(system_error.toErrorInstance(globalThis), 500); + if (Environment.isMac) { + if (!std.os.S.ISREG(stat.mode)) { + if (auto_close) { + _ = JSC.Node.Syscall.close(fd); + } + + var err = JSC.Node.Syscall.Error{ + .errno = @intCast(JSC.Node.Syscall.Error.Int, @enumToInt(std.os.E.INVAL)), + .path = file.pathlike.path.slice(), + .syscall = .sendfile, + }; + var sys = err.toSystemError(); + sys.message = ZigString.init("MacOS does not support sending non-regular files"); + this.runErrorHandler(sys.toErrorInstance( + this.server.globalThis, + )); + return; } + } - return; + if (Environment.isLinux) { + if (!(std.os.S.ISREG(stat.mode) or std.os.S.ISFIFO(stat.mode))) { + if (auto_close) { + _ = JSC.Node.Syscall.close(fd); + } + + var err = JSC.Node.Syscall.Error{ + .errno = @intCast(JSC.Node.Syscall.Error.Int, @enumToInt(std.os.E.INVAL)), + .path = file.pathlike.path.slice(), + .syscall = .sendfile, + }; + var sys = err.toSystemError(); + sys.message = ZigString.init("File must be regular or FIFO"); + this.runErrorHandler(sys.toErrorInstance( + this.server.globalThis, + )); + return; + } } - this.blob.size = size; + this.blob.size = @intCast(Blob.SizeType, stat.size); this.needs_content_length = true; this.sendfile = .{ .fd = fd, - .remain = size, + .remain = this.blob.size, + .auto_close = auto_close, .socket_fd = if (!this.aborted) this.resp.getNativeHandle() else -999, }; - if (this.aborted) { - _ = JSC.Node.Syscall.close(fd); - this.finalize(); - return; - } - - this.resp.runCorked(*RequestContext, renderMetadata, this); + this.resp.runCorked(*RequestContext, renderMetadataAndNewline, this); - if (size == 0) { + if (this.blob.size == 0) { this.cleanupAndFinalizeAfterSendfile(); return; } - this.setAbortHandler(); - - // TODO: fix this to be MSGHDR - _ = std.os.write(this.sendfile.socket_fd, "\r\n") catch 0; - _ = this.onSendfile(); } - pub fn renderSendFile(this: *RequestContext, blob: JSC.WebCore.Blob) void { - JSC.WebCore.Blob.doOpenAndStatFile( - &this.blob, - *RequestContext, - this, - onPrepareSendfileWrap, - blob.globalThis, - ); + pub fn renderMetadataAndNewline(this: *RequestContext) void { + this.renderMetadata(); + this.resp.prepareForSendfile(); } pub fn doSendfile(this: *RequestContext, blob: Blob) void { @@ -939,12 +978,12 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (this.has_sendfile_ctx) return; this.has_sendfile_ctx = true; - this.setAbortHandler(); if (comptime can_sendfile) { return this.renderSendFile(blob); } + this.setAbortHandler(); this.blob.doReadFileInternal(*RequestContext, this, onReadFile, this.server.globalThis); } @@ -996,7 +1035,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (this.blob.needsToReadFile()) { this.req.setYield(false); - this.setAbortHandler(); if (!this.has_sendfile_ctx) this.doSendfile(this.blob); return; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 890a74554..7ed624d8c 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -301,7 +301,6 @@ const AsyncTransformTask = @import("./api/transpiler.zig").TransformTask.AsyncTr const BunTimerTimeoutTask = Bun.Timer.Timeout.TimeoutTask; const ReadFileTask = WebCore.Blob.Store.ReadFile.ReadFileTask; const WriteFileTask = WebCore.Blob.Store.WriteFile.WriteFileTask; -const OpenAndStatFileTask = WebCore.Blob.Store.OpenAndStatFile.OpenAndStatFileTask; // const PromiseTask = JSInternalPromise.Completion.PromiseTask; pub const Task = TaggedPointerUnion(.{ FetchTasklet, @@ -309,7 +308,6 @@ pub const Task = TaggedPointerUnion(.{ AsyncTransformTask, BunTimerTimeoutTask, ReadFileTask, - OpenAndStatFileTask, CopyFilePromiseTask, WriteFileTask, AnyTask, @@ -594,13 +592,6 @@ pub const VirtualMachine = struct { finished += 1; vm_.active_tasks -|= 1; }, - @field(Task.Tag, @typeName(OpenAndStatFileTask)) => { - var transform_task: *OpenAndStatFileTask = task.get(OpenAndStatFileTask).?; - transform_task.*.runFromJS(); - transform_task.deinit(); - finished += 1; - vm_.active_tasks -|= 1; - }, @field(Task.Tag, @typeName(WriteFileTask)) => { var transform_task: *WriteFileTask = task.get(WriteFileTask).?; transform_task.*.runFromJS(); diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index b92d0882f..1d4703bc1 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -1683,129 +1683,6 @@ pub const Blob = struct { }; } - pub const OpenAndStatFile = struct { - const OpenFrameType = if (Environment.isMac) - void - else - @Frame(OpenAndStatFile.getFdLinux); - - open_frame: OpenFrameType = undefined, - errno: ?anyerror = null, - system_error: ?JSC.SystemError = null, - open_completion: HTTPClient.NetworkThread.Completion = undefined, - opened_fd: JSC.Node.FileDescriptor = null_fd, - size: SizeType = 0, - - store: *Store = undefined, - file_store: FileStore, - - onCompleteCtx: *anyopaque = undefined, - onCompleteCallback: OnCompleteCallback = undefined, - - task: HTTPClient.NetworkThread.Task = undefined, - - pub const OnCompleteCallback = fn ( - ctx: *anyopaque, - fd: JSC.Node.FileDescriptor, - size: SizeType, - system_error: ?SystemError, - global: *JSGlobalObject, - ) void; - - pub usingnamespace FileOpenerMixin(OpenAndStatFile); - pub usingnamespace FileCloserMixin(OpenAndStatFile); - - pub fn createWithCtx( - allocator: std.mem.Allocator, - store: *Store, - ctx: *anyopaque, - onCompleteCallback: OnCompleteCallback, - ) !*OpenAndStatFile { - var read_file = try allocator.create(OpenAndStatFile); - read_file.* = OpenAndStatFile{ - .file_store = store.data.file, - - .store = store, - .onCompleteCtx = ctx, - .onCompleteCallback = onCompleteCallback, - }; - store.ref(); - return read_file; - } - - pub const OpenAndStatFileTask = JSC.IOTask(@This()); - - pub fn run(this: *OpenAndStatFile, task: *OpenAndStatFileTask) void { - var frame = HTTPClient.getAllocator().create(@Frame(runAsync)) catch unreachable; - _ = @asyncCall(std.mem.asBytes(frame), undefined, runAsync, .{ this, task }); - } - - fn runAsync(this: *OpenAndStatFile, task: *OpenAndStatFileTask) void { - this._runAsync(); - task.onFinish(); - suspend { - HTTPClient.getAllocator().destroy(@frame()); - } - } - - pub fn then(this: *OpenAndStatFile, globalThis: *JSC.JSGlobalObject) void { - var cb = this.onCompleteCallback; - var cb_ctx = this.onCompleteCtx; - const fd = this.opened_fd; - const _size = this.size; - const system_error_ = this.system_error; - var store = this.store; - - if (system_error_) |err| { - cb(cb_ctx, -1, 0, err, globalThis); - } else { - cb(cb_ctx, fd, _size, null, globalThis); - } - store.deref(); - bun.default_allocator.destroy(this); - } - - fn _runAsync(this: *OpenAndStatFile) void { - this.opened_fd = null_fd; - if (this.file_store.pathlike == .fd) { - this.opened_fd = this.file_store.pathlike.fd; - } - const fd = if (this.opened_fd == null_fd) - this.getFd() catch return - else - this.opened_fd; - - this.doWithFd(fd); - } - - pub fn doWithFd(this: *OpenAndStatFile, fd: JSC.Node.FileDescriptor) void { - const stat: std.os.Stat = switch (JSC.Node.Syscall.fstat(fd)) { - .result => |result| result, - .err => |err| { - this.errno = AsyncIO.asError(err.errno); - this.system_error = err.toSystemError(); - return; - }, - }; - - if (Environment.isMac) { - if (!std.os.S.ISREG(stat.mode)) { - this.errno = error.ENOTSUP; - return; - } - } - - if (Environment.isLinux) { - if (!(std.os.S.ISREG(stat.mode) or std.os.S.ISFIFO(stat.mode))) { - this.errno = error.ENOTSUP; - return; - } - } - - this.size = @truncate(SizeType, @intCast(u64, @maximum(@intCast(i64, stat.size), 0))); - } - }; - pub const ReadFile = struct { const OpenFrameType = if (Environment.isMac) void @@ -3308,17 +3185,6 @@ pub const Blob = struct { read_file_task.schedule(); } - pub fn doOpenAndStatFile(this: *Blob, comptime Handler: type, ctx: Handler, comptime Function: anytype, global: *JSGlobalObject) void { - var file_read = Store.OpenAndStatFile.createWithCtx( - bun.default_allocator, - this.store.?, - ctx, - Function, - ) catch unreachable; - var read_file_task = Store.OpenAndStatFile.OpenAndStatFileTask.createOnJSThread(bun.default_allocator, global, file_read) catch unreachable; - read_file_task.schedule(); - } - pub fn doReadFile(this: *Blob, comptime Function: anytype, comptime lifetime: Lifetime, global: *JSGlobalObject) JSValue { const Handler = NewReadFileHandler(Function, lifetime); var promise = JSPromise.create(global); |