UNPKG

allproxy

Version:

AllProxy: MITM HTTP Debugging Tool.

207 lines 12.1 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 HexFormatter_1 = __importDefault(require("./formatters/HexFormatter")); const Listen_1 = __importDefault(require("./Listen")); const Zlib_1 = require("./Zlib"); const Protobuf_1 = require("./Protobuf"); const GenerateCertKey_1 = __importDefault(require("./GenerateCertKey")); const ConsoleLog_1 = __importDefault(require("./ConsoleLog")); const settings = { maxConcurrentStreams: undefined }; /** * Important: This module must remain at the project root to properly set the document root for the index.html. */ class GrpcProxy { static forwardProxy(port, isSecure = false) { return __awaiter(this, void 0, void 0, function* () { const proxyConfig = new ProxyConfig_1.default(); proxyConfig.isSecure = isSecure; proxyConfig.protocol = 'grpc:'; proxyConfig.path = port.toString(); return GrpcProxy.start(proxyConfig, true); }); } static reverseProxy(proxyConfig) { return __awaiter(this, void 0, void 0, function* () { return GrpcProxy.start(proxyConfig); }); } static start(proxyConfig, isForwardProxy = false) { return __awaiter(this, void 0, void 0, function* () { const server = proxyConfig.isSecure ? http2_1.default.createSecureServer(Object.assign({ settings }, yield (0, GenerateCertKey_1.default)(proxyConfig.hostname))) : http2_1.default.createServer({ settings }); if (!isForwardProxy) { proxyConfig._server = server; } let port = yield (0, Listen_1.default)('GrpcProxy', server, +proxyConfig.path); proxyConfig.path = port + ''; // update port server.on('error', (e) => ConsoleLog_1.default.info('GrpcProxy error:', e)); server.on('request', onRequest); function onRequest(clientReq, clientRes) { return __awaiter(this, void 0, void 0, function* () { // eslint-disable-next-line node/no-deprecated-api const reqUrl = url_1.default.parse(clientReq.url ? clientReq.url : ''); ConsoleLog_1.default.info('GRPC:' + clientReq.method + ' ' + clientReq.url); ConsoleLog_1.default.debug('GrpcProxy onRequest', reqUrl.path); const sequenceNumber = Global_1.default.nextSequenceNumber(); const remoteAddress = clientReq.socket.remoteAddress; const httpMessage = new HttpMessage_1.default(proxyConfig.isSecure ? 'https:' : 'http:', proxyConfig, sequenceNumber, remoteAddress, clientReq.method, clientReq.url, clientReq.headers); const urlPath = reqUrl.path || ""; let protoNames = (0, Protobuf_1.getProtoNames)(urlPath); const protoFile = protoNames ? protoNames[0] : ""; const requestBodyPromise = getReqBody(clientReq, protoFile, protoNames ? protoNames[1] : null); httpMessage.emitMessageToBrowser(''); // No request body received yet proxyRequest(proxyConfig, requestBodyPromise); function proxyRequest(proxyConfig, requestBodyPromise) { clientReq.on('error', function (error) { console.error(sequenceNumber, 'Client connection error', JSON.stringify(error, null, 2)); }); const headers = clientReq.headers; let authority = proxyConfig.hostname; if (proxyConfig.port) authority += ':' + proxyConfig.port; headers[http2_1.default.constants.HTTP2_HEADER_AUTHORITY] = authority; let { protocol, hostname, port } = isForwardProxy ? reqUrl : proxyConfig; if (!isForwardProxy) { protocol = proxyConfig.isSecure ? 'https:' : 'http:'; } if (port === undefined) { port = protocol === 'https:' ? 443 : 80; } const proxyClient = http2_1.default.connect(`${protocol}//${hostname}:${port}`, { settings }); proxyClient.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 trailers = {}; const proxyStream = proxyClient.request(headers); let needToSendTrailers = false; proxyStream.on('trailers', (headers, _flags) => { ConsoleLog_1.default.debug('GrpcProxy on trailers', headers); clientRes.addTrailers(headers); trailers = headers; }); proxyStream.on('response', (headers, flags) => { ConsoleLog_1.default.debug('GrpcProxy on response', clientReq.url, headers, 'flags:', flags); if (headers['grpc-status']) { trailers['grpc-status'] = headers['grpc-status']; trailers['grpc-message'] = headers['grpc-message']; headers['grpc-status'] = headers['grpc-message'] = undefined; needToSendTrailers = true; } clientRes.stream.respond(headers, { waitForTrailers: true }); // proxyStream.pipe(clientRes, { // end: 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('GrpcProxy end of response received'); if (needToSendTrailers) { clientRes.addTrailers(trailers); } clientRes.end(); proxyClient.close(); const requestBody = yield requestBodyPromise; // chunks.push(headers) const resBody = yield getResBody(headers, chunks, protoFile, protoNames ? protoNames[2] : null); const allHeaders = Object.assign(Object.assign({}, headers), trailers); httpMessage.emitMessageToBrowser(requestBody, headers[':status'], allHeaders, resBody); })); }); // Forward the client request clientReq.pipe(proxyStream, { end: true }); } function getReqBody(clientReq, protoFile, protoMessageName) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve) => { // eslint-disable-next-line no-unreachable let buffer; clientReq.on('data', function (chunk) { if (buffer) { buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length); } else { buffer = chunk; } }); // eslint-disable-next-line no-unreachable clientReq.on('end', function () { return __awaiter(this, void 0, void 0, function* () { if (protoMessageName) { const json = yield (0, Protobuf_1.decodeProtobuf)(protoFile, GrpcProxy.getPackageName(reqUrl.path), protoMessageName, buffer); if (json) resolve(json); } resolve(HexFormatter_1.default.format(buffer)); }); }); }); }); } function getResBody(headers, chunks, protoFile, protoMessageName) { return __awaiter(this, void 0, void 0, function* () { 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); if (protoMessageName) { const json = yield (0, Protobuf_1.decodeProtobuf)(protoFile, GrpcProxy.getPackageName(reqUrl.path), protoMessageName, resBuffer); if (json) return json; } return HexFormatter_1.default.format(resBuffer); }); } }); } return port; }); } static getPackageName(path) { if (path === null) return ""; const tokens = path.split('/'); return tokens.length > 1 ? tokens[1].split('.')[0] : ""; } static destructor(proxyConfig) { if (proxyConfig._server) { ConsoleLog_1.default.debug('GrpcProxy destructor ', proxyConfig.path); proxyConfig._server.close(); } } } exports.default = GrpcProxy; //# sourceMappingURL=GrpcProxy.js.map