Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4453792
http.mjs
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
26 KB
Referenced Files
None
Subscribers
None
http.mjs
View Options
import net from 'node:net';
import httpParser from 'ant:internal/http_parser';
import httpWriter from 'ant:internal/http_writer';
import { EventEmitter } from 'node:events';
import { STATUS_CODES } from 'ant:internal/http_metadata';
function createHeadersObject() {
return Object.create(null);
}
function normalizeHeaderName(name) {
return String(name).toLowerCase();
}
function appendHeaderValue(headers, key, value) {
if (key === 'set-cookie') {
if (!headers[key]) headers[key] = [];
headers[key].push(value);
return;
}
if (headers[key] === undefined) {
headers[key] = value;
return;
}
if (key === 'cookie') headers[key] += '; ' + value;
else headers[key] += ', ' + value;
}
function buildHeaders(rawHeaders) {
const headers = createHeadersObject();
let i = 0;
for (i = 0; i + 1 < rawHeaders.length; i += 2) {
appendHeaderValue(headers, normalizeHeaderName(rawHeaders[i]), String(rawHeaders[i + 1]));
}
return headers;
}
function bufferFrom(value, encoding) {
if (value === undefined || value === null) return Buffer.alloc(0);
if (Buffer.isBuffer(value)) return value;
if (value && typeof value === 'object' && typeof value.byteLength === 'number') return Buffer.from(value);
if (typeof value === 'string') return Buffer.from(value, encoding || 'utf8');
return Buffer.from(String(value), encoding || 'utf8');
}
function appendRawHeader(rawHeaders, name, value) {
if (Array.isArray(value)) {
value.forEach(item => rawHeaders.push(name, String(item)));
return;
}
rawHeaders.push(name, String(value));
}
function hasResponseBody(method, statusCode) {
if (method === 'HEAD') return false;
if (statusCode === 204 || statusCode === 304) return false;
return true;
}
function applyHeaderObject(target, headers) {
if (!headers) return;
if (Array.isArray(headers)) {
for (let i = 0; i + 1 < headers.length; i += 2) {
target.setHeader(headers[i], headers[i + 1]);
}
return;
}
Object.keys(headers).forEach(name => {
target.setHeader(name, headers[name]);
});
}
function makeSocketState(server, socket) {
return {
server,
socket,
buffered: Buffer.alloc(0),
activeResponse: null,
closed: false
};
}
function appendSocketChunk(buffered, chunk) {
const next = bufferFrom(chunk);
if (buffered.length === 0) return next;
return Buffer.concat([buffered, next]);
}
function handleClientError(server, socket, error) {
if (server.listenerCount('clientError') > 0) {
server.emit('clientError', error, socket);
return;
}
if (socket && !socket.destroyed) {
socket.end(httpWriter.writeBasicResponse(400, 'Bad Request', 'text/plain;charset=UTF-8', 'Bad Request', false));
}
}
function clientNotImplemented() {
throw new Error('node:http client transport is not implemented yet');
}
function isURLLike(value) {
return !!value && typeof value === 'object' && typeof value.href === 'string';
}
function cloneOptionObject(value) {
return value && typeof value === 'object' ? { ...value } : {};
}
function normalizeRequestArgs(input, options, callback) {
let resolvedInput = input;
let resolvedOptions = options;
let resolvedCallback = callback;
if (typeof resolvedOptions === 'function') {
resolvedCallback = resolvedOptions;
resolvedOptions = undefined;
}
if (typeof resolvedInput === 'function' || resolvedInput === undefined || resolvedInput === null) {
resolvedCallback = typeof resolvedInput === 'function' ? resolvedInput : resolvedCallback;
resolvedInput = undefined;
}
return {
input: resolvedInput,
options: cloneOptionObject(resolvedOptions),
callback: resolvedCallback
};
}
function defaultPortForProtocol(protocol) {
return protocol === 'https:' ? 443 : 80;
}
function hostIncludesExplicitPort(host) {
if (host === undefined || host === null) return false;
const value = String(host);
const bracketEnd = value.lastIndexOf(']');
const colonIndex = value.lastIndexOf(':');
return colonIndex > bracketEnd;
}
function buildRequestOptions(input, options) {
const requestOptions = cloneOptionObject(options);
if (typeof input === 'string' || isURLLike(input)) {
const url = new URL(String(input));
if (requestOptions.protocol === undefined) requestOptions.protocol = url.protocol;
if (requestOptions.hostname === undefined && requestOptions.host === undefined) {
requestOptions.hostname = url.hostname;
}
if (requestOptions.port === undefined && url.port) requestOptions.port = url.port;
if (requestOptions.path === undefined) requestOptions.path = `${url.pathname}${url.search}`;
if (requestOptions.auth === undefined && url.username) {
requestOptions.auth = url.password ? `${url.username}:${url.password}` : url.username;
}
} else if (input && typeof input === 'object') {
Object.assign(requestOptions, input);
}
if (!requestOptions.protocol) requestOptions.protocol = 'http:';
if (!requestOptions.method) requestOptions.method = 'GET';
if (!requestOptions.path) requestOptions.path = '/';
if (
(requestOptions.port === undefined || requestOptions.port === null || requestOptions.port === '') &&
!hostIncludesExplicitPort(requestOptions.host)
) {
requestOptions.port = defaultPortForProtocol(requestOptions.protocol);
}
return requestOptions;
}
function buildRequestUrl(options) {
const protocol = options.protocol || 'http:';
const base = new URL(`${protocol}//localhost/`);
if (options.hostname !== undefined && options.hostname !== null) base.hostname = String(options.hostname);
else if (options.host !== undefined && options.host !== null) base.host = String(options.host);
if (options.port !== undefined && options.port !== null && options.port !== '') {
base.port = String(options.port);
}
if (options.auth) {
const auth = String(options.auth);
const sep = auth.indexOf(':');
if (sep === -1) base.username = auth;
else {
base.username = auth.slice(0, sep);
base.password = auth.slice(sep + 1);
}
}
return new URL(String(options.path || '/'), base).toString();
}
function createAbortError(message) {
const error = new Error(message || 'The operation was aborted');
error.name = 'AbortError';
return error;
}
function buildRequestHeadersObject(headers) {
const normalized = createHeadersObject();
if (!headers) return normalized;
Object.keys(headers).forEach(name => {
const value = headers[name];
normalized[name] = Array.isArray(value) ? value.join(', ') : String(value);
});
return normalized;
}
function buildHeadersFromFetch(response) {
const headers = createHeadersObject();
const rawHeaders = [];
for (const [name, value] of response.headers.entries()) {
rawHeaders.push(name, value);
appendHeaderValue(headers, normalizeHeaderName(name), value);
}
return { headers, rawHeaders };
}
function getFetchBody(chunks) {
if (!chunks || chunks.length === 0) return undefined;
if (chunks.length === 1) return chunks[0];
return Buffer.concat(chunks);
}
// compatibility stub only
function createAgentState() {
return Object.create(null);
}
export class OutgoingMessage extends EventEmitter {}
class FetchIncomingMessage extends EventEmitter {
constructor(response) {
super();
const { headers, rawHeaders } = buildHeadersFromFetch(response);
this.socket = null;
this.connection = null;
this.statusCode = response.status;
this.statusMessage = response.statusText || STATUS_CODES[response.status] || '';
this.headers = headers;
this.rawHeaders = rawHeaders;
this.httpVersion = '1.1';
this.httpVersionMajor = 1;
this.httpVersionMinor = 1;
this.complete = false;
this.aborted = false;
this.destroyed = false;
this.readableEnded = false;
this.url = response.url;
this._reader = response.body && typeof response.body.getReader === 'function' ? response.body.getReader() : null;
this._encoding = null;
this._decoder = null;
this._pumpStarted = false;
this._closeEmitted = false;
}
_emitClose() {
if (this._closeEmitted) return;
this._closeEmitted = true;
this.emit('close');
}
async _pumpBody() {
if (this._pumpStarted) return;
this._pumpStarted = true;
if (!this._reader) {
this.complete = true;
this.readableEnded = true;
this.emit('end');
this._emitClose();
return;
}
try {
for (;;) {
const { done, value } = await this._reader.read();
if (done) break;
if (!value || value.byteLength === 0) continue;
const chunk = Buffer.from(value);
if (this._decoder) {
const text = this._decoder.decode(chunk, { stream: true });
if (text.length > 0) this.emit('data', text);
} else if (this._encoding) {
this.emit('data', chunk.toString(this._encoding));
} else {
this.emit('data', chunk);
}
}
if (this._decoder) {
const finalChunk = this._decoder.decode();
if (finalChunk.length > 0) this.emit('data', finalChunk);
}
this.complete = true;
this.readableEnded = true;
this.emit('end');
this._emitClose();
} catch (error) {
if (this.destroyed) return;
this.destroyed = true;
this.aborted = true;
this.emit('error', error);
this._emitClose();
}
}
setEncoding(encoding) {
this._encoding = encoding || 'utf8';
if (typeof TextDecoder === 'function') {
try {
this._decoder = new TextDecoder(this._encoding === 'utf8' ? 'utf-8' : this._encoding);
} catch {
this._decoder = null;
}
}
return this;
}
resume() {
queueMicrotask(() => {
this._pumpBody();
});
return this;
}
destroy(error) {
if (this.destroyed) return this;
this.destroyed = true;
this.aborted = true;
if (this._reader && typeof this._reader.cancel === 'function') {
Promise.resolve(this._reader.cancel(error)).catch(() => {});
}
if (error) this.emit('error', error);
this._emitClose();
return this;
}
}
export class ClientRequest extends OutgoingMessage {
constructor(options = {}, callback) {
super();
this.agent = options.agent ?? globalAgent;
this.method = String(options.method || 'GET').toUpperCase();
this.protocol = options.protocol || 'http:';
this.host = options.host ?? options.hostname ?? 'localhost';
this.hostname = options.hostname ?? options.host ?? 'localhost';
this.port = options.port ?? defaultPortForProtocol(this.protocol);
this.path = options.path || '/';
this.socket = null;
this.connection = null;
this.destroyed = false;
this.aborted = false;
this.finished = false;
this.reusedSocket = false;
this._headers = createHeadersObject();
this._bodyChunks = [];
this._controller = typeof AbortController === 'function' ? new AbortController() : null;
this._timeout = 0;
this._timeoutHandle = null;
this._dispatchStarted = false;
this._requestUrl = buildRequestUrl(options);
this._closeEmitted = false;
this._timedOut = false;
if (options.headers) {
Object.keys(options.headers).forEach(name => {
this.setHeader(name, options.headers[name]);
});
}
if (typeof callback === 'function') this.once('response', callback);
if (options.timeout !== undefined) this.setTimeout(options.timeout);
}
_emitClose() {
if (this._closeEmitted) return;
this._closeEmitted = true;
this.emit('close');
}
_clearTimeoutTimer() {
if (!this._timeoutHandle) return;
clearTimeout(this._timeoutHandle);
this._timeoutHandle = null;
}
_armTimeoutTimer() {
this._clearTimeoutTimer();
if (!(this._timeout > 0)) return;
this._timeoutHandle = setTimeout(() => {
if (this.destroyed) return;
this._timedOut = true;
this.emit('timeout');
if (this._controller) this._controller.abort(createAbortError('Request timed out'));
}, this._timeout);
}
_dispatch() {
if (this._dispatchStarted || this.destroyed) return;
this._dispatchStarted = true;
this._armTimeoutTimer();
Promise.resolve()
.then(async () => {
const response = await fetch(this._requestUrl, {
method: this.method,
headers: buildRequestHeadersObject(this._headers),
body: getFetchBody(this._bodyChunks),
signal: this._controller ? this._controller.signal : undefined
});
if (this.destroyed) return;
this._clearTimeoutTimer();
const incoming = new FetchIncomingMessage(response);
incoming.on('close', () => {
this._emitClose();
});
this.emit('response', incoming);
queueMicrotask(() => {
incoming._pumpBody();
});
})
.catch(error => {
this._clearTimeoutTimer();
if (this.destroyed || this._timedOut) {
this._emitClose();
return;
}
this.emit('error', error);
this._emitClose();
});
}
setHeader(name, value) {
this._headers[normalizeHeaderName(name)] = value;
return this;
}
getHeader(name) {
return this._headers[normalizeHeaderName(name)];
}
getHeaders() {
return { ...this._headers };
}
removeHeader(name) {
delete this._headers[normalizeHeaderName(name)];
return this;
}
setTimeout(msecs, callback) {
this._timeout = Number(msecs) || 0;
if (typeof callback === 'function') this.on('timeout', callback);
if (this._dispatchStarted && !this.destroyed) this._armTimeoutTimer();
return this;
}
setNoDelay() {
return this;
}
setSocketKeepAlive() {
return this;
}
write(chunk, encoding, callback) {
if (this.finished) throw new Error('write after end');
this._bodyChunks.push(bufferFrom(chunk, typeof encoding === 'string' ? encoding : undefined));
if (typeof encoding === 'function') callback = encoding;
if (typeof callback === 'function') callback();
return true;
}
end(chunk, encoding, callback) {
if (this.finished) return this;
if (typeof chunk === 'function') {
callback = chunk;
chunk = undefined;
encoding = undefined;
} else if (typeof encoding === 'function') {
callback = encoding;
encoding = undefined;
}
if (chunk !== undefined && chunk !== null) {
this.write(chunk, encoding);
}
this.finished = true;
this.emit('finish');
if (typeof callback === 'function') callback();
this._dispatch();
return this;
}
abort() {
return this.destroy(createAbortError('Request aborted'));
}
destroy(error) {
if (this.destroyed) return this;
this.destroyed = true;
this.aborted = true;
this._clearTimeoutTimer();
if (this._controller) this._controller.abort(error || createAbortError('Request destroyed'));
if (error) this.emit('error', error);
this._emitClose();
return this;
}
}
export class Agent extends EventEmitter {
constructor(options = {}) {
super();
this.options = options && typeof options === 'object' ? options : {};
this.requests = createAgentState();
this.sockets = createAgentState();
this.freeSockets = createAgentState();
this.keepAlive = !!this.options.keepAlive;
this.keepAliveMsecs = this.options.keepAliveMsecs ?? 1000;
this.maxSockets = this.options.maxSockets ?? Infinity;
this.maxFreeSockets = this.options.maxFreeSockets ?? 256;
this.maxTotalSockets = this.options.maxTotalSockets ?? Infinity;
this.totalSocketCount = 0;
this.defaultPort = 80;
this.protocol = 'http:';
this.scheduling = this.options.scheduling ?? 'lifo';
}
createConnection() {
clientNotImplemented();
}
createSocket(_options, callback) {
const error = new Error('node:http Agent client transport is not implemented yet');
if (typeof callback === 'function') callback(error);
else throw error;
}
addRequest() {
clientNotImplemented();
}
removeSocket() {}
keepSocketAlive() {
return false;
}
reuseSocket() {}
destroy() {
return this;
}
getName(options = {}) {
if (options.socketPath) return String(options.socketPath);
const host = options.host ?? 'localhost';
const port = options.port ?? this.defaultPort;
const localAddress = options.localAddress ? `:${options.localAddress}` : '';
const family = options.family ? `:${options.family}` : '';
return `${host}:${port}${localAddress}${family}`;
}
}
export const globalAgent = new Agent();
export class IncomingMessage extends EventEmitter {
constructor(socket, parsed) {
super();
this.socket = socket;
this.connection = socket;
this.method = parsed.method;
this.url = parsed.target;
this.headers = buildHeaders(parsed.rawHeaders || []);
this.rawHeaders = (parsed.rawHeaders || []).slice();
this.httpVersion = parsed.httpVersion;
this.httpVersionMajor = parsed.httpVersionMajor;
this.httpVersionMinor = parsed.httpVersionMinor;
this._keepAlive = !!parsed.keepAlive;
this.complete = false;
this.aborted = false;
this.destroyed = false;
this.readableEnded = false;
this._body = bufferFrom(parsed.body);
this._bodyConsumed = false;
}
_takeBody() {
if (this._bodyConsumed) return Buffer.alloc(0);
this._bodyConsumed = true;
return this._body;
}
_deliverBody() {
if (this.destroyed || this.complete) return;
const body = this._takeBody();
if (body.length > 0) this.emit('data', body);
this.complete = true;
this.readableEnded = true;
this.emit('end');
this.emit('close');
}
[Symbol.asyncIterator]() {
const body = this._takeBody();
let emitted = false;
let finished = false;
return {
next: async () => {
if (finished) return { done: true, value: undefined };
if (!emitted && body.length > 0) {
emitted = true;
return { done: false, value: body };
}
finished = true;
this.complete = true;
this.readableEnded = true;
return { done: true, value: undefined };
},
return: async () => {
finished = true;
this.complete = true;
this.readableEnded = true;
return { done: true, value: undefined };
},
[Symbol.asyncIterator]() {
return this;
}
};
}
destroy(error) {
if (this.destroyed) return this;
this.destroyed = true;
if (error) this.emit('error', error);
if (this.socket && !this.socket.destroyed) this.socket.destroy(error);
return this;
}
setTimeout(msecs, callback) {
if (this.socket && this.socket.setTimeout) this.socket.setTimeout(msecs, callback);
return this;
}
}
export class ServerResponse extends EventEmitter {
constructor(req, socket, socketState) {
super();
this.req = req;
this.socket = socket;
this.connection = socket;
this.statusCode = 200;
this.statusMessage = undefined;
this.sendDate = true;
this.strictContentLength = false;
this.headersSent = false;
this.writableEnded = false;
this.writableFinished = false;
this.finished = false;
this._headers = createHeadersObject();
this._headerNames = createHeadersObject();
this._socketState = socketState;
this._streaming = false;
}
setHeader(name, value) {
const headerName = String(name);
const key = normalizeHeaderName(headerName);
this._headers[key] = value;
this._headerNames[key] = headerName;
return this;
}
getHeader(name) {
return this._headers[normalizeHeaderName(name)];
}
getHeaders() {
const copy = createHeadersObject();
Object.keys(this._headers).forEach(key => {
copy[key] = this._headers[key];
});
return copy;
}
getHeaderNames() {
return Object.keys(this._headers);
}
hasHeader(name) {
return this._headers[normalizeHeaderName(name)] !== undefined;
}
appendHeader(name, value) {
const key = normalizeHeaderName(name);
const existing = this._headers[key];
if (existing === undefined) {
this._headers[key] = value;
} else if (Array.isArray(existing)) {
if (Array.isArray(value)) existing.push(...value);
else existing.push(value);
} else {
this._headers[key] = Array.isArray(value) ? [existing, ...value] : [existing, value];
}
if (!this._headerNames[key]) this._headerNames[key] = String(name);
return this;
}
removeHeader(name) {
const key = normalizeHeaderName(name);
delete this._headers[key];
delete this._headerNames[key];
return this;
}
writeHead(statusCode, statusMessage, headers) {
this.statusCode = statusCode | 0;
if (typeof statusMessage === 'string') {
this.statusMessage = statusMessage;
applyHeaderObject(this, headers);
} else {
applyHeaderObject(this, statusMessage);
}
return this;
}
_rawHeaders() {
const rawHeaders = [];
if (this.sendDate && !this.hasHeader('date')) {
this.setHeader('Date', new Date().toUTCString());
}
Object.keys(this._headers).forEach(key => {
const name = this._headerNames[key] || key;
appendRawHeader(rawHeaders, name, this._headers[key]);
});
return rawHeaders;
}
_shouldKeepAlive() {
const connection = this.getHeader('connection');
if (typeof connection === 'string') {
if (connection.toLowerCase() === 'close') return false;
if (connection.toLowerCase() === 'keep-alive') return true;
}
return !!(this.req && this.req._keepAlive);
}
_writeHead(bodyIsStream, bodySize) {
if (this.headersSent) return;
const statusText = this.statusMessage || STATUS_CODES[this.statusCode] || httpWriter.defaultStatusText(this.statusCode);
const head = httpWriter.writeHead(this.statusCode, statusText, this._rawHeaders(), bodyIsStream, bodySize, this._shouldKeepAlive());
this.headersSent = true;
this.socket.write(head);
}
write(chunk, encoding, callback) {
const body = hasResponseBody(this.req && this.req.method, this.statusCode)
? bufferFrom(chunk, typeof encoding === 'string' ? encoding : undefined)
: Buffer.alloc(0);
if (typeof encoding === 'function') callback = encoding;
if (!this.headersSent) this._writeHead(true, 0);
this._streaming = true;
if (body.length > 0) this.socket.write(httpWriter.writeChunk(body));
if (typeof callback === 'function') callback();
return true;
}
end(chunk, encoding, callback) {
let body = Buffer.alloc(0);
let keepAlive = false;
if (this.writableEnded) return this;
if (typeof encoding === 'function') {
callback = encoding;
encoding = undefined;
}
if (hasResponseBody(this.req && this.req.method, this.statusCode)) {
body = bufferFrom(chunk, typeof encoding === 'string' ? encoding : undefined);
}
if (this._streaming) {
if (!this.headersSent) this._writeHead(true, 0);
if (body.length > 0) this.socket.write(httpWriter.writeChunk(body));
this.socket.write(httpWriter.writeFinalChunk());
} else {
this._writeHead(false, body.length);
if (body.length > 0) this.socket.write(body);
}
this.writableEnded = true;
this.writableFinished = true;
this.finished = true;
this.emit('finish');
this.emit('close');
keepAlive = this._shouldKeepAlive();
this._socketState.activeResponse = null;
if (typeof callback === 'function') callback();
if (!keepAlive) this.socket.end();
else if (this._socketState.buffered.length > 0) this._socketState.server._drainSocket(this.socket, this._socketState);
return this;
}
setTimeout(msecs, callback) {
if (this.socket && this.socket.setTimeout) this.socket.setTimeout(msecs, callback);
return this;
}
}
export class Server extends net.Server {
constructor(options, requestListener) {
const serverOptions = typeof options === 'function' ? undefined : options;
const onRequest = typeof options === 'function' ? options : requestListener;
super(serverOptions);
this.timeout = 0;
this.headersTimeout = 60000;
this.requestTimeout = 300000;
this.keepAliveTimeout = 5000;
this.maxHeadersCount = 2000;
this.on('connection', socket => {
this._attachSocket(socket);
});
if (typeof onRequest === 'function') this.on('request', onRequest);
}
_attachSocket(socket) {
const state = makeSocketState(this, socket);
if (this.timeout > 0 && socket.setTimeout) {
socket.setTimeout(this.timeout, () => {
this.emit('timeout', socket);
});
}
socket.on('data', chunk => {
state.buffered = appendSocketChunk(state.buffered, chunk);
this._drainSocket(socket, state);
});
socket.on('error', error => {
handleClientError(this, socket, error);
});
socket.on('close', () => {
state.closed = true;
});
}
_drainSocket(socket, state) {
while (!state.closed) {
let parsed = null;
if (state.activeResponse && !state.activeResponse.writableEnded) return;
if (state.buffered.length === 0) return;
try {
parsed = httpParser.parseRequest(state.buffered);
} catch (error) {
state.closed = true;
handleClientError(this, socket, error);
return;
}
if (parsed === null) return;
if (!parsed.consumed || parsed.consumed < 0) {
state.closed = true;
handleClientError(this, socket, new Error('Invalid HTTP parser state'));
return;
}
state.buffered = parsed.consumed < state.buffered.length ? state.buffered.subarray(parsed.consumed) : Buffer.alloc(0);
const req = new IncomingMessage(socket, parsed);
const res = new ServerResponse(req, socket, state);
state.activeResponse = res;
this.emit('request', req, res);
req._deliverBody();
if (!res.writableEnded) return;
}
}
setTimeout(msecs, callback) {
this.timeout = msecs | 0;
if (typeof callback === 'function') this.on('timeout', callback);
return this;
}
}
export function createServer(options, requestListener) {
return new Server(options, requestListener);
}
export function request(input, options, callback) {
const normalized = normalizeRequestArgs(input, options, callback);
const requestOptions = buildRequestOptions(normalized.input, normalized.options);
return new ClientRequest(requestOptions, normalized.callback);
}
export function get(input, options, callback) {
const req = request(input, options, callback);
req.end();
return req;
}
export { METHODS, STATUS_CODES } from 'ant:internal/http_metadata';
File Metadata
Details
Attached
Mime Type
application/javascript
Expires
Sat, May 2, 1:56 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
544245
Default Alt Text
http.mjs (26 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment