diff options
-rw-r--r-- | src/bun.js/api/server.zig | 6 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 236 | ||||
-rw-r--r-- | src/bun.js/webcore/streams.zig | 21 | ||||
-rw-r--r-- | src/bun_js.zig | 2 | ||||
m--------- | src/deps/uws | 0 | ||||
-rw-r--r-- | src/deps/uws.zig | 9 | ||||
-rw-r--r-- | src/io/io_darwin.zig | 5 | ||||
-rw-r--r-- | src/io/io_linux.zig | 8 | ||||
-rw-r--r-- | test/apps/bun-install.sh | 5 | ||||
-rw-r--r-- | test/bun.js/ffi.test.fixture.callback.c | 35 | ||||
-rw-r--r-- | test/bun.js/ffi.test.fixture.receiver.c | 35 | ||||
-rw-r--r-- | test/bun.js/serve.test.ts | 56 |
12 files changed, 230 insertions, 188 deletions
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 12b383f05..2410b5e7c 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -2148,14 +2148,8 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { } this.listener = socket; - const needs_post_handler = this.vm.uws_event_loop == null; this.vm.uws_event_loop = uws.Loop.get(); this.ref(); - - if (needs_post_handler) { - _ = this.vm.uws_event_loop.?.addPostHandler(*JSC.EventLoop, this.vm.eventLoop(), JSC.EventLoop.tick); - _ = this.vm.uws_event_loop.?.addPreHandler(*JSC.EventLoop, this.vm.eventLoop(), JSC.EventLoop.tick); - } } pub fn ref(this: *ThisServer) void { diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index 1907838d0..d4688beac 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -23,6 +23,7 @@ const js = JSC.C; pub const WorkPool = @import("../work_pool.zig").WorkPool; pub const WorkPoolTask = @import("../work_pool.zig").Task; const NetworkThread = @import("http").NetworkThread; +const uws = @import("uws"); pub fn ConcurrentPromiseTask(comptime Context: type) type { return struct { @@ -325,7 +326,6 @@ pub const EventLoop = struct { // TODO: fix this technical debt pub fn tick(this: *EventLoop) void { - var poller = &this.virtual_machine.poller; var ctx = this.virtual_machine; this.tickConcurrent(); var global_vm = ctx.global.vm(); @@ -338,11 +338,13 @@ pub const EventLoop = struct { this.tickConcurrent(); if (this.tasks.count > 0) continue; } + break; + } - this.global.vm().doWork(); - poller.tick(); + this.global.vm().doWork(); - break; + while (this.tickWithCount() > 0) { + this.tickConcurrent(); } this.global.handleRejectedPromises(); @@ -354,7 +356,7 @@ pub const EventLoop = struct { ctx.global.vm().releaseWeakRefs(); ctx.global.vm().drainMicrotasks(); - if (ctx.us_loop_reference_count > 0 and !ctx.is_us_loop_entered and (ctx.uws_event_loop.?.num_polls > 0 or this.start_server_on_next_tick)) { + if (ctx.poller.loop != null and ctx.poller.loop.?.active > 0 or (ctx.us_loop_reference_count > 0 and !ctx.is_us_loop_entered and (ctx.uws_event_loop.?.num_polls > 0 or this.start_server_on_next_tick))) { if (this.tickConcurrentWithCount() > 0) { this.tick(); } else { @@ -380,8 +382,6 @@ pub const EventLoop = struct { if (promise.status(this.global.vm()) == .Pending) { if (this.virtual_machine.uws_event_loop != null) { this.runUSocketsLoop(); - } else if (this.waker) |*waker| { - _ = waker.wait() catch 0; } } } @@ -411,8 +411,11 @@ pub const EventLoop = struct { pub fn ensureWaker(this: *EventLoop) void { JSC.markBinding(); - if (this.waker == null) { - this.waker = AsyncIO.Waker.init(this.virtual_machine.allocator) catch unreachable; + if (this.virtual_machine.uws_event_loop == null) { + var actual = uws.Loop.get().?; + this.virtual_machine.uws_event_loop = actual; + _ = actual.addPostHandler(*JSC.EventLoop, this, JSC.EventLoop.tick); + _ = actual.addPreHandler(*JSC.VM, this.virtual_machine.global.vm(), JSC.VM.drainMicrotasks); } } @@ -432,113 +435,143 @@ pub const EventLoop = struct { loop.nextTick(*EventLoop, this, onDefer); } } - - if (this.waker) |*waker| { - waker.wake() catch unreachable; - } } }; pub const Poller = struct { /// kqueue() or epoll() /// 0 == unset - watch_fd: i32 = 0, - active: u32 = 0, - - pub const PlatformSpecificFlags = struct {}; + loop: ?*uws.Loop = null, - const Completion = fn (ctx: ?*anyopaque, sizeOrOffset: i64, flags: u16) void; - const kevent64 = std.os.system.kevent64_s; - pub fn dispatchKQueueEvent(kqueue_event: *const kevent64) void { + pub fn dispatchKQueueEvent(loop: *uws.Loop, kqueue_event: *const std.os.Kevent) void { if (comptime !Environment.isMac) { unreachable; } + var ptr = Pollable.from(@intToPtr(?*anyopaque, kqueue_event.udata)); - const ptr = @intToPtr(?*anyopaque, kqueue_event.udata); - const callback: Completion = @intToPtr(Completion, kqueue_event.ext[0]); - callback(ptr, @bitCast(i64, kqueue_event.data), kqueue_event.flags); + switch (ptr.tag()) { + @field(Pollable.Tag, "FileBlobLoader") => { + var loader = ptr.as(FileBlobLoader); + loop.active -= 1; + loader.onPoll(@bitCast(i64, kqueue_event.data), kqueue_event.flags); + }, + else => unreachable, + } + } + + fn dispatchEpollEvent(loop: *uws.Loop, epoll_event: *linux.epoll_event) void { + var ptr = Pollable.from(@intToPtr(?*anyopaque, epoll_event.data.ptr)); + switch (ptr.tag()) { + @field(Pollable.Tag, "FileBlobLoader") => { + var loader = ptr.as(FileBlobLoader); + loop.active -= 1; + loader.onPoll(0, 0); + }, + else => unreachable, + } } const timeout = std.mem.zeroes(std.os.timespec); + const linux = std.os.linux; + + const FileBlobLoader = JSC.WebCore.FileBlobLoader; + + /// epoll only allows one pointer + /// We unfortunately need two pointers: one for a function call and one for the context + /// We use a tagged pointer union and then call the function with the context pointer + pub const Pollable = TaggedPointerUnion(.{ + FileBlobLoader, + AsyncIO.Waker, + }); + const Kevent = std.os.Kevent; + const kevent = std.c.kevent; + + pub fn watch(this: *Poller, fd: JSC.Node.FileDescriptor, flag: Flag, comptime ContextType: type, ctx: *ContextType) JSC.Maybe(void) { + if (this.loop == null) { + this.loop = uws.Loop.get(); + JSC.VirtualMachine.vm.uws_event_loop = this.loop.?; + } + const watcher_fd = this.loop.?.fd; - pub fn watch(this: *Poller, fd: JSC.Node.FileDescriptor, flag: Flag, ctx: ?*anyopaque, completion: Completion) JSC.Maybe(void) { if (comptime Environment.isLinux) { - // std.debug.assert(this.watch_fd != 0); - // TODO: - return JSC.Maybe(void).success; - } else if (comptime Environment.isMac) { - if (this.watch_fd == 0) { - this.watch_fd = std.c.kqueue(); - if (this.watch_fd == -1) { - defer this.watch_fd = 0; - return JSC.Maybe(void).errnoSys(this.watch_fd, .kqueue).?; - } + const flags: u32 = switch (flag) { + .read => linux.EPOLL.IN | linux.EPOLL.HUP | linux.EPOLL.ONESHOT, + .write => linux.EPOLL.OUT | linux.EPOLL.HUP | linux.EPOLL.ERR | linux.EPOLL.ONESHOT, + }; + + var event = linux.epoll_event{ .events = flags, .data = .{ .u64 = @ptrToInt(Pollable.init(ctx).ptr()) } }; + + const ctl = linux.epoll_ctl( + watcher_fd, + linux.EPOLL.CTL_ADD, + fd, + &event, + ); + + if (JSC.Maybe(void).errnoSys(ctl, .epoll_ctl)) |errno| { + return errno; } - var events_list = std.mem.zeroes([2]kevent64); - events_list[0] = switch (flag) { + this.loop.?.num_polls += 1; + this.loop.?.active += 1; + } else if (comptime Environment.isMac) { + var changelist = std.mem.zeroes([2]std.os.system.kevent64_s); + changelist[0] = switch (flag) { .read => .{ .ident = @intCast(u64, fd), .filter = std.os.system.EVFILT_READ, .data = 0, .fflags = 0, - .udata = @ptrToInt(ctx), + .udata = @ptrToInt(Pollable.init(ctx).ptr()), .flags = std.c.EV_ADD | std.c.EV_ENABLE | std.c.EV_ONESHOT, - .ext = .{ @ptrToInt(completion), 0 }, + .ext = .{ 0, 0 }, }, .write => .{ .ident = @intCast(u64, fd), .filter = std.os.system.EVFILT_WRITE, .data = 0, .fflags = 0, - .udata = @ptrToInt(ctx), + .udata = @ptrToInt(Pollable.init(ctx).ptr()), .flags = std.c.EV_ADD | std.c.EV_ENABLE | std.c.EV_ONESHOT, - .ext = .{ @ptrToInt(completion), 0 }, + .ext = .{ 0, 0 }, }, }; + // output events only include change errors + const KEVENT_FLAG_ERROR_EVENTS = 0x000002; + // The kevent() system call returns the number of events placed in // the eventlist, up to the value given by nevents. If the time // limit expires, then kevent() returns 0. const rc = std.os.system.kevent64( - this.watch_fd, - &events_list, + watcher_fd, + &changelist, 1, // The same array may be used for the changelist and eventlist. - &events_list, + &changelist, 1, - 0, + KEVENT_FLAG_ERROR_EVENTS, &timeout, ); - // If an error occurs while // processing an element of the changelist and there is enough room // in the eventlist, then the event will be placed in the eventlist // with EV_ERROR set in flags and the system error in data. - if (events_list[0].flags == std.c.EV_ERROR) { - return JSC.Maybe(void).errnoSys(events_list[0].data, .kevent).?; + if (changelist[0].flags == std.c.EV_ERROR) { + return JSC.Maybe(void).errnoSys(changelist[0].data, .kevent).?; // Otherwise, -1 will be returned, and errno will be set to // indicate the error condition. } - switch (rc) { - std.math.minInt(@TypeOf(rc))...-1 => return JSC.Maybe(void).errnoSys(@enumToInt(std.c.getErrno(rc)), .kevent).?, - 0 => { - this.active += 1; - return JSC.Maybe(void).success; - }, - 1 => { - // if we immediately get an event, we can skip the reference counting - dispatchKQueueEvent(&events_list[0]); - return JSC.Maybe(void).success; - }, - 2 => { - dispatchKQueueEvent(&events_list[0]); + const errno = std.c.getErrno(rc); + if (errno == .SUCCESS) { + this.loop.?.num_polls += 1; + this.loop.?.active += 1; + return JSC.Maybe(void).success; + } - this.active -= 1; - dispatchKQueueEvent(&events_list[1]); - return JSC.Maybe(void).success; - }, + switch (rc) { + std.math.minInt(@TypeOf(rc))...-1 => return JSC.Maybe(void).errnoSys(@enumToInt(errno), .kevent).?, else => unreachable, } } else { @@ -546,67 +579,26 @@ pub const Poller = struct { } } - const kqueue_events_ = std.mem.zeroes([4]kevent64); pub fn tick(this: *Poller) void { - if (comptime Environment.isMac) { - if (this.active == 0) return; - - var events_list = kqueue_events_; - // ub extern "c" fn kevent64( - // kq: c_int, - // changelist: [*]const kevent64_s, - // nchanges: c_int, - // eventlist: [*]kevent64_s, - // nevents: c_int, - // flags: c_uint, - // timeout: ?*const timespec, - // ) c_int; - const rc = std.os.system.kevent64( - this.watch_fd, - &events_list, - 0, - // The same array may be used for the changelist and eventlist. - &events_list, - 4, - 0, - &timeout, - ); + var loop = this.loop orelse return; + if (loop.active == 0) return; + loop.tick(); + } - switch (rc) { - std.math.minInt(@TypeOf(rc))...-1 => { - // EINTR is fine - switch (std.c.getErrno(rc)) { - .INTR => return, - else => |errno| std.debug.panic("kevent64() failed: {d}", .{errno}), - } - }, - 0 => {}, - 1 => { - this.active -= 1; - dispatchKQueueEvent(&events_list[0]); - }, - 2 => { - this.active -= 2; - dispatchKQueueEvent(&events_list[0]); - dispatchKQueueEvent(&events_list[1]); - }, - 3 => { - this.active -= 3; - dispatchKQueueEvent(&events_list[0]); - dispatchKQueueEvent(&events_list[1]); - dispatchKQueueEvent(&events_list[2]); - }, - 4 => { - this.active -= 4; - dispatchKQueueEvent(&events_list[0]); - dispatchKQueueEvent(&events_list[1]); - dispatchKQueueEvent(&events_list[2]); - dispatchKQueueEvent(&events_list[3]); - }, - else => unreachable, - } - } + pub fn onTick(loop: *uws.Loop, tagged_pointer: ?*anyopaque) callconv(.C) void { + _ = loop; + _ = tagged_pointer; + if (comptime Environment.isMac) + dispatchKQueueEvent(loop, &loop.ready_polls[@intCast(usize, loop.current_ready_poll)]) + else if (comptime Environment.isLinux) + dispatchEpollEvent(loop, &loop.ready_polls[@intCast(usize, loop.current_ready_poll)]); } pub const Flag = enum { read, write }; + + comptime { + if (!JSC.is_bindgen) { + @export(onTick, .{ .name = "Bun__internal_dispatch_ready_poll" }); + } + } }; diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 1158a8dd2..b7af9a8df 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -2328,9 +2328,13 @@ pub const FileBlobLoader = struct { }; } - pub fn watch(this: *FileReader) void { - _ = JSC.VirtualMachine.vm.poller.watch(this.fd, .read, this, callback); + pub fn watch(this: *FileReader) ?JSC.Node.Syscall.Error { + switch (JSC.VirtualMachine.vm.poller.watch(this.fd, .read, FileBlobLoader, this)) { + .err => |err| return err, + else => {}, + } this.scheduled_count += 1; + return null; } const Concurrent = struct { @@ -2658,7 +2662,8 @@ pub const FileBlobLoader = struct { // this handles: // - empty file // - stream closed for some reason - if ((result == 0 and remaining == 0)) { + // - FIFO returned EOF + if ((result == 0 and (remaining == 0 or std.os.S.ISFIFO(this.mode)))) { this.finalize(); return .{ .done = {} }; } @@ -2692,7 +2697,11 @@ pub const FileBlobLoader = struct { this.protected_view = view; this.protected_view.protect(); this.buf = read_buf; - this.watch(); + if (this.watch()) |watch_fail| { + this.finalize(); + return .{ .err = watch_fail }; + } + return .{ .pending = &this.pending, }; @@ -2713,8 +2722,8 @@ pub const FileBlobLoader = struct { } } - pub fn callback(task: ?*anyopaque, sizeOrOffset: i64, _: u16) void { - var this: *FileReader = bun.cast(*FileReader, task.?); + /// Called from Poller + pub fn onPoll(this: *FileBlobLoader, sizeOrOffset: i64, _: u16) void { std.debug.assert(this.started); this.scheduled_count -= 1; const protected_view = this.protected_view; diff --git a/src/bun_js.zig b/src/bun_js.zig index 118cf00f9..679576821 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -153,7 +153,7 @@ pub const Run = struct { this.vm.active_tasks > 0) { this.vm.event_loop.ensureWaker(); - _ = this.vm.event_loop.waker.?.wait() catch 0; + _ = this.vm.uws_event_loop.?.run(); } } } diff --git a/src/deps/uws b/src/deps/uws -Subproject 1b46cf9ace5f55f713d55bb8c086f5878d13245 +Subproject d8ea7819243dba0d0201807a65556bb6db55baf diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 353db4da9..b26020714 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -279,7 +279,10 @@ pub const Loop = extern struct { current_ready_poll: c_int, /// Loop's own file descriptor - fd: c_int, + fd: i32, + + /// Number of polls owned by Bun + active: u32 = 0, /// The list of ready polls ready_polls: [1024]EventType, @@ -323,7 +326,7 @@ pub const Loop = extern struct { } pub fn tick(this: *Loop) void { - us_loop_tick(this); + us_loop_run_bun_tick(this); } pub fn nextTick(this: *Loop, comptime UserType: type, user_data: UserType, comptime deferCallback: fn (ctx: UserType) void) void { @@ -376,7 +379,7 @@ pub const Loop = extern struct { extern fn us_loop_free(loop: ?*Loop) void; extern fn us_loop_ext(loop: ?*Loop) ?*anyopaque; extern fn us_loop_run(loop: ?*Loop) void; - extern fn us_loop_tick(loop: ?*Loop) void; + extern fn us_loop_run_bun_tick(loop: ?*Loop) void; extern fn us_wakeup_loop(loop: ?*Loop) void; extern fn us_loop_integrate(loop: ?*Loop) void; extern fn us_loop_iteration_number(loop: ?*Loop) c_longlong; diff --git a/src/io/io_darwin.zig b/src/io/io_darwin.zig index 78bf30290..f40ba60c7 100644 --- a/src/io/io_darwin.zig +++ b/src/io/io_darwin.zig @@ -545,7 +545,10 @@ pub const Waker = struct { ) bool; pub fn init(allocator: std.mem.Allocator) !Waker { - const kq = try os.kqueue(); + return initWithFileDescriptor(allocator, try std.os.kqueue()); + } + + pub fn initWithFileDescriptor(allocator: std.mem.Allocator, kq: i32) !Waker { assert(kq > -1); var machport_buf = try allocator.alloc(u8, 1024); const machport = io_darwin_create_machport( diff --git a/src/io/io_linux.zig b/src/io/io_linux.zig index d3af77d7c..00cc0c03e 100644 --- a/src/io/io_linux.zig +++ b/src/io/io_linux.zig @@ -987,9 +987,13 @@ pub const Completion = struct { pub const Waker = struct { fd: os.fd_t, - pub fn init(_: std.mem.Allocator) !Waker { + pub fn init(allocator: std.mem.Allocator) !Waker { + return try initWithFileDescriptor(allocator, try std.os.eventfd(0, 0)); + } + + pub fn initWithFileDescriptor(_: std.mem.Allocator, fd: os.fd_t) Waker { return Waker{ - .fd = try os.eventfd(0, 0), + .fd = fd, }; } diff --git a/test/apps/bun-install.sh b/test/apps/bun-install.sh index af13f6fc3..78fcc2837 100644 --- a/test/apps/bun-install.sh +++ b/test/apps/bun-install.sh @@ -26,9 +26,10 @@ chmod +x index.js JS_RUNTIME=${JS_RUNTIME:-"$(which bun)"} # If this fails to run, it means we didn't link @babel/parser correctly -realpath -e ./node_modules/.bin/parser +$(which grealpath || which realpath) -e ./node_modules/.bin/parser >/dev/null -# If this fails to run, it means we didn't link esbuild correctly +# If this fails to run, it means we didn't link esbuild correctly or esbuild's install script broke +# - https://github.com/evanw/esbuild/issues/2558 ./node_modules/.bin/esbuild --version >/dev/null if [ "$JS_RUNTIME" == "node" ]; then diff --git a/test/bun.js/ffi.test.fixture.callback.c b/test/bun.js/ffi.test.fixture.callback.c index 7de63120f..cb38eebe8 100644 --- a/test/bun.js/ffi.test.fixture.callback.c +++ b/test/bun.js/ffi.test.fixture.callback.c @@ -147,23 +147,39 @@ static bool JSVALUE_IS_NUMBER(EncodedJSValue val) { } +// JSValue numbers-as-pointers are represented as a 52-bit integer +// Previously, the pointer was stored at the end of the 64-bit value +// Now, they're stored at the beginning of the 64-bit value +// This behavior change enables the JIT to handle it better +// It also is better readability when console.log(myPtr) static void* JSVALUE_TO_PTR(EncodedJSValue val) { - // must be a double if (val.asInt64 == TagValueNull) return 0; - return (void*)(val.asInt64 - DoubleEncodeOffset); + val.asInt64 -= DoubleEncodeOffset; + size_t ptr = (size_t)val.asDouble; + return (void*)ptr; } static EncodedJSValue PTR_TO_JSVALUE(void* ptr) { EncodedJSValue val; - if (ptr == 0) { - val.asInt64 = TagValueNull; - return val; + if (ptr == 0) + { + val.asInt64 = TagValueNull; + return val; } - val.asInt64 = (int64_t)ptr + DoubleEncodeOffset; + + val.asDouble = (double)(size_t)ptr; + val.asInt64 += DoubleEncodeOffset; return val; } +static EncodedJSValue DOUBLE_TO_JSVALUE(double val) { + EncodedJSValue res; + res.asDouble = val; + res.asInt64 += DoubleEncodeOffset; + return res; +} + static int32_t JSVALUE_TO_INT32(EncodedJSValue val) { return val.asInt64; } @@ -175,12 +191,7 @@ static EncodedJSValue INT32_TO_JSVALUE(int32_t val) { } -static EncodedJSValue DOUBLE_TO_JSVALUE(double val) { - EncodedJSValue res; - res.asDouble = val; - res.asInt64 += DoubleEncodeOffset; - return res; -} + static EncodedJSValue FLOAT_TO_JSVALUE(float val) { return DOUBLE_TO_JSVALUE((double)val); diff --git a/test/bun.js/ffi.test.fixture.receiver.c b/test/bun.js/ffi.test.fixture.receiver.c index 550d64b81..f8417805d 100644 --- a/test/bun.js/ffi.test.fixture.receiver.c +++ b/test/bun.js/ffi.test.fixture.receiver.c @@ -148,23 +148,39 @@ static bool JSVALUE_IS_NUMBER(EncodedJSValue val) { } +// JSValue numbers-as-pointers are represented as a 52-bit integer +// Previously, the pointer was stored at the end of the 64-bit value +// Now, they're stored at the beginning of the 64-bit value +// This behavior change enables the JIT to handle it better +// It also is better readability when console.log(myPtr) static void* JSVALUE_TO_PTR(EncodedJSValue val) { - // must be a double if (val.asInt64 == TagValueNull) return 0; - return (void*)(val.asInt64 - DoubleEncodeOffset); + val.asInt64 -= DoubleEncodeOffset; + size_t ptr = (size_t)val.asDouble; + return (void*)ptr; } static EncodedJSValue PTR_TO_JSVALUE(void* ptr) { EncodedJSValue val; - if (ptr == 0) { - val.asInt64 = TagValueNull; - return val; + if (ptr == 0) + { + val.asInt64 = TagValueNull; + return val; } - val.asInt64 = (int64_t)ptr + DoubleEncodeOffset; + + val.asDouble = (double)(size_t)ptr; + val.asInt64 += DoubleEncodeOffset; return val; } +static EncodedJSValue DOUBLE_TO_JSVALUE(double val) { + EncodedJSValue res; + res.asDouble = val; + res.asInt64 += DoubleEncodeOffset; + return res; +} + static int32_t JSVALUE_TO_INT32(EncodedJSValue val) { return val.asInt64; } @@ -176,12 +192,7 @@ static EncodedJSValue INT32_TO_JSVALUE(int32_t val) { } -static EncodedJSValue DOUBLE_TO_JSVALUE(double val) { - EncodedJSValue res; - res.asDouble = val; - res.asInt64 += DoubleEncodeOffset; - return res; -} + static EncodedJSValue FLOAT_TO_JSVALUE(float val) { return DOUBLE_TO_JSVALUE((double)val); diff --git a/test/bun.js/serve.test.ts b/test/bun.js/serve.test.ts index 95428c028..317d94100 100644 --- a/test/bun.js/serve.test.ts +++ b/test/bun.js/serve.test.ts @@ -39,7 +39,9 @@ describe("streaming", () => { }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch( + `http://${server.hostname}:${server.port}` + ); if (response.status > 0) { expect(response.status).toBe(500); expect(await response.text()).toBe("fail"); @@ -77,7 +79,9 @@ describe("streaming", () => { }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch( + `http://${server.hostname}:${server.port}` + ); // connection terminated if (response.status > 0) { expect(response.status).toBe(200); @@ -110,7 +114,7 @@ describe("streaming", () => { }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); const text = await response.text(); expect(text.length).toBe(textToExpect.length); expect(text).toBe(textToExpect); @@ -134,7 +138,7 @@ describe("streaming", () => { ); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -157,8 +161,18 @@ describe("streaming", () => { }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); - expect(response.status).toBe(500); + var response; + try { + response = await fetch(`http://${server.hostname}:${server.port}`); + } catch (e) { + if (e.name !== "ConnectionClosed") { + throw e; + } + } + + if (response) { + expect(response.status).toBe(500); + } } catch (e) { if (!e || !(e instanceof TestPass)) { throw e; @@ -191,7 +205,7 @@ describe("streaming", () => { }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(response.status).toBe(500); expect(await response.text()).toBe("Fail"); expect(pass).toBe(true); @@ -222,7 +236,7 @@ describe("streaming", () => { ); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -244,7 +258,7 @@ describe("streaming", () => { ); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); const text = await response.text(); expect(text).toBe(textToExpect); server.stop(); @@ -270,7 +284,7 @@ describe("streaming", () => { ); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -295,7 +309,7 @@ describe("streaming", () => { ); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -324,7 +338,7 @@ describe("streaming", () => { ); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -337,7 +351,7 @@ it("should work for a hello world", async () => { return new Response(`Hello, world!`); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe("Hello, world!"); server.stop(); }); @@ -352,7 +366,7 @@ it("should work for a blob", async () => { return new Response(new Blob([textToExpect])); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -367,7 +381,7 @@ it("should work for a blob stream", async () => { return new Response(new Blob([textToExpect]).stream()); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -382,7 +396,7 @@ it("should work for a file", async () => { return new Response(file(fixture)); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -397,7 +411,7 @@ it("should work for a file stream", async () => { return new Response(file(fixture).stream()); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); server.stop(); }); @@ -416,7 +430,7 @@ it("fetch should work with headers", async () => { }); }, }); - const response = await fetch(`http://127.0.0.1:${server.port}`, { + const response = await fetch(`http://${server.hostname}:${server.port}`, { headers: { "X-Foo": "bar", }, @@ -444,7 +458,7 @@ it(`should work for a file ${count} times serial`, async () => { // it's hard to say if this only happens here due to some weird stuff with the test runner // or if it's "real" issue for (let i = 0; i < count; i++) { - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); } @@ -467,7 +481,7 @@ it(`should work for text ${count} times serial`, async () => { // it's hard to say if this only happens here due to some weird stuff with the test runner // or if it's "real" issue for (let i = 0; i < count; i++) { - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); } @@ -488,7 +502,7 @@ it(`should work for ArrayBuffer ${count} times serial`, async () => { // it's hard to say if this only happens here due to some weird stuff with the test runner // or if it's "real" issue for (let i = 0; i < count; i++) { - const response = await fetch(`http://127.0.0.1:${server.port}`); + const response = await fetch(`http://${server.hostname}:${server.port}`); expect(await response.text()).toBe(textToExpect); } |