94 lines
2.7 KiB
JavaScript
94 lines
2.7 KiB
JavaScript
|
import DefaultHandler from "./default.js"
|
||
|
import path from "path";
|
||
|
import * as tls from "tls";
|
||
|
import { pipeline, Readable } from "stream";
|
||
|
|
||
|
/**
|
||
|
* async wrapper around tls.connect
|
||
|
*/
|
||
|
async function tlsConnect(...args) {
|
||
|
return new Promise(function (resolve, reject) {
|
||
|
try {
|
||
|
const connection = tls.connect(...args, function () {
|
||
|
console.log("Got connection");
|
||
|
resolve(connection);
|
||
|
});
|
||
|
connection.on("error", function (err) {
|
||
|
console.error(err);
|
||
|
reject(err);
|
||
|
});
|
||
|
}catch (err) {
|
||
|
console.error(err);
|
||
|
reject(err);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Make Gemini request to URL
|
||
|
// Return the content
|
||
|
async function geminiReq(toServe, socket) {
|
||
|
const url = new URL(`gemini://${toServe}`);
|
||
|
|
||
|
const options = {
|
||
|
port: url.port || 1965,
|
||
|
host: url.hostname,
|
||
|
servername: url.hostname, //The servername used in SNI
|
||
|
};
|
||
|
|
||
|
console.log(options);
|
||
|
const connection = await tlsConnect(options);
|
||
|
|
||
|
// connection.write("gemini://example.com:1973/testdata\r\n");
|
||
|
|
||
|
await new Promise(function (resolve, reject) {
|
||
|
pipeline(
|
||
|
Readable.from((function* () {
|
||
|
const req = `${url.toString()}\r\n`;
|
||
|
console.log(`Sending proxy request ${req}`);
|
||
|
yield req;
|
||
|
})()),
|
||
|
connection,
|
||
|
socket,
|
||
|
function (err) {
|
||
|
if (err) {
|
||
|
console.log("Pipeline failed");
|
||
|
reject(err);
|
||
|
}else {
|
||
|
resolve();
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
export default class ReverseProxyHandler extends DefaultHandler {
|
||
|
constructor (proxyRoot) {
|
||
|
super();
|
||
|
|
||
|
this.proxyRoot = proxyRoot;
|
||
|
this.subpathMatches = true;
|
||
|
}
|
||
|
|
||
|
async handle (url, p, socket) {
|
||
|
console.log(`${url} matched, attempting to proxy to ${this.proxyRoot}`);
|
||
|
const relativePath = path.relative(p, url.pathname);
|
||
|
//Concat and normalize the passed URL as being relative to the base path
|
||
|
const toServe = path.join(this.proxyRoot, relativePath);
|
||
|
//If the resulting path is a parent, relative to proxyRoot, disallow that
|
||
|
if (path.relative(this.proxyRoot, toServe).startsWith("..")) {
|
||
|
return "50";
|
||
|
}
|
||
|
|
||
|
console.log(`Attempting to proxy ${toServe}.`);
|
||
|
try {
|
||
|
await geminiReq(toServe, socket)
|
||
|
return "";
|
||
|
}catch (err) {
|
||
|
console.log("Something went wrong with the proxy.");
|
||
|
console.error(err);
|
||
|
// You could debate whether this is a 43 or a 42 or a 41
|
||
|
return "42 Internal Proxy Error\r\n"
|
||
|
}
|
||
|
}
|
||
|
}
|