import { core, primordials } from "ext:core/mod.js";
const {
ArrayPrototypePush,
FunctionPrototypeCall,
ObjectDefineProperty,
ObjectSetPrototypeOf,
StringPrototypeCharCodeAt,
StringPrototypeSlice,
StringPrototypeToLowerCase,
Symbol,
} = primordials;
import { finished, Readable } from "node:stream";
const { nextTick } = core.loadExtScript("ext:deno_node/_next_tick.ts");
const kHeaders = Symbol("kHeaders");
const kHeadersDistinct = Symbol("kHeadersDistinct");
const kHeadersCount = Symbol("kHeadersCount");
const kTrailers = Symbol("kTrailers");
const kTrailersDistinct = Symbol("kTrailersDistinct");
const kTrailersCount = Symbol("kTrailersCount");
function readStart(socket) {
if (socket && !socket._paused && socket.readable) {
socket.resume();
}
}
function readStop(socket) {
if (socket) {
socket.pause();
}
}
function IncomingMessage(socket, options) {
let streamOptions;
if (socket) {
streamOptions = {
highWaterMark: options?.highWaterMark ?? socket.readableHighWaterMark,
};
}
FunctionPrototypeCall(Readable, this, streamOptions);
this._readableState.readingMore = true;
this.socket = socket;
this.httpVersionMajor = null;
this.httpVersionMinor = null;
this.httpVersion = null;
this.complete = false;
this[kHeaders] = null;
this[kHeadersCount] = 0;
this.rawHeaders = [];
this[kTrailers] = null;
this[kTrailersCount] = 0;
this.rawTrailers = [];
this.joinDuplicateHeaders = false;
this.aborted = false;
this.upgrade = null;
this.url = "";
this.method = null;
this.statusCode = null;
this.statusMessage = null;
this.client = socket;
this._consuming = false;
this._dumped = false;
}
ObjectSetPrototypeOf(IncomingMessage.prototype, Readable.prototype);
ObjectSetPrototypeOf(IncomingMessage, Readable);
ObjectDefineProperty(IncomingMessage.prototype, "connection", {
__proto__: null,
get: function () {
return this.socket;
},
set: function (val) {
this.socket = val;
},
});
ObjectDefineProperty(IncomingMessage.prototype, "headers", {
__proto__: null,
get: function () {
if (!this[kHeaders]) {
this[kHeaders] = {};
const src = this.rawHeaders;
const dst = this[kHeaders];
for (let n = 0; n < this[kHeadersCount]; n += 2) {
this._addHeaderLine(src[n + 0], src[n + 1], dst);
}
}
return this[kHeaders];
},
set: function (val) {
this[kHeaders] = val;
},
});
ObjectDefineProperty(IncomingMessage.prototype, "headersDistinct", {
__proto__: null,
get: function () {
if (!this[kHeadersDistinct]) {
this[kHeadersDistinct] = {};
const src = this.rawHeaders;
const dst = this[kHeadersDistinct];
for (let n = 0; n < this[kHeadersCount]; n += 2) {
this._addHeaderLineDistinct(src[n + 0], src[n + 1], dst);
}
}
return this[kHeadersDistinct];
},
set: function (val) {
this[kHeadersDistinct] = val;
},
});
ObjectDefineProperty(IncomingMessage.prototype, "trailers", {
__proto__: null,
get: function () {
if (!this[kTrailers]) {
this[kTrailers] = {};
const src = this.rawTrailers;
const dst = this[kTrailers];
for (let n = 0; n < this[kTrailersCount]; n += 2) {
this._addHeaderLine(src[n + 0], src[n + 1], dst);
}
}
return this[kTrailers];
},
set: function (val) {
this[kTrailers] = val;
},
});
ObjectDefineProperty(IncomingMessage.prototype, "trailersDistinct", {
__proto__: null,
get: function () {
if (!this[kTrailersDistinct]) {
this[kTrailersDistinct] = {};
const src = this.rawTrailers;
const dst = this[kTrailersDistinct];
for (let n = 0; n < this[kTrailersCount]; n += 2) {
this._addHeaderLineDistinct(src[n + 0], src[n + 1], dst);
}
}
return this[kTrailersDistinct];
},
set: function (val) {
this[kTrailersDistinct] = val;
},
});
IncomingMessage.prototype.setTimeout = function setTimeout(msecs, callback) {
if (callback) {
this.on("timeout", callback);
}
this.socket.setTimeout(msecs);
return this;
};
IncomingMessage.prototype._read = function _read(_n) {
if (!this._consuming) {
this._readableState.readingMore = false;
this._consuming = true;
}
if (this.socket.readable) {
readStart(this.socket);
}
};
IncomingMessage.prototype._destroy = function _destroy(err, cb) {
if (!this.readableEnded || !this.complete) {
this.aborted = true;
this.emit("aborted");
}
if (this.socket && !this.socket.destroyed && this.aborted) {
this.socket.destroy(err);
const cleanup = finished(this.socket, (e) => {
if (e?.code === "ERR_STREAM_PREMATURE_CLOSE") {
e = null;
}
cleanup();
nextTick(onError, this, e || err, cb);
});
} else {
nextTick(onError, this, err, cb);
}
};
IncomingMessage.prototype._addHeaderLines = _addHeaderLines;
function _addHeaderLines(headers, n) {
if (headers?.length) {
let dest;
if (this.complete) {
this.rawTrailers = headers;
this[kTrailersCount] = n;
dest = this[kTrailers];
} else {
this.rawHeaders = headers;
this[kHeadersCount] = n;
dest = this[kHeaders];
}
if (dest) {
for (let i = 0; i < n; i += 2) {
this._addHeaderLine(headers[i], headers[i + 1], dest);
}
}
}
}
function matchKnownFields(field, lowercased) {
switch (field.length) {
case 3:
if (field === "Age" || field === "age") return "age";
break;
case 4:
if (field === "Host" || field === "host") return "host";
if (field === "From" || field === "from") return "from";
if (field === "ETag" || field === "etag") return "etag";
if (field === "Date" || field === "date") return "\u0000date";
if (field === "Vary" || field === "vary") return "\u0000vary";
break;
case 6:
if (field === "Server" || field === "server") return "server";
if (field === "Cookie" || field === "cookie") return "\u0002cookie";
if (field === "Origin" || field === "origin") return "\u0000origin";
if (field === "Expect" || field === "expect") return "\u0000expect";
if (field === "Accept" || field === "accept") return "\u0000accept";
break;
case 7:
if (field === "Referer" || field === "referer") return "referer";
if (field === "Expires" || field === "expires") return "expires";
if (field === "Upgrade" || field === "upgrade") return "\u0000upgrade";
break;
case 8:
if (field === "Location" || field === "location") return "location";
if (field === "If-Match" || field === "if-match") {
return "\u0000if-match";
}
break;
case 10:
if (field === "User-Agent" || field === "user-agent") {
return "user-agent";
}
if (field === "Set-Cookie" || field === "set-cookie") return "\u0001";
if (field === "Connection" || field === "connection") {
return "\u0000connection";
}
break;
case 11:
if (field === "Retry-After" || field === "retry-after") {
return "retry-after";
}
break;
case 12:
if (field === "Content-Type" || field === "content-type") {
return "content-type";
}
if (field === "Max-Forwards" || field === "max-forwards") {
return "max-forwards";
}
break;
case 13:
if (field === "Authorization" || field === "authorization") {
return "authorization";
}
if (field === "Last-Modified" || field === "last-modified") {
return "last-modified";
}
if (field === "Cache-Control" || field === "cache-control") {
return "\u0000cache-control";
}
if (field === "If-None-Match" || field === "if-none-match") {
return "\u0000if-none-match";
}
break;
case 14:
if (field === "Content-Length" || field === "content-length") {
return "content-length";
}
break;
case 15:
if (field === "Accept-Encoding" || field === "accept-encoding") {
return "\u0000accept-encoding";
}
if (field === "Accept-Language" || field === "accept-language") {
return "\u0000accept-language";
}
if (field === "X-Forwarded-For" || field === "x-forwarded-for") {
return "\u0000x-forwarded-for";
}
break;
case 16:
if (field === "Content-Encoding" || field === "content-encoding") {
return "\u0000content-encoding";
}
if (field === "X-Forwarded-Host" || field === "x-forwarded-host") {
return "\u0000x-forwarded-host";
}
break;
case 17:
if (field === "If-Modified-Since" || field === "if-modified-since") {
return "if-modified-since";
}
if (field === "Transfer-Encoding" || field === "transfer-encoding") {
return "\u0000transfer-encoding";
}
if (field === "X-Forwarded-Proto" || field === "x-forwarded-proto") {
return "\u0000x-forwarded-proto";
}
break;
case 19:
if (
field === "Proxy-Authorization" || field === "proxy-authorization"
) {
return "proxy-authorization";
}
if (
field === "If-Unmodified-Since" || field === "if-unmodified-since"
) {
return "if-unmodified-since";
}
break;
}
if (lowercased) {
return "\u0000" + field;
}
return matchKnownFields(StringPrototypeToLowerCase(field), true);
}
IncomingMessage.prototype._addHeaderLine = _addHeaderLine;
function _addHeaderLine(field, value, dest) {
if (
field.length > 2 && field[1] === "-" &&
(field[0] === "X" || field[0] === "x")
) {
field = StringPrototypeToLowerCase(field);
if (typeof dest[field] === "string") {
dest[field] += ", " + value;
} else {
dest[field] = value;
}
return;
}
field = matchKnownFields(field);
const flag = StringPrototypeCharCodeAt(field, 0);
if (flag === 0 || flag === 2) {
field = StringPrototypeSlice(field, 1);
if (typeof dest[field] === "string") {
dest[field] += (flag === 0 ? ", " : "; ") + value;
} else {
dest[field] = value;
}
} else if (flag === 1) {
if (dest["set-cookie"] !== undefined) {
ArrayPrototypePush(dest["set-cookie"], value);
} else {
dest["set-cookie"] = [value];
}
} else if (this.joinDuplicateHeaders) {
if (dest[field] === undefined) {
dest[field] = value;
} else {
dest[field] += ", " + value;
}
} else if (dest[field] === undefined) {
dest[field] = value;
}
}
IncomingMessage.prototype._addHeaderLineDistinct = _addHeaderLineDistinct;
function _addHeaderLineDistinct(field, value, dest) {
field = StringPrototypeToLowerCase(field);
if (!dest[field]) {
dest[field] = [value];
} else {
ArrayPrototypePush(dest[field], value);
}
}
IncomingMessage.prototype._dumpAndCloseReadable =
function _dumpAndCloseReadable() {
this._dumped = true;
this._readableState.ended = true;
this._readableState.endEmitted = true;
this._readableState.destroyed = true;
this._readableState.closed = true;
this._readableState.closeEmitted = true;
};
IncomingMessage.prototype._dump = function _dump() {
if (!this._dumped) {
this._dumped = true;
this.removeAllListeners("data");
this.resume();
}
};
function onError(self, error, cb) {
if (self.listenerCount("error") === 0) {
cb();
} else {
cb(error);
}
}
export { IncomingMessage, readStart, readStop };
export default {
IncomingMessage,
readStart,
readStop,
};