Skip to content

Commit

Permalink
Replace node-http-proxy with ProxyCore
Browse files Browse the repository at this point in the history
  • Loading branch information
layerssss committed Nov 23, 2020
1 parent fd50317 commit 03d7a25
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 22 deletions.
130 changes: 130 additions & 0 deletions lib/proxy_core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
const http = require("http");
const https = require("https");
const tls = require("tls");
const net = require("net");

class ProxyCore {
getModules(urlString) {
const url = new URL(urlString);
if (url.protocol === "https:") return [https, tls];
if (url.protocol === "http:") return [http, net];
throw new Error(`Unsupported protocol ${url.protocol}`);
}

async proxyRequest({ request, response, upstream, ssl }) {
const upstreamUrl = new URL(upstream);
const { hostname, port } = upstreamUrl;
const upstreamRequestOptions = {
method: request.method,
host: hostname,
port,
path: request.url,
setHost: false,
headers: this.transformRequestHeaders(request.headers, {
upstreamUrl,
request,
ssl
})
};
const upstreamRequest = this.getModules(upstream)[0].request(
upstreamRequestOptions
);

request.pipe(upstreamRequest);
request.on("error", () => upstreamRequest.destroy());
upstreamRequest.on("error", () => {
if (response.headersSent) {
request.destroy();
} else {
response.writeHead(502, {
"Content-Type": "text/plain"
});
response.end("Bad Gateway");
}
});

upstreamRequest.on("response", upstreamResponse => {
const { statusCode, statusMessage } = upstreamResponse;
const headers = this.transformResponseHeaders(upstreamResponse.headers, {
upstreamUrl,
request,
ssl
});
response.writeHead(statusCode, statusMessage, headers);
response.flushHeaders();
upstreamResponse.pipe(response);
upstreamResponse.on("error", () => response.destroy());
response.on("error", () => upstreamResponse.destroy());
});

await new Promise(resolve => {
upstreamRequest.on("close", resolve);
});
}

async proxyUpgrade({ request, socket, head, upstream, ssl }) {
const upstreamUrl = new URL(upstream);
const upstreamConnectOptions = {
host: upstreamUrl.hostname,
port: upstreamUrl.port || (ssl ? 443 : 80),
servername: upstreamUrl.hostname
};
const upstreamSocket = this.getModules(upstream)[1].connect(
upstreamConnectOptions
);
const upsteamHeaders = this.transformRequestHeaders(request.headers, {
upstreamUrl,
ssl,
request
});
const upsteamPath = request.url;
socket.unshift(
Buffer.concat([
Buffer.from(
[
`${request.method} ${upsteamPath} HTTP/${request.httpVersion}`,
...Object.entries(upsteamHeaders).map(
([name, value]) => `${name}: ${value}`
)
].join("\r\n")
),
Buffer.from("\r\n\r\n"),
head
])
);
socket.pipe(upstreamSocket).pipe(socket);
socket.on("error", () => upstreamSocket.destroy());
upstreamSocket.on("error", () => socket.destroy());

await new Promise(resolve => {
upstreamSocket.on("connect", resolve);
});
}

transformRequestHeaders(headers, { upstreamUrl, ssl, request }) {
const headersTransformed = { ...headers };
// generate xfwd headers
const origAddr = request.socket.remoteAddress.replace("::ffff:", "");
const origPort = request.socket.localPort;
const origProto = ssl ? "https" : "http";
for (const [suffix, value, append] of [
["for", origAddr, true],
["port", origPort, false],
["proto", origProto, false]
]) {
const name = `x-forwarded-${suffix}`;
if (headersTransformed[name] && append)
headersTransformed[name] = `${headersTransformed[name]},${value}`;
else headersTransformed[name] = value;
}
return headersTransformed;
}

transformResponseHeaders(headers) {
return {
...headers
};
}
}

module.exports = ProxyCore;
39 changes: 19 additions & 20 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ const Fs = require("fs");
const Os = require("os");
const Url = require("url");
const Tls = require("tls");
const { promisify } = require("util");
const Username = require("username");
const Express = require("express");
const Websocket = require("ws");
const Path = require("path");
const Portfinder = require("portfinder");
const HttpProxy = require("http-proxy");

const LocalhostD = require("./localhostd.js");
const ProxyCore = require("./proxy_core.js");
const createKeys = require("./create_keys.js");
const createCaCertificate = require("./create_ca_certificate.js");
const createDomainCertificate = require("./create_domain_certificate.js");
Expand Down Expand Up @@ -58,15 +57,7 @@ class Server {
debug: this._debug.bind(this)
});

this._httpProxy = new HttpProxy({
xfwd: true,
headers: {
"X-LocalhostD-Version": require("../package.json").version
}
});

this._httpProxyWeb = promisify(this._httpProxy.web).bind(this._httpProxy);
this._httpProxyWs = promisify(this._httpProxy.ws).bind(this._httpProxy);
this._proxyCore = new ProxyCore();

this._proxyServer = Http.createServer();
this._proxyServer.on("connect", (request, socket, head) =>
Expand Down Expand Up @@ -125,9 +116,7 @@ class Server {
}

getUsageMessage() {
return `You need to configure your browser to use HTTP(S) proxy at http://${
this._proxyServerBind
}:${this._proxyServerPort} to access localhostd-hosted applications.`;
return `You need to configure your browser to use HTTP(S) proxy at http://${this._proxyServerBind}:${this._proxyServerPort} to access localhostd-hosted applications.`;
}

getUIHost() {
Expand Down Expand Up @@ -392,8 +381,11 @@ class Server {
`${request.method} ${clientHostname}:${clientPort}${url.path}`
);

await this._httpProxyWeb(request, response, {
target: `http://${clientHostname}:${clientPort}`
this._proxyCore.proxyRequest({
request,
response,
upstream: `http://${clientHostname}:${clientPort}`,
ssl
});
})
.catch(error => {
Expand Down Expand Up @@ -450,8 +442,12 @@ class Server {

socket.on("close", () => clearInterval(pingTimer));

await this._httpProxyWs(request, socket, head, {
target: `ws://localhost:${application.port}`
this._proxyCore.proxyUpgrade({
request,
socket,
head,
upstream: `http://localhost:${application.port}`,
ssl
});
})
.catch(error => {
Expand Down Expand Up @@ -490,8 +486,11 @@ class Server {
`${request.method} http://localhost:${application.port}${request.url}`
);

await this._httpProxyWeb(request, response, {
target: `http://localhost:${application.port}`
this._proxyCore.proxyRequest({
request,
response,
upstream: `http://localhost:${application.port}`,
ssl
});
})
.catch(error => {
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
"electron-updater": "^4.0.5",
"execa": "^1.0.0",
"express": "^4.16.3",
"http-proxy": "^1.17.0",
"lodash": "^4.17.11",
"mkdirp": "^0.5.1",
"node-forge": "0.7.6",
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5397,7 +5397,7 @@ http-proxy-middleware@~0.18.0:
lodash "^4.17.5"
micromatch "^3.1.9"

http-proxy@^1.16.2, http-proxy@^1.17.0:
http-proxy@^1.16.2:
version "1.17.0"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a"
integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==
Expand Down

0 comments on commit 03d7a25

Please sign in to comment.