UNPKG

allproxy

Version:

AllProxy: MITM HTTP Debugging Tool.

199 lines 9.49 kB
"use strict"; 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