http proxy support (with support for external proxies such as WebOne)

This commit is contained in:
zefie
2021-07-17 09:23:00 -04:00
parent e542328093
commit bffc7b7945
8 changed files with 127 additions and 21 deletions

View File

@@ -10,6 +10,7 @@ This open source server is in alpha status. Use at your own risk.
- Can handle client "relogin" and "reconnect" events
- Can now use `.async.js` files with asynchronous requests
- Can handle more than one box at a time
- Support for HTTP Proxy (direct, or enhanced with an external proxy such as [WebOne](https://github.com/atauenis/webone))
### Current issues:
- May not run on non-development Windows machines (VS2019 with node and python)

View File

@@ -38,14 +38,12 @@ data += `width=250 bgcolor=#444444 text=#ffdd33 cursor=#cc9933 selected>
</form>
<h4>zefie's server only</h4>
<a href="wtv-music:/content/index.html">Music Collection</a><br>
<a href="wtv-music:/midi/index.html">Matt Test</a><br>
<a href="wtv-home:/zefie">zefie's stuff and things</a>
<h4>Test Stuff (probably broken)</h4>
<a href="wtv-update:/update?">HackTV Updater Test</a><br>
<a href="wtv-flashrom:/willie">Ultra Willies</a><br>
<a href="client:showalert?message=If%20you%20choose%20to%20disconnect%20and%20return%20to%20HackTV%20home%2C%20you%20may%20not%20be%20able%20to%20reconnect%20to%20the%20update%20server%20until%20you%20power%20cycle%20your%20box.%3Cbr%3E%3Cbr%3EAre%20you%20sure%20you%20would%20like%20to%20go%20offline%3F&buttonlabel1=No&buttonaction1=client:donothing&buttonlabel2=Yes&buttonaction2=wtv-tricks%3A%2Fgo-offline%3Ftitle%3DHackTV%2520Home">Disconnect and go to HackTV Home</a><br>
<!-- <a href="buttonaction2=wtv-home:/unlock">Unlock Full Client (Options, Goto, etc)</a><br> -->
</body>
</html>`

View File

@@ -1 +0,0 @@
pnm://192.168.11.188:7070/clients/zefie/wtvrom/Daybreak.ra

View File

@@ -0,0 +1,18 @@
<html>
<head>
<title>zefie's playground</title>
<DISPLAY showwhencomplete options showoptions noscroll>
</head>
<body bgcolor="black" link="gold" vlink="gold" alink="gold" text="gold">
<h3>zefie's playgrounf</h3>
Moved these off the main page to clean it up :)<br>
The stuff on this page is probably useless to you so why not move it off the main page.
<ul>
<li><a href="wtv-music:/content/index.html">Music Collection</a></li>
<li><a href="wtv-music:/midi/index.html">Matt Test</a><</li>
<ul>
</body>
</html>`

View File

@@ -76,23 +76,23 @@ function getFileExt(path) {
return path.reverse().split(".")[0].reverse();
}
function doErrorPage(code) {
function doErrorPage(code, data = null) {
var headers, data = null;
switch (code) {
case 404:
data = "The service could not find the requested page.";
if (data === null) data = "The service could not find the requested page.";
headers = "404 "+data+"\r\n";
headers += "Content-Type: text/html\r\n";
break;
case 400:
data = "An internal server error has occured.";
if (data === null) data = "An internal server error has occured.";
headers = "400 HackTV ran into a technical problem.\r\n";
headers += "Content-Type: text/html\r\n";
break;
default:
// what we send when we did not detect a wtv-url.
// e.g. when a pc browser connects
data = "Hello, stranger!";
if (data === null) data = "Hello, stranger!";
headers = "HTTP/1.1 200 OK\r\n";
headers += "Content-Type: text/html\r\n";
break;
@@ -258,7 +258,7 @@ async function processURL(socket, request_headers) {
var urlToPath = service_vault_dir.replace(/\\/g, "/") + "/" + service_name + "/" + shortURL.split(':/')[1];
console.log(" * Incoming headers on socket ID", socket.id, request_headers);
processPath(socket, urlToPath, request_headers, query, service_name);
} else if (shortURL.indexOf('http://') >= 0) {
} else if (shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0) {
doHTTPProxy(socket, request_headers);
} else {
// error reading headers (no request_url provided)
@@ -271,8 +271,93 @@ async function processURL(socket, request_headers) {
}
}
async function doHTTPProxy(socket, headers_obj) {
console.log(socket.id, headers_obj);
async function doHTTPProxy(socket, request_headers) {
console.log(socket.id, request_headers);
var request_type = request_headers['request'].indexOf('https://') ? 'http' : 'https'
switch (request_type) {
case "https":
var proxy_agent = https;
break;
case "http":
var proxy_agent = http;
break;
}
var request_data = new Array();
request_data['method'] = request_headers['request'].split(' ')[0];
var request_url_split = request_headers['request_url'].split('/');
request_data['host'] = request_url_split[2];
if (request_data['host'].indexOf(':') > 0) {
request_data['port'] = request_data['host'].split(':')[1];
request_data['host'] = request_data['host'].split(':')[0];
} else {
if (request_type === 'https') request_data['port'] = 443;
else request_data['port'] = 80;
}
for (var i = 0; i < 3; i++) request_url_split.shift();
request_data['path'] = "/" + request_url_split.join('/');
if (request_data['method'] && request_data['host'] && request_data['path']) {
var options = {
host: request_data['host'],
port: request_data['port'],
path: request_data['path'],
method: request_data['method'],
headers: {
"User-Agent": request_headers['User-Agent'] || "WebTV"
}
}
if (request_headers['post_data']) {
if (request_headers['Content-type']) options.headers['Content-type'] = request_headers['Content-type'];
if (request_headers['Content-length']) options.headers['Content-length'] = request_headers['Content-length'];
}
if (services_configured.services[request_type].use_external_proxy && services_configured.services[request_type].external_proxy_port) {
options.host = services_configured.services[request_type].external_proxy_host;
options.port = services_configured.services[request_type].external_proxy_port;
options.path = request_headers['request'].split(' ')[1];
options.headers['Host'] = request_data['host'];
}
const req = proxy_agent.request(options, function (res) {
var data = '';
res.on('data', d => {
data += d;
})
res.on('error', function (err) {
console.log(" * Unhandled Proxy Request Error:", err);
});
res.on('end', function () {
console.log(` * Proxy Request ${request_type.toUpperCase()} ${res.statusCode} for ${request_headers['request']}`)
var headers = new Array();
headers['http_response'] = res.statusCode + " " + res.statusMessage;
headers['wtv-connection-close'] = false;
if (res.headers['server']) headers['Server'] = res.headers['server'];
if (res.headers['connection']) headers['Connection'] = res.headers['connection'] == "close" ? "Keep-Alive" : "Close";
if (res.headers['date']) headers['Date'] = res.headers['date'];
if (res.headers['content-type']) headers['Content-type'] = res.headers['content-type'];
if (res.headers['cookie']) headers['Cookie'] = res.headers['cookie'];
// content-length is best auto-calculated
//if (res.headers['content-length']) headers['Content-Length'] = res.headers['content-length'];
if (res.headers['vary']) headers['Vary'] = res.headers['vary'];
if (res.headers['location']) headers['Location'] = res.headers['location'];
if (data.substring(0, 4) == "\r\n\r\n") data = data.substring(4);
if (data.substring(0, 2) == "\n\n") data = data.substring(2);
sendToClient(socket, headers, data);
});
});
if (request_headers['post_data']) {
req.write(Buffer.from(request_headers['post_data'].toString(CryptoJS.enc.Hex), 'hex'), function () {
req.end();
});
} else {
req.end();
}
}
}
async function headerStringToObj(headers, response = false) {
@@ -378,7 +463,7 @@ async function sendToClient(socket, headers_obj, data) {
socket_session_data[socket.id].buffer = null;
if (socket_session_data[socket.id].close_me) socket.end();
if (headers_obj['Connection']) {
if (headers_obj['Connection'].toLowerCase() == "close") {
if (headers_obj['Connection'].toLowerCase() == "close" && !headers['wtv-connection-close'] == "false") {
socket.destroy();
}
}
@@ -482,7 +567,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
if (returnHeadersBeforeSecure) {
headers = await checkForPostData(socket, headers, data, data_hex, returnHeadersBeforeSecure);
headers = await checkForPostData(socket, headers, data, data_hex);
return headers;
}
@@ -535,7 +620,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
}
}
headers = await checkForPostData(socket, headers, data);
headers = await checkForPostData(socket, headers, data, data_hex);
if (!headers['request_url']) {
// still no url, likely lost encryption stream, tell client to relog
socket_session_data[socket.id].secure = false;
@@ -559,7 +644,7 @@ Content-type: text/html`;
}
}
async function checkForPostData(socket, headers, data) {
async function checkForPostData(socket, headers, data, data_hex) {
if (headers['request']) {
if (headers['request'].substring(0, 4) == "POST") {
if (data_hex.indexOf("0d0a0d0a") != -1) {
@@ -625,9 +710,10 @@ async function handleSocket(socket) {
});
socket.on('timeout', async function () {
socket.setTimeout(0);
// start the async chain
processRequest(this, socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex));
if (socket_session_data[socket.id].buffer) {
processRequest(this, socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex));
}
});
socket.on('error', (err) => {

View File

@@ -1,6 +1,6 @@
{
"name": "hacktv_updsrv",
"version": "0.5.2",
"version": "0.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "hacktv_updsrv",
"version": "0.7.0",
"version": "0.7.1",
"description": "hacktv_updsrv",
"main": "app.js",
"author": {

View File

@@ -3,8 +3,8 @@
"service_ip": "192.168.11.8"
},
"services": {
"wtv-1800": {
"host": null,
"port": 1615,
"connections": 1
},
@@ -45,7 +45,11 @@
},
"http": {
"port": 1650,
"connections": 3
"connections": 3,
"use_external_proxy": false,
"external_proxy_host": null,
"external_proxy_port": 8080
}
}
}