allproxy
Version:
AllProxy: MITM HTTP Debugging Tool.
199 lines • 9.49 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = __importDefault(require("url"));
const http2_1 = __importDefault(require("http2"));
const Global_1 = __importDefault(require("./Global"));
const ProxyConfig_1 = __importDefault(require("../../common/ProxyConfig"));
const HttpMessage_1 = __importDefault(require("./HttpMessage"));
const querystring_1 = __importDefault(require("querystring"));
const Listen_1 = __importDefault(require("./Listen"));
const GenerateCertKey_1 = __importDefault(require("./GenerateCertKey"));
const Zlib_1 = require("./Zlib");
const ConsoleLog_1 = __importDefault(require("./ConsoleLog"));
/**
* Important: This module must remain at the project root to properly set the document root for the index.html.
*/
class Https2Server {
constructor(hostname, port, proxyType) {
this.proxyType = 'forward';
this.port = 443;
this.ephemeralPort = 0;
this.server = null;
this.resolvePromise = () => 1;
this.promise = new Promise((resolve) => {
this.resolvePromise = resolve;
});
this.proxyType = proxyType;
this.hostname = hostname;
this.port = port;
}
destructor() {
this.server && this.server.close();
}
start() {
return __awaiter(this, void 0, void 0, function* () {
const certKey = yield (0, GenerateCertKey_1.default)(this.hostname);
this.server = http2_1.default.createSecureServer(Object.assign({ allowHTTP1: true }, certKey), this.onRequest.bind(this));
yield (0, Listen_1.default)('Https2Server', this.server, 0); // assign port number
this.ephemeralPort = this.server.address().port;
this.resolvePromise(0);
});
}
waitForServerToStart() {
return __awaiter(this, void 0, void 0, function* () {
yield this.promise;
});
}
getEphemeralPort() {
return this.ephemeralPort;
}
onRequest(clientReq, clientRes) {
return __awaiter(this, void 0, void 0, function* () {
clientReq.on('error', function (error) {
console.error(sequenceNumber, 'Client connection error', JSON.stringify(error, null, 2));
});
// eslint-disable-next-line node/no-deprecated-api
const reqUrl = url_1.default.parse(clientReq.url ? clientReq.url : '');
ConsoleLog_1.default.info('Https2Server onRequest', reqUrl.path);
const clientHostName = yield Global_1.default.resolveIp(clientReq.socket.remoteAddress);
const sequenceNumber = Global_1.default.nextSequenceNumber();
const remoteAddress = clientReq.socket.remoteAddress;
let proxyConfig = Global_1.default.socketIoManager.findProxyConfigMatchingURL('https:', clientHostName, reqUrl, this.proxyType);
// Always proxy forward proxy requests
if (proxyConfig === undefined && this.proxyType === 'forward') {
proxyConfig = new ProxyConfig_1.default();
proxyConfig.path = reqUrl.pathname;
proxyConfig.protocol = 'https:';
proxyConfig.hostname = this.hostname;
proxyConfig.port = this.port;
proxyConfig.isSecure = true;
}
;
const httpMessage = new HttpMessage_1.default('https:', proxyConfig, sequenceNumber, remoteAddress, clientReq.method, clientReq.url, clientReq.headers);
if (proxyConfig === undefined) {
const msg = 'No matching proxy configuration found for ' + reqUrl.pathname;
clientRes.writeHead(404, msg);
clientRes.end();
httpMessage.emitMessageToBrowser(msg);
}
else {
const requestBodyPromise = getReqBody(clientReq);
httpMessage.emitMessageToBrowser(''); // No request body received yet
this.proxyRequest(reqUrl, clientReq, clientRes, httpMessage, proxyConfig, requestBodyPromise);
}
function getReqBody(clientReq) {
return new Promise(resolve => {
// eslint-disable-next-line no-unreachable
let requestBody = '';
let rawData = '';
clientReq.on('data', function (chunk) {
rawData += chunk;
});
// eslint-disable-next-line no-unreachable
clientReq.on('end', function () {
return __awaiter(this, void 0, void 0, function* () {
try {
requestBody = JSON.parse(rawData);
}
catch (e) {
const contentType = clientReq.headers['content-type'];
if (contentType && contentType.indexOf('application/x-www-form-urlencoded') !== -1) {
requestBody = querystring_1.default.parse(rawData);
}
else {
requestBody = rawData;
}
}
resolve(requestBody);
});
});
});
}
});
}
proxyRequest(_reqUrl, clientReq, clientRes, httpMessage, proxyConfig, requestBodyPromise) {
return __awaiter(this, void 0, void 0, function* () {
const headers = clientReq.headers;
const authority = this.hostname + ':' + this.port;
headers[http2_1.default.constants.HTTP2_HEADER_AUTHORITY] = authority;
delete headers.host;
delete headers.connection;
delete headers.upgrade;
const url = `https://${authority}`;
const clientHttp2Session = http2_1.default.connect(url);
clientHttp2Session.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
const requestBody = yield requestBodyPromise;
httpMessage.emitMessageToBrowser(requestBody, 503, {}, { err, 'allproxy-config': proxyConfig });
clientRes.writeHead(503);
if (typeof err == 'object') {
err = JSON.stringify(err);
}
clientRes.write(err);
clientRes.end();
}));
const chunks = [];
let proxyStream;
try {
proxyStream = clientHttp2Session.request(headers);
}
catch (e) {
ConsoleLog_1.default.info('Https2Server', headers);
throw e;
}
proxyStream.on('response', (headers, flags) => {
ConsoleLog_1.default.debug('Http2Server on response', clientReq.url, headers, 'flags:', flags);
if (clientRes.stream) {
clientRes.stream.respond(headers, { waitForTrailers: true });
}
proxyStream.on('data', function (chunk) {
clientRes.write(chunk);
chunks.push(chunk);
});
proxyStream.on('end', () => __awaiter(this, void 0, void 0, function* () {
ConsoleLog_1.default.debug('Http2Server end of response received');
clientRes.end();
if (clientHttp2Session) {
clientHttp2Session.close();
}
const requestBody = yield requestBodyPromise;
// chunks.push(headers)
const resBody = getResBody(headers, chunks);
httpMessage.emitMessageToBrowser(requestBody, headers[':status'], headers, resBody);
}));
});
// Forward the client request
clientReq.pipe(proxyStream, {
end: true
});
});
}
}
exports.default = Https2Server;
function getResBody(headers, chunks) {
if (chunks.length === 0)
return '';
let resBuffer = chunks.reduce((prevChunk, chunk) => Buffer.concat([prevChunk, chunk], prevChunk.length + chunk.length));
resBuffer = (0, Zlib_1.decompressResponse)(headers, resBuffer);
const resString = resBuffer.toString();
let resBody = '';
try {
resBody = JSON.parse(resString); // assume JSON
}
catch (e) {
resBody = resString;
}
return resBody;
}
//# sourceMappingURL=Https2Server.js.map