@deepkit/framework
Version:
276 lines • 14.8 kB
JavaScript
/*@ts-ignore*/
import { __ΩLoggerInterface } from '@deepkit/logger';
/*@ts-ignore*/
import { __ΩTransportConnection } from '@deepkit/rpc';
function __assignType(fn, args) {
fn.__type = args;
return fn;
}
/*
* Deepkit Framework
* Copyright (C) 2021 Deepkit UG, Marc J. Schmidt
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the MIT License.
*
* You should have received a copy of the MIT License along with this program.
*/
import { RpcKernel, RpcKernelBaseConnection, RpcKernelConnection, SessionState } from '@deepkit/rpc';
import http from 'http';
import https from 'https';
import ws from 'ws';
import selfsigned from 'selfsigned';
import { HttpConfig, HttpKernel, HttpRequest, HttpResponse } from '@deepkit/http';
import { InjectorContext } from '@deepkit/injector';
import { RpcControllers, RpcInjectorContext } from './rpc.js';
// @ts-ignore
import { join } from 'path';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { sleep } from '@deepkit/core';
// @ts-ignore
import compression from 'compression';
import { constants } from 'zlib';
const __ΩWebServerOptions = ['host', 'port', 'varPath', 'compression', 'httpsPort', () => HttpConfig, 'http', 'server', 'gracefulShutdownTimeout', 'ssl', 'sslKey', 'sslCertificate', 'sslCa', 'sslCrl', 'sslOptions', 'selfSigned', 'WebServerOptions', 'P&4!\'4"&4#\'4$\'4%8P7&4\'!4(8\'4))4*&4+8&4,8&4-8&4.8P!!K4/8)408Mw1y'];
export { __ΩWebServerOptions as __ΩWebServerOptions };
const __ΩRpcServerListener = ['close', 'RpcServerListener', 'PPP$$`J1!Mw"y'];
export { __ΩRpcServerListener as __ΩRpcServerListener };
const __ΩRpcServerCreateConnection = [() => __ΩTransportConnection, 'transport', () => HttpRequest, 'request', () => RpcKernelBaseConnection, '', 'RpcServerCreateConnection', 'PPn!2"P7#2$8P7%v&Mw\'y'];
export { __ΩRpcServerCreateConnection as __ΩRpcServerCreateConnection };
const __ΩRpcServerOptions = ['server', 'RpcServerOptions', 'PP!!J4!8Mw"y'];
export { __ΩRpcServerOptions as __ΩRpcServerOptions };
const __ΩRpcServerInterface = [() => __ΩRpcServerOptions, 'options', () => __ΩRpcServerCreateConnection, 'createRpcConnection', 'start', 'RpcServerInterface', 'PPn!2"n#2$$1%Mw&y'];
export { __ΩRpcServerInterface as __ΩRpcServerInterface };
export class RpcServer {
start(options, createRpcConnection) {
const { Server } = ws;
const server = new Server(options);
server.on('connection', __assignType((ws, req) => {
const connection = createRpcConnection({
writeBinary: __assignType(function writeBinary(message) {
ws.send(message);
}, ['message', 'writeBinary', 'P"2!"/"']),
close: __assignType(function close() {
ws.close();
}, ['close', 'P"/!']),
bufferedAmount: __assignType(function bufferedAmount() {
return ws.bufferedAmount;
}, ['bufferedAmount', 'P\'/!']),
clientAddress: __assignType(function clientAddress() {
return req.getRemoteAddress();
}, ['clientAddress', 'P&/!'])
}, req);
ws.on('message', __assignType((message) => {
connection.feed(message);
}, ['message', '', 'PW2!"/"']));
ws.on('error', __assignType((error) => {
connection.close(error);
}, ['error', '', 'P"2!"/"']));
ws.on('close', () => {
connection.close();
});
}, ['ws', () => HttpRequest, 'req', '', 'P"2!P7"2#"/$']));
return {
close: __assignType(function close() {
server.close();
}, ['close', 'P"/!'])
};
}
}
RpcServer.__type = [() => __ΩRpcServerOptions, 'options', () => __ΩRpcServerCreateConnection, 'createRpcConnection', () => __ΩRpcServerListener, 'start', () => __ΩRpcServerInterface, 'RpcServer', 'Pn!2"n#2$n%0&5n\'x"w('];
export class WebWorkerFactory {
constructor(httpKernel, logger, rpcControllers, injectorContext, rpcServer, rpcKernel) {
this.httpKernel = httpKernel;
this.logger = logger;
this.rpcControllers = rpcControllers;
this.injectorContext = injectorContext;
this.rpcServer = rpcServer;
this.rpcKernel = rpcKernel;
}
create(id, options) {
return new WebWorker(id, this.logger, this.httpKernel, this.rpcKernel, this.injectorContext, options, this.rpcServer);
}
}
WebWorkerFactory.__type = [() => HttpKernel, 'httpKernel', () => __ΩLoggerInterface, 'logger', () => RpcControllers, 'rpcControllers', () => InjectorContext, 'injectorContext', () => RpcServer, 'rpcServer', () => RpcKernel, 'rpcKernel', 'constructor', 'id', () => __ΩWebServerOptions, 'options', () => WebWorker, 'create', 'WebWorkerFactory', 'PP7!2"<n#2$:P7%2&<P7\'2(<P7)2*<P7+2,<"0-P\'2.n/20P71025w3'];
export class WebMemoryWorkerFactory extends WebWorkerFactory {
create(id, options) {
return new WebMemoryWorker(id, this.logger, this.httpKernel, this.rpcKernel, this.injectorContext, options, this.rpcServer);
}
}
WebMemoryWorkerFactory.__type = [() => WebWorkerFactory, 'id', () => __ΩWebServerOptions, 'options', () => WebMemoryWorker, 'create', 'WebMemoryWorkerFactory', 'P7!P\'2"n#2$P7%0&5w\''];
export function createRpcConnection(rootScopedContext, rpcKernel, transport, request) {
const injector = rootScopedContext.createChildScope('rpc');
injector.set(HttpRequest, request);
injector.set(RpcInjectorContext, injector);
const connection = rpcKernel.createConnection(transport, injector);
injector.set(SessionState, connection.sessionState);
injector.set(RpcKernelConnection, connection);
injector.set(RpcKernelBaseConnection, connection);
return connection;
}
createRpcConnection.__type = [() => InjectorContext, 'rootScopedContext', () => RpcKernel, 'rpcKernel', () => __ΩTransportConnection, 'transport', () => HttpRequest, 'request', 'createRpcConnection', 'PP7!2"P7#2$n%2&P7\'2(8"/)'];
export class WebWorker {
constructor(id, logger, httpKernel, rpcKernel, injectorContext, options, rpcServer) {
this.id = id;
this.logger = logger;
this.httpKernel = httpKernel;
this.rpcKernel = rpcKernel;
this.injectorContext = injectorContext;
this.options = options;
this.rpcServer = rpcServer;
//during shutdown, we don't want to accept new connections
this.shuttingDown = false;
this.activeRequests = 0;
this.compressionOptions = {
level: 0,
chunkSize: constants.Z_DEFAULT_CHUNK,
memLevel: constants.Z_DEFAULT_MEMLEVEL,
strategy: constants.Z_DEFAULT_STRATEGY,
windowBits: constants.Z_DEFAULT_WINDOWBITS,
};
this.handleRequest = this.handleRequest.bind(this);
if (this.options.compression) {
this.compressionOptions.level = this.options.compression;
}
}
handleRequest(request, response) {
if (this.shuttingDown) {
response.writeHead(503, 'Service Unavailable');
response.end();
return;
}
if (this.compressionOptions.level > 0) {
// this modifies response object, so it must be called before any data is written
compression(this.compressionOptions)(request, response, () => undefined);
}
this.activeRequests++;
response.on('close', () => {
this.activeRequests--;
});
return this.httpKernel.handleRequest(request, response);
}
applyServerSettings(server) {
const config = this.options.http;
if ('undefined' !== typeof config.timeout)
server.timeout = config.timeout;
if ('undefined' !== typeof config.requestTimeout)
server.requestTimeout = config.requestTimeout;
if ('undefined' !== typeof config.headersTimeout)
server.headersTimeout = config.headersTimeout;
if ('undefined' !== typeof config.maxHeadersCount)
server.maxHeadersCount = config.maxHeadersCount;
if ('undefined' !== typeof config.keepAliveTimeout)
server.keepAliveTimeout = config.keepAliveTimeout;
if ('undefined' !== typeof config.maxRequestsPerSocket)
server.maxRequestsPerSocket = config.maxRequestsPerSocket;
}
start() {
if (this.options.server) {
this.server = this.options.server;
this.server.on('request', this.handleRequest);
}
else {
if (this.options.ssl) {
const options = this.options.sslOptions || {};
if (this.options.selfSigned) {
const keyPath = join(this.options.varPath, `self-signed-${this.options.host}.key`);
const certificatePath = join(this.options.varPath, `self-signed-${this.options.host}.cert`);
if (existsSync(keyPath) && existsSync(certificatePath)) {
options.key = readFileSync(keyPath, 'utf8');
options.cert = readFileSync(certificatePath, 'utf8');
}
else {
const attrs = [{ name: 'commonName', value: this.options.host }];
const pems = selfsigned.generate(attrs, { days: 365 });
options.cert = pems.cert;
options.key = pems.private;
writeFileSync(keyPath, pems.private, 'utf8');
writeFileSync(certificatePath, pems.cert, 'utf8');
this.logger.log(`Self signed certificate for ${this.options.host} created at ${certificatePath}`);
this.logger.log(`Tip: If you want to open this server via chrome for localhost, use chrome://flags/#allow-insecure-localhost`);
}
}
if (!options.key && this.options.sslKey)
options.key = readFileSync(this.options.sslKey, 'utf8');
if (!options.ca && this.options.sslCa)
options.ca = readFileSync(this.options.sslCa, 'utf8');
if (!options.cert && this.options.sslCertificate)
options.cert = readFileSync(this.options.sslCertificate, 'utf8');
if (!options.crl && this.options.sslCrl)
options.crl = readFileSync(this.options.sslCrl, 'utf8');
this.servers = new https.Server(Object.assign({ IncomingMessage: HttpRequest, ServerResponse: HttpResponse, }, options), this.handleRequest);
this.servers.requestTimeout;
this.servers.listen(this.options.httpsPort || this.options.port, this.options.host);
}
const startHttpServer = !this.servers || (this.servers && this.options.httpsPort);
if (startHttpServer) {
this.server = new http.Server({ IncomingMessage: HttpRequest, ServerResponse: HttpResponse }, this.handleRequest);
this.server.listen(this.options.port, this.options.host);
}
}
if (this.servers)
this.applyServerSettings(this.servers);
if (this.server)
this.applyServerSettings(this.server);
this.startRpc();
}
startRpc() {
if (this.server) {
this.rpcListener = this.rpcServer.start({ server: this.server }, __assignType((transport, request) => {
if (this.shuttingDown) {
transport.close();
throw new Error('Server is shutting down');
}
return createRpcConnection(this.injectorContext, this.rpcKernel, transport, request);
}, ['transport', () => HttpRequest, 'request', '', 'P"2!P7"2#8"/$']));
}
if (this.servers) {
this.rpcListener = this.rpcServer.start({ server: this.servers }, __assignType((transport, request) => {
if (this.shuttingDown) {
transport.close();
throw new Error('Server is shutting down');
}
return createRpcConnection(this.injectorContext, this.rpcKernel, transport, request);
}, ['transport', () => HttpRequest, 'request', '', 'P"2!P7"2#8"/$']));
}
}
async close(graceful = false) {
if (graceful) {
if (this.options.server && this.server) {
this.server.off('request', this.handleRequest);
}
this.shuttingDown = true;
//wait until all http requests are finished
if (this.activeRequests) {
this.logger.log(`Waiting ${this.options.gracefulShutdownTimeout}s for all ${this.activeRequests} http requests to finish ...`);
const started = Date.now();
while (this.activeRequests) {
//if timeout is exceeded
if (this.options.gracefulShutdownTimeout && (Date.now() - started) / 1000 > this.options.gracefulShutdownTimeout) {
this.logger.log(`Timeout of ${this.options.gracefulShutdownTimeout}s exceeded. Closing ${this.activeRequests} open http requests.`);
break;
}
await sleep(0.1);
}
}
}
if (this.rpcListener)
await this.rpcListener.close();
if (this.server)
this.server.close();
if (this.servers)
this.servers.close();
}
}
WebWorker.__type = [() => __ΩRpcServerListener, 'rpcListener', 'server', 'servers', 'shuttingDown', function () { return false; }, 'activeRequests', function () { return 0; }, 'compressionOptions', function () { return {
level: 0,
chunkSize: constants.Z_DEFAULT_CHUNK,
memLevel: constants.Z_DEFAULT_MEMLEVEL,
strategy: constants.Z_DEFAULT_STRATEGY,
windowBits: constants.Z_DEFAULT_WINDOWBITS,
}; }, 'id', () => __ΩLoggerInterface, 'logger', () => HttpKernel, 'httpKernel', () => RpcKernel, 'rpcKernel', () => InjectorContext, 'injectorContext', () => __ΩWebServerOptions, 'options', () => RpcServer, 'rpcServer', 'constructor', () => HttpRequest, 'request', () => HttpResponse, 'response', 'handleRequest', 'applyServerSettings', 'start', 'startRpc', 'graceful', 'close', 'WebWorker', 'n!3"8<P!!J3#8<!3$8<)3%<>&\'3\'<>(!3)<>*P\'2+:9n,2-:P7.2/:P7021:P7223<n425<P7627;"08PP792:P7;2<"0=P!2#"0>P"0?P"0@;P"2A"0B5wC'];
export class WebMemoryWorker extends WebWorker {
start() {
}
}
WebMemoryWorker.__type = [() => WebWorker, 'start', 'WebMemoryWorker', 'P7!P"0"5w#'];
//# sourceMappingURL=worker.js.map