"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
// @ts-ignore
const spdy_1 = __importDefault(require("spdy"));
const grpc = __importStar(require("@grpc/grpc-js"));
const protoLoader = __importStar(require("@grpc/proto-loader"));
const logger_1 = __importDefault(require("../logger"));
const GrpcParser_1 = __importDefault(require("../parser/GrpcParser"));
const WebsocketParser_1 = __importDefault(require("../parser/WebsocketParser"));
// @ts-ignore
const uuid_1 = require("uuid");
const clients = [];
/**
* Defines all protocols:
* Currently active:
* - HTTP
* - HTTPS
* - HTTP2
* - gRPC
* - Websocket
* @param {string} grpcMocksDir location of grpc mocks, not initialized as constructor variable, because grpc is an optional protocol
* instead it will be initialized in initGRPC method, if called.
*/
class Protocols {
/**
*
* @param {express.Application} app Express application to form the listener for http and https server
* @param {number} port HTTP server port
* @param {number} httpsPort HTTPs server port - currently initialized in constructor optionally but in future it'll be initialized if https is enabled
*/
constructor(app, port, httpsPort) {
/**
* Initialize HTTP server at specified port
* @returns {void}
*/
this.initHttp = () => {
http_1.default.createServer(this.app).listen(this.port, () => {
logger_1.default.info(`Worker sharing HTTP server at http://localhost:${this.port} ⛳`);
this.app.emit("server-started");
});
};
/**
* Initialize HTTPs server at specified port
* @param {string} key location of server.key file
* @param {string} cert location of server.cert file
* @returns {void}
*/
this.initHttps = (key, cert) => {
let privateKey = fs_1.default.readFileSync(key, "utf8");
let certificate = fs_1.default.readFileSync(cert, "utf8");
let credentials = { key: privateKey, cert: certificate };
https_1.default.createServer(credentials, this.app).listen(this.httpsPort, () => {
logger_1.default.info(`Worker sharing HTTPs server at https://localhost:${this.httpsPort} ⛳`);
});
};
/**
* Initializes a gRPC server at specified host and port
* - Set location of gRPC mocks to be used by metod camouflageMock
* - Get an array of all .protofile in specified protos directory
* - Run forEach on the array and read and load package definition for each protofile in protos dir
* - For each definition, get the package details from all .proto files and store in a master packages object
* - Initialize a grpcServer
* - Create an insecure binding to given grpc host and port, and start the server
* - For each package, filter out objects with service definition, discard rest
* - For each method in the service definition, attach a generic handler, finally add service to running server
* - Handlers will vary based on the type of request, i.e. unary, bidi streams or one sided streams
* - Finally add all services to the server
* @param {string} grpcProtosDir location of proto files
* @param {string} grpcMocksDir location of mock files for grpc
* @param {string} grpcHost grpc host
* @param {number} grpcPort grpc port
*/
this.initGrpc = (grpcProtosDir, grpcMocksDir, grpcHost, grpcPort) => {
this.grpcMocksDir = grpcMocksDir;
const grpcParser = new GrpcParser_1.default(this.grpcMocksDir);
const availableProtoFiles = fs_1.default.readdirSync(grpcProtosDir);
let grpcObjects = [];
let packages = [];
availableProtoFiles.forEach((availableProtoFile) => {
let packageDef = protoLoader.loadSync(path_1.default.join(grpcProtosDir, availableProtoFile), {});
let definition = grpc.loadPackageDefinition(packageDef);
grpcObjects.push(definition);
});
grpcObjects.forEach((grpcObject) => {
Object.keys(grpcObject).forEach((availablePackage) => {
packages.push(grpcObject[`${availablePackage}`]);
});
});
const server = new grpc.Server();
server.bindAsync(`${grpcHost}:${grpcPort}`, grpc.ServerCredentials.createInsecure(), (err) => {
if (err)
logger_1.default.error(err.message);
logger_1.default.info(`Worker sharing gRPC server at ${grpcHost}:${grpcPort} ⛳`);
server.start();
});
packages.forEach((entry) => {
let keys = Object.keys(entry);
keys = keys.filter((key) => {
return entry[key]["service"] !== undefined;
});
keys.forEach((key) => {
let service = entry[key]["service"];
let methods = Object.keys(service);
let methodDefinition = {};
methods.forEach((method) => {
if (!service[method]["responseStream"] && !service[method]["requestStream"]) {
logger_1.default.debug(`Registering Unary method: ${method}`);
methodDefinition[method] = grpcParser.camouflageMock;
}
if (service[method]["responseStream"] && !service[method]["requestStream"]) {
logger_1.default.debug(`Registering method with server side streaming: ${method}`);
methodDefinition[method] = grpcParser.camouflageMockServerStream;
}
if (!service[method]["responseStream"] && service[method]["requestStream"]) {
logger_1.default.debug(`Registering method with client side streaming: ${method}`);
methodDefinition[method] = grpcParser.camouflageMockClientStream;
}
if (service[method]["responseStream"] && service[method]["requestStream"]) {
logger_1.default.debug(`Registering method with BIDI streaming: ${method}`);
methodDefinition[method] = grpcParser.camouflageMockBidiStream;
}
});
server.addService(service, methodDefinition);
});
});
};
/**
* Initializes an HTTP2 server
* @param {number} http2Port
* @param {string} http2key
* @param {string} http2cert
*/
this.initHttp2 = (http2Port, http2key, http2cert) => {
spdy_1.default
.createServer({
key: fs_1.default.readFileSync(http2key),
cert: fs_1.default.readFileSync(http2cert),
}, this.app)
.listen(http2Port, (err) => {
if (err)
logger_1.default.error(err.message);
logger_1.default.info(`Worker sharing HTTP2 server at https://localhost:${http2Port} ⛳`);
});
};
/**
* Initializes a WebSocketserver
* @param {number} wsPort
* @param {string} wsMockDir
*/
this.initws = (wsPort, wsMockDir) => {
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: wsPort });
logger_1.default.info(`Worker sharing WS server at ws://localhost:${wsPort} ⛳`);
const websocketParser = new WebsocketParser_1.default(wss);
wss.on("connection", (ws, request) => {
const clientId = uuid_1.v4();
clients.push(clientId);
// @ts-ignore
ws["clientId"] = clientId;
let mockFile = path_1.default.join(wsMockDir, ...request.url.substring(1).split("/"), "connection.mock");
if (fs_1.default.existsSync(mockFile)) {
websocketParser.sendConnect(ws, request, clients, clientId, "joining", mockFile);
}
else {
websocketParser.sendConnect(ws, request, clients, clientId, "joining");
}
ws.on("message", (message) => {
logger_1.default.debug(`Client sent message ${message}`);
mockFile = path_1.default.join(wsMockDir, ...request.url.substring(1).split("/"), "message.mock");
if (fs_1.default.existsSync(mockFile)) {
websocketParser.send(mockFile, ws, request, message);
}
else {
logger_1.default.error(`No suitable message.mock file found for ${request.url}`);
}
});
ws.on("close", () => {
clients.splice(clients.indexOf(clientId), 1);
websocketParser.sendConnect(ws, request, clients, clientId, "leaving");
});
});
};
this.app = app;
this.port = port;
this.httpsPort = httpsPort;
}
}
exports.default = Protocols;
//# sourceMappingURL=index.js.map