UNPKG

@deepkit/framework

Version:

276 lines 14.8 kB
/*@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