diff --git a/src/js/node/_http_server.ts b/src/js/node/_http_server.ts index 282248b5675b..ccf3e3d214b2 100644 --- a/src/js/node/_http_server.ts +++ b/src/js/node/_http_server.ts @@ -1,7 +1,7 @@ // Hardcoded module "node:_http_server" const EventEmitter: typeof import("node:events").EventEmitter = require("node:events"); const { Stream } = require("node:stream"); -const { Socket: NetSocket } = require("node:net"); +const { Socket: NetSocket, Server: NetServer } = require("node:net"); const { _checkInvalidHeaderChar: checkInvalidHeaderChar, chunkExpression, @@ -276,7 +276,7 @@ function emitRequestCloseNT(self) { } function emitListeningNextTick(self, hostname, port) { - if ((self.listening = !!self[serverSymbol])) { + if (self[serverSymbol]) { // TODO: remove the arguments // Note does not pass any arguments. self.emit("listening", null, hostname, port); @@ -301,7 +301,6 @@ function Server(options, callback): void { EventEmitter.$call(this); this.on("listening", setupConnectionsTracking); - this.listening = false; this._unref = false; this.timeout = 0; this.maxRequestsPerSocket = 0; @@ -406,7 +405,18 @@ function Server(options, callback): void { if (callback) this.on("request", callback); return this; } -$toClass(Server, "Server", EventEmitter); +$toClass(Server, "Server", NetServer); + +// http.Server is backed by Bun.serve (held in [serverSymbol]); net.Server's +// `listening` getter keys off `_handle`, which http.Server never populates. +Object.defineProperty(Server.prototype, "listening", { + __proto__: null, + get() { + return !!this[serverSymbol]; + }, + configurable: true, + enumerable: true, +}); Server.prototype[kIncomingMessage] = undefined; @@ -478,7 +488,6 @@ Server.prototype.closeAllConnections = function () { } this[serverSymbol] = undefined; clearInterval(this[kConnectionsCheckingInterval]); - this.listening = false; server.stop(true); }; @@ -510,7 +519,6 @@ Server.prototype.close = function (optionalCallback?) { } this[serverSymbol] = undefined; if (typeof optionalCallback === "function") setCloseCallback(this, optionalCallback); - this.listening = false; server.closeIdleConnections(); server.stop(); return this; diff --git a/test/js/node/http/node-http.test.ts b/test/js/node/http/node-http.test.ts index a204e6d37259..1cc82940fbd5 100644 --- a/test/js/node/http/node-http.test.ts +++ b/test/js/node/http/node-http.test.ts @@ -24,7 +24,7 @@ import http, { } from "node:http"; import https, { createServer as createHttpsServer } from "node:https"; import type { AddressInfo } from "node:net"; -import { connect, createServer as createNetServer } from "node:net"; +import net, { connect, createServer as createNetServer } from "node:net"; import { tmpdir } from "node:os"; import * as path from "node:path"; import { PassThrough, Writable } from "node:stream"; @@ -145,6 +145,29 @@ describe("node:http", () => { listenResponse.close(); }); + // https://github.com/oven-sh/bun/issues/4360 + it("http.Server inherits from net.Server", async () => { + expect(http.Server.prototype instanceof net.Server).toBe(true); + expect(net.Server.prototype instanceof http.Server).toBe(false); + expect(Object.getPrototypeOf(http.Server.prototype)).toBe(net.Server.prototype); + expect(Object.getPrototypeOf(http.Server)).toBe(net.Server); + + const server = createServer(); + expect(server instanceof http.Server).toBe(true); + expect(server instanceof net.Server).toBe(true); + expect(server instanceof EventEmitter).toBe(true); + + expect(server.listening).toBe(false); + server.listen(0); + try { + await once(server, "listening"); + expect(server.listening).toBe(true); + } finally { + server.close(); + } + expect(server.listening).toBe(false); + }); + it("listen callback should be bound to server", async () => { const server = createServer(); const { resolve, reject, promise } = Promise.withResolvers();