astronomical-theater/handlers/revproxy.js

96 lines
2.9 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.matchesSubpaths = 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 {
// Add back url.search
await geminiReq(toServe + url.search, socket);
// geminiReq has already handled writing data back to the stream
return false;
}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"
}
}
}