allproxy
Version:
AllProxy: MITM HTTP Debugging Tool.
207 lines • 12.1 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 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