Skip to content

Commit

Permalink
checkpoint: added xhr wrapper, client scripts now served as static as…
Browse files Browse the repository at this point in the history
…sets

- es modules for each script
- initializer snippet still injected into page

this makes surviv.io work again
  • Loading branch information
nfriedly committed Mar 26, 2021
1 parent ad1dce1 commit 3a63029
Show file tree
Hide file tree
Showing 5 changed files with 461 additions and 60 deletions.
10 changes: 10 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ module.exports = {
files: ["examples/*/*.js"],
rules: {
"node/no-missing-require": "off",
}
}, {
files: ["lib/client/*.mjs"],
parserOptions: {
sourceType: "module",
},
env: {
node: false,
browser: true,
es6: true,
},
},
],
Expand Down
96 changes: 44 additions & 52 deletions lib/client-scripts.js
Original file line number Diff line number Diff line change
@@ -1,71 +1,60 @@
"use strict";

var fs = require("fs");
var path = require("path");
var Transform = require("stream").Transform;
var serveStatic = require('serve-static');
var finalhandler = require('finalhandler');
var contentTypes = require("./content-types");

// https://developer.mozilla.org/en-US/docs/Glossary/IIFE
function makeIIFEScript(fn) {
return "<script>\n(" + fn.toString() + ")();\n</script>";
}

module.exports = function (config) {
var CLIENT_WEBSOCKET_SCRIPT = makeIIFEScript(function () {
/* eslint-env browser */
console.log("begin unblocker client scripts");
var _WebSocket = WebSocket;
var proxyHost = location.host;
var isSecure = location.protocol === "https";
var prefix = "PREFIX";
var target = location.pathname.substr(prefix.length);
var targetURL = new URL(target);
// ws:// or wss:// then at least one char for location,
// then either the end or a path
var reWsUrl = /ws(s?):\/\/([^/]+)($|\/.*)/;
window.WebSocket = function (url, protocols) {
var parsedUrl = url.match(reWsUrl);
if (parsedUrl) {
var wsSecure = parsedUrl[1];
// force downgrade if wss:// is called on insecure page
// (in case the proxy only supports http)
var wsProto = isSecure ? "ws" + wsSecure + "://" : "ws://";
var wsHost = parsedUrl[2];
// deal with "relative" js that uses the current url rather than a hard-coded one
if (wsHost === location.host || wsHost === location.hostname) {
// todo: handle situation where ws hostname === location.hostname but ports differ
wsHost = targetURL.host;
}
var wsPath = parsedUrl[3];
// prefix the websocket with the proxy server
return new _WebSocket(
wsProto +
proxyHost +
prefix +
"http" +
wsSecure +
"://" +
wsHost +
wsPath
);
}
// fallback in case the regex failed
return new _WebSocket(url, protocols);
};
}).replace("PREFIX", config.prefix);
const clientDir = "client";
const clientDirAbsolute = path.join(__dirname, clientDir)
const staticPrefix = config.prefix + clientDir;

// todo: enable immtable caching if NODE_ENV is production
const handleStaticRequest = serveStatic(clientDirAbsolute, {
index: false, // don't serve up index.html files for /
fallthrough: false, // anything missing is a 404
});

function server(data) {
const req = data.clientRequest;
const res = data.clientResponse;
const url = req.url;
if (url.startsWith(staticPrefix) && url.length > staticPrefix.length) {
// trim the URL down to be relative to the client dir
req.originalUrl = url;
req.url = url.substr(staticPrefix.length);
// todo: allow handlers and middleware to be async, then drop finalhandler
handleStaticRequest(req, res, finalhandler(req, res));
return true; // true = this request has been handled, no need to process it further
}
}

const CLIENT_SCRIPT_INIT =
'<script type="module">\n' +
fs
.readFileSync(path.join(__dirname, clientDir, "init.template.mjs"))
.toString()
.replaceAll("CLIENT_PATH", staticPrefix)
.replace("PREFIX", config.prefix) +
"</script>";

function createStream() {
return new Transform({
decodeStrings: false,
transform: function (chunk, encoding, next) {
var updated = chunk
.toString()
.replace(/(<head[^>]*>)/i, "$1\n" + CLIENT_WEBSOCKET_SCRIPT + "\n");
.replace(/(<head[^>]*>)/i, "$1\n" + CLIENT_SCRIPT_INIT + "\n");
this.push(updated, "utf8");
next();
},
});
}

function clientScripts(data) {
function injector(data) {
// todo: catch fetch and XMLHttpRequest and force those through the proxy
// todo: fix postMessage
// config._proxyWebsockets &&
Expand All @@ -74,7 +63,10 @@ module.exports = function (config) {
}
}

clientScripts.createStream = createStream; // for testing

return clientScripts;
injector.createStream = createStream;
return {
server,
injector,
createStream, // for testing
};
};
19 changes: 15 additions & 4 deletions lib/unblocker.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Unblocker.websockets = require("./websockets.js");
var defaultConfig = {
prefix: "/proxy/",
host: null, // can be used to override the url used in redirects
localHandlers: [],
requestMiddleware: [],
responseMiddleware: [],
standardMiddleware: true,
Expand Down Expand Up @@ -60,6 +61,7 @@ function expandConfig(config) {
var metaRobots = Unblocker.metaRobots(config);
var contentLength = Unblocker.contentLength(config);

// this applies to every request that gets proxied
config.requestMiddleware = [
host,
referer,
Expand All @@ -82,8 +84,9 @@ function expandConfig(config) {
if (config.clientScripts) {
// insert clientScripts after the urlPrefixer
var clientScripts = Unblocker.clientScripts(config);
config.localHandlers.unshift(clientScripts.server);
const position = config.responseMiddleware.indexOf(urlPrefixer) + 1;
config.responseMiddleware.splice(position, 0, clientScripts);
config.responseMiddleware.splice(position, 0, clientScripts.injector);
}
}

Expand Down Expand Up @@ -203,7 +206,7 @@ function Unblocker(config) {
response.end();
}

function preFlight(clientRequest, clientResponse, clientSocket) {
function initData(clientRequest, clientResponse, clientSocket) {
// convenience methods
clientRequest.thisHost = thisHost.bind(thisHost, clientRequest);
clientRequest.thisSite = thisSite.bind(thisSite, clientRequest);
Expand Down Expand Up @@ -244,9 +247,17 @@ function Unblocker(config) {
return data;
}

function handleLocally(data) {
return config.localHandlers.some(handler => handler(data) || data.clientResponse.headersSent)
}

// regular web requests
function handleRequest(clientRequest, clientResponse, next) {
const data = preFlight(clientRequest, clientResponse);
const data = initData(clientRequest, clientResponse);

if (handleLocally(data)) {
return;
}

clientResponse.redirectTo = redirectTo.bind(
redirectTo,
Expand Down Expand Up @@ -288,7 +299,7 @@ function Unblocker(config) {
) {
debugWS("handling websocket req to", clientRequest.url);

const data = preFlight(clientRequest, null, clientSocket);
const data = initData(clientRequest, null, clientSocket);

data.clientHead = clientHead;

Expand Down
Loading

0 comments on commit 3a63029

Please sign in to comment.