import { core, primordials } from "ext:core/mod.js";
const {
ArrayIsArray,
ArrayPrototypeIncludes,
ArrayPrototypePush,
ArrayPrototypeShift,
ArrayPrototypeSlice,
Error,
ErrorPrototype,
FunctionPrototypeApply,
FunctionPrototypeBind,
FunctionPrototypeCall,
MathMin,
NumberIsFinite,
ObjectKeys,
ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf,
SafeArrayIterator,
SafeMap,
SafeMapIterator,
SafeSet,
SafeSetIterator,
String,
StringPrototypeIncludes,
StringPrototypeIndexOf,
StringPrototypeSlice,
StringPrototypeSplit,
StringPrototypeStartsWith,
Symbol,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetByteOffset,
} = primordials;
import net from "node:net";
const { Buffer } = core.loadExtScript("ext:deno_node/internal/buffer.mjs");
const { ok: assert } = core.loadExtScript("ext:deno_node/assert.ts");
import {
_checkInvalidHeaderChar as checkInvalidHeaderChar,
chunkExpression,
continueExpression,
freeParser,
HTTPParser,
isLenient,
kIncomingMessage,
parsers,
prepareError,
} from "node:_http_common";
import {
kUniqueHeaders,
OutgoingMessage,
parseUniqueHeadersOption,
validateHeaderName,
validateHeaderValue,
} from "node:_http_outgoing";
const { kNeedDrain, kOutHeaders } = core.loadExtScript(
"ext:deno_node/internal/http.ts",
);
import { IncomingMessage } from "node:_http_incoming";
const {
connResetException,
ERR_HTTP_HEADERS_SENT,
ERR_HTTP_SOCKET_ASSIGNED,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_CHAR,
ERR_OUT_OF_RANGE,
} = core.loadExtScript("ext:deno_node/internal/errors.ts");
const { kEmptyObject } = core.loadExtScript("ext:deno_node/internal/util.mjs");
const {
kDestroy,
kTimeout,
suspendTimeout,
} = core.loadExtScript("ext:deno_node/internal/timers.mjs");
const {
validateBoolean,
validateFunction,
validateInteger,
validateLinkHeaderValue,
validateObject,
} = core.loadExtScript("ext:deno_node/internal/validators.mjs");
const { nextTick } = core.loadExtScript("ext:deno_node/_next_tick.ts");
const {
enterAsyncResourceIfActive,
exitAsyncResourceIfActive,
} = core.loadExtScript("ext:deno_node/internal/async_hooks.ts");
const { enqueueNodePerformanceEntry, hasNodeObserverForType } = core
.loadExtScript(
"ext:deno_node/perf_hooks.js",
);
import {
applyAddressOverride,
notifyAddressOverrideServing,
SERVER_KIND_NODE_HTTP2,
startOverrideListener,
} from "ext:deno_node/internal/http/address_override.js";
const {
otelState,
builtinTracer,
ContextManager,
telemetry,
} = core.loadExtScript("ext:deno_telemetry/telemetry.ts");
const { channel } = core.loadExtScript("ext:deno_node/diagnostics_channel.js");
const onServerRequestStartChannel = channel("http.server.request.start");
const onServerResponseCreatedChannel = channel("http.server.response.created");
const onServerResponseFinishChannel = channel("http.server.response.finish");
const kServerResponse = Symbol("ServerResponse");
const kConnectionsKey = Symbol("http.server.connections");
const kConnectionsCheckingInterval = Symbol(
"http.server.connectionsCheckingInterval",
);
const kOtelSpan = Symbol("kOtelSpan");
const kOtelStartTime = Symbol("kOtelStartTime");
const kOtelReqBodySize = Symbol("kOtelReqBodySize");
const kPerfStartTime = Symbol("kPerfStartTime");
let otelMetrics = null;
function getOtelMetrics() {
if (otelMetrics) return otelMetrics;
const meter = telemetry.meterProvider.getMeter("deno.http.server");
otelMetrics = {
activeRequests: meter.createUpDownCounter("http.server.active_requests", {
description: "Number of active HTTP server requests.",
unit: "{request}",
}),
requestDuration: meter.createHistogram("http.server.request.duration", {
description: "Duration of HTTP server requests.",
unit: "s",
advice: {
explicitBucketBoundaries: [
0.005,
0.01,
0.025,
0.05,
0.075,
0.1,
0.25,
0.5,
0.75,
1,
2.5,
5,
7.5,
10,
],
},
}),
requestBodySize: meter.createHistogram("http.server.request.body.size", {
description: "Size of HTTP server request bodies.",
unit: "By",
}),
responseBodySize: meter.createHistogram("http.server.response.body.size", {
description: "Size of HTTP server response bodies.",
unit: "By",
}),
};
return otelMetrics;
}
const kLenientAll = HTTPParser.kLenientAll | 0;
const kLenientNone = HTTPParser.kLenientNone | 0;
const kOnExecute = HTTPParser.kOnExecute | 0;
const kOnMessageBegin = HTTPParser.kOnMessageBegin | 0;
const _kOnTimeout = HTTPParser.kOnTimeout | 0;
class ConnectionsList {
constructor() {
this._all = new SafeSet();
this._active = new SafeMap(); }
add(socket) {
this._all.add(socket);
}
remove(socket) {
this._all.delete(socket);
this._active.delete(socket);
}
pushActive(socket) {
this._active.set(socket, {
headersCompleted: false,
startTime: performance.now(),
req: null,
});
}
popActive(socket) {
this._active.delete(socket);
}
popActiveIfReq(socket, req) {
const entry = this._active.get(socket);
if (entry && entry.req === req) {
this._active.delete(socket);
}
}
markHeadersCompleted(socket, req) {
const entry = this._active.get(socket);
if (entry) {
entry.headersCompleted = true;
entry.req = req;
}
}
expired(headersTimeout, requestTimeout) {
const now = performance.now();
const result = [];
for (const { 0: socket, 1: entry } of new SafeMapIterator(this._active)) {
const elapsed = now - entry.startTime;
if (!entry.headersCompleted && headersTimeout > 0) {
if (elapsed >= headersTimeout) {
ArrayPrototypePush(result, { socket });
continue;
}
}
if (requestTimeout > 0 && elapsed >= requestTimeout) {
if (entry.req?.complete) {
continue;
}
ArrayPrototypePush(result, { socket });
}
}
return result;
}
}
function onRequestTimeout(socket) {
const err = new Error("ERR_HTTP_REQUEST_TIMEOUT");
err.code = "ERR_HTTP_REQUEST_TIMEOUT";
FunctionPrototypeCall(socketOnError, socket, err);
}
const STATUS_CODES = {
100: "Continue",
101: "Switching Protocols",
102: "Processing",
103: "Early Hints",
200: "OK",
201: "Created",
202: "Accepted",
203: "Non-Authoritative Information",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
207: "Multi-Status",
208: "Already Reported",
226: "IM Used",
300: "Multiple Choices",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
307: "Temporary Redirect",
308: "Permanent Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Payload Too Large",
414: "URI Too Long",
415: "Unsupported Media Type",
416: "Range Not Satisfiable",
417: "Expectation Failed",
418: "I'm a Teapot",
421: "Misdirected Request",
422: "Unprocessable Entity",
423: "Locked",
424: "Failed Dependency",
425: "Too Early",
426: "Upgrade Required",
428: "Precondition Required",
429: "Too Many Requests",
431: "Request Header Fields Too Large",
451: "Unavailable For Legal Reasons",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported",
506: "Variant Also Negotiates",
507: "Insufficient Storage",
508: "Loop Detected",
509: "Bandwidth Limit Exceeded",
510: "Not Extended",
511: "Network Authentication Required",
};
function ServerResponse(req, options) {
FunctionPrototypeCall(OutgoingMessage, this, options);
if (req.method === "HEAD") this._hasBody = false;
this.req = req;
this.sendDate = true;
this._sent100 = false;
this._expect_continue = false;
if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {
this.useChunkedEncodingByDefault = chunkExpression.test(
req.headers?.te,
);
this.shouldKeepAlive = false;
}
if (onServerResponseCreatedChannel.hasSubscribers) {
onServerResponseCreatedChannel.publish({
request: req,
response: this,
});
}
}
ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype);
ObjectSetPrototypeOf(ServerResponse, OutgoingMessage);
ServerResponse.prototype.statusCode = 200;
ServerResponse.prototype.statusMessage = undefined;
function onServerResponseClose() {
if (this._httpMessage) {
emitCloseNT(this._httpMessage);
}
}
ServerResponse.prototype.assignSocket = function assignSocket(socket) {
if (socket._httpMessage) {
throw new ERR_HTTP_SOCKET_ASSIGNED();
}
socket._httpMessage = this;
if (socket._parent) {
socket._parent._httpMessage = this;
socket._parent._httpMessageDetached = false;
}
socket.on("close", onServerResponseClose);
this.socket = socket;
this.emit("socket", socket);
this._flush();
};
ServerResponse.prototype.detachSocket = function detachSocket(socket) {
assert(socket._httpMessage === this);
socket.removeListener("close", onServerResponseClose);
socket._httpMessage = null;
socket._httpMessageDetached = true;
if (socket._parent) {
socket._parent._httpMessage = null;
socket._parent._httpMessageDetached = true;
}
this.socket = null;
};
ServerResponse.prototype.writeContinue = function writeContinue(cb) {
this._writeRaw("HTTP/1.1 100 Continue\r\n\r\n", "ascii", cb);
this._sent100 = true;
};
ServerResponse.prototype.writeProcessing = function writeProcessing(cb) {
this._writeRaw("HTTP/1.1 102 Processing\r\n\r\n", "ascii", cb);
};
ServerResponse.prototype.writeInformation = function writeInformation(
statusCode,
headers,
cb,
) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT("write");
}
if (typeof headers === "function") {
cb = headers;
headers = undefined;
}
validateInteger(statusCode, "statusCode", 100, 199);
let head = `HTTP/1.1 ${statusCode} ${
STATUS_CODES[statusCode] || "unknown"
}\r\n`;
if (headers !== null && headers !== undefined) {
const keys = ObjectKeys(headers);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
validateHeaderName(key);
const value = headers[key];
validateHeaderValue(key, value);
head += key + ": " + value + "\r\n";
}
}
head += "\r\n";
this._writeRaw(head, "ascii", cb);
};
ServerResponse.prototype.writeEarlyHints = function writeEarlyHints(
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;
}
if (checkInvalidHeaderChar(link)) {
throw new ERR_INVALID_CHAR("header content", "Link");
}
head += "Link: " + link + "\r\n";
const keys = ObjectKeys(hints);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key !== "link") {
validateHeaderName(key);
const value = hints[key];
validateHeaderValue(key, value);
head += key + ": " + value + "\r\n";
}
}
head += "\r\n";
this._writeRaw(head, "ascii", cb);
};
ServerResponse.prototype._implicitHeader = function _implicitHeader() {
this.writeHead(this.statusCode);
};
ServerResponse.prototype.writeHead = function writeHead(
statusCode,
reason,
obj,
) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT("write");
}
statusCode |= 0;
if (statusCode < 100 || statusCode > 999) {
throw new ERR_INVALID_ARG_TYPE(
"statusCode",
"integer [100, 999]",
statusCode,
);
}
if (typeof reason === "string") {
this.statusMessage = reason;
} else {
this.statusMessage ||= STATUS_CODES[statusCode] || "unknown";
obj ??= reason;
}
this.statusCode = statusCode;
if (statusCode === 204 || statusCode === 304) {
this._hasBody = false;
}
let headers;
if (this[kOutHeaders]) {
if (ArrayIsArray(obj)) {
if (obj.length % 2 !== 0) {
throw new ERR_INVALID_ARG_VALUE("headers", obj);
}
for (let n = 0; n < obj.length; n += 2) {
const k = obj[n + 0];
if (k) this.removeHeader(k);
}
for (let n = 0; n < obj.length; n += 2) {
const k = obj[n + 0];
if (k) this.appendHeader(k, obj[n + 1]);
}
} else if (obj) {
const keys = ObjectKeys(obj);
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
if (k) this.setHeader(k, obj[k]);
}
}
headers = this[kOutHeaders];
} else {
headers = obj;
}
if (checkInvalidHeaderChar(this.statusMessage)) {
throw new ERR_INVALID_CHAR("statusMessage");
}
const statusLine = "HTTP/1.1 " + statusCode + " " + this.statusMessage +
"\r\n";
this._storeHeader(statusLine, headers);
return this;
};
function emitCloseNT(self) {
self.destroyed = true;
self._closed = true;
self.emit("close");
}
function connectionListener(socket) {
connectionListenerInternal(this, socket);
}
function connectionListenerInternal(server, socket) {
socket.server = server;
if (!server[kConnectionsKey]) server[kConnectionsKey] = new ConnectionsList();
const connections = server[kConnectionsKey];
connections.add(socket);
const onConnectionClose = () => connections.remove(socket);
socket.on("close", onConnectionClose);
if (server.timeout && typeof socket.setTimeout === "function") {
socket.setTimeout(server.timeout);
}
socket.on("timeout", socketOnTimeout);
const parser = parsers.alloc();
const lenient = server.insecureHTTPParser === undefined
? isLenient()
: server.insecureHTTPParser;
parser.initialize(
HTTPParser.REQUEST,
{},
server.maxHeaderSize || 0,
lenient ? kLenientAll : kLenientNone,
);
parser.socket = socket;
socket.parser = parser;
if (typeof server.maxHeadersCount === "number") {
parser.maxHeaderPairs = server.maxHeadersCount << 1;
}
const state = {
onData: null,
onEnd: null,
onClose: null,
onConnectionClose: onConnectionClose,
onDrain: null,
outgoing: [],
incoming: [],
outgoingData: 0,
requestsCount: 0,
keepAliveTimeoutSet: false,
keepAliveTimeout: null,
keepAliveTimeoutMsecs: 0,
keepAliveTimeoutSuspended: false,
};
state.onData = FunctionPrototypeBind(
socketOnData,
undefined,
server,
socket,
parser,
state,
);
state.onEnd = FunctionPrototypeBind(
socketOnEnd,
undefined,
server,
socket,
parser,
state,
);
state.onClose = FunctionPrototypeBind(
socketOnClose,
undefined,
socket,
state,
);
state.onDrain = FunctionPrototypeBind(
socketOnDrain,
undefined,
socket,
state,
);
socket.on("data", state.onData);
socket.on("error", socketOnError);
socket.on("end", state.onEnd);
socket.on("close", state.onClose);
socket.on("drain", state.onDrain);
parser.onIncoming = FunctionPrototypeBind(
parserOnIncoming,
undefined,
server,
socket,
state,
);
connections.pushActive(socket);
if (
socket._handle?.isStreamBase &&
!socket._handle._consumed
) {
parser._consumed = true;
socket._handle._consumed = true;
parser.consume(socket._handle);
}
parser[kOnExecute] = FunctionPrototypeBind(
onParserExecute,
undefined,
server,
socket,
parser,
state,
);
parser[kOnMessageBegin] = FunctionPrototypeBind(
onParserMessageBegin,
undefined,
server,
socket,
);
socket._paused = false;
}
function onParserMessageBegin(server, socket) {
const connections = server[kConnectionsKey];
if (connections) {
connections.popActive(socket);
connections.pushActive(socket);
}
}
function onParserExecute(server, socket, parser, state, ret, d) {
if (!state.keepAliveTimeoutSet) {
socket._unrefTimer?.();
}
if (d !== undefined && !Buffer.isBuffer(d)) {
d = Buffer.from(
TypedArrayPrototypeGetBuffer(d),
TypedArrayPrototypeGetByteOffset(d),
TypedArrayPrototypeGetByteLength(d),
);
}
if (d !== undefined) {
parser._lastRawPacket = d;
}
onParserExecuteCommon(server, socket, parser, state, ret, d);
}
function socketOnTimeout() {
const req = this.parser?.incoming;
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 socketOnClose(socket, state) {
destroySuspendedKeepAliveTimeout(state);
freeParser(socket.parser, undefined, socket);
abortIncoming(state.incoming);
}
function abortIncoming(incoming) {
while (incoming.length) {
const req = ArrayPrototypeShift(incoming);
req.destroy(connResetException("aborted"));
}
}
function socketOnEnd(server, socket, parser, state) {
const ret = parser.finish();
if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, ret)) {
prepareError(ret, parser, parser._lastRawPacket);
FunctionPrototypeCall(socketOnError, socket, ret);
return;
}
if (!server.httpAllowHalfOpen) {
abortIncoming(state.incoming);
if (socket.writable) socket.end();
} else if (state.outgoing.length) {
state.outgoing[state.outgoing.length - 1]._last = true;
} else if (socket._httpMessage) {
socket._httpMessage._last = true;
} else if (socket.writable) {
socket.end();
}
}
function socketOnData(server, socket, parser, state, d) {
assert(googLength(d));
parser._lastRawPacket = d;
const ret = parser.execute(d);
onParserExecuteCommon(server, socket, parser, state, ret, d);
}
function googLength(d) {
return d.length || TypedArrayPrototypeGetByteLength(d);
}
function onParserExecuteCommon(server, socket, parser, state, ret, d) {
resetSocketTimeout(server, socket, state);
if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, ret)) {
prepareError(ret, parser, d);
FunctionPrototypeCall(socketOnError, socket, ret);
return;
}
if (parser.incoming?.upgrade) {
const bytesParsed = ret;
const req = parser.incoming;
const eventName = req.method === "CONNECT" ? "connect" : "upgrade";
if (server.listenerCount(eventName) === 0) {
socket.destroy();
return;
}
socket.removeListener("data", state.onData);
socket.removeListener("end", state.onEnd);
socket.removeListener("close", state.onClose);
socket.removeListener("close", state.onConnectionClose);
socket.removeListener("drain", state.onDrain);
socket.removeListener("error", socketOnError);
socket.removeListener("timeout", socketOnTimeout);
const connections = server[kConnectionsKey];
if (connections) connections.remove(socket);
parser.finish();
freeParser(parser, req, socket);
const bodyHead = d.slice(bytesParsed);
socket.readableFlowing = null;
server.emit(eventName, req, socket, bodyHead);
}
}
function resetSocketTimeout(server, socket, state, allowKeepAliveReuse = true) {
if (!state.keepAliveTimeoutSet) return;
const keepAliveTimeout = state.keepAliveTimeout;
if (
allowKeepAliveReuse &&
server.timeout === 0 &&
socket.setTimeout === net.Socket.prototype.setTimeout &&
socket[kTimeout] === keepAliveTimeout &&
ArrayPrototypeIncludes(socket.listeners("timeout"), socketOnTimeout)
) {
suspendTimeout(keepAliveTimeout);
socket[kTimeout] = null;
socket.timeout = 0;
state.keepAliveTimeoutSet = false;
state.keepAliveTimeoutSuspended = true;
return;
}
socket.setTimeout(server.timeout || 0);
state.keepAliveTimeoutSet = false;
state.keepAliveTimeout = null;
state.keepAliveTimeoutMsecs = 0;
state.keepAliveTimeoutSuspended = false;
}
function socketOnDrain(socket, state) {
const needPause = state.outgoingData > socket.writableHighWaterMark;
if (socket._paused && !needPause) {
socket._paused = false;
if (socket.parser) {
socket.resume();
}
}
const msg = socket._httpMessage;
if (msg && !msg.finished && msg[kNeedDrain]) {
msg[kNeedDrain] = false;
msg.emit("drain");
}
}
function destroySuspendedKeepAliveTimeout(state) {
if (!state.keepAliveTimeoutSuspended) return;
const keepAliveTimeout = state.keepAliveTimeout;
if (keepAliveTimeout !== null) {
keepAliveTimeout[kDestroy]();
}
state.keepAliveTimeout = null;
state.keepAliveTimeoutMsecs = 0;
state.keepAliveTimeoutSuspended = false;
}
const badRequestResponse =
"HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n";
const requestHeaderFieldsTooLargeResponse =
"HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n";
const requestTimeoutResponse =
"HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n";
function socketOnError(e) {
this.removeListener("error", socketOnError);
this.on("error", noop);
if (this.parser) {
this.parser.finish();
freeParser(this.parser, undefined, this);
}
if (!this.server.emit("clientError", e, this)) {
if (
this.writable &&
(!this._httpMessage || !this._httpMessage._headerSent)
) {
let response;
switch (e.code) {
case "HPE_HEADER_OVERFLOW":
response = requestHeaderFieldsTooLargeResponse;
break;
case "ERR_HTTP_REQUEST_TIMEOUT":
response = requestTimeoutResponse;
break;
default:
response = badRequestResponse;
break;
}
this.write(response);
}
this.destroy(e);
}
}
function noop() {}
function updateOutgoingData(socket, state, delta) {
state.outgoingData += delta;
if (
socket._paused &&
state.outgoingData < socket.writableHighWaterMark
) {
return;
}
socketOnDrain(socket, state);
}
function parserOnIncoming(server, socket, state, req, keepAlive) {
if (
socket.isDenoServeAddressOverride &&
(StringPrototypeStartsWith(req.url, "http://") ||
StringPrototypeStartsWith(req.url, "https://"))
) {
const schemeEnd = StringPrototypeIndexOf(req.url, "://") + 3;
const pathStart = StringPrototypeIndexOf(req.url, "/", schemeEnd);
req.url = pathStart === -1 ? "/" : StringPrototypeSlice(req.url, pathStart);
}
resetSocketTimeout(server, socket, state, !req.upgrade);
const connections = server[kConnectionsKey];
if (connections) connections.markHeadersCompleted(socket, req);
if (req.upgrade && req.method !== "CONNECT") {
if (
server.shouldUpgradeCallback !== undefined &&
!server.shouldUpgradeCallback(req)
) {
req.upgrade = false;
}
}
if (req.upgrade) {
req.upgrade = req.method === "CONNECT" || true;
if (req.upgrade) return 0;
}
ArrayPrototypePush(state.incoming, req);
if (hasNodeObserverForType("http")) {
req[kPerfStartTime] = performance.now();
}
if (!socket._paused) {
const ws = socket._writableState;
if (
ws?.needDrain || state.outgoingData >= socket.writableHighWaterMark
) {
socket._paused = true;
socket.pause();
}
}
const res = new server[kServerResponse](req, {
rejectNonStandardBodyWrites: server.rejectNonStandardBodyWrites,
highWaterMark: server.highWaterMark,
});
res._keepAliveTimeout = server.keepAliveTimeout;
res._maxRequestsPerSocket = server.maxRequestsPerSocket;
res._onPendingData = FunctionPrototypeBind(
updateOutgoingData,
undefined,
socket,
state,
);
res.shouldKeepAlive = keepAlive;
res[kUniqueHeaders] = server[kUniqueHeaders];
if (server.optimizeEmptyRequests && isRequestKnownEmpty(req)) {
req._dumpAndCloseReadable();
}
if (otelState.TRACING_ENABLED) {
let context = ContextManager.active();
if (otelState.PROPAGATORS.length > 0) {
for (
const propagator of new SafeArrayIterator(otelState.PROPAGATORS)
) {
context = propagator.extract(context, req.headers, {
get(carrier, key) {
return carrier[key];
},
keys(carrier) {
return ObjectKeys(carrier);
},
});
}
}
const span = builtinTracer().startSpan(req.method, { kind: 1 }, context); const url = req.url || "/";
const host = req.headers?.host || "localhost";
const scheme = socket.encrypted ? "https" : "http";
span.setAttribute("http.request.method", req.method);
span.setAttribute("url.full", `${scheme}://${host}${url}`);
span.setAttribute("url.scheme", scheme);
span.setAttribute("url.path", StringPrototypeSplit(url, "?")[0]);
span.setAttribute(
"url.query",
StringPrototypeIncludes(url, "?")
? StringPrototypeSplit(url, "?")[1]
: "",
);
res[kOtelSpan] = span;
}
if (otelState.METRICS_ENABLED) {
res[kOtelStartTime] = performance.now();
res[kOtelReqBodySize] = 0;
const metrics = getOtelMetrics();
const scheme = socket.encrypted ? "https" : "http";
metrics.activeRequests.add(1, {
"http.request.method": req.method,
"url.scheme": scheme,
});
}
if (socket._httpMessage) {
ArrayPrototypePush(state.outgoing, res);
} else {
res.assignSocket(socket);
}
res.on(
"finish",
FunctionPrototypeBind(
resOnFinish,
undefined,
req,
res,
socket,
state,
server,
),
);
if (onServerRequestStartChannel.hasSubscribers) {
onServerRequestStartChannel.publish({
request: req,
response: res,
socket,
server,
});
}
const prevAsyncResource = enterAsyncResourceIfActive(req);
try {
let handled = false;
if (req.httpVersionMajor === 1 && req.httpVersionMinor === 1) {
if (
server.requireHostHeader !== false &&
req.headers.host === undefined
) {
res.writeHead(400, ["Connection", "close"]);
res.end();
return 0;
}
const isRequestsLimitSet =
typeof server.maxRequestsPerSocket === "number" &&
server.maxRequestsPerSocket > 0;
if (isRequestsLimitSet) {
state.requestsCount++;
res.maxRequestsOnConnectionReached =
server.maxRequestsPerSocket <= state.requestsCount;
}
if (
isRequestsLimitSet &&
server.maxRequestsPerSocket < state.requestsCount
) {
handled = true;
server.emit("dropRequest", req, socket);
res.writeHead(503);
res.end();
} else if (req.headers.expect !== undefined) {
handled = true;
if (continueExpression.test(req.headers.expect)) {
res._expect_continue = true;
if (server.listenerCount("checkContinue") > 0) {
server.emit("checkContinue", req, res);
} else {
res.writeContinue();
server.emit("request", req, res);
}
} else if (server.listenerCount("checkExpectation") > 0) {
server.emit("checkExpectation", req, res);
} else {
res.writeHead(417);
res.end();
}
}
}
if (!handled) {
server.emit("request", req, res);
}
} finally {
exitAsyncResourceIfActive(prevAsyncResource);
}
return 0;
}
function isRequestKnownEmpty(req) {
return req.headers["content-length"] === undefined &&
req.headers["transfer-encoding"] === undefined;
}
function resOnFinish(req, res, socket, state, server) {
if (onServerResponseFinishChannel.hasSubscribers) {
onServerResponseFinishChannel.publish({
request: req,
response: res,
socket,
server,
});
}
assert(state.incoming.length === 0 || state.incoming[0] === req);
const span = res[kOtelSpan];
if (span) {
span.setAttribute("http.response.status_code", String(res.statusCode));
if (res.statusCode >= 500) {
span.setAttribute("error.type", String(res.statusCode));
span.setStatus({ code: 2 });
}
span.end();
res[kOtelSpan] = null;
}
const perfStartTime = req[kPerfStartTime];
if (perfStartTime !== undefined && hasNodeObserverForType("http")) {
enqueueNodePerformanceEntry({
name: "HttpRequest",
entryType: "http",
startTime: perfStartTime,
duration: performance.now() - perfStartTime,
detail: {
req: {
method: req.method,
url: req.url || "/",
headers: req.headers,
},
res: {
statusCode: res.statusCode,
statusMessage: res.statusMessage ||
STATUS_CODES[res.statusCode] || "",
headers: res.getHeaders(),
},
},
});
}
if (res[kOtelStartTime] !== undefined) {
const durationS = (performance.now() - res[kOtelStartTime]) / 1000;
const scheme = socket.encrypted ? "https" : "http";
const metricAttrs = {
"http.request.method": req.method,
"http.response.status_code": res.statusCode,
"network.protocol.version":
`${req.httpVersionMajor}.${req.httpVersionMinor}`,
"url.scheme": scheme,
};
const metrics = getOtelMetrics();
metrics.activeRequests.add(-1, {
"http.request.method": req.method,
"url.scheme": scheme,
});
metrics.requestDuration.record(durationS, metricAttrs);
metrics.requestBodySize.record(res[kOtelReqBodySize] || 0, metricAttrs);
metrics.responseBodySize.record(0, metricAttrs);
res[kOtelStartTime] = undefined;
}
ArrayPrototypeShift(state.incoming);
if (!req._consuming && !req._readableState?.resumeScheduled) {
req._dump();
}
res.detachSocket(socket);
clearIncoming(req);
nextTick(emitCloseNT, res);
const connections = server[kConnectionsKey];
if (connections) connections.popActiveIfReq(socket, req);
if (res._last) {
if (typeof socket.destroySoon === "function") {
socket.destroySoon();
} else {
socket.end();
}
} else if (state.outgoing.length === 0) {
if (!server.listening) {
socket.destroy();
} else {
const keepAliveTimeout = NumberIsFinite(server.keepAliveTimeout) &&
server.keepAliveTimeout >= 0
? server.keepAliveTimeout
: 0;
if (keepAliveTimeout) {
const timeoutMsecs = keepAliveTimeout + 1000;
const suspendedTimeout = state.keepAliveTimeout;
if (
state.keepAliveTimeoutSuspended &&
socket.setTimeout === net.Socket.prototype.setTimeout &&
socket[kTimeout] === null &&
state.keepAliveTimeoutMsecs === timeoutMsecs
) {
socket[kTimeout] = suspendedTimeout;
socket.timeout = timeoutMsecs;
suspendedTimeout.refresh();
} else {
if (state.keepAliveTimeoutSuspended) {
suspendedTimeout[kDestroy]();
}
socket.setTimeout(timeoutMsecs);
state.keepAliveTimeout = socket[kTimeout];
state.keepAliveTimeoutMsecs = timeoutMsecs;
}
state.keepAliveTimeoutSet = true;
state.keepAliveTimeoutSuspended = false;
} else if (
state.keepAliveTimeoutSuspended
) {
state.keepAliveTimeout[kDestroy]();
state.keepAliveTimeout = null;
state.keepAliveTimeoutMsecs = 0;
state.keepAliveTimeoutSuspended = false;
}
}
} else {
const m = ArrayPrototypeShift(state.outgoing);
if (m) {
m.assignSocket(socket);
}
}
}
function clearIncoming(req) {
req ||= this;
const parser = req.socket?.parser;
if (parser && parser.incoming === req) {
if (req.readableEnded) {
parser.incoming = null;
} else {
req.on("end", clearIncoming);
}
}
}
function Server(options, requestListener) {
if (!ObjectPrototypeIsPrototypeOf(Server.prototype, this)) {
return new Server(options, requestListener);
}
if (typeof options === "function") {
requestListener = options;
options = kEmptyObject;
} else if (options == null) {
options = kEmptyObject;
} else {
validateObject(options, "options");
}
FunctionPrototypeCall(storeHTTPOptions, this, options);
FunctionPrototypeCall(net.Server, this, {
allowHalfOpen: true,
noDelay: options.noDelay ?? true,
keepAlive: options.keepAlive,
keepAliveInitialDelay: options.keepAliveInitialDelay,
highWaterMark: options.highWaterMark,
});
if (requestListener) {
this.on("request", requestListener);
}
this.httpAllowHalfOpen = false;
this.on("connection", connectionListener);
this.on("listening", setupConnectionsTracking);
this.timeout = 0;
this.maxHeadersCount = null;
this.maxRequestsPerSocket = 0;
this[kUniqueHeaders] = parseUniqueHeadersOption(options.uniqueHeaders);
}
class ConnectionsCheckingInterval {
_timerId = 0;
_destroyed = false;
}
function setupConnectionsTracking() {
this[kConnectionsKey] ||= new ConnectionsList();
destroyConnectionsCheckingInterval(this[kConnectionsCheckingInterval]);
const interval = this.connectionsCheckingInterval || 30_000;
const handle = new ConnectionsCheckingInterval();
handle._timerId = core.createSystemInterval(
FunctionPrototypeBind(checkConnections, this),
interval,
);
this[kConnectionsCheckingInterval] = handle;
}
function destroyConnectionsCheckingInterval(handle) {
if (handle && !handle._destroyed) {
core.cancelTimer(handle._timerId);
handle._destroyed = true;
}
}
function checkConnections() {
if (this.headersTimeout === 0 && this.requestTimeout === 0) {
return;
}
const expired = this[kConnectionsKey].expired(
this.headersTimeout,
this.requestTimeout,
);
for (let i = 0; i < expired.length; i++) {
const socket = expired[i].socket;
if (socket) {
onRequestTimeout(socket);
}
}
}
function httpServerPreClose(server) {
server.closeIdleConnections();
destroyConnectionsCheckingInterval(server[kConnectionsCheckingInterval]);
}
function storeHTTPOptions(options) {
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
this[kServerResponse] = options.ServerResponse || ServerResponse;
const highWaterMark = options.highWaterMark;
if (highWaterMark !== undefined) {
validateInteger(highWaterMark, "options.highWaterMark", 1);
}
this.highWaterMark = highWaterMark;
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 optimizeEmptyRequests = options.optimizeEmptyRequests;
if (optimizeEmptyRequests !== undefined) {
validateBoolean(
optimizeEmptyRequests,
"options.optimizeEmptyRequests",
);
}
this.optimizeEmptyRequests = optimizeEmptyRequests;
const requestTimeout = options.requestTimeout;
if (requestTimeout !== undefined) {
validateInteger(requestTimeout, "requestTimeout", 0);
this.requestTimeout = requestTimeout;
} else {
this.requestTimeout = 300_000; }
const headersTimeout = options.headersTimeout;
if (headersTimeout !== undefined) {
validateInteger(headersTimeout, "headersTimeout", 0);
this.headersTimeout = headersTimeout;
} else {
this.headersTimeout = MathMin(60_000, this.requestTimeout);
}
if (
this.requestTimeout > 0 && this.headersTimeout > 0 &&
this.headersTimeout > this.requestTimeout
) {
throw new 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;
}
const connectionsCheckingInterval = options.connectionsCheckingInterval;
if (connectionsCheckingInterval !== undefined) {
validateInteger(
connectionsCheckingInterval,
"connectionsCheckingInterval",
1,
);
this.connectionsCheckingInterval = connectionsCheckingInterval;
} else {
this.connectionsCheckingInterval = 30_000;
}
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;
}
const shouldUpgradeCallback = options.shouldUpgradeCallback;
if (shouldUpgradeCallback !== undefined) {
validateFunction(
shouldUpgradeCallback,
"options.shouldUpgradeCallback",
);
this.shouldUpgradeCallback = shouldUpgradeCallback;
} else {
this.shouldUpgradeCallback = function () {
return this.listenerCount("upgrade") > 0;
};
}
}
ObjectSetPrototypeOf(Server.prototype, net.Server.prototype);
ObjectSetPrototypeOf(Server, net.Server);
Server.prototype.listen = function listen(...args) {
const applied = applyAddressOverride();
switch (applied.mode) {
case "none":
return FunctionPrototypeApply(net.Server.prototype.listen, this, args);
case "tcp": {
let cb;
const last = args[args.length - 1];
if (typeof last === "function") {
cb = last;
args = ArrayPrototypeSlice(args, 0, -1);
}
const rewritten = [{ host: applied.host, port: applied.port }];
if (cb) ArrayPrototypePush(rewritten, cb);
this.once("listening", notifyAddressOverrideServing);
return FunctionPrototypeApply(
net.Server.prototype.listen,
this,
rewritten,
);
}
case "override-only": {
let cb;
const last = args[args.length - 1];
if (typeof last === "function") cb = last;
if (cb) this.once("listening", cb);
this._handle = {
close() {},
ref() {},
unref() {},
};
startOverrideListener(this, applied.override, connectionListener);
nextTick(() => this.emit("listening"));
return this;
}
case "duplicate": {
startOverrideListener(this, applied.override, connectionListener);
return FunctionPrototypeApply(net.Server.prototype.listen, this, args);
}
}
};
Server.prototype.setTimeout = function setTimeout(msecs, callback) {
this.timeout = msecs;
if (callback) {
this.on("timeout", callback);
}
return this;
};
Server.prototype.close = function close(cb) {
httpServerPreClose(this);
return FunctionPrototypeCall(net.Server.prototype.close, this, cb);
};
Server.prototype.closeAllConnections = function closeAllConnections() {
const connections = this[kConnectionsKey];
if (connections) {
for (const socket of new SafeSetIterator(connections._all)) {
socket.destroy();
}
}
};
Server.prototype.closeIdleConnections = function closeIdleConnections() {
const connections = this[kConnectionsKey];
if (connections) {
for (const socket of new SafeSetIterator(connections._all)) {
if (!socket._httpMessage && socket._httpMessageDetached) {
socket.destroy();
}
}
}
};
export {
applyAddressOverride,
connectionListener as _connectionListener,
httpServerPreClose,
kConnectionsCheckingInterval,
kIncomingMessage,
kServerResponse,
notifyAddressOverrideServing,
Server,
SERVER_KIND_NODE_HTTP2,
ServerResponse,
setupConnectionsTracking,
startOverrideListener,
STATUS_CODES,
storeHTTPOptions,
};
export default {
_connectionListener: connectionListener,
httpServerPreClose,
kConnectionsCheckingInterval,
kIncomingMessage,
kServerResponse,
Server,
ServerResponse,
setupConnectionsTracking,
STATUS_CODES,
storeHTTPOptions,
};