@manzoomeh/basicore.server
Version:
java script version of Manzoomeh Negaran http web server for nodejs
244 lines (236 loc) • 7.36 kB
JavaScript
import tls from "tls";
import fs from "fs";
import {
NonSecureHttpHostEndPoint,
SecureHttpHostEndPoint,
} from "./endPoint/endPoints.js";
import {
HostManagerOptions,
HostEndPointOptions,
HostServiceOptions,
SslCertificateOptions,
SniCertificateOptions,
} from "./models/model.js";
import {
EdgeProxyHostService,
HostService,
SqlProxyHostService,
StaticFileProxyHostService,
HostEndPoint,
RouterHostService,
} from "./services/hostServices.js";
import H2HttpHostEndPoint from "./endPoint/h2HttpHostEndPoint.js";
export default class HostManager {
/**@type {HostEndPoint[]} */
hosts;
/**
* @param {HostManagerOptions} options
*/
constructor(options) {
this.#loadEndpoints(options);
// Catch uncaughtException
process.on("uncaughtException", (error, origin) => {
console.error("UNCAUGHT EXCEPTION");
console.error(error);
console.error(origin);
process.exit(1);
});
}
listen() {
this.hosts.forEach((host) => {
try {
host.listen();
} catch (ex) {
console.error(ex);
}
});
}
/**
* @param {HostManagerOptions} options
*/
#loadEndpoints(options) {
this.hosts = [];
var services = this.#loadServices(options.Services);
for (const name in options.EndPoints) {
if (Object.hasOwnProperty.call(options.EndPoints, name)) {
const endPointsOptions = options.EndPoints[name];
if (endPointsOptions.Active) {
var service = null;
if (typeof endPointsOptions.Routing === "string") {
service = services.find((x) => x.name == endPointsOptions.Routing);
} else if (typeof endPointsOptions.Routing === "object") {
service = new RouterHostService(
"name-router",
endPointsOptions.Routing,
services
);
}
if (service) {
switch ((endPointsOptions.Type || "http").toLowerCase()) {
case "http": {
this.#createHttpEndPoint(name, endPointsOptions, service);
break;
}
default: {
console.error(
`${endPointsOptions.Type} not support in this version of web server`
);
break;
}
}
} else {
console.error(`Related router not found for ${name} endpoint`);
}
}
}
}
}
/**
* @param {string} name
* @param {HostEndPointOptions} options
* @param {HostService} service
* @returns {HostEndPoint}
*/
#createHttpEndPoint(name, options, service) {
options.Addresses.forEach((address) => {
const [ip, port] = address.EndPoint.split(":", 2);
if (address.Certificate) {
/**@type {tls.SecureContextOptions}*/
var options = {};
switch (address.Certificate.Type) {
case "ssl": {
/**@type {SslCertificateOptions} */
var sslOptions = address.Certificate;
options.cert = fs.readFileSync(sslOptions.FilePath);
options.key = fs.readFileSync(sslOptions.KeyPath);
break;
}
case "sni": {
/**@type {SniCertificateOptions} */
var sniOptions = address.Certificate;
const hostLookup = {};
sniOptions.Hosts.forEach((host) => {
const cert = fs.readFileSync(host.FilePath);
const key = fs.readFileSync(host.KeyPath);
host.HostNames.forEach((hostName) => {
hostLookup[hostName.toLowerCase()] = { cert, key };
});
});
const sniCallback = (serverName, callback) => {
console.log(serverName);
const set = hostLookup[serverName.toLowerCase()];
if (set) {
callback(
null,
new tls.createSecureContext({
cert: set.cert,
key: set.key,
})
);
} else {
console.log(
`In sni setting no certificate found fot '${serverName}'`
);
}
};
options.SNICallback = sniCallback;
break;
}
}
if (!address.Certificate.Http2) {
this.hosts.push(
new SecureHttpHostEndPoint(ip, port, service, options)
);
} else {
this.hosts.push(new H2HttpHostEndPoint(ip, port, service, options));
}
} else {
this.hosts.push(new NonSecureHttpHostEndPoint(ip, port, service));
}
});
}
/**
* @param {NodeJS.Dict<HostServiceOptions>} services
* @returns {HostService[]}
*/
#loadServices(services) {
/**@type {HostService[]} */
const retVal = new Array();
for (const name in services) {
if (Object.hasOwnProperty.call(services, name)) {
const serviceOptions = services[name];
try {
switch (serviceOptions.Type.toLowerCase()) {
case "sql": {
retVal.push(this.#createSqlDispatcher(name, serviceOptions));
break;
}
case "file": {
retVal.push(this.#createFileDispatcher(name, serviceOptions));
break;
}
default: {
console.error(
`${serviceOptions.Type} not support in this version of web server`
);
break;
}
}
} catch (ex) {
console.error(ex);
}
}
}
return retVal;
}
/**
* @param {string} name
* @param {HostServiceOptions} options
* @returns {HostService}
*/
#createSqlDispatcher(name, options) {
/**@type {HostService} */
var service = null;
const sqlConnection = options.Settings["Connections.sql.RoutingData"];
if (sqlConnection) {
service = new SqlProxyHostService(name, sqlConnection);
} else {
const edgeConnection =
options.Settings["Connections.edge-socket.RoutingData"];
if (edgeConnection) {
const [ip, port] = edgeConnection.split(":");
service = new EdgeProxyHostService(name, ip, port);
}
}
if (!service) {
throw new Error(`can't detect dispatcher for service '${name}'!`);
}
return service;
}
/**
* @param {string} name
* @param {HostServiceOptions} options
* @returns {HostService}
*/
#createFileDispatcher(name, options) {
/**@type {HostService} */
var service = null;
const rootPath = options.Settings["Directory"];
if (rootPath) {
service = new StaticFileProxyHostService(name, rootPath);
} else {
throw new error(
`The 'Directory' setting not set for file dispatcher in '${name}' service!`
);
}
return service;
}
/**
* @param {object} jsonObj
* @returns {HostManager}
*/
static fromJson(jsonObj) {
var options = new HostManagerOptions();
return new HostManager(Object.assign(options, jsonObj));
}
}