// Hardcoded module "node:_http_server" const EventEmitter: typeof import("node:events").EventEmitter = require("node:events"); const { Duplex, Stream } = require("node:stream"); const { _checkInvalidHeaderChar: checkInvalidHeaderChar } = require("node:_http_common"); const { validateObject, validateLinkHeaderValue, validateBoolean, validateInteger } = require("internal/validators"); const { isPrimary } = require("internal/cluster/isPrimary"); const { throwOnInvalidTLSArray } = require("internal/tls"); const { kInternalSocketData, serverSymbol, kHandle, kRealListen, tlsSymbol, optionsSymbol, kDeferredTimeouts, kDeprecatedReplySymbol, headerStateSymbol, NodeHTTPHeaderState, kPendingCallbacks, kRequest, kCloseCallback, NodeHTTPResponseFlags, headersSymbol, emitErrorNextTickIfErrorListenerNT, getIsNextIncomingMessageHTTPS, setIsNextIncomingMessageHTTPS, callCloseCallback, emitCloseNT, ConnResetException, NodeHTTPResponseAbortEvent, STATUS_CODES, isTlsSymbol, hasServerResponseFinished, OutgoingMessagePrototype, NodeHTTPBodyReadState, controllerSymbol, firstWriteSymbol, deferredSymbol, eofInProgress, runSymbol, drainMicrotasks, setServerIdleTimeout, setServerCustomOptions, getMaxHTTPHeaderSize, } = require("internal/http"); const NumberIsNaN = Number.isNaN; const { format } = require("internal/util/inspect"); const { IncomingMessage } = require("node:_http_incoming"); const { OutgoingMessage } = require("node:_http_outgoing"); const { kIncomingMessage } = require("node:_http_common"); const kConnectionsCheckingInterval = Symbol("http.server.connectionsCheckingInterval"); const getBunServerAllClosedPromise = $newZigFunction("node_http_binding.zig", "getBunServerAllClosedPromise", 1); const sendHelper = $newZigFunction("node_cluster_binding.zig", "sendHelperChild", 3); const kServerResponse = Symbol("ServerResponse"); const kRejectNonStandardBodyWrites = Symbol("kRejectNonStandardBodyWrites"); const GlobalPromise = globalThis.Promise; const kEmptyBuffer = Buffer.alloc(0); const ObjectKeys = Object.keys; const MathMin = Math.min; let cluster; function emitCloseServer(self: Server) { callCloseCallback(self); self.emit("close"); } function emitCloseNTServer(this: Server) { process.nextTick(emitCloseServer, this); } function setCloseCallback(self, callback) { if (callback === self[kCloseCallback]) { return; } if (self[kCloseCallback]) { throw new Error("Close callback already set"); } self[kCloseCallback] = callback; } function assignSocketInternal(self, socket) { if (socket._httpMessage) { throw $ERR_HTTP_SOCKET_ASSIGNED("Socket already assigned"); } socket._httpMessage = self; setCloseCallback(socket, onServerResponseClose); self.socket = socket; self.emit("socket", socket); } function onServerResponseClose() { // EventEmitter.emit makes a copy of the 'close' listeners array before // calling the listeners. detachSocket() unregisters onServerResponseClose // but if detachSocket() is called, directly or indirectly, by a 'close' // listener, onServerResponseClose is still in that copy of the listeners // array. That is, in the example below, b still gets called even though // it's been removed by a: // // const EventEmitter = require('events'); // const obj = new EventEmitter(); // obj.on('event', a); // obj.on('event', b); // function a() { obj.removeListener('event', b) } // function b() { throw "BAM!" } // obj.emit('event'); // throws // // Ergo, we need to deal with stale 'close' events and handle the case // where the ServerResponse object has already been deconstructed. // Fortunately, that requires only a single if check. :-) const httpMessage = this._httpMessage; if (httpMessage) { emitCloseNT(httpMessage); } } function strictContentLength(response) { if (response.strictContentLength) { let contentLength = response._contentLength ?? response.getHeader("content-length"); if ( contentLength && response._hasBody && !response._removedContLen && !response.chunkedEncoding && !response.hasHeader("transfer-encoding") ) { if (typeof contentLength === "number") { return contentLength; } else if (typeof contentLength === "string") { contentLength = parseInt(contentLength, 10); if (NumberIsNaN(contentLength)) { return; } return contentLength; } } } } const ServerResponse_writeDeprecated = function _write(chunk, encoding, callback) { if ($isCallable(encoding)) { callback = encoding; encoding = undefined; } if (!$isCallable(callback)) { callback = undefined; } if (encoding && encoding !== "buffer") { chunk = Buffer.from(chunk, encoding); } if (this.destroyed || this.finished) { if (chunk) { emitErrorNextTickIfErrorListenerNT(this, $ERR_STREAM_WRITE_AFTER_END(), callback); } return false; } if (this[firstWriteSymbol] === undefined && !this.headersSent) { this[firstWriteSymbol] = chunk; if (callback) callback(); return; } ensureReadableStreamController.$call(this, controller => { controller.write(chunk); if (callback) callback(); }); }; function onNodeHTTPServerSocketTimeout() { const req = this[kRequest]; const reqTimeout = req && !req.complete && req.emit("timeout", this); const res = this._httpMessage; const resTimeout = res && res.emit("timeout", this); const serverTimeout = this.server.emit("timeout", this); if (!reqTimeout && !resTimeout && !serverTimeout) this.destroy(); } function emitRequestCloseNT(self) { callCloseCallback(self); self.emit("close"); } function emitListeningNextTick(self, hostname, port) { if ((self.listening = !!self[serverSymbol])) { // TODO: remove the arguments // Note does not pass any arguments. self.emit("listening", null, hostname, port); } } function Server(options, callback): void { if (!(this instanceof Server)) return new Server(options, callback); EventEmitter.$call(this); this[kConnectionsCheckingInterval] = { _destroyed: false }; this.listening = false; this._unref = false; this.maxRequestsPerSocket = 0; this[kInternalSocketData] = undefined; this[tlsSymbol] = null; this.noDelay = true; if (typeof options === "function") { callback = options; options = {}; } else if (options == null) { options = {}; } else { validateObject(options, "options"); options = { ...options }; let cert = options.cert; if (cert) { throwOnInvalidTLSArray("options.cert", cert); this[isTlsSymbol] = true; } let key = options.key; if (key) { throwOnInvalidTLSArray("options.key", key); this[isTlsSymbol] = true; } let ca = options.ca; if (ca) { throwOnInvalidTLSArray("options.ca", ca); this[isTlsSymbol] = true; } let passphrase = options.passphrase; if (passphrase && typeof passphrase !== "string") { throw $ERR_INVALID_ARG_TYPE("options.passphrase", "string", passphrase); } let serverName = options.servername; if (serverName && typeof serverName !== "string") { throw $ERR_INVALID_ARG_TYPE("options.servername", "string", serverName); } let secureOptions = options.secureOptions || 0; if (secureOptions && typeof secureOptions !== "number") { throw $ERR_INVALID_ARG_TYPE("options.secureOptions", "number", secureOptions); } if (this[isTlsSymbol]) { this[tlsSymbol] = { serverName, key, cert, ca, passphrase, secureOptions, }; } else { this[tlsSymbol] = null; } } this[optionsSymbol] = options; storeHTTPOptions.$call(this, options); if (callback) this.on("request", callback); return this; } $toClass(Server, "Server", EventEmitter); Server.prototype[kIncomingMessage] = undefined; Server.prototype[kServerResponse] = undefined; Server.prototype[kConnectionsCheckingInterval] = undefined; Server.prototype.ref = function () { this._unref = false; this[serverSymbol]?.ref?.(); return this; }; Server.prototype.unref = function () { this._unref = true; this[serverSymbol]?.unref?.(); return this; }; Server.prototype.closeAllConnections = function () { const server = this[serverSymbol]; if (!server) { return; } this[serverSymbol] = undefined; const connectionsCheckingInterval = this[kConnectionsCheckingInterval]; if (connectionsCheckingInterval) { connectionsCheckingInterval._destroyed = true; } this.listening = false; server.stop(true); }; Server.prototype.closeIdleConnections = function () { const server = this[serverSymbol]; server.closeIdleConnections(); }; Server.prototype.close = function (optionalCallback?) { const server = this[serverSymbol]; if (!server) { if (typeof optionalCallback === "function") process.nextTick(optionalCallback, $ERR_SERVER_NOT_RUNNING()); return; } this[serverSymbol] = undefined; const connectionsCheckingInterval = this[kConnectionsCheckingInterval]; if (connectionsCheckingInterval) { connectionsCheckingInterval._destroyed = true; } if (typeof optionalCallback === "function") setCloseCallback(this, optionalCallback); this.listening = false; server.closeIdleConnections(); server.stop(); }; Server.prototype[EventEmitter.captureRejectionSymbol] = function (err, event, ...args) { switch (event) { case "request": { const { 1: res } = args; if (!res.headersSent && !res.writableEnded) { // Don't leak headers. const names = res.getHeaderNames(); for (let i = 0; i < names.length; i++) { res.removeHeader(names[i]); } res.statusCode = 500; res.end(STATUS_CODES[500]); } else { res.destroy(); } break; } default: // net.Server.prototype[EventEmitter.captureRejectionSymbol].apply(this, arguments); // .apply(this, arguments); const { 1: res } = args; res?.socket?.destroy(); break; } }; Server.prototype[Symbol.asyncDispose] = function () { const { resolve, reject, promise } = Promise.withResolvers(); this.close(function (err, ...args) { if (err) { reject(err); } else resolve(...args); }); return promise; }; Server.prototype.address = function () { if (!this[serverSymbol]) return null; return this[serverSymbol].address; }; Server.prototype.listen = function () { const server = this; let port, host, onListen; let socketPath; let tls = this[tlsSymbol]; // This logic must align with: // - https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/net.js#L274-L307 if (arguments.length > 0) { if (($isObject(arguments[0]) || $isCallable(arguments[0])) && arguments[0] !== null) { // (options[...][, cb]) port = arguments[0].port; host = arguments[0].host; socketPath = arguments[0].path; const otherTLS = arguments[0].tls; if (otherTLS && $isObject(otherTLS)) { tls = otherTLS; } } else if (typeof arguments[0] === "string" && !(Number(arguments[0]) >= 0)) { // (path[...][, cb]) socketPath = arguments[0]; } else { // ([port][, host][...][, cb]) port = arguments[0]; if (arguments.length > 1 && typeof arguments[1] === "string") { host = arguments[1]; } } } // Bun defaults to port 3000. // Node defaults to port 0. if (port === undefined && !socketPath) { port = 0; } if (typeof port === "string") { const portNumber = parseInt(port); if (!Number.isNaN(portNumber)) { port = portNumber; } } if ($isCallable(arguments[arguments.length - 1])) { onListen = arguments[arguments.length - 1]; } try { // listenInCluster if (isPrimary) { server[kRealListen](tls, port, host, socketPath, false, onListen); return this; } if (cluster === undefined) cluster = require("node:cluster"); // TODO: our net.Server and http.Server use different Bun APIs and our IPC doesnt support sending and receiving handles yet. use reusePort instead for now. // const serverQuery = { // // address: address, // port: port, // addressType: 4, // // fd: fd, // // flags, // // backlog, // // ...options, // }; // cluster._getServer(server, serverQuery, function listenOnPrimaryHandle(err, handle) { // // err = checkBindError(err, port, handle); // // if (err) { // // throw new ExceptionWithHostPort(err, "bind", address, port); // // } // if (err) { // throw err; // } // server[kRealListen](port, host, socketPath, onListen); // }); server.once("listening", () => { cluster.worker.state = "listening"; const address = server.address(); const message = { act: "listening", port: (address && address.port) || port, data: null, addressType: 4, }; sendHelper(message, null); }); server[kRealListen](tls, port, host, socketPath, true, onListen); } catch (err) { setTimeout(() => server.emit("error", err), 1); } return this; }; Server.prototype[kRealListen] = function (tls, port, host, socketPath, reusePort, onListen) { { const ResponseClass = this[optionsSymbol].ServerResponse || ServerResponse; const RequestClass = this[optionsSymbol].IncomingMessage || IncomingMessage; const canUseInternalAssignSocket = ResponseClass?.prototype.assignSocket === ServerResponse.prototype.assignSocket; let isHTTPS = false; let server = this; if (tls) { this.serverName = tls.serverName || host || "localhost"; } this[serverSymbol] = Bun.serve({ idleTimeout: 0, // nodejs dont have a idleTimeout by default tls, port, hostname: host, unix: socketPath, reusePort, // Bindings to be used for WS Server websocket: { open(ws) { ws.data.open(ws); }, message(ws, message) { ws.data.message(ws, message); }, close(ws, code, reason) { ws.data.close(ws, code, reason); }, drain(ws) { ws.data.drain(ws); }, ping(ws, data) { ws.data.ping(ws, data); }, pong(ws, data) { ws.data.pong(ws, data); }, }, maxRequestBodySize: Number.MAX_SAFE_INTEGER, onNodeHTTPRequest( bunServer, url: string, method: string, headersObject: Record, headersArray: string[], handle, hasBody: boolean, socketHandle, isSocketNew, socket, isAncientHTTP: boolean, ) { const prevIsNextIncomingMessageHTTPS = getIsNextIncomingMessageHTTPS(); setIsNextIncomingMessageHTTPS(isHTTPS); if (!socket) { socket = new NodeHTTPServerSocket(server, socketHandle, !!tls); } const http_req = new RequestClass(kHandle, url, method, headersObject, headersArray, handle, hasBody, socket); if (isAncientHTTP) { http_req.httpVersion = "1.0"; } if (method === "CONNECT") { // Handle CONNECT method for HTTP tunneling/proxy if (server.listenerCount("connect") > 0) { // For CONNECT, emit the event and let the handler respond // Don't assign the socket to a response for CONNECT // The handler should write the raw response socket[kEnableStreaming](true); const { promise, resolve } = $newPromiseCapability(Promise); socket.once("close", resolve); server.emit("connect", http_req, socket, kEmptyBuffer); return promise; } else { // Node.js will close the socket and will NOT respond with 400 Bad Request socketHandle.close(); } return; } socket[kEnableStreaming](false); const http_res = new ResponseClass(http_req, { [kHandle]: handle, [kRejectNonStandardBodyWrites]: server.rejectNonStandardBodyWrites, }); setIsNextIncomingMessageHTTPS(prevIsNextIncomingMessageHTTPS); handle.onabort = onServerRequestEvent.bind(socket); // start buffering data if any, the user will need to resume() or .on("data") to read it if (hasBody) { handle.pause(); } drainMicrotasks(); let resolveFunction; let didFinish = false; const isRequestsLimitSet = typeof server.maxRequestsPerSocket === "number" && server.maxRequestsPerSocket > 0; let reachedRequestsLimit = false; if (isRequestsLimitSet) { const requestCount = (socket._requestCount || 0) + 1; socket._requestCount = requestCount; if (server.maxRequestsPerSocket < requestCount) { reachedRequestsLimit = true; } } if (isSocketNew && !reachedRequestsLimit) { server.emit("connection", socket); } socket[kRequest] = http_req; const is_upgrade = http_req.headers.upgrade; if (!is_upgrade) { if (canUseInternalAssignSocket) { // ~10% performance improvement in JavaScriptCore due to avoiding .once("close", ...) and removing a listener assignSocketInternal(http_res, socket); } else { http_res.assignSocket(socket); } } function onClose() { didFinish = true; if (resolveFunction) resolveFunction(); } setCloseCallback(http_res, onClose); if (reachedRequestsLimit) { server.emit("dropRequest", http_req, socket); http_res.writeHead(503); http_res.end(); socket.destroy(); } else if (is_upgrade) { server.emit("upgrade", http_req, socket, kEmptyBuffer); if (!socket._httpMessage) { if (canUseInternalAssignSocket) { // ~10% performance improvement in JavaScriptCore due to avoiding .once("close", ...) and removing a listener assignSocketInternal(http_res, socket); } else { http_res.assignSocket(socket); } } } else if (http_req.headers.expect !== undefined) { if (http_req.headers.expect === "100-continue") { if (server.listenerCount("checkContinue") > 0) { server.emit("checkContinue", http_req, http_res); } else { http_res.writeContinue(); server.emit("request", http_req, http_res); } } else if (server.listenerCount("checkExpectation") > 0) { server.emit("checkExpectation", http_req, http_res); } else { http_res.writeHead(417); http_res.end(); } } else { server.emit("request", http_req, http_res); } socket.cork(); if (handle.finished || didFinish) { handle = undefined; http_res[kCloseCallback] = undefined; http_res.detachSocket(socket); return; } if (http_res.socket) { http_res.on("finish", http_res.detachSocket.bind(http_res, socket)); } const { resolve, promise } = $newPromiseCapability(Promise); resolveFunction = resolve; return promise; }, // Be very careful not to access (web) Request object // properties: // - request.url // - request.headers // // We want to avoid triggering the getter for these properties because // that will cause the data to be cloned twice, which costs memory & performance. // fetch(req, _server) { // var pendingResponse; // var pendingError; // var reject = err => { // if (pendingError) return; // pendingError = err; // if (rejectFunction) rejectFunction(err); // }; // var reply = function (resp) { // if (pendingResponse) return; // pendingResponse = resp; // if (resolveFunction) resolveFunction(resp); // }; // const prevIsNextIncomingMessageHTTPS = isNextIncomingMessageHTTPS; // isNextIncomingMessageHTTPS = isHTTPS; // const http_req = new RequestClass(req, { // [typeSymbol]: NodeHTTPIncomingRequestType.FetchRequest, // }); // assignEventCallback(req, onRequestEvent.bind(http_req)); // isNextIncomingMessageHTTPS = prevIsNextIncomingMessageHTTPS; // const upgrade = http_req.headers.upgrade; // const http_res = new ResponseClass(http_req, { [kDeprecatedReplySymbol]: reply }); // http_req.socket[kInternalSocketData] = [server, http_res, req]; // server.emit("connection", http_req.socket); // const rejectFn = err => reject(err); // http_req.once("error", rejectFn); // http_res.once("error", rejectFn); // if (upgrade) { // server.emit("upgrade", http_req, http_req.socket, kEmptyBuffer); // } else { // server.emit("request", http_req, http_res); // } // if (pendingError) { // throw pendingError; // } // if (pendingResponse) { // return pendingResponse; // } // var { promise, resolve: resolveFunction, reject: rejectFunction } = $newPromiseCapability(GlobalPromise); // return promise; // }, }); getBunServerAllClosedPromise(this[serverSymbol]).$then(emitCloseNTServer.bind(this)); isHTTPS = this[serverSymbol].protocol === "https"; // always set strict method validation to true for node.js compatibility setServerCustomOptions( this[serverSymbol], this.requireHostHeader, true, typeof this.maxHeaderSize !== "undefined" ? this.maxHeaderSize : getMaxHTTPHeaderSize(), onServerClientError.bind(this), ); if (this?._unref) { this[serverSymbol]?.unref?.(); } if ($isCallable(onListen)) { this.once("listening", onListen); } if (this[kDeferredTimeouts]) { for (const { msecs, callback } of this[kDeferredTimeouts]) { this.setTimeout(msecs, callback); } delete this[kDeferredTimeouts]; } setTimeout(emitListeningNextTick, 1, this, this[serverSymbol]?.hostname, this[serverSymbol]?.port); } }; Server.prototype.setTimeout = function (msecs, callback) { const server = this[serverSymbol]; if (server) { setServerIdleTimeout(server, Math.ceil(msecs / 1000)); if (typeof callback === "function") this.once("timeout", callback); } else { (this[kDeferredTimeouts] ??= []).push({ msecs, callback }); } return this; }; function onServerRequestEvent(this: NodeHTTPServerSocket, event: NodeHTTPResponseAbortEvent) { const socket: NodeHTTPServerSocket = this; switch (event) { case NodeHTTPResponseAbortEvent.abort: { if (!socket.destroyed) { socket.destroy(); } break; } case NodeHTTPResponseAbortEvent.timeout: { socket.emit("timeout"); break; } } } // uWS::HttpParserError enum HttpParserError { HTTP_PARSER_ERROR_NONE = 0, HTTP_PARSER_ERROR_INVALID_CHUNKED_ENCODING = 1, HTTP_PARSER_ERROR_INVALID_CONTENT_LENGTH = 2, HTTP_PARSER_ERROR_INVALID_TRANSFER_ENCODING = 3, HTTP_PARSER_ERROR_MISSING_HOST_HEADER = 4, HTTP_PARSER_ERROR_INVALID_REQUEST = 5, HTTP_PARSER_ERROR_REQUEST_HEADER_FIELDS_TOO_LARGE = 6, HTTP_PARSER_ERROR_INVALID_HTTP_VERSION = 7, HTTP_PARSER_ERROR_INVALID_EOF = 8, HTTP_PARSER_ERROR_INVALID_METHOD = 9, HTTP_PARSER_ERROR_INVALID_HEADER_TOKEN = 10, } function onServerClientError(ssl: boolean, socket: unknown, errorCode: number, rawPacket: ArrayBuffer) { const self = this as Server; let err; switch (errorCode) { case HttpParserError.HTTP_PARSER_ERROR_INVALID_CONTENT_LENGTH: err = $HPE_UNEXPECTED_CONTENT_LENGTH("Parse Error"); break; case HttpParserError.HTTP_PARSER_ERROR_INVALID_TRANSFER_ENCODING: err = $HPE_INVALID_TRANSFER_ENCODING("Parse Error"); break; case HttpParserError.HTTP_PARSER_ERROR_INVALID_EOF: err = $HPE_INVALID_EOF_STATE("Parse Error"); break; case HttpParserError.HTTP_PARSER_ERROR_INVALID_METHOD: err = $HPE_INVALID_METHOD("Parse Error: Invalid method encountered"); err.bytesParsed = 1; // always 1 for now because is the first byte of the request line break; case HttpParserError.HTTP_PARSER_ERROR_INVALID_HEADER_TOKEN: err = $HPE_INVALID_HEADER_TOKEN("Parse Error: Invalid header token encountered"); break; case HttpParserError.HTTP_PARSER_ERROR_REQUEST_HEADER_FIELDS_TOO_LARGE: err = $HPE_HEADER_OVERFLOW("Parse Error: Header overflow"); err.bytesParsed = rawPacket.byteLength; break; default: err = $HPE_INTERNAL("Parse Error"); break; } err.rawPacket = rawPacket; const nodeSocket = new NodeHTTPServerSocket(self, socket, ssl); self.emit("connection", nodeSocket); self.emit("clientError", err, nodeSocket); if (nodeSocket.listenerCount("error") > 0) { nodeSocket.emit("error", err); } } const kBytesWritten = Symbol("kBytesWritten"); const kEnableStreaming = Symbol("kEnableStreaming"); const NodeHTTPServerSocket = class Socket extends Duplex { bytesRead = 0; connecting = false; timeout = 0; [kBytesWritten] = 0; [kHandle]; server: Server; _httpMessage; _secureEstablished = false; #pendingCallback = null; constructor(server: Server, handle, encrypted) { super(); this.server = server; this[kHandle] = handle; this._secureEstablished = !!handle?.secureEstablished; handle.onclose = this.#onClose.bind(this); handle.duplex = this; this.encrypted = encrypted; this.on("timeout", onNodeHTTPServerSocketTimeout); } get bytesWritten() { const handle = this[kHandle]; return handle ? (handle.response?.getBytesWritten?.() ?? handle.bytesWritten ?? this[kBytesWritten] ?? 0) : (this[kBytesWritten] ?? 0); } set bytesWritten(value) { this[kBytesWritten] = value; } [kEnableStreaming](enable: boolean) { const handle = this[kHandle]; if (handle) { if (enable) { handle.ondata = this.#onData.bind(this); handle.ondrain = this.#onDrain.bind(this); } else { handle.ondata = undefined; handle.ondrain = undefined; } } } #onDrain() { const handle = this[kHandle]; this[kBytesWritten] = handle ? (handle.response?.getBytesWritten?.() ?? handle.bytesWritten ?? 0) : 0; const callback = this.#pendingCallback; if (callback) { this.#pendingCallback = null; (callback as Function)(); } this.emit("drain"); } #onData(chunk, last) { if (chunk) { this.push(chunk); } if (last) { const handle = this[kHandle]; if (handle) { handle.ondata = undefined; } this.push(null); } } #closeHandle(handle, callback) { this[kHandle] = undefined; handle.onclose = this.#onCloseForDestroy.bind(this, callback); handle.close(); // lets sync check and destroy the request if it's not complete const message = this._httpMessage; const req = message?.req; if (req && !req.complete) { // at this point the handle is not destroyed yet, lets destroy the request req.destroy(); } } #onClose() { this[kHandle] = null; const message = this._httpMessage; const req = message?.req; if (req && !req.complete && !req[kHandle]?.upgraded) { // At this point the socket is already destroyed; let's avoid UAF req[kHandle] = undefined; if (req.listenerCount("error") > 0) { req.destroy(new ConnResetException("aborted")); } else { req.destroy(); } } this.emit("close"); } #onCloseForDestroy(closeCallback) { this.#onClose(); if ($isCallable(closeCallback)) closeCallback(); } _onTimeout() { const handle = this[kHandle]; const response = handle?.response; // If there is a response, and it has pending data, // we suppress the timeout because a write is in progress. if (response && response.writableLength > 0) { return; } this.emit("timeout"); } _unrefTimer() { // for compatibility } address() { return this[kHandle]?.remoteAddress || null; } get bufferSize() { return this.writableLength; } connect(_port, _host, _connectListener) { return this; } _destroy(err, callback) { const handle = this[kHandle]; if (!handle) { if ($isCallable(callback)) callback(err); return; } handle.ondata = undefined; if (handle.closed) { const onclose = handle.onclose; handle.onclose = undefined; if ($isCallable(onclose)) { onclose.$call(handle); } if ($isCallable(callback)) callback(err); return; } this.#closeHandle(handle, callback); } _final(callback) { const handle = this[kHandle]; if (!handle) { callback(); return; } handle.end(); callback(); } get localAddress() { return this[kHandle]?.localAddress?.address; } get localFamily() { return this[kHandle]?.localAddress?.family; } get localPort() { return this[kHandle]?.localAddress?.port; } get pending() { return this.connecting; } #resumeSocket() { const handle = this[kHandle]; const response = handle?.response; if (response) { const resumed = response.resume(); if (resumed && resumed !== true) { const bodyReadState = handle.hasBody; const message = this._httpMessage; const req = message?.req; if ((bodyReadState & NodeHTTPBodyReadState.done) !== 0) { emitServerSocketEOFNT(this, req); } if (req) { req.push(resumed); } this.push(resumed); } } } _read(_size) { // https://github.com/nodejs/node/blob/13e3aef053776be9be262f210dc438ecec4a3c8d/lib/net.js#L725-L737 this.#resumeSocket(); } get readyState() { if (this.connecting) return "opening"; if (this.readable) { return this.writable ? "open" : "readOnly"; } else { return this.writable ? "writeOnly" : "closed"; } } ref() { return this; } get remoteAddress() { return this.address()?.address; } set remoteAddress(val) { // initialize the object so that other properties wouldn't be lost this.address().address = val; } get remotePort() { return this.address()?.port; } set remotePort(val) { // initialize the object so that other properties wouldn't be lost this.address().port = val; } get remoteFamily() { return this.address()?.family; } set remoteFamily(val) { // initialize the object so that other properties wouldn't be lost this.address().family = val; } resetAndDestroy() {} setKeepAlive(_enable = false, _initialDelay = 0) {} setNoDelay(_noDelay = true) { return this; } setTimeout(_timeout, _callback) { return this; } setEncoding(_encoding) { const err = new Error("Changing the socket encoding is not allowed per RFC7230 Section 3."); err.code = "ERR_HTTP_SOCKET_ENCODING"; throw err; } unref() { return this; } _write(_chunk, _encoding, _callback) { const handle = this[kHandle]; // only enable writting if we can drain let err; try { if (handle && handle.ondrain && !handle.write(_chunk, _encoding)) { this.#pendingCallback = _callback; return false; } } catch (e) { err = e; } if (err) _callback(err); else _callback(); } pause() { const handle = this[kHandle]; const response = handle?.response; if (response) { response.pause(); } return super.pause(); } resume() { this.#resumeSocket(); return super.resume(); } get [kInternalSocketData]() { return this[kHandle]?.response; } } as unknown as typeof import("node:net").Socket; function _writeHead(statusCode, reason, obj, response) { const originalStatusCode = statusCode; let hasContentLength = response.hasHeader("content-length"); statusCode |= 0; if (statusCode < 100 || statusCode > 999) { throw $ERR_HTTP_INVALID_STATUS_CODE(format("%s", originalStatusCode)); } if (typeof reason === "string") { // writeHead(statusCode, reasonPhrase[, headers]) response.statusMessage = reason; } else { // writeHead(statusCode[, headers]) if (!response.statusMessage) response.statusMessage = STATUS_CODES[statusCode] || "unknown"; obj ??= reason; } if (checkInvalidHeaderChar(response.statusMessage)) throw $ERR_INVALID_CHAR("statusMessage"); response.statusCode = statusCode; { // Slow-case: when progressive API and header fields are passed. let k; if ($isArray(obj)) { const length = obj.length; // Append all the headers provided in the array: if (length && $isArray(obj[0])) { for (let i = 0; i < length; i++) { const k = obj[i]; if (k) response.appendHeader(k[0], k[1]); } } else { if (length % 2 !== 0) { throw $ERR_INVALID_ARG_VALUE("headers", obj); } // Test non-chunked message does not have trailer header set, // message will be terminated by the first empty line after the // header fields, regardless of the header fields present in the // message, and thus cannot contain a message body or 'trailers'. if ( (response.chunkedEncoding !== true || response.hasHeader("content-length")) && (response._trailer || response.hasHeader("trailer")) ) { throw $ERR_HTTP_TRAILER_INVALID("Trailers are invalid with this transfer encoding"); } // Headers in obj should override previous headers but still // allow explicit duplicates. To do so, we first remove any // existing conflicts, then use appendHeader. for (let n = 0; n < length; n += 2) { k = obj[n + 0]; response.removeHeader(k); } for (let n = 0; n < length; n += 2) { k = obj[n]; if (k) response.appendHeader(k, obj[n + 1]); } } } else if (obj) { const keys = Object.keys(obj); const length = keys.length; // Retain for(;;) loop for performance reasons // Refs: https://github.com/nodejs/node/pull/30958 for (let i = 0; i < length; i++) { k = keys[i]; if (k) response.setHeader(k, obj[k]); } } if ( (response.chunkedEncoding !== true || response.hasHeader("content-length")) && (response._trailer || response.hasHeader("trailer")) ) { // remove the invalid content-length or trailer header if (hasContentLength) { response.removeHeader("trailer"); } else { response.removeHeader("content-length"); } throw $ERR_HTTP_TRAILER_INVALID("Trailers are invalid with this transfer encoding"); } } updateHasBody(response, statusCode); } Object.defineProperty(NodeHTTPServerSocket, "name", { value: "Socket" }); function ServerResponse(req, options): void { if (!(this instanceof ServerResponse)) return new ServerResponse(req, options); OutgoingMessage.$call(this, options); this.useChunkedEncodingByDefault = true; if ((this[kDeprecatedReplySymbol] = options?.[kDeprecatedReplySymbol])) { this[controllerSymbol] = undefined; this[firstWriteSymbol] = undefined; this[deferredSymbol] = undefined; this.write = ServerResponse_writeDeprecated; this.end = ServerResponse_finalDeprecated; } this.req = req; this.sendDate = true; this._sent100 = false; this[headerStateSymbol] = NodeHTTPHeaderState.none; this[kPendingCallbacks] = []; this.finished = false; // this is matching node's behaviour // https://github.com/nodejs/node/blob/cf8c6994e0f764af02da4fa70bc5962142181bf3/lib/_http_server.js#L192 if (req.method === "HEAD") this._hasBody = false; if (options) { const handle = options[kHandle]; if (handle) { this[kHandle] = handle; } else { this[kHandle] = req[kHandle]; } this[kRejectNonStandardBodyWrites] = options[kRejectNonStandardBodyWrites] ?? false; } else { this[kHandle] = req[kHandle]; } this.statusCode = 200; this.statusMessage = undefined; this.chunkedEncoding = false; } $toClass(ServerResponse, "ServerResponse", OutgoingMessage); ServerResponse.prototype._removedConnection = false; ServerResponse.prototype._removedContLen = false; ServerResponse.prototype._hasBody = true; ServerResponse.prototype._ended = false; ServerResponse.prototype[kRejectNonStandardBodyWrites] = undefined; Object.defineProperty(ServerResponse.prototype, "headersSent", { get() { return ( this[headerStateSymbol] === NodeHTTPHeaderState.sent || this[headerStateSymbol] === NodeHTTPHeaderState.assigned ); }, set(value) { this[headerStateSymbol] = value ? NodeHTTPHeaderState.sent : NodeHTTPHeaderState.none; }, }); ServerResponse.prototype._writeRaw = function (chunk, encoding, callback) { return this.socket.write(chunk, encoding, callback); }; ServerResponse.prototype.writeEarlyHints = function (hints, cb) { let head = "HTTP/1.1 103 Early Hints\r\n"; validateObject(hints, "hints"); if (hints.link === null || hints.link === undefined) { return; } const link = validateLinkHeaderValue(hints.link); if (link.length === 0) { return; } head += "Link: " + link + "\r\n"; for (const key of ObjectKeys(hints)) { if (key !== "link") { head += key + ": " + hints[key] + "\r\n"; } } head += "\r\n"; this._writeRaw(head, "ascii", cb); }; ServerResponse.prototype.writeProcessing = function (cb) { this._writeRaw("HTTP/1.1 102 Processing\r\n\r\n", "ascii", cb); }; ServerResponse.prototype.writeContinue = function (cb) { this.socket[kHandle]?.response?.writeContinue(); cb?.(); }; // This end method is actually on the OutgoingMessage prototype in Node.js // But we don't want it for the fetch() response version. ServerResponse.prototype.end = function (chunk, encoding, callback) { const handle = this[kHandle]; if (handle?.aborted) { return this; } if ($isCallable(chunk)) { callback = chunk; chunk = undefined; encoding = undefined; } else if ($isCallable(encoding)) { callback = encoding; encoding = undefined; } else if (!$isCallable(callback)) { callback = undefined; } if (hasServerResponseFinished(this, chunk, callback)) { return this; } if (chunk && !this._hasBody) { if (this[kRejectNonStandardBodyWrites]) { throw $ERR_HTTP_BODY_NOT_ALLOWED(); } else { // node.js just ignores the write in this case chunk = undefined; } } if (!handle) { if ($isCallable(callback)) { process.nextTick(callback); } return this; } const headerState = this[headerStateSymbol]; callWriteHeadIfObservable(this, headerState); const flags = handle.flags; if (!!(flags & NodeHTTPResponseFlags.closed_or_completed)) { // node.js will return true if the handle is closed but the internal state is not // and will not throw or emit an error return true; } if (headerState !== NodeHTTPHeaderState.sent) { handle.cork(() => { handle.writeHead(this.statusCode, this.statusMessage, this[headersSymbol]); // If handle.writeHead throws, we don't want headersSent to be set to true. // So we set it here. this[headerStateSymbol] = NodeHTTPHeaderState.sent; // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/_http_outgoing.js#L987 this._contentLength = handle.end(chunk, encoding, undefined, strictContentLength(this)); }); } else { // If there's no data but you already called end, then you're done. // We can ignore it in that case. if (!(!chunk && handle.ended) && !handle.aborted) { handle.end(chunk, encoding, undefined, strictContentLength(this)); } } this._header = " "; const req = this.req; const socket = req.socket; if (!req._consuming && !req?._readableState?.resumeScheduled) { req._dump(); } this.detachSocket(socket); this.finished = true; process.nextTick(self => { self._ended = true; }, this); this.emit("prefinish"); this._callPendingCallbacks(); if (callback) { process.nextTick( function (callback, self) { // In Node.js, the "finish" event triggers the "close" event. // So it shouldn't become closed === true until after "finish" is emitted and the callback is called. self.emit("finish"); try { callback(); } catch (err) { self.emit("error", err); } process.nextTick(emitCloseNT, self); }, callback, this, ); } else { process.nextTick(function (self) { self.emit("finish"); process.nextTick(emitCloseNT, self); }, this); } return this; }; Object.defineProperty(ServerResponse.prototype, "writable", { get() { return !this._ended || !hasServerResponseFinished(this); }, }); ServerResponse.prototype.write = function (chunk, encoding, callback) { const handle = this[kHandle]; if ($isCallable(chunk)) { callback = chunk; chunk = undefined; encoding = undefined; } else if ($isCallable(encoding)) { callback = encoding; encoding = undefined; } else if (!$isCallable(callback)) { callback = undefined; } if (hasServerResponseFinished(this, chunk, callback)) { return false; } if (chunk && !this._hasBody) { if (this[kRejectNonStandardBodyWrites]) { throw $ERR_HTTP_BODY_NOT_ALLOWED(); } else { // node.js just ignores the write in this case chunk = undefined; } } let result = 0; const headerState = this[headerStateSymbol]; callWriteHeadIfObservable(this, headerState); if (!handle) { if (this.socket) { return this.socket.write(chunk, encoding, callback); } else { return OutgoingMessagePrototype.write.$call(this, chunk, encoding, callback); } } const flags = handle.flags; if (!!(flags & NodeHTTPResponseFlags.closed_or_completed)) { // node.js will return true if the handle is closed but the internal state is not // and will not throw or emit an error return true; } if (this[headerStateSymbol] !== NodeHTTPHeaderState.sent) { handle.cork(() => { handle.writeHead(this.statusCode, this.statusMessage, this[headersSymbol]); // If handle.writeHead throws, we don't want headersSent to be set to true. // So we set it here. this[headerStateSymbol] = NodeHTTPHeaderState.sent; result = handle.write(chunk, encoding, allowWritesToContinue.bind(this), strictContentLength(this)); }); } else { result = handle.write(chunk, encoding, allowWritesToContinue.bind(this), strictContentLength(this)); } if (result < 0) { if (callback) { // The write was buffered due to backpressure. // We need to defer the callback until the write actually goes through. this[kPendingCallbacks].push(callback); } return false; } this._callPendingCallbacks(); if (callback) { process.nextTick(callback); } this.emit("drain"); return true; }; ServerResponse.prototype._callPendingCallbacks = function () { const originalLength = this[kPendingCallbacks].length; for (let i = 0; i < originalLength; ++i) { process.nextTick(this[kPendingCallbacks][i]); } if (this[kPendingCallbacks].length == originalLength) { // If the array wasn't somehow appended to, just set it to an empty array this[kPendingCallbacks] = []; } else { // Otherwise, splice it. this[kPendingCallbacks].splice(0, originalLength); } }; ServerResponse.prototype._finish = function () { this.emit("prefinish"); }; ServerResponse.prototype.detachSocket = function (socket) { if (socket._httpMessage === this) { if (socket[kCloseCallback]) socket[kCloseCallback] = undefined; socket.removeListener("close", onServerResponseClose); socket._httpMessage = null; } this.socket = null; }; ServerResponse.prototype._implicitHeader = function () { if (this.headersSent) return; // @ts-ignore this.writeHead(this.statusCode); }; Object.defineProperty(ServerResponse.prototype, "writableNeedDrain", { get() { return !this.destroyed && !this.finished && (this[kHandle]?.bufferedAmount ?? 1) !== 0; }, }); Object.defineProperty(ServerResponse.prototype, "writableFinished", { get() { return !!(this.finished && (!this[kHandle] || this[kHandle].finished)); }, }); Object.defineProperty(ServerResponse.prototype, "writableLength", { get() { return this.writableFinished ? 0 : (this[kHandle]?.bufferedAmount ?? 0); }, }); Object.defineProperty(ServerResponse.prototype, "writableHighWaterMark", { get() { return 64 * 1024; }, }); Object.defineProperty(ServerResponse.prototype, "closed", { get() { return this._closed; }, }); ServerResponse.prototype._send = function (data, encoding, callback, _byteLength) { const handle = this[kHandle]; if (!handle) { return OutgoingMessagePrototype._send.$apply(this, arguments); } if (this[headerStateSymbol] !== NodeHTTPHeaderState.sent) { handle.cork(() => { handle.writeHead(this.statusCode, this.statusMessage, this[headersSymbol]); this[headerStateSymbol] = NodeHTTPHeaderState.sent; handle.write(data, encoding, callback, strictContentLength(this)); }); } else { handle.write(data, encoding, callback, strictContentLength(this)); } }; ServerResponse.prototype.writeHead = function (statusCode, statusMessage, headers) { if (this.headersSent) { throw $ERR_HTTP_HEADERS_SENT("writeHead"); } _writeHead(statusCode, statusMessage, headers, this); this[headerStateSymbol] = NodeHTTPHeaderState.assigned; return this; }; ServerResponse.prototype.assignSocket = function (socket) { if (socket._httpMessage) { throw $ERR_HTTP_SOCKET_ASSIGNED("Socket already assigned"); } socket._httpMessage = this; socket.once("close", onServerResponseClose); this.socket = socket; this.emit("socket", socket); }; Object.defineProperty(ServerResponse.prototype, "shouldKeepAlive", { get() { return this[kHandle]?.shouldKeepAlive ?? true; }, set(_value) { // throw new Error('not implemented'); }, }); ServerResponse.prototype.destroy = function (_err?: Error) { if (this.destroyed) return this; const handle = this[kHandle]; this.destroyed = true; if (handle) { handle.abort(); } this?.socket?.destroy(); this.emit("close"); return this; }; ServerResponse.prototype.emit = function (event) { if (event === "close") { callCloseCallback(this); } return Stream.prototype.emit.$apply(this, arguments); }; ServerResponse.prototype.flushHeaders = function () { if (this[headerStateSymbol] === NodeHTTPHeaderState.sent) return; // Should be idempotent. if (this[headerStateSymbol] !== NodeHTTPHeaderState.assigned) this._implicitHeader(); const handle = this[kHandle]; if (handle) { if (this[headerStateSymbol] === NodeHTTPHeaderState.assigned) { this[headerStateSymbol] = NodeHTTPHeaderState.sent; handle.writeHead(this.statusCode, this.statusMessage, this[headersSymbol]); } handle.flushHeaders(); } }; function updateHasBody(response, statusCode) { // RFC 2616, 10.2.5: // The 204 response MUST NOT include a message-body, and thus is always // terminated by the first empty line after the header fields. // RFC 2616, 10.3.5: // The 304 response MUST NOT contain a message-body, and thus is always // terminated by the first empty line after the header fields. // RFC 2616, 10.1 Informational 1xx: // This class of status code indicates a provisional response, // consisting only of the Status-Line and optional headers, and is // terminated by an empty line. if (statusCode === 204 || statusCode === 304 || (statusCode >= 100 && statusCode <= 199)) { response._hasBody = false; } else { response._hasBody = true; } } function emitServerSocketEOF(self, req) { self.push(null); if (req) { req.push(null); req.complete = true; } } function emitServerSocketEOFNT(self, req) { if (req) { req[eofInProgress] = true; } process.nextTick(emitServerSocketEOF, self); } let OriginalWriteHeadFn, OriginalImplicitHeadFn; function callWriteHeadIfObservable(self, headerState) { if ( headerState === NodeHTTPHeaderState.none && !(self.writeHead === OriginalWriteHeadFn && self._implicitHeader === OriginalImplicitHeadFn) ) { self.writeHead(self.statusCode, self.statusMessage, self[headersSymbol]); } } function allowWritesToContinue() { this._callPendingCallbacks(); this.emit("drain"); } function drainHeadersIfObservable() { if (this._implicitHeader === OriginalImplicitHeadFn && this.writeHead === OriginalWriteHeadFn) { return; } this._implicitHeader(); } function ServerResponse_finalDeprecated(chunk, encoding, callback) { if ($isCallable(encoding)) { callback = encoding; encoding = undefined; } if (!$isCallable(callback)) { callback = undefined; } if (this.destroyed || this.finished) { if (chunk) { emitErrorNextTickIfErrorListenerNT(this, $ERR_STREAM_WRITE_AFTER_END(), callback); } return false; } if (encoding && encoding !== "buffer") { chunk = Buffer.from(chunk, encoding); } const req = this.req; const shouldEmitClose = req && req.emit && !this.finished; if (!this.headersSent) { let data = this[firstWriteSymbol]; if (chunk) { if (data) { if (encoding) { data = Buffer.from(data, encoding); } data = new Blob([data, chunk]); } else { data = chunk; } } else if (!data) { data = undefined; } else { data = new Blob([data]); } this[firstWriteSymbol] = undefined; this.finished = true; this.headersSent = true; // https://github.com/oven-sh/bun/issues/3458 drainHeadersIfObservable.$call(this); this[kDeprecatedReplySymbol]( new Response(data, { headers: this[headersSymbol], status: this.statusCode, statusText: this.statusMessage ?? STATUS_CODES[this.statusCode], }), ); if (shouldEmitClose) { req.complete = true; process.nextTick(emitRequestCloseNT, req); } callback?.(); return; } this.finished = true; ensureReadableStreamController.$call(this, controller => { if (chunk && encoding) { chunk = Buffer.from(chunk, encoding); } let prom; if (chunk) { controller.write(chunk); prom = controller.end(); } else { prom = controller.end(); } const handler = () => { callback(); const deferred = this[deferredSymbol]; if (deferred) { this[deferredSymbol] = undefined; deferred(); } }; if ($isPromise(prom)) prom.then(handler, handler); else handler(); }); } // ServerResponse.prototype._final = ServerResponse_finalDeprecated; ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead; OriginalWriteHeadFn = ServerResponse.prototype.writeHead; OriginalImplicitHeadFn = ServerResponse.prototype._implicitHeader; function storeHTTPOptions(options) { this[kIncomingMessage] = options.IncomingMessage || IncomingMessage; this[kServerResponse] = options.ServerResponse || ServerResponse; const maxHeaderSize = options.maxHeaderSize; if (maxHeaderSize !== undefined) validateInteger(maxHeaderSize, "maxHeaderSize", 0); this.maxHeaderSize = maxHeaderSize; const insecureHTTPParser = options.insecureHTTPParser; if (insecureHTTPParser !== undefined) validateBoolean(insecureHTTPParser, "options.insecureHTTPParser"); this.insecureHTTPParser = insecureHTTPParser; const requestTimeout = options.requestTimeout; if (requestTimeout !== undefined) { validateInteger(requestTimeout, "requestTimeout", 0); this.requestTimeout = requestTimeout; } else { this.requestTimeout = 300_000; // 5 minutes } const headersTimeout = options.headersTimeout; if (headersTimeout !== undefined) { validateInteger(headersTimeout, "headersTimeout", 0); this.headersTimeout = headersTimeout; } else { this.headersTimeout = MathMin(60_000, this.requestTimeout); // Minimum between 60 seconds or requestTimeout } if (this.requestTimeout > 0 && this.headersTimeout > 0 && this.headersTimeout > this.requestTimeout) { throw $ERR_OUT_OF_RANGE("headersTimeout", "<= requestTimeout", headersTimeout); } const keepAliveTimeout = options.keepAliveTimeout; if (keepAliveTimeout !== undefined) { validateInteger(keepAliveTimeout, "keepAliveTimeout", 0); this.keepAliveTimeout = keepAliveTimeout; } else { this.keepAliveTimeout = 5_000; // 5 seconds; } const connectionsCheckingInterval = options.connectionsCheckingInterval; if (connectionsCheckingInterval !== undefined) { validateInteger(connectionsCheckingInterval, "connectionsCheckingInterval", 0); this.connectionsCheckingInterval = connectionsCheckingInterval; } else { this.connectionsCheckingInterval = 30_000; // 30 seconds } const requireHostHeader = options.requireHostHeader; if (requireHostHeader !== undefined) { validateBoolean(requireHostHeader, "options.requireHostHeader"); this.requireHostHeader = requireHostHeader; } else { this.requireHostHeader = true; } const joinDuplicateHeaders = options.joinDuplicateHeaders; if (joinDuplicateHeaders !== undefined) { validateBoolean(joinDuplicateHeaders, "options.joinDuplicateHeaders"); } this.joinDuplicateHeaders = joinDuplicateHeaders; const rejectNonStandardBodyWrites = options.rejectNonStandardBodyWrites; if (rejectNonStandardBodyWrites !== undefined) { validateBoolean(rejectNonStandardBodyWrites, "options.rejectNonStandardBodyWrites"); this.rejectNonStandardBodyWrites = rejectNonStandardBodyWrites; } else { this.rejectNonStandardBodyWrites = false; } } function ensureReadableStreamController(run) { const thisController = this[controllerSymbol]; if (thisController) return run(thisController); this.headersSent = true; let firstWrite = this[firstWriteSymbol]; const old_run = this[runSymbol]; if (old_run) { old_run.push(run); return; } this[runSymbol] = [run]; this[kDeprecatedReplySymbol]( new Response( new ReadableStream({ type: "direct", pull: controller => { this[controllerSymbol] = controller; if (firstWrite) controller.write(firstWrite); firstWrite = undefined; for (let run of this[runSymbol]) { run(controller); } if (!this.finished) { const { promise, resolve } = $newPromiseCapability(GlobalPromise); this[deferredSymbol] = resolve; return promise; } }, }), { headers: this[headersSymbol], status: this.statusCode, statusText: this.statusMessage ?? STATUS_CODES[this.statusCode], }, ), ); } export default { Server, ServerResponse, kConnectionsCheckingInterval, };