From a7be044a02dc69752e581b8cf3fb375c0a89fc79 Mon Sep 17 00:00:00 2001
From: Russell2259 <84256826+Russell2259@users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:46:57 -0700
Subject: [PATCH] start on modified bare server
---
lib/bare-server-modified/AbstractMessage.d.ts | 34 ++
lib/bare-server-modified/AbstractMessage.js | 61 +++
lib/bare-server-modified/BareServer.d.ts | 90 +++++
lib/bare-server-modified/BareServer.js | 168 ++++++++
lib/bare-server-modified/Meta.d.ts | 33 ++
lib/bare-server-modified/Meta.js | 43 +++
lib/bare-server-modified/V1.d.ts | 2 +
lib/bare-server-modified/V1.js | 254 ++++++++++++
lib/bare-server-modified/V2.d.ts | 2 +
lib/bare-server-modified/V2.js | 364 ++++++++++++++++++
lib/bare-server-modified/V3.d.ts | 2 +
lib/bare-server-modified/V3.js | 305 +++++++++++++++
lib/bare-server-modified/createServer.d.ts | 52 +++
lib/bare-server-modified/createServer.js | 99 +++++
lib/bare-server-modified/encodeProtocol.d.ts | 3 +
lib/bare-server-modified/encodeProtocol.js | 48 +++
lib/bare-server-modified/headerUtil.d.ts | 8 +
lib/bare-server-modified/headerUtil.js | 48 +++
lib/bare-server-modified/remoteUtil.d.ts | 9 +
lib/bare-server-modified/remoteUtil.js | 36 ++
lib/bare-server-modified/requestUtil.d.ts | 13 +
lib/bare-server-modified/requestUtil.js | 185 +++++++++
lib/bare-server-modified/splitHeaderUtil.d.ts | 14 +
lib/bare-server-modified/splitHeaderUtil.js | 60 +++
package-lock.json | 2 +-
package.json | 4 +-
26 files changed, 1936 insertions(+), 3 deletions(-)
create mode 100644 lib/bare-server-modified/AbstractMessage.d.ts
create mode 100644 lib/bare-server-modified/AbstractMessage.js
create mode 100644 lib/bare-server-modified/BareServer.d.ts
create mode 100644 lib/bare-server-modified/BareServer.js
create mode 100644 lib/bare-server-modified/Meta.d.ts
create mode 100644 lib/bare-server-modified/Meta.js
create mode 100644 lib/bare-server-modified/V1.d.ts
create mode 100644 lib/bare-server-modified/V1.js
create mode 100644 lib/bare-server-modified/V2.d.ts
create mode 100644 lib/bare-server-modified/V2.js
create mode 100644 lib/bare-server-modified/V3.d.ts
create mode 100644 lib/bare-server-modified/V3.js
create mode 100644 lib/bare-server-modified/createServer.d.ts
create mode 100644 lib/bare-server-modified/createServer.js
create mode 100644 lib/bare-server-modified/encodeProtocol.d.ts
create mode 100644 lib/bare-server-modified/encodeProtocol.js
create mode 100644 lib/bare-server-modified/headerUtil.d.ts
create mode 100644 lib/bare-server-modified/headerUtil.js
create mode 100644 lib/bare-server-modified/remoteUtil.d.ts
create mode 100644 lib/bare-server-modified/remoteUtil.js
create mode 100644 lib/bare-server-modified/requestUtil.d.ts
create mode 100644 lib/bare-server-modified/requestUtil.js
create mode 100644 lib/bare-server-modified/splitHeaderUtil.d.ts
create mode 100644 lib/bare-server-modified/splitHeaderUtil.js
diff --git a/lib/bare-server-modified/AbstractMessage.d.ts b/lib/bare-server-modified/AbstractMessage.d.ts
new file mode 100644
index 0000000..8a5dbba
--- /dev/null
+++ b/lib/bare-server-modified/AbstractMessage.d.ts
@@ -0,0 +1,34 @@
+///
+///
+import type { IncomingMessage, ServerResponse } from 'node:http';
+import { Headers } from 'headers-polyfill';
+import type { BareHeaders } from './requestUtil.js';
+export interface RequestInit {
+ method: string;
+ path: string;
+ headers: Headers | BareHeaders;
+}
+/**
+ * Abstraction for the data read from IncomingMessage
+ */
+export declare class Request {
+ body: IncomingMessage;
+ method: string;
+ headers: Headers;
+ url: URL;
+ constructor(body: IncomingMessage, init: RequestInit);
+}
+export type ResponseBody = Buffer | IncomingMessage;
+export interface ResponseInit {
+ headers?: Headers | BareHeaders;
+ status?: number;
+ statusText?: string;
+}
+export declare class Response {
+ body?: ResponseBody;
+ status: number;
+ statusText?: string;
+ headers: Headers;
+ constructor(body: ResponseBody | undefined, init?: ResponseInit);
+}
+export declare function writeResponse(response: Response, res: ServerResponse): boolean;
diff --git a/lib/bare-server-modified/AbstractMessage.js b/lib/bare-server-modified/AbstractMessage.js
new file mode 100644
index 0000000..47fc4db
--- /dev/null
+++ b/lib/bare-server-modified/AbstractMessage.js
@@ -0,0 +1,61 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.writeResponse = exports.Response = exports.Request = void 0;
+const node_stream_1 = require("node:stream");
+const headers_polyfill_1 = require("headers-polyfill");
+/**
+ * Abstraction for the data read from IncomingMessage
+ */
+class Request {
+ body;
+ method;
+ headers;
+ url;
+ constructor(body, init) {
+ this.body = body;
+ this.method = init.method;
+ this.headers = new headers_polyfill_1.Headers(init.headers);
+ // Parse the URL pathname. Host doesn't matter.
+ this.url = new URL(init.path, 'http://bare-server-node');
+ }
+}
+exports.Request = Request;
+class Response {
+ body;
+ status;
+ statusText;
+ headers;
+ constructor(body, init = {}) {
+ if (body) {
+ this.body = body instanceof node_stream_1.Stream ? body : Buffer.from(body);
+ }
+ if (typeof init.status === 'number') {
+ this.status = init.status;
+ }
+ else {
+ this.status = 200;
+ }
+ if (typeof init.statusText === 'string') {
+ this.statusText = init.statusText;
+ }
+ this.headers = new headers_polyfill_1.Headers(init.headers);
+ }
+}
+exports.Response = Response;
+function writeResponse(response, res) {
+ for (const [header, value] of response.headers)
+ res.setHeader(header, value);
+ res.writeHead(response.status, response.statusText);
+ if (response.body instanceof node_stream_1.Stream) {
+ const { body } = response;
+ res.on('close', () => body.destroy());
+ body.pipe(res);
+ }
+ else if (response.body instanceof Buffer)
+ res.end(response.body);
+ else
+ res.end();
+ return true;
+}
+exports.writeResponse = writeResponse;
+//# sourceMappingURL=AbstractMessage.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/BareServer.d.ts b/lib/bare-server-modified/BareServer.d.ts
new file mode 100644
index 0000000..aa2b28c
--- /dev/null
+++ b/lib/bare-server-modified/BareServer.d.ts
@@ -0,0 +1,90 @@
+///
+///
+///
+///
+///
+///
+///
+import type { LookupOneOptions } from 'node:dns';
+import EventEmitter from 'node:events';
+import type { Agent as HttpAgent, IncomingMessage, ServerResponse } from 'node:http';
+import type { Agent as HttpsAgent } from 'node:https';
+import type { Duplex } from 'node:stream';
+import type WebSocket from 'ws';
+import { Request, Response } from './AbstractMessage.js';
+import type { JSONDatabaseAdapter } from './Meta.js';
+export interface BareErrorBody {
+ code: string;
+ id: string;
+ message?: string;
+ stack?: string;
+}
+export declare class BareError extends Error {
+ status: number;
+ body: BareErrorBody;
+ constructor(status: number, body: BareErrorBody);
+}
+export declare const pkg: {
+ version: string;
+};
+export declare function json(status: number, json: T): Response;
+export type BareMaintainer = {
+ email?: string;
+ website?: string;
+};
+export type BareProject = {
+ name?: string;
+ description?: string;
+ email?: string;
+ website?: string;
+ repository?: string;
+ version?: string;
+};
+export type BareLanguage = 'NodeJS' | 'ServiceWorker' | 'Deno' | 'Java' | 'PHP' | 'Rust' | 'C' | 'C++' | 'C#' | 'Ruby' | 'Go' | 'Crystal' | 'Shell' | string;
+export type BareManifest = {
+ maintainer?: BareMaintainer;
+ project?: BareProject;
+ versions: string[];
+ language: BareLanguage;
+ memoryUsage?: number;
+};
+export interface Options {
+ logErrors: boolean;
+ /**
+ * Callback for filtering the remote URL.
+ * @returns Nothing
+ * @throws An error if the remote is bad.
+ */
+ filterRemote?: (remote: Readonly) => Promise | void;
+ /**
+ * DNS lookup
+ * May not get called when remote.host is an IP
+ * Use in combination with filterRemote to block IPs
+ */
+ lookup: (hostname: string, options: LookupOneOptions, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void) => void;
+ localAddress?: string;
+ family?: number;
+ maintainer?: BareMaintainer;
+ httpAgent: HttpAgent;
+ httpsAgent: HttpsAgent;
+ database: JSONDatabaseAdapter;
+ wss: WebSocket.Server;
+}
+export type RouteCallback = (request: Request, response: ServerResponse, options: Options) => Promise | Response;
+export type SocketRouteCallback = (request: Request, socket: Duplex, head: Buffer, options: Options) => Promise | void;
+export default class Server extends EventEmitter {
+ routes: Map;
+ socketRoutes: Map;
+ versions: string[];
+ private closed;
+ private directory;
+ private options;
+ /**
+ * Remove all timers and listeners
+ */
+ close(): void;
+ shouldRoute(request: IncomingMessage): boolean;
+ get instanceInfo(): BareManifest;
+ routeUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer): Promise;
+ routeRequest(req: IncomingMessage, res: ServerResponse): Promise;
+}
diff --git a/lib/bare-server-modified/BareServer.js b/lib/bare-server-modified/BareServer.js
new file mode 100644
index 0000000..ce2d153
--- /dev/null
+++ b/lib/bare-server-modified/BareServer.js
@@ -0,0 +1,168 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.json = exports.pkg = exports.BareError = void 0;
+const node_events_1 = __importDefault(require("node:events"));
+const node_fs_1 = require("node:fs");
+const node_path_1 = require("node:path");
+const http_errors_1 = __importDefault(require("http-errors"));
+const AbstractMessage_js_1 = require("./AbstractMessage.js");
+class BareError extends Error {
+ status;
+ body;
+ constructor(status, body) {
+ super(body.message || body.code);
+ this.status = status;
+ this.body = body;
+ }
+}
+exports.BareError = BareError;
+exports.pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'package.json'), 'utf-8'));
+const project = {
+ name: 'bare-server-node',
+ description: 'TOMPHTTP NodeJS Bare Server',
+ repository: 'https://github.com/tomphttp/bare-server-node',
+ version: exports.pkg.version,
+};
+function json(status, json) {
+ const send = Buffer.from(JSON.stringify(json, null, '\t'));
+ return new AbstractMessage_js_1.Response(send, {
+ status,
+ headers: {
+ 'content-type': 'application/json',
+ 'content-length': send.byteLength.toString(),
+ },
+ });
+}
+exports.json = json;
+class Server extends node_events_1.default {
+ routes = new Map();
+ socketRoutes = new Map();
+ versions = [];
+ closed = false;
+ directory;
+ options;
+ /**
+ * @internal
+ */
+ constructor(directory, options) {
+ super();
+ this.directory = directory;
+ this.options = options;
+ }
+ /**
+ * Remove all timers and listeners
+ */
+ close() {
+ this.closed = true;
+ this.emit('close');
+ }
+ shouldRoute(request) {
+ return (!this.closed &&
+ request.url !== undefined &&
+ request.url.startsWith(this.directory));
+ }
+ get instanceInfo() {
+ return {
+ versions: this.versions,
+ language: 'NodeJS',
+ memoryUsage: Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100,
+ maintainer: this.options.maintainer,
+ project,
+ };
+ }
+ async routeUpgrade(req, socket, head) {
+ const request = new AbstractMessage_js_1.Request(req, {
+ method: req.method,
+ path: req.url,
+ headers: req.headers,
+ });
+ const service = request.url.pathname.slice(this.directory.length - 1);
+ if (this.socketRoutes.has(service)) {
+ const call = this.socketRoutes.get(service);
+ try {
+ await call(request, socket, head, this.options);
+ }
+ catch (error) {
+ if (this.options.logErrors) {
+ console.error(error);
+ }
+ socket.end();
+ }
+ }
+ else {
+ socket.end();
+ }
+ }
+ async routeRequest(req, res) {
+ const request = new AbstractMessage_js_1.Request(req, {
+ method: req.method,
+ path: req.url,
+ headers: req.headers,
+ });
+ const service = request.url.pathname.slice(this.directory.length - 1);
+ let response;
+ try {
+ if (request.method === 'OPTIONS') {
+ response = new AbstractMessage_js_1.Response(undefined, { status: 200 });
+ }
+ else if (service === '/') {
+ response = json(200, this.instanceInfo);
+ }
+ else if (this.routes.has(service)) {
+ const call = this.routes.get(service);
+ response = await call(request, res, this.options);
+ }
+ else {
+ throw new http_errors_1.default.NotFound();
+ }
+ }
+ catch (error) {
+ if (this.options.logErrors)
+ console.error(error);
+ if (http_errors_1.default.isHttpError(error)) {
+ response = json(error.statusCode, {
+ code: 'UNKNOWN',
+ id: `error.${error.name}`,
+ message: error.message,
+ stack: error.stack,
+ });
+ }
+ else if (error instanceof Error) {
+ response = json(500, {
+ code: 'UNKNOWN',
+ id: `error.${error.name}`,
+ message: error.message,
+ stack: error.stack,
+ });
+ }
+ else {
+ response = json(500, {
+ code: 'UNKNOWN',
+ id: 'error.Exception',
+ message: error,
+ stack: new Error(error).stack,
+ });
+ }
+ if (!(response instanceof AbstractMessage_js_1.Response)) {
+ if (this.options.logErrors) {
+ console.error('Cannot', request.method, request.url.pathname, ': Route did not return a response.');
+ }
+ throw new http_errors_1.default.InternalServerError();
+ }
+ }
+ response.headers.set('x-robots-tag', 'noindex');
+ response.headers.set('access-control-allow-headers', '*');
+ response.headers.set('access-control-allow-origin', '*');
+ response.headers.set('access-control-allow-methods', '*');
+ response.headers.set('access-control-expose-headers', '*');
+ // don't fetch preflight on every request...
+ // instead, fetch preflight every 10 minutes
+ response.headers.set('access-control-max-age', '7200');
+ (0, AbstractMessage_js_1.writeResponse)(response, res);
+ }
+}
+exports.default = Server;
+//# sourceMappingURL=BareServer.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/Meta.d.ts b/lib/bare-server-modified/Meta.d.ts
new file mode 100644
index 0000000..46f3c3b
--- /dev/null
+++ b/lib/bare-server-modified/Meta.d.ts
@@ -0,0 +1,33 @@
+import type { BareHeaders } from './requestUtil';
+export interface MetaV1 {
+ v: 1;
+ response?: {
+ headers: BareHeaders;
+ };
+}
+export interface MetaV2 {
+ v: 2;
+ response?: {
+ status: number;
+ statusText: string;
+ headers: BareHeaders;
+ };
+ sendHeaders: BareHeaders;
+ remote: string;
+ forwardHeaders: string[];
+}
+export default interface CommonMeta {
+ value: MetaV1 | MetaV2;
+ expires: number;
+}
+export interface Database {
+ get(key: string): string | undefined | PromiseLike;
+ set(key: string, value: string): unknown;
+ has(key: string): boolean | PromiseLike;
+ delete(key: string): boolean | PromiseLike;
+ entries(): IterableIterator<[string, string]> | PromiseLike>;
+}
+/**
+ * Routine
+ */
+export declare function cleanupDatabase(database: Database): Promise;
diff --git a/lib/bare-server-modified/Meta.js b/lib/bare-server-modified/Meta.js
new file mode 100644
index 0000000..c7886e0
--- /dev/null
+++ b/lib/bare-server-modified/Meta.js
@@ -0,0 +1,43 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.cleanupDatabase = exports.JSONDatabaseAdapter = void 0;
+/**
+ * @internal
+ */
+class JSONDatabaseAdapter {
+ impl;
+ constructor(impl) {
+ this.impl = impl;
+ }
+ async get(key) {
+ const res = await this.impl.get(key);
+ if (typeof res === 'string')
+ return JSON.parse(res);
+ }
+ async set(key, value) {
+ return await this.impl.set(key, JSON.stringify(value));
+ }
+ async has(key) {
+ return await this.impl.has(key);
+ }
+ async delete(key) {
+ return await this.impl.delete(key);
+ }
+ async *[Symbol.asyncIterator]() {
+ for (const [id, value] of await this.impl.entries()) {
+ yield [id, JSON.parse(value)];
+ }
+ }
+}
+exports.JSONDatabaseAdapter = JSONDatabaseAdapter;
+/**
+ * Routine
+ */
+async function cleanupDatabase(database) {
+ const adapter = new JSONDatabaseAdapter(database);
+ for await (const [id, { expires }] of adapter)
+ if (expires < Date.now())
+ database.delete(id);
+}
+exports.cleanupDatabase = cleanupDatabase;
+//# sourceMappingURL=Meta.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/V1.d.ts b/lib/bare-server-modified/V1.d.ts
new file mode 100644
index 0000000..d152b77
--- /dev/null
+++ b/lib/bare-server-modified/V1.d.ts
@@ -0,0 +1,2 @@
+import type Server from './BareServer.js';
+export default function registerV1(server: Server): void;
diff --git a/lib/bare-server-modified/V1.js b/lib/bare-server-modified/V1.js
new file mode 100644
index 0000000..be0c6a9
--- /dev/null
+++ b/lib/bare-server-modified/V1.js
@@ -0,0 +1,254 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const headers_polyfill_1 = require("headers-polyfill");
+const AbstractMessage_js_1 = require("./AbstractMessage.js");
+const BareServer_js_1 = require("./BareServer.js");
+const encodeProtocol_js_1 = require("./encodeProtocol.js");
+const headerUtil_js_1 = require("./headerUtil.js");
+const remoteUtil_js_1 = require("./remoteUtil.js");
+const requestUtil_js_1 = require("./requestUtil.js");
+const validProtocols = ['http:', 'https:', 'ws:', 'wss:'];
+function loadForwardedHeaders(forward, target, request) {
+ for (const header of forward) {
+ const value = request.headers.get(header);
+ if (value !== null)
+ target[header] = value;
+ }
+}
+function readHeaders(request) {
+ const remote = Object.create(null);
+ const headers = Object.create(null);
+ for (const remoteProp of ['host', 'port', 'protocol', 'path']) {
+ const header = `x-bare-${remoteProp}`;
+ const value = request.headers.get(header);
+ if (value === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was not specified.`,
+ });
+ switch (remoteProp) {
+ case 'port':
+ if (isNaN(parseInt(value))) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was not a valid integer.`,
+ });
+ }
+ break;
+ case 'protocol':
+ if (!validProtocols.includes(value)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was invalid`,
+ });
+ }
+ break;
+ }
+ remote[remoteProp] = value;
+ }
+ const xBareHeaders = request.headers.get('x-bare-headers');
+ if (xBareHeaders === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.x-bare-headers`,
+ message: `Header was not specified.`,
+ });
+ try {
+ const json = JSON.parse(xBareHeaders);
+ for (const header in json) {
+ const value = json[header];
+ if (typeof value === 'string') {
+ headers[header] = value;
+ }
+ else if (Array.isArray(value)) {
+ const array = [];
+ for (const val of value) {
+ if (typeof val !== 'string') {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `bare.headers.${header}`,
+ message: `Header was not a String.`,
+ });
+ }
+ array.push(val);
+ }
+ headers[header] = array;
+ }
+ else {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `bare.headers.${header}`,
+ message: `Header was not a String.`,
+ });
+ }
+ }
+ }
+ catch (error) {
+ if (error instanceof SyntaxError) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.x-bare-headers`,
+ message: `Header contained invalid JSON. (${error.message})`,
+ });
+ }
+ else {
+ throw error;
+ }
+ }
+ const xBareForwardHeaders = request.headers.get('x-bare-forward-headers');
+ if (xBareForwardHeaders === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `Header was not specified.`,
+ });
+ try {
+ loadForwardedHeaders(JSON.parse(xBareForwardHeaders), headers, request);
+ }
+ catch (error) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `Header contained invalid JSON. (${error instanceof Error ? error.message : error})`,
+ });
+ }
+ return { remote: (0, remoteUtil_js_1.remoteToURL)(remote), headers };
+}
+const tunnelRequest = async (request, res, options) => {
+ const abort = new AbortController();
+ request.body.on('close', () => {
+ if (!request.body.complete)
+ abort.abort();
+ });
+ res.on('close', () => {
+ abort.abort();
+ });
+ const { remote, headers } = readHeaders(request);
+ const response = await (0, requestUtil_js_1.fetch)(request, abort.signal, headers, remote, options);
+ const responseHeaders = new headers_polyfill_1.Headers();
+ for (const header in response.headers) {
+ if (header === 'content-encoding' || header === 'x-content-encoding')
+ responseHeaders.set('content-encoding', (0, headerUtil_js_1.flattenHeader)(response.headers[header]));
+ else if (header === 'content-length')
+ responseHeaders.set('content-length', (0, headerUtil_js_1.flattenHeader)(response.headers[header]));
+ }
+ responseHeaders.set('x-bare-headers', JSON.stringify((0, headerUtil_js_1.mapHeadersFromArray)((0, headerUtil_js_1.rawHeaderNames)(response.rawHeaders), {
+ ...response.headers,
+ })));
+ responseHeaders.set('x-bare-status', response.statusCode.toString());
+ responseHeaders.set('x-bare-status-text', response.statusMessage);
+ return new AbstractMessage_js_1.Response(response, { status: 200, headers: responseHeaders });
+};
+const metaExpiration = 30e3;
+const wsMeta = async (request, res, options) => {
+ if (request.method === 'OPTIONS') {
+ return new AbstractMessage_js_1.Response(undefined, { status: 200 });
+ }
+ const id = request.headers.get('x-bare-id');
+ if (id === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: 'request.headers.x-bare-id',
+ message: 'Header was not specified',
+ });
+ const meta = await options.database.get(id);
+ // check if meta isn't undefined and if the version equals 1
+ if (meta?.value.v !== 1)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: 'request.headers.x-bare-id',
+ message: 'Unregistered ID',
+ });
+ await options.database.delete(id);
+ return (0, BareServer_js_1.json)(200, {
+ headers: meta.value.response?.headers,
+ });
+};
+const wsNewMeta = async (request, res, options) => {
+ const id = (0, requestUtil_js_1.randomHex)(16);
+ await options.database.set(id, {
+ value: { v: 1 },
+ expires: Date.now() + metaExpiration,
+ });
+ return new AbstractMessage_js_1.Response(Buffer.from(id));
+};
+const tunnelSocket = async (request, socket, head, options) => {
+ const abort = new AbortController();
+ request.body.on('close', () => {
+ if (!request.body.complete)
+ abort.abort();
+ });
+ socket.on('close', () => {
+ abort.abort();
+ });
+ if (!request.headers.has('sec-websocket-protocol')) {
+ socket.end();
+ return;
+ }
+ const [firstProtocol, data] = request.headers
+ .get('sec-websocket-protocol')
+ .split(/,\s*/g);
+ if (firstProtocol !== 'bare') {
+ socket.end();
+ return;
+ }
+ const { remote, headers, forward_headers: forwardHeaders, id, } = JSON.parse((0, encodeProtocol_js_1.decodeProtocol)(data));
+ loadForwardedHeaders(forwardHeaders, headers, request);
+ const [remoteResponse, remoteSocket] = await (0, requestUtil_js_1.upgradeFetch)(request, abort.signal, headers, (0, remoteUtil_js_1.remoteToURL)(remote), options);
+ remoteSocket.on('close', () => {
+ // console.log('Remote closed');
+ socket.end();
+ });
+ socket.on('close', () => {
+ // console.log('Serving closed');
+ remoteSocket.end();
+ });
+ remoteSocket.on('error', (error) => {
+ if (options.logErrors) {
+ console.error('Remote socket error:', error);
+ }
+ socket.end();
+ });
+ socket.on('error', (error) => {
+ if (options.logErrors) {
+ console.error('Serving socket error:', error);
+ }
+ remoteSocket.end();
+ });
+ if (typeof id === 'string') {
+ const meta = await options.database.get(id);
+ if (meta?.value.v === 1) {
+ meta.value.response = {
+ headers: (0, headerUtil_js_1.mapHeadersFromArray)((0, headerUtil_js_1.rawHeaderNames)(remoteResponse.rawHeaders), {
+ ...remoteResponse.headers,
+ }),
+ };
+ await options.database.set(id, meta);
+ }
+ }
+ const responseHeaders = [
+ `HTTP/1.1 101 Switching Protocols`,
+ `Upgrade: websocket`,
+ `Connection: Upgrade`,
+ `Sec-WebSocket-Protocol: bare`,
+ `Sec-WebSocket-Accept: ${remoteResponse.headers['sec-websocket-accept']}`,
+ ];
+ if ('sec-websocket-extensions' in remoteResponse.headers) {
+ responseHeaders.push(`Sec-WebSocket-Extensions: ${remoteResponse.headers['sec-websocket-extensions']}`);
+ }
+ socket.write(responseHeaders.concat('', '').join('\r\n'));
+ remoteSocket.pipe(socket);
+ socket.pipe(remoteSocket);
+};
+function registerV1(server) {
+ server.routes.set('/v1/', tunnelRequest);
+ server.routes.set('/v1/ws-new-meta', wsNewMeta);
+ server.routes.set('/v1/ws-meta', wsMeta);
+ server.socketRoutes.set('/v1/', tunnelSocket);
+ server.versions.push('v1');
+}
+exports.default = registerV1;
+//# sourceMappingURL=V1.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/V2.d.ts b/lib/bare-server-modified/V2.d.ts
new file mode 100644
index 0000000..602953c
--- /dev/null
+++ b/lib/bare-server-modified/V2.d.ts
@@ -0,0 +1,2 @@
+import type Server from './BareServer.js';
+export default function registerV2(server: Server): void;
diff --git a/lib/bare-server-modified/V2.js b/lib/bare-server-modified/V2.js
new file mode 100644
index 0000000..05c93dc
--- /dev/null
+++ b/lib/bare-server-modified/V2.js
@@ -0,0 +1,364 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const headers_polyfill_1 = require("headers-polyfill");
+const AbstractMessage_js_1 = require("./AbstractMessage.js");
+const BareServer_js_1 = require("./BareServer.js");
+const headerUtil_js_1 = require("./headerUtil.js");
+const remoteUtil_js_1 = require("./remoteUtil.js");
+const requestUtil_js_1 = require("./requestUtil.js");
+const splitHeaderUtil_js_1 = require("./splitHeaderUtil.js");
+const validProtocols = ['http:', 'https:', 'ws:', 'wss:'];
+const forbiddenForwardHeaders = [
+ 'connection',
+ 'transfer-encoding',
+ 'host',
+ 'connection',
+ 'origin',
+ 'referer',
+];
+const forbiddenPassHeaders = [
+ 'vary',
+ 'connection',
+ 'transfer-encoding',
+ 'access-control-allow-headers',
+ 'access-control-allow-methods',
+ 'access-control-expose-headers',
+ 'access-control-max-age',
+ 'access-control-request-headers',
+ 'access-control-request-method',
+];
+// common defaults
+const defaultForwardHeaders = [
+ 'accept-encoding',
+ 'accept-language',
+ 'sec-websocket-extensions',
+ 'sec-websocket-key',
+ 'sec-websocket-version',
+];
+const defaultPassHeaders = [
+ 'content-encoding',
+ 'content-length',
+ 'last-modified',
+];
+// defaults if the client provides a cache key
+const defaultCacheForwardHeaders = [
+ 'if-modified-since',
+ 'if-none-match',
+ 'cache-control',
+];
+const defaultCachePassHeaders = ['cache-control', 'etag'];
+const cacheNotModified = 304;
+function loadForwardedHeaders(forward, target, request) {
+ for (const header of forward) {
+ if (request.headers.has(header)) {
+ target[header] = request.headers.get(header);
+ }
+ }
+}
+const splitHeaderValue = /,\s*/g;
+function readHeaders(request) {
+ const remote = Object.create(null);
+ const sendHeaders = Object.create(null);
+ const passHeaders = [...defaultPassHeaders];
+ const passStatus = [];
+ const forwardHeaders = [...defaultForwardHeaders];
+ // should be unique
+ const cache = request.url.searchParams.has('cache');
+ if (cache) {
+ passHeaders.push(...defaultCachePassHeaders);
+ passStatus.push(cacheNotModified);
+ forwardHeaders.push(...defaultCacheForwardHeaders);
+ }
+ const headers = (0, splitHeaderUtil_js_1.joinHeaders)(request.headers);
+ for (const remoteProp of ['host', 'port', 'protocol', 'path']) {
+ const header = `x-bare-${remoteProp}`;
+ const value = headers.get(header);
+ if (value === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was not specified.`,
+ });
+ switch (remoteProp) {
+ case 'port':
+ if (isNaN(parseInt(value))) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was not a valid integer.`,
+ });
+ }
+ break;
+ case 'protocol':
+ if (!validProtocols.includes(value)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was invalid`,
+ });
+ }
+ break;
+ }
+ remote[remoteProp] = value;
+ }
+ const xBareHeaders = headers.get('x-bare-headers');
+ if (xBareHeaders === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.x-bare-headers`,
+ message: `Header was not specified.`,
+ });
+ try {
+ const json = JSON.parse(xBareHeaders);
+ for (const header in json) {
+ const value = json[header];
+ if (typeof value === 'string') {
+ sendHeaders[header] = value;
+ }
+ else if (Array.isArray(value)) {
+ const array = [];
+ for (const val of value) {
+ if (typeof val !== 'string') {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `bare.headers.${header}`,
+ message: `Header was not a String.`,
+ });
+ }
+ array.push(val);
+ }
+ sendHeaders[header] = array;
+ }
+ else
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `bare.headers.${header}`,
+ message: `Header was not a String.`,
+ });
+ }
+ }
+ catch (error) {
+ if (error instanceof SyntaxError) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.x-bare-headers`,
+ message: `Header contained invalid JSON. (${error.message})`,
+ });
+ }
+ else {
+ throw error;
+ }
+ }
+ if (headers.has('x-bare-pass-status')) {
+ const parsed = headers.get('x-bare-pass-status').split(splitHeaderValue);
+ for (const value of parsed) {
+ const number = parseInt(value);
+ if (isNaN(number)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.x-bare-pass-status`,
+ message: `Array contained non-number value.`,
+ });
+ }
+ else {
+ passStatus.push(number);
+ }
+ }
+ }
+ if (headers.has('x-bare-pass-headers')) {
+ const parsed = headers.get('x-bare-pass-headers').split(splitHeaderValue);
+ for (let header of parsed) {
+ header = header.toLowerCase();
+ if (forbiddenPassHeaders.includes(header)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'FORBIDDEN_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `A forbidden header was passed.`,
+ });
+ }
+ else {
+ passHeaders.push(header);
+ }
+ }
+ }
+ if (headers.has('x-bare-forward-headers')) {
+ const parsed = headers
+ .get('x-bare-forward-headers')
+ .split(splitHeaderValue);
+ for (let header of parsed) {
+ header = header.toLowerCase();
+ if (forbiddenForwardHeaders.includes(header)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'FORBIDDEN_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `A forbidden header was forwarded.`,
+ });
+ }
+ else {
+ forwardHeaders.push(header);
+ }
+ }
+ }
+ return {
+ remote: (0, remoteUtil_js_1.remoteToURL)(remote),
+ sendHeaders,
+ passHeaders,
+ passStatus,
+ forwardHeaders,
+ };
+}
+const tunnelRequest = async (request, res, options) => {
+ const abort = new AbortController();
+ request.body.on('close', () => {
+ if (!request.body.complete)
+ abort.abort();
+ });
+ res.on('close', () => {
+ abort.abort();
+ });
+ const { remote, sendHeaders, passHeaders, passStatus, forwardHeaders } = readHeaders(request);
+ loadForwardedHeaders(forwardHeaders, sendHeaders, request);
+ const response = await (0, requestUtil_js_1.fetch)(request, abort.signal, sendHeaders, remote, options);
+ const responseHeaders = new headers_polyfill_1.Headers();
+ for (const header of passHeaders) {
+ if (!(header in response.headers))
+ continue;
+ responseHeaders.set(header, (0, headerUtil_js_1.flattenHeader)(response.headers[header]));
+ }
+ const status = passStatus.includes(response.statusCode)
+ ? response.statusCode
+ : 200;
+ if (status !== cacheNotModified) {
+ responseHeaders.set('x-bare-status', response.statusCode.toString());
+ responseHeaders.set('x-bare-status-text', response.statusMessage);
+ responseHeaders.set('x-bare-headers', JSON.stringify((0, headerUtil_js_1.mapHeadersFromArray)((0, headerUtil_js_1.rawHeaderNames)(response.rawHeaders), {
+ ...response.headers,
+ })));
+ }
+ return new AbstractMessage_js_1.Response(response, {
+ status,
+ headers: (0, splitHeaderUtil_js_1.splitHeaders)(responseHeaders),
+ });
+};
+const metaExpiration = 30e3;
+const getMeta = async (request, res, options) => {
+ if (request.method === 'OPTIONS') {
+ return new AbstractMessage_js_1.Response(undefined, { status: 200 });
+ }
+ const id = request.headers.get('x-bare-id');
+ if (id === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: 'request.headers.x-bare-id',
+ message: 'Header was not specified',
+ });
+ const meta = await options.database.get(id);
+ if (meta?.value.v !== 2)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: 'request.headers.x-bare-id',
+ message: 'Unregistered ID',
+ });
+ if (!meta.value.response)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: 'request.headers.x-bare-id',
+ message: 'Meta not ready',
+ });
+ await options.database.delete(id);
+ const responseHeaders = new headers_polyfill_1.Headers();
+ responseHeaders.set('x-bare-status', meta.value.response.status.toString());
+ responseHeaders.set('x-bare-status-text', meta.value.response.statusText);
+ responseHeaders.set('x-bare-headers', JSON.stringify(meta.value.response.headers));
+ return new AbstractMessage_js_1.Response(undefined, {
+ status: 200,
+ headers: (0, splitHeaderUtil_js_1.splitHeaders)(responseHeaders),
+ });
+};
+const newMeta = async (request, res, options) => {
+ const { remote, sendHeaders, forwardHeaders } = readHeaders(request);
+ const id = (0, requestUtil_js_1.randomHex)(16);
+ await options.database.set(id, {
+ expires: Date.now() + metaExpiration,
+ value: {
+ v: 2,
+ remote: remote.toString(),
+ sendHeaders,
+ forwardHeaders,
+ },
+ });
+ return new AbstractMessage_js_1.Response(Buffer.from(id));
+};
+const tunnelSocket = async (request, socket, head, options) => {
+ const abort = new AbortController();
+ request.body.on('close', () => {
+ if (!request.body.complete)
+ abort.abort();
+ });
+ socket.on('close', () => {
+ abort.abort();
+ });
+ if (!request.headers.has('sec-websocket-protocol')) {
+ socket.end();
+ return;
+ }
+ const id = request.headers.get('sec-websocket-protocol');
+ const meta = await options.database.get(id);
+ if (meta?.value.v !== 2) {
+ socket.end();
+ return;
+ }
+ loadForwardedHeaders(meta.value.forwardHeaders, meta.value.sendHeaders, request);
+ const [remoteResponse, remoteSocket] = await (0, requestUtil_js_1.upgradeFetch)(request, abort.signal, meta.value.sendHeaders, new URL(meta.value.remote), options);
+ remoteSocket.on('close', () => {
+ socket.end();
+ });
+ socket.on('close', () => {
+ remoteSocket.end();
+ });
+ remoteSocket.on('error', (error) => {
+ if (options.logErrors) {
+ console.error('Remote socket error:', error);
+ }
+ socket.end();
+ });
+ socket.on('error', (error) => {
+ if (options.logErrors) {
+ console.error('Serving socket error:', error);
+ }
+ remoteSocket.end();
+ });
+ const remoteHeaders = new headers_polyfill_1.Headers(remoteResponse.headers);
+ meta.value.response = {
+ headers: (0, headerUtil_js_1.mapHeadersFromArray)((0, headerUtil_js_1.rawHeaderNames)(remoteResponse.rawHeaders), {
+ ...remoteResponse.headers,
+ }),
+ status: remoteResponse.statusCode,
+ statusText: remoteResponse.statusMessage,
+ };
+ await options.database.set(id, meta);
+ const responseHeaders = [
+ `HTTP/1.1 101 Switching Protocols`,
+ `Upgrade: websocket`,
+ `Connection: Upgrade`,
+ `Sec-WebSocket-Protocol: ${id}`,
+ ];
+ if (remoteHeaders.has('sec-websocket-extensions')) {
+ responseHeaders.push(`Sec-WebSocket-Extensions: ${remoteHeaders.get('sec-websocket-extensions')}`);
+ }
+ if (remoteHeaders.has('sec-websocket-accept')) {
+ responseHeaders.push(`Sec-WebSocket-Accept: ${remoteHeaders.get('sec-websocket-accept')}`);
+ }
+ socket.write(responseHeaders.concat('', '').join('\r\n'));
+ remoteSocket.pipe(socket);
+ socket.pipe(remoteSocket);
+};
+function registerV2(server) {
+ server.routes.set('/v2/', tunnelRequest);
+ server.routes.set('/v2/ws-new-meta', newMeta);
+ server.routes.set('/v2/ws-meta', getMeta);
+ server.socketRoutes.set('/v2/', tunnelSocket);
+ server.versions.push('v2');
+}
+exports.default = registerV2;
+//# sourceMappingURL=V2.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/V3.d.ts b/lib/bare-server-modified/V3.d.ts
new file mode 100644
index 0000000..53c098f
--- /dev/null
+++ b/lib/bare-server-modified/V3.d.ts
@@ -0,0 +1,2 @@
+import type Server from './BareServer.js';
+export default function registerV3(server: Server): void;
diff --git a/lib/bare-server-modified/V3.js b/lib/bare-server-modified/V3.js
new file mode 100644
index 0000000..802bcb3
--- /dev/null
+++ b/lib/bare-server-modified/V3.js
@@ -0,0 +1,305 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const headers_polyfill_1 = require("headers-polyfill");
+const AbstractMessage_js_1 = require("./AbstractMessage.js");
+const BareServer_js_1 = require("./BareServer.js");
+const headerUtil_js_1 = require("./headerUtil.js");
+const remoteUtil_js_1 = require("./remoteUtil.js");
+const requestUtil_js_1 = require("./requestUtil.js");
+const splitHeaderUtil_js_1 = require("./splitHeaderUtil.js");
+const forbiddenForwardHeaders = [
+ 'connection',
+ 'transfer-encoding',
+ 'host',
+ 'connection',
+ 'origin',
+ 'referer',
+];
+const forbiddenPassHeaders = [
+ 'vary',
+ 'connection',
+ 'transfer-encoding',
+ 'access-control-allow-headers',
+ 'access-control-allow-methods',
+ 'access-control-expose-headers',
+ 'access-control-max-age',
+ 'access-control-request-headers',
+ 'access-control-request-method',
+];
+// common defaults
+const defaultForwardHeaders = ['accept-encoding', 'accept-language'];
+const defaultPassHeaders = [
+ 'content-encoding',
+ 'content-length',
+ 'last-modified',
+];
+// defaults if the client provides a cache key
+const defaultCacheForwardHeaders = [
+ 'if-modified-since',
+ 'if-none-match',
+ 'cache-control',
+];
+const defaultCachePassHeaders = ['cache-control', 'etag'];
+const cacheNotModified = 304;
+function loadForwardedHeaders(forward, target, request) {
+ for (const header of forward) {
+ if (request.headers.has(header)) {
+ target[header] = request.headers.get(header);
+ }
+ }
+}
+const splitHeaderValue = /,\s*/g;
+function readHeaders(request) {
+ const sendHeaders = Object.create(null);
+ const passHeaders = [...defaultPassHeaders];
+ const passStatus = [];
+ const forwardHeaders = [...defaultForwardHeaders];
+ // should be unique
+ const cache = request.url.searchParams.has('cache');
+ if (cache) {
+ passHeaders.push(...defaultCachePassHeaders);
+ passStatus.push(cacheNotModified);
+ forwardHeaders.push(...defaultCacheForwardHeaders);
+ }
+ const headers = (0, splitHeaderUtil_js_1.joinHeaders)(request.headers);
+ const xBareURL = headers.get('x-bare-url');
+ if (xBareURL === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.x-bare-url`,
+ message: `Header was not specified.`,
+ });
+ const remote = (0, remoteUtil_js_1.urlToRemote)(new URL(xBareURL));
+ const xBareHeaders = headers.get('x-bare-headers');
+ if (xBareHeaders === null)
+ throw new BareServer_js_1.BareError(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.x-bare-headers`,
+ message: `Header was not specified.`,
+ });
+ try {
+ const json = JSON.parse(xBareHeaders);
+ for (const header in json) {
+ const value = json[header];
+ if (typeof value === 'string') {
+ sendHeaders[header] = value;
+ }
+ else if (Array.isArray(value)) {
+ const array = [];
+ for (const val of value) {
+ if (typeof val !== 'string') {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `bare.headers.${header}`,
+ message: `Header was not a String.`,
+ });
+ }
+ array.push(val);
+ }
+ sendHeaders[header] = array;
+ }
+ else {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `bare.headers.${header}`,
+ message: `Header was not a String.`,
+ });
+ }
+ }
+ }
+ catch (error) {
+ if (error instanceof SyntaxError) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.x-bare-headers`,
+ message: `Header contained invalid JSON. (${error.message})`,
+ });
+ }
+ else {
+ throw error;
+ }
+ }
+ if (headers.has('x-bare-pass-status')) {
+ const parsed = headers.get('x-bare-pass-status').split(splitHeaderValue);
+ for (const value of parsed) {
+ const number = parseInt(value);
+ if (isNaN(number)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.x-bare-pass-status`,
+ message: `Array contained non-number value.`,
+ });
+ }
+ else {
+ passStatus.push(number);
+ }
+ }
+ }
+ if (headers.has('x-bare-pass-headers')) {
+ const parsed = headers.get('x-bare-pass-headers').split(splitHeaderValue);
+ for (let header of parsed) {
+ header = header.toLowerCase();
+ if (forbiddenPassHeaders.includes(header)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'FORBIDDEN_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `A forbidden header was passed.`,
+ });
+ }
+ else {
+ passHeaders.push(header);
+ }
+ }
+ }
+ if (headers.has('x-bare-forward-headers')) {
+ const parsed = headers
+ .get('x-bare-forward-headers')
+ .split(splitHeaderValue);
+ for (let header of parsed) {
+ header = header.toLowerCase();
+ if (forbiddenForwardHeaders.includes(header)) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'FORBIDDEN_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `A forbidden header was forwarded.`,
+ });
+ }
+ else {
+ forwardHeaders.push(header);
+ }
+ }
+ }
+ return {
+ remote: (0, remoteUtil_js_1.remoteToURL)(remote),
+ sendHeaders,
+ passHeaders,
+ passStatus,
+ forwardHeaders,
+ };
+}
+const tunnelRequest = async (request, res, options) => {
+ const abort = new AbortController();
+ request.body.on('close', () => {
+ if (!request.body.complete)
+ abort.abort();
+ });
+ res.on('close', () => {
+ abort.abort();
+ });
+ const { remote, sendHeaders, passHeaders, passStatus, forwardHeaders } = readHeaders(request);
+ loadForwardedHeaders(forwardHeaders, sendHeaders, request);
+ const response = await (0, requestUtil_js_1.fetch)(request, abort.signal, sendHeaders, remote, options);
+ const responseHeaders = new headers_polyfill_1.Headers();
+ for (const header of passHeaders) {
+ if (!(header in response.headers))
+ continue;
+ responseHeaders.set(header, (0, headerUtil_js_1.flattenHeader)(response.headers[header]));
+ }
+ const status = passStatus.includes(response.statusCode)
+ ? response.statusCode
+ : 200;
+ if (status !== cacheNotModified) {
+ responseHeaders.set('x-bare-status', response.statusCode.toString());
+ responseHeaders.set('x-bare-status-text', response.statusMessage);
+ responseHeaders.set('x-bare-headers', JSON.stringify((0, headerUtil_js_1.mapHeadersFromArray)((0, headerUtil_js_1.rawHeaderNames)(response.rawHeaders), {
+ ...response.headers,
+ })));
+ }
+ return new AbstractMessage_js_1.Response(response, {
+ status,
+ headers: (0, splitHeaderUtil_js_1.splitHeaders)(responseHeaders),
+ });
+};
+function readSocket(socket) {
+ return new Promise((resolve, reject) => {
+ const messageListener = (event) => {
+ cleanup();
+ if (typeof event.data !== 'string')
+ return reject(new TypeError('the first websocket message was not a text frame'));
+ try {
+ resolve(JSON.parse(event.data));
+ }
+ catch (err) {
+ reject(err);
+ }
+ };
+ const closeListener = () => {
+ cleanup();
+ };
+ const cleanup = () => {
+ socket.removeEventListener('message', messageListener);
+ socket.removeEventListener('close', closeListener);
+ clearTimeout(timeout);
+ };
+ const timeout = setTimeout(() => {
+ cleanup();
+ reject(new Error('Timed out before metadata could be read'));
+ }, 10e3);
+ socket.addEventListener('message', messageListener);
+ socket.addEventListener('close', closeListener);
+ });
+}
+const tunnelSocket = async (request, socket, head, options) => options.wss.handleUpgrade(request.body, socket, head, async (client) => {
+ let _remoteSocket;
+ try {
+ const connectPacket = await readSocket(client);
+ if (connectPacket.type !== 'connect')
+ throw new Error('Client did not send open packet.');
+ loadForwardedHeaders(connectPacket.forwardHeaders, connectPacket.headers, request);
+ const [remoteReq, remoteSocket] = await (0, requestUtil_js_1.webSocketFetch)(request, connectPacket.headers, new URL(connectPacket.remote), connectPacket.protocols, options);
+ _remoteSocket = remoteSocket;
+ const setCookieHeader = remoteReq.headers['set-cookie'];
+ const setCookies = setCookieHeader !== undefined
+ ? Array.isArray(setCookieHeader)
+ ? setCookieHeader
+ : [setCookieHeader]
+ : [];
+ client.send(JSON.stringify({
+ type: 'open',
+ protocol: remoteSocket.protocol,
+ setCookies,
+ }),
+ // use callback to wait for this message to buffer and finally send before doing any piping
+ // otherwise the client will receive a random message from the remote before our open message
+ () => {
+ remoteSocket.addEventListener('message', (event) => {
+ client.send(event.data);
+ });
+ client.addEventListener('message', (event) => {
+ remoteSocket.send(event.data);
+ });
+ remoteSocket.addEventListener('close', () => {
+ client.close();
+ });
+ client.addEventListener('close', () => {
+ remoteSocket.close();
+ });
+ remoteSocket.addEventListener('error', (error) => {
+ if (options.logErrors) {
+ console.error('Remote socket error:', error);
+ }
+ client.close();
+ });
+ client.addEventListener('error', (error) => {
+ if (options.logErrors) {
+ console.error('Serving socket error:', error);
+ }
+ remoteSocket.close();
+ });
+ });
+ }
+ catch (err) {
+ if (options.logErrors)
+ console.error(err);
+ client.close();
+ if (_remoteSocket)
+ _remoteSocket.close();
+ }
+});
+function registerV3(server) {
+ server.routes.set('/v3/', tunnelRequest);
+ server.socketRoutes.set('/v3/', tunnelSocket);
+ server.versions.push('v3');
+}
+exports.default = registerV3;
+//# sourceMappingURL=V3.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/createServer.d.ts b/lib/bare-server-modified/createServer.d.ts
new file mode 100644
index 0000000..2838420
--- /dev/null
+++ b/lib/bare-server-modified/createServer.d.ts
@@ -0,0 +1,52 @@
+///
+///
+import { Agent as HttpAgent } from 'node:http';
+import { Agent as HttpsAgent } from 'node:https';
+import BareServer from './BareServer.js';
+import type { BareMaintainer, Options } from './BareServer.js';
+import type { Database } from './Meta.js';
+export declare const validIPFamily: number[];
+export type IPFamily = 0 | 4 | 6;
+export interface BareServerInit {
+ logErrors?: boolean;
+ localAddress?: string;
+ /**
+ * When set, the default logic for blocking local IP addresses is disabled.
+ */
+ filterRemote?: Options['filterRemote'];
+ /**
+ * When set, the default logic for blocking local IP addresses is disabled.
+ */
+ lookup?: Options['lookup'];
+ /**
+ * If local IP addresses/DNS records should be blocked.
+ * @default true
+ */
+ blockLocal?: boolean;
+ /**
+ * IP address family to use when resolving `host` or `hostname`. Valid values are `0`, `4`, and `6`. When unspecified/0, both IP v4 and v6 will be used.
+ */
+ family?: IPFamily | number;
+ maintainer?: BareMaintainer;
+ httpAgent?: HttpAgent;
+ httpsAgent?: HttpsAgent;
+ /**
+ * If legacy clients should be supported (v1 & v2). If this is set to false, the database can be safely ignored.
+ * @default true
+ */
+ legacySupport?: boolean;
+ database?: Database;
+}
+export interface Address {
+ address: string;
+ family: number;
+}
+/**
+ * Converts the address and family of a DNS lookup callback into an array if it wasn't already
+ */
+export declare function toAddressArray(address: string | Address[], family?: number): Address[];
+/**
+ * Create a Bare server.
+ * This will handle all lifecycles for unspecified options (httpAgent, httpsAgent, metaMap).
+ */
+export declare function createBareServer(directory: string, init?: BareServerInit): BareServer;
diff --git a/lib/bare-server-modified/createServer.js b/lib/bare-server-modified/createServer.js
new file mode 100644
index 0000000..695d300
--- /dev/null
+++ b/lib/bare-server-modified/createServer.js
@@ -0,0 +1,99 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.createBareServer = exports.toAddressArray = exports.validIPFamily = void 0;
+const node_dns_1 = require("node:dns");
+const node_http_1 = require("node:http");
+const node_https_1 = require("node:https");
+const ipaddr_js_1 = require("ipaddr.js");
+const ws_1 = require("ws");
+const BareServer_js_1 = __importDefault(require("./BareServer.js"));
+const Meta_js_1 = require("./Meta.js");
+const V1_js_1 = __importDefault(require("./V1.js"));
+const V2_js_1 = __importDefault(require("./V2.js"));
+const V3_js_1 = __importDefault(require("./V3.js"));
+exports.validIPFamily = [0, 4, 6];
+/**
+ * Converts the address and family of a DNS lookup callback into an array if it wasn't already
+ */
+function toAddressArray(address, family) {
+ if (typeof address === 'string')
+ return [
+ {
+ address,
+ family,
+ },
+ ];
+ else
+ return address;
+}
+exports.toAddressArray = toAddressArray;
+/**
+ * Create a Bare server.
+ * This will handle all lifecycles for unspecified options (httpAgent, httpsAgent, metaMap).
+ */
+function createBareServer(directory, init = {}) {
+ if (typeof directory !== 'string')
+ throw new Error('Directory must be specified.');
+ if (!directory.startsWith('/') || !directory.endsWith('/'))
+ throw new RangeError('Directory must start and end with /');
+ init.logErrors ??= false;
+ const cleanup = [];
+ if (typeof init.family === 'number' && !exports.validIPFamily.includes(init.family))
+ throw new RangeError('init.family must be one of: 0, 4, 6');
+ if (init.blockLocal ?? true) {
+ init.filterRemote ??= (url) => {
+ // if the remote is an IP then it didn't go through the init.lookup hook
+ // isValid determines if this is so
+ if ((0, ipaddr_js_1.isValid)(url.hostname) && (0, ipaddr_js_1.parse)(url.hostname).range() !== 'unicast')
+ throw new RangeError('Forbidden IP');
+ };
+ init.lookup ??= (hostname, options, callback) => (0, node_dns_1.lookup)(hostname, options, (err, address, family) => {
+ if (address &&
+ toAddressArray(address, family).some(({ address }) => (0, ipaddr_js_1.parse)(address).range() !== 'unicast'))
+ callback(new RangeError('Forbidden IP'), '', -1);
+ else
+ callback(err, address, family);
+ });
+ }
+ if (!init.httpAgent) {
+ const httpAgent = new node_http_1.Agent({
+ keepAlive: true,
+ });
+ init.httpAgent = httpAgent;
+ cleanup.push(() => httpAgent.destroy());
+ }
+ if (!init.httpsAgent) {
+ const httpsAgent = new node_https_1.Agent({
+ keepAlive: true,
+ });
+ init.httpsAgent = httpsAgent;
+ cleanup.push(() => httpsAgent.destroy());
+ }
+ if (!init.database) {
+ const database = new Map();
+ const interval = setInterval(() => (0, Meta_js_1.cleanupDatabase)(database), 1000);
+ init.database = database;
+ cleanup.push(() => clearInterval(interval));
+ }
+ const server = new BareServer_js_1.default(directory, {
+ ...init,
+ database: new Meta_js_1.JSONDatabaseAdapter(init.database),
+ wss: new ws_1.WebSocketServer({ noServer: true }),
+ });
+ init.legacySupport ??= true;
+ if (init.legacySupport) {
+ (0, V1_js_1.default)(server);
+ (0, V2_js_1.default)(server);
+ }
+ (0, V3_js_1.default)(server);
+ server.once('close', () => {
+ for (const cb of cleanup)
+ cb();
+ });
+ return server;
+}
+exports.createBareServer = createBareServer;
+//# sourceMappingURL=createServer.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/encodeProtocol.d.ts b/lib/bare-server-modified/encodeProtocol.d.ts
new file mode 100644
index 0000000..9468b7f
--- /dev/null
+++ b/lib/bare-server-modified/encodeProtocol.d.ts
@@ -0,0 +1,3 @@
+export declare function validProtocol(protocol: string): boolean;
+export declare function encodeProtocol(protocol: string): string;
+export declare function decodeProtocol(protocol: string): string;
diff --git a/lib/bare-server-modified/encodeProtocol.js b/lib/bare-server-modified/encodeProtocol.js
new file mode 100644
index 0000000..cc9d769
--- /dev/null
+++ b/lib/bare-server-modified/encodeProtocol.js
@@ -0,0 +1,48 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.decodeProtocol = exports.encodeProtocol = exports.validProtocol = void 0;
+const validChars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
+const reserveChar = '%';
+function validProtocol(protocol) {
+ for (let i = 0; i < protocol.length; i++) {
+ const char = protocol[i];
+ if (!validChars.includes(char)) {
+ return false;
+ }
+ }
+ return true;
+}
+exports.validProtocol = validProtocol;
+function encodeProtocol(protocol) {
+ let result = '';
+ for (let i = 0; i < protocol.length; i++) {
+ const char = protocol[i];
+ if (validChars.includes(char) && char !== reserveChar) {
+ result += char;
+ }
+ else {
+ const code = char.charCodeAt(0);
+ result += reserveChar + code.toString(16).padStart(2, '0');
+ }
+ }
+ return result;
+}
+exports.encodeProtocol = encodeProtocol;
+function decodeProtocol(protocol) {
+ let result = '';
+ for (let i = 0; i < protocol.length; i++) {
+ const char = protocol[i];
+ if (char === reserveChar) {
+ const code = parseInt(protocol.slice(i + 1, i + 3), 16);
+ const decoded = String.fromCharCode(code);
+ result += decoded;
+ i += 2;
+ }
+ else {
+ result += char;
+ }
+ }
+ return result;
+}
+exports.decodeProtocol = decodeProtocol;
+//# sourceMappingURL=encodeProtocol.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/headerUtil.d.ts b/lib/bare-server-modified/headerUtil.d.ts
new file mode 100644
index 0000000..ecac542
--- /dev/null
+++ b/lib/bare-server-modified/headerUtil.d.ts
@@ -0,0 +1,8 @@
+import type { BareHeaders } from './requestUtil.js';
+export declare function objectFromRawHeaders(raw: string[]): BareHeaders;
+export declare function rawHeaderNames(raw: string[]): string[];
+export declare function mapHeadersFromArray(from: string[], to: BareHeaders): BareHeaders;
+/**
+ * Converts a header into an HTTP-ready comma joined header.
+ */
+export declare function flattenHeader(value: string | string[]): string;
diff --git a/lib/bare-server-modified/headerUtil.js b/lib/bare-server-modified/headerUtil.js
new file mode 100644
index 0000000..f9913e9
--- /dev/null
+++ b/lib/bare-server-modified/headerUtil.js
@@ -0,0 +1,48 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.flattenHeader = exports.mapHeadersFromArray = exports.rawHeaderNames = exports.objectFromRawHeaders = void 0;
+function objectFromRawHeaders(raw) {
+ const result = Object.create(null);
+ for (let i = 0; i < raw.length; i += 2) {
+ const [header, value] = raw.slice(i, i + 2);
+ if (header in result) {
+ const v = result[header];
+ if (Array.isArray(v))
+ v.push(value);
+ else
+ result[header] = [v, value];
+ }
+ else
+ result[header] = value;
+ }
+ return result;
+}
+exports.objectFromRawHeaders = objectFromRawHeaders;
+function rawHeaderNames(raw) {
+ const result = [];
+ for (let i = 0; i < raw.length; i += 2) {
+ if (!result.includes(raw[i]))
+ result.push(raw[i]);
+ }
+ return result;
+}
+exports.rawHeaderNames = rawHeaderNames;
+function mapHeadersFromArray(from, to) {
+ for (const header of from) {
+ if (header.toLowerCase() in to) {
+ const value = to[header.toLowerCase()];
+ delete to[header.toLowerCase()];
+ to[header] = value;
+ }
+ }
+ return to;
+}
+exports.mapHeadersFromArray = mapHeadersFromArray;
+/**
+ * Converts a header into an HTTP-ready comma joined header.
+ */
+function flattenHeader(value) {
+ return Array.isArray(value) ? value.join(', ') : value;
+}
+exports.flattenHeader = flattenHeader;
+//# sourceMappingURL=headerUtil.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/remoteUtil.d.ts b/lib/bare-server-modified/remoteUtil.d.ts
new file mode 100644
index 0000000..1b317cb
--- /dev/null
+++ b/lib/bare-server-modified/remoteUtil.d.ts
@@ -0,0 +1,9 @@
+export interface BareRemote {
+ host: string;
+ port: number | string;
+ path: string;
+ protocol: string;
+}
+export declare function remoteToURL(remote: BareRemote): URL;
+export declare function resolvePort(url: URL): number;
+export declare function urlToRemote(url: URL): BareRemote;
diff --git a/lib/bare-server-modified/remoteUtil.js b/lib/bare-server-modified/remoteUtil.js
new file mode 100644
index 0000000..71e8739
--- /dev/null
+++ b/lib/bare-server-modified/remoteUtil.js
@@ -0,0 +1,36 @@
+"use strict";
+/*
+ * Utilities for converting remotes to URLs
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.urlToRemote = exports.resolvePort = exports.remoteToURL = void 0;
+function remoteToURL(remote) {
+ return new URL(`${remote.protocol}${remote.host}:${remote.port}${remote.path}`);
+}
+exports.remoteToURL = remoteToURL;
+function resolvePort(url) {
+ if (url.port)
+ return Number(url.port);
+ switch (url.protocol) {
+ case 'ws:':
+ case 'http:':
+ return 80;
+ case 'wss:':
+ case 'https:':
+ return 443;
+ default:
+ // maybe blob
+ return 0;
+ }
+}
+exports.resolvePort = resolvePort;
+function urlToRemote(url) {
+ return {
+ protocol: url.protocol,
+ host: url.hostname,
+ port: resolvePort(url),
+ path: url.pathname + url.search,
+ };
+}
+exports.urlToRemote = urlToRemote;
+//# sourceMappingURL=remoteUtil.js.map
\ No newline at end of file
diff --git a/lib/bare-server-modified/requestUtil.d.ts b/lib/bare-server-modified/requestUtil.d.ts
new file mode 100644
index 0000000..97e891f
--- /dev/null
+++ b/lib/bare-server-modified/requestUtil.d.ts
@@ -0,0 +1,13 @@
+///
+///
+///
+import type { IncomingMessage } from 'node:http';
+import type { Duplex } from 'node:stream';
+import WebSocket from 'ws';
+import type { Request } from './AbstractMessage.js';
+import type { Options } from './BareServer.js';
+export type BareHeaders = Record;
+export declare function randomHex(byteLength: number): string;
+export declare function fetch(request: Request, signal: AbortSignal, requestHeaders: BareHeaders, remote: URL, options: Options): Promise;
+export declare function upgradeFetch(request: Request, signal: AbortSignal, requestHeaders: BareHeaders, remote: URL, options: Options): Promise<[res: IncomingMessage, socket: Duplex, head: Buffer]>;
+export declare function webSocketFetch(request: Request, requestHeaders: BareHeaders, remote: URL, protocols: string[], options: Options): Promise<[req: IncomingMessage, socket: WebSocket]>;
diff --git a/lib/bare-server-modified/requestUtil.js b/lib/bare-server-modified/requestUtil.js
new file mode 100644
index 0000000..792ad0c
--- /dev/null
+++ b/lib/bare-server-modified/requestUtil.js
@@ -0,0 +1,185 @@
+"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.upgradeFetch = exports.fetch = exports.randomHex = void 0;
+const node_crypto_1 = require("node:crypto");
+const node_http_1 = require("node:http");
+const node_https_1 = require("node:https");
+const ws_1 = __importDefault(require("ws"));
+const BareServer_js_1 = require("./BareServer.js");
+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 fetch(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}'`);
+ request.body.pipe(outgoing);
+ 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.fetch = fetch;
+async function upgradeFetch(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.upgradeFetch = upgradeFetch;
+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
\ No newline at end of file
diff --git a/lib/bare-server-modified/splitHeaderUtil.d.ts b/lib/bare-server-modified/splitHeaderUtil.d.ts
new file mode 100644
index 0000000..ed9a454
--- /dev/null
+++ b/lib/bare-server-modified/splitHeaderUtil.d.ts
@@ -0,0 +1,14 @@
+import { Headers } from 'headers-polyfill';
+/**
+ *
+ * Splits headers according to spec
+ * @param headers
+ * @returns Split headers
+ */
+export declare function splitHeaders(headers: Headers): Headers;
+/**
+ * Joins headers according to spec
+ * @param headers
+ * @returns Joined headers
+ */
+export declare function joinHeaders(headers: Headers): Headers;
diff --git a/lib/bare-server-modified/splitHeaderUtil.js b/lib/bare-server-modified/splitHeaderUtil.js
new file mode 100644
index 0000000..26baa27
--- /dev/null
+++ b/lib/bare-server-modified/splitHeaderUtil.js
@@ -0,0 +1,60 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.joinHeaders = exports.splitHeaders = void 0;
+const headers_polyfill_1 = require("headers-polyfill");
+const BareServer_js_1 = require("./BareServer.js");
+const MAX_HEADER_VALUE = 3072;
+/**
+ *
+ * Splits headers according to spec
+ * @param headers
+ * @returns Split headers
+ */
+function splitHeaders(headers) {
+ const output = new headers_polyfill_1.Headers(headers);
+ if (headers.has('x-bare-headers')) {
+ const value = headers.get('x-bare-headers');
+ if (value.length > MAX_HEADER_VALUE) {
+ output.delete('x-bare-headers');
+ let split = 0;
+ for (let i = 0; i < value.length; i += MAX_HEADER_VALUE) {
+ const part = value.slice(i, i + MAX_HEADER_VALUE);
+ const id = split++;
+ output.set(`x-bare-headers-${id}`, `;${part}`);
+ }
+ }
+ }
+ return output;
+}
+exports.splitHeaders = splitHeaders;
+/**
+ * Joins headers according to spec
+ * @param headers
+ * @returns Joined headers
+ */
+function joinHeaders(headers) {
+ const output = new headers_polyfill_1.Headers(headers);
+ const prefix = 'x-bare-headers';
+ if (headers.has(`${prefix}-0`)) {
+ const join = [];
+ for (const [header, value] of headers) {
+ if (!header.startsWith(prefix)) {
+ continue;
+ }
+ if (!value.startsWith(';')) {
+ throw new BareServer_js_1.BareError(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Value didn't begin with semi-colon.`,
+ });
+ }
+ const id = parseInt(header.slice(prefix.length + 1));
+ join[id] = value.slice(1);
+ output.delete(header);
+ }
+ output.set(prefix, join.join(''));
+ }
+ return output;
+}
+exports.joinHeaders = joinHeaders;
+//# sourceMappingURL=splitHeaderUtil.js.map
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 5e6de78..4c28b25 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.2.1",
"license": "GNU-3.0-or-later",
"dependencies": {
- "@tomphttp/bare-server-node": "*",
+ "@tomphttp/bare-server-node": "^2.0.1",
"cors": "*",
"easyviolet": "github:Russell2259/Easyviolet",
"express": "*",
diff --git a/package.json b/package.json
index 153b458..6a3f214 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"author": "Polaris Development Group",
"license": "GNU-3.0-or-later",
"dependencies": {
- "@tomphttp/bare-server-node": "*",
+ "@tomphttp/bare-server-node": "^2.0.1",
"cors": "*",
"easyviolet": "github:Russell2259/Easyviolet",
"express": "*",
@@ -22,4 +22,4 @@
"mime": "*",
"uuid": "^9.0.1"
}
-}
\ No newline at end of file
+}