mirror of
https://github.com/QuiteAFancyEmerald/Holy-Unblocker.git
synced 2025-05-13 03:50:02 -04:00
Added Corrosion Fix for Heroku
Credits: https://github.com/BinBashBanana/Corrosion-Heroku (very epic person)
This commit is contained in:
parent
8d88daf1c2
commit
b0c48df26c
25 changed files with 13 additions and 10 deletions
22
lib/server/decompress.js
Normal file
22
lib/server/decompress.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
const zlib = require('zlib');
|
||||
|
||||
function decompress(ctx) {
|
||||
if (!ctx.body || !ctx.remoteResponse) return;
|
||||
try {
|
||||
switch(ctx.headers['content-encoding']) {
|
||||
case 'br':
|
||||
ctx.body = zlib.brotliDecompressSync(ctx.body);
|
||||
break;
|
||||
case 'gzip':
|
||||
ctx.body = zlib.gunzipSync(ctx.body);
|
||||
break;
|
||||
case 'deflate':
|
||||
ctx.body = zlib.inflateRawSync(ctx.body);
|
||||
break;
|
||||
};
|
||||
} catch(err) {};
|
||||
delete ctx.headers['content-encoding'];
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = decompress;
|
21
lib/server/gateway.js
Normal file
21
lib/server/gateway.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
function createGateway(ctx) {
|
||||
return function gateway(clientRequest, clientResponse) {
|
||||
const chunks = [];
|
||||
clientRequest.on('data', chunk =>
|
||||
chunks.push(chunk)
|
||||
).on('end', () => {
|
||||
const body = chunks.length ? Buffer.concat(chunks) : '';
|
||||
const query = clientRequest.method == 'POST' ? new URLSearchParams((body || '').toString()) : new URLSearchParams((clientRequest.url.split('?')[1] || ''));
|
||||
if (!query.has('url')) return clientResponse.end();
|
||||
const url = query.get('url');
|
||||
if (/https?:\/\/([a-zA-Z0-9\-\_])|([a-zA-Z0-9\-\_])\.([a-zA-Z])/.test(url)) {
|
||||
clientResponse.writeHead(301, { Location: ctx.url.wrap(/https?:\/\//.test(url) ? url : 'http://' + url) });
|
||||
clientResponse.end();
|
||||
} else {
|
||||
clientResponse.writeHead(301, { Location: ctx.url.wrap('https://www.google.com/search?q=' + url) });
|
||||
clientResponse.end();
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
module.exports = createGateway;
|
45
lib/server/headers.js
Normal file
45
lib/server/headers.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
function requestHeaders(ctx) {
|
||||
if (ctx.headers.cookie && ctx.rewrite.config.cookie) ctx.headers.cookie = ctx.rewrite.cookies.decode(ctx.headers.cookie, { url: ctx.url, });
|
||||
else delete ctx.headers.cookie;
|
||||
if (ctx.headers.origin) {
|
||||
if (ctx.clientSocket) {
|
||||
const params = new URLSearchParams((ctx.clientRequest.url.split('?')[1] || ''));
|
||||
delete ctx.headers.origin;
|
||||
delete ctx.headers.host;
|
||||
ctx.headers.Origin = params.get('origin') || ctx.url.origin;
|
||||
ctx.headers.Host = ctx.url.host;
|
||||
// Some websocket servers oddly only accept Host and Origin headers if the first character of the header is uppercase.
|
||||
} else {
|
||||
ctx.headers.origin = ctx.url.origin;
|
||||
};
|
||||
};
|
||||
|
||||
if (ctx.headers.referer) {
|
||||
try {
|
||||
ctx.headers.referer = new URL(ctx.rewrite.url.unwrap(ctx.headers.referer, { origin: ctx.origin, })).href;
|
||||
} catch(err) {
|
||||
ctx.headers.referer = ctx.url.href;
|
||||
};
|
||||
};
|
||||
for (let header in ctx.headers) {
|
||||
if (header.startsWith('cf-') || header.startsWith('x-forwarded') || header == 'cdn-loop') delete ctx.headers[header];
|
||||
};
|
||||
ctx.headers.host = ctx.url.host;
|
||||
return true;
|
||||
};
|
||||
|
||||
function responseHeaders(ctx) {
|
||||
if (ctx.headers.location) ctx.headers.location = ctx.rewrite.url.wrap(ctx.headers.location, { base: ctx.url, origin: ctx.origin, });
|
||||
if (ctx.headers['set-cookie']) ctx.headers['set-cookie'] = ctx.rewrite.cookies.encode(ctx.headers['set-cookie'], { domain: ctx.clientRequest.headers.host, url: ctx.url, });
|
||||
[
|
||||
'content-length',
|
||||
'content-security-policy',
|
||||
'content-security-policy-report-only',
|
||||
'strict-transport-security',
|
||||
'x-frame-options'
|
||||
].forEach(name => delete ctx.headers[name]);
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.requestHeaders = requestHeaders;
|
||||
exports.responseHeaders = responseHeaders;
|
61
lib/server/index.js
Normal file
61
lib/server/index.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
const webpack = require('webpack');
|
||||
const createWebSocketProxy = require('./upgrade');
|
||||
const createRequestProxy = require('./request');
|
||||
const createGateway = require('./gateway');
|
||||
const middleware = {
|
||||
...require('./headers'),
|
||||
...require('./middleware'),
|
||||
decompress: require('./decompress'),
|
||||
rewriteBody: require('./rewrite-body'),
|
||||
};
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const defaultConfig = {
|
||||
prefix: '/service/',
|
||||
codec: 'plain',
|
||||
forceHttps: false,
|
||||
ws: true,
|
||||
cookie: true,
|
||||
title: 'Service',
|
||||
requestMiddleware: [],
|
||||
responseMiddleware: [],
|
||||
standardMiddleware: true,
|
||||
};
|
||||
|
||||
class Corrosion extends require('../rewrite') {
|
||||
constructor(config = defaultConfig) {
|
||||
super(Object.assign(defaultConfig, config));
|
||||
if (this.config.standardMiddleware) {
|
||||
this.config.requestMiddleware.unshift(
|
||||
middleware.requestHeaders,
|
||||
);
|
||||
this.config.responseMiddleware.unshift(
|
||||
middleware.responseHeaders,
|
||||
middleware.decompress,
|
||||
middleware.rewriteBody,
|
||||
);
|
||||
};
|
||||
this.gateway = createGateway(this);
|
||||
this.upgrade = createWebSocketProxy(this);
|
||||
this.request = createRequestProxy(this);
|
||||
if (!fs.existsSync(path.join(__dirname, 'bundle.js'))) this.bundleScripts();
|
||||
};
|
||||
bundleScripts() {
|
||||
webpack({
|
||||
mode: 'none',
|
||||
entry: path.join(__dirname, '../../lib/browser/index.js'),
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: 'bundle.js',
|
||||
}
|
||||
}, err =>
|
||||
console.log(err || 'Bundled scripts')
|
||||
);
|
||||
};
|
||||
get script() {
|
||||
return fs.existsSync(path.join(__dirname, 'bundle.js')) ? fs.readFileSync(path.join(__dirname, 'bundle.js')) : 'Client script is still compiling or has crashed.'
|
||||
};
|
||||
};
|
||||
|
||||
Corrosion.middleware = middleware;
|
||||
module.exports = Corrosion;
|
14
lib/server/middleware.js
Normal file
14
lib/server/middleware.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
function address(arr = []) {
|
||||
return function (ctx) {
|
||||
ctx.address = arr[Math.floor(Math.random() * arr.length)];
|
||||
};
|
||||
};
|
||||
|
||||
function blacklist(arr = [], page = '') {
|
||||
return function (ctx) {
|
||||
if (arr.includes(ctx.url.hostname)) ctx.clientResponse.end(page);
|
||||
};
|
||||
};
|
||||
|
||||
exports.address = address;
|
||||
exports.blacklist = blacklist;
|
83
lib/server/request.js
Normal file
83
lib/server/request.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
const http = require('http');
|
||||
const https = require('https');
|
||||
function createRequestProxy(ctx) {
|
||||
return async function onRequest(clientRequest, clientResponse) {
|
||||
try {
|
||||
if (new RegExp(`^${ctx.prefix}gateway/?`).test(clientRequest.url)) return ctx.gateway(clientRequest, clientResponse);
|
||||
if (clientRequest.url.startsWith(`${ctx.prefix}index.js`)) {
|
||||
clientResponse.setHeader('Content-Type', 'application/javascript');
|
||||
return clientResponse.end(ctx.script);
|
||||
};
|
||||
const urlData = ctx.url.unwrap(clientRequest.url, { flags: true, leftovers: true, });
|
||||
urlData.value = new URL(urlData.value);
|
||||
const requestContext = {
|
||||
url: urlData.value,
|
||||
flags: urlData.flags,
|
||||
origin: ((clientRequest.socket.encrypted || ctx.config.forceHttps) ? 'https://' : 'http://') + clientRequest.headers.host,
|
||||
body: await getChunks(clientRequest),
|
||||
headers: { ...clientRequest.headers },
|
||||
method: clientRequest.method,
|
||||
rewrite: ctx,
|
||||
agent: new ((urlData.value.protocol == 'https:' || ctx.config.forceHttps) ? https : http).Agent({
|
||||
rejectUnauthorized: false,
|
||||
}),
|
||||
address: null,
|
||||
clientRequest,
|
||||
clientResponse,
|
||||
};
|
||||
for (let i in ctx.config.requestMiddleware) ctx.config.requestMiddleware[i](requestContext);
|
||||
if (clientResponse.writableEnded) return;
|
||||
((requestContext.url.protocol == 'https:' || ctx.config.forceHttps) ? https : http).request({
|
||||
headers: requestContext.headers,
|
||||
method: requestContext.method,
|
||||
hostname: requestContext.url.hostname,
|
||||
port: requestContext.url.port,
|
||||
path: requestContext.url.pathname + requestContext.url.search,
|
||||
agent: requestContext.agent,
|
||||
localAddress: requestContext.address,
|
||||
rejectUnauthorized: false,
|
||||
}, async remoteResponse => {
|
||||
const responseContext = {
|
||||
url: requestContext.url,
|
||||
flags: requestContext.flags,
|
||||
origin: requestContext.origin,
|
||||
body: await getChunks(remoteResponse),
|
||||
headers: { ...remoteResponse.headers },
|
||||
statusCode: remoteResponse.statusCode,
|
||||
agent: requestContext.agent,
|
||||
address: requestContext.address,
|
||||
method: requestContext.method,
|
||||
rewrite: ctx,
|
||||
clientRequest,
|
||||
clientResponse,
|
||||
remoteResponse,
|
||||
};
|
||||
for (let i in ctx.config.responseMiddleware) ctx.config.responseMiddleware[i](responseContext);
|
||||
if (clientResponse.writableEnded) return;
|
||||
clientResponse.writeHead(responseContext.statusCode, responseContext.headers);
|
||||
clientResponse.end((responseContext.body || ''));
|
||||
}).on('error', err => {
|
||||
if (clientResponse.writableEnded) return;
|
||||
clientResponse.setHeader('Content-Type', 'text/plain');
|
||||
clientResponse.end(err.toString())
|
||||
}).end(requestContext.body);
|
||||
} catch(err) {
|
||||
if (clientResponse.writableEnded) return;
|
||||
clientResponse.setHeader('Content-Type', 'text/plain');
|
||||
clientResponse.end(err.toString());
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function getChunks(stream) {
|
||||
const chunks = [];
|
||||
return new Promise(resolve =>
|
||||
stream.on('data', chunk =>
|
||||
chunks.push(chunk)
|
||||
).on('end', () =>
|
||||
chunks.length ? resolve(Buffer.concat(chunks)) : resolve(null)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = createRequestProxy;
|
36
lib/server/rewrite-body.js
Normal file
36
lib/server/rewrite-body.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
const route = [
|
||||
{
|
||||
types: ['text/html'],
|
||||
handler: 'html',
|
||||
},
|
||||
{
|
||||
types: ['text/css'],
|
||||
handler: 'css',
|
||||
},
|
||||
{
|
||||
types: ['application/javascript', 'application/x-javascript', 'text/javascript', 'text/x-javascript'],
|
||||
handler: 'js',
|
||||
},
|
||||
]
|
||||
function rewriteBody(ctx) {
|
||||
if (!ctx.body || !ctx.remoteResponse || ctx.flags.includes('xhr')) return;
|
||||
const meta = {
|
||||
base: ctx.url,
|
||||
origin: ctx.origin,
|
||||
};
|
||||
const data = route.find(entry => ctx.flags == entry.handler) || route.find(entry => entry.types.includes((ctx.headers['content-type'] || '').split(';')[0])) || {};
|
||||
|
||||
switch(data.handler) {
|
||||
case 'html':
|
||||
ctx.body = ctx.rewrite.html.process(ctx.body.toString(), { ...meta, document: true });
|
||||
break;
|
||||
case 'css':
|
||||
ctx.body = ctx.rewrite.css.process(ctx.body.toString(), meta);
|
||||
break;
|
||||
case 'js':
|
||||
ctx.body = ctx.rewrite.js.process(ctx.body.toString(), ctx.url);
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = rewriteBody;
|
56
lib/server/upgrade.js
Normal file
56
lib/server/upgrade.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
const http = require('http');
|
||||
const https = require('https');
|
||||
|
||||
function createWebSocketProxy(ctx) {
|
||||
return function onUpgrade(clientRequest, clientSocket, clientHead) {
|
||||
try {
|
||||
const urlData = ctx.url.unwrap(clientRequest.url, { flags: true, });
|
||||
urlData.value = new URL(urlData.value);
|
||||
const requestContext = {
|
||||
url: urlData.value,
|
||||
flags: urlData.flags,
|
||||
body: null,
|
||||
headers: { ...clientRequest.headers },
|
||||
method: clientRequest.method,
|
||||
rewrite: ctx,
|
||||
agent: new ((urlData.value.protocol == 'https:' || ctx.config.forceHttps) ? https : http).Agent({
|
||||
rejectUnauthorized: false,
|
||||
}),
|
||||
address: null,
|
||||
clientRequest,
|
||||
clientSocket,
|
||||
clientHead,
|
||||
};
|
||||
ctx.config.requestMiddleware.forEach(fn => fn(requestContext));
|
||||
((requestContext.url.protocol == 'https:' || ctx.config.forceHttps) ? https : http).request({
|
||||
headers: requestContext.headers,
|
||||
method: requestContext.method,
|
||||
hostname: requestContext.url.hostname,
|
||||
port: requestContext.url.port,
|
||||
path: requestContext.url.pathname + requestContext.url.search,
|
||||
agent: requestContext.agent,
|
||||
localAddress: requestContext.address,
|
||||
}).on('upgrade', (remoteResponse, remoteSocket, remoteHead) => {
|
||||
let handshake = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n';
|
||||
for (let key in remoteResponse.headers) {
|
||||
handshake += `${key}: ${remoteResponse.headers[key]}\r\n`;
|
||||
};
|
||||
handshake += '\r\n';
|
||||
clientSocket.write(handshake);
|
||||
clientSocket.write(remoteHead);
|
||||
remoteSocket.on('close', () => clientSocket.end());
|
||||
clientSocket.on('close', () => remoteSocket.end());
|
||||
remoteSocket.on('error', () => clientSocket.end());
|
||||
clientSocket.on('error', () => remoteSocket.end());
|
||||
remoteSocket.pipe(clientSocket);
|
||||
clientSocket.pipe(remoteSocket);
|
||||
}).on('error', () => {
|
||||
clientSocket.end()
|
||||
}).end();
|
||||
} catch(err) {
|
||||
clientSocket.end();
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = createWebSocketProxy;
|
Loading…
Add table
Add a link
Reference in a new issue