UNPKG

@tomphttp/bare-server-node

Version:

The Bare Server implementation in NodeJS.

191 lines 7.05 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.webSocketFetch = exports.bareUpgradeFetch = exports.bareFetch = exports.randomHex = exports.nullBodyStatus = exports.nullMethod = void 0; const node_crypto_1 = require("node:crypto"); const node_http_1 = require("node:http"); const node_https_1 = require("node:https"); const node_stream_1 = require("node:stream"); const ws_1 = __importDefault(require("ws")); const BareServer_js_1 = require("./BareServer.js"); exports.nullMethod = ['GET', 'HEAD']; exports.nullBodyStatus = [101, 204, 205, 304]; function randomHex(byteLength) { const bytes = new Uint8Array(byteLength); (0, node_crypto_1.getRandomValues)(bytes); let hex = ''; for (const byte of bytes) hex += byte.toString(16).padStart(2, '0'); return hex; } exports.randomHex = randomHex; function outgoingError(error) { if (error instanceof Error) { switch (error.code) { case 'ENOTFOUND': return new BareServer_js_1.BareError(500, { code: 'HOST_NOT_FOUND', id: 'request', message: 'The specified host could not be resolved.', }); case 'ECONNREFUSED': return new BareServer_js_1.BareError(500, { code: 'CONNECTION_REFUSED', id: 'response', message: 'The remote rejected the request.', }); case 'ECONNRESET': return new BareServer_js_1.BareError(500, { code: 'CONNECTION_RESET', id: 'response', message: 'The request was forcibly closed.', }); case 'ETIMEOUT': return new BareServer_js_1.BareError(500, { code: 'CONNECTION_TIMEOUT', id: 'response', message: 'The response timed out.', }); } } return error; } async function bareFetch(request, signal, requestHeaders, remote, options) { if (options.filterRemote) await options.filterRemote(remote); const req = { method: request.method, headers: requestHeaders, setHost: false, signal, localAddress: options.localAddress, family: options.family, lookup: options.lookup, }; let outgoing; // NodeJS will convert the URL into HTTP options automatically // see https://github.com/nodejs/node/blob/e30e71665cab94118833cc536a43750703b19633/lib/internal/url.js#L1277 if (remote.protocol === 'https:') outgoing = (0, node_https_1.request)(remote, { ...req, agent: options.httpsAgent, }); else if (remote.protocol === 'http:') outgoing = (0, node_http_1.request)(remote, { ...req, agent: options.httpAgent, }); else throw new RangeError(`Unsupported protocol: '${remote.protocol}'`); if (request.body) node_stream_1.Readable.fromWeb(request.body).pipe(outgoing); else outgoing.end(); return await new Promise((resolve, reject) => { outgoing.on('response', (response) => { resolve(response); }); outgoing.on('upgrade', (req, socket) => { reject('Remote did not send a response'); socket.destroy(); }); outgoing.on('error', (error) => { reject(outgoingError(error)); }); }); } exports.bareFetch = bareFetch; async function bareUpgradeFetch(request, signal, requestHeaders, remote, options) { if (options.filterRemote) await options.filterRemote(remote); const req = { headers: requestHeaders, method: request.method, timeout: 12e3, setHost: false, signal, localAddress: options.localAddress, family: options.family, lookup: options.lookup, }; let outgoing; // NodeJS will convert the URL into HTTP options automatically // see https://github.com/nodejs/node/blob/e30e71665cab94118833cc536a43750703b19633/lib/internal/url.js#L1277 // calling .replace on remote may look like it replaces other occurrences of wss:, but it only replaces the first which is remote.protocol if (remote.protocol === 'wss:') outgoing = (0, node_https_1.request)(remote.toString().replace('wss:', 'https:'), { ...req, agent: options.httpsAgent, }); else if (remote.protocol === 'ws:') outgoing = (0, node_http_1.request)(remote.toString().replace('ws:', 'http:'), { ...req, agent: options.httpAgent, }); else throw new RangeError(`Unsupported protocol: '${remote.protocol}'`); outgoing.end(); return await new Promise((resolve, reject) => { outgoing.on('response', (res) => { reject(new Error('Remote did not upgrade the WebSocket')); res.destroy(); }); outgoing.on('upgrade', (res, socket, head) => { resolve([res, socket, head]); }); outgoing.on('error', (error) => { reject(outgoingError(error)); }); }); } exports.bareUpgradeFetch = bareUpgradeFetch; async function webSocketFetch(request, requestHeaders, remote, protocols, options) { if (options.filterRemote) await options.filterRemote(remote); const req = { headers: requestHeaders, method: request.method, timeout: 12e3, setHost: false, localAddress: options.localAddress, family: options.family, lookup: options.lookup, }; let outgoing; if (remote.protocol === 'wss:') outgoing = new ws_1.default(remote, protocols, { ...req, agent: options.httpsAgent, }); else if (remote.protocol === 'ws:') outgoing = new ws_1.default(remote, protocols, { ...req, agent: options.httpAgent, }); else throw new RangeError(`Unsupported protocol: '${remote.protocol}'`); return await new Promise((resolve, reject) => { let request; const cleanup = () => { outgoing.removeEventListener('open', openListener); outgoing.removeEventListener('open', openListener); }; outgoing.on('upgrade', (req) => { request = req; }); const openListener = () => { cleanup(); resolve([request, outgoing]); }; const errorListener = (event) => { cleanup(); reject(outgoingError(event.error)); }; outgoing.addEventListener('open', openListener); outgoing.addEventListener('error', errorListener); }); } exports.webSocketFetch = webSocketFetch; //# sourceMappingURL=requestUtil.js.map