diff --git a/src/core/server/http/base_path_proxy_server.ts b/src/core/server/http/base_path_proxy_server.ts index f265fb942155dcf..b0c2144d7189a6e 100644 --- a/src/core/server/http/base_path_proxy_server.ts +++ b/src/core/server/http/base_path_proxy_server.ts @@ -59,6 +59,8 @@ export class BasePathProxyServer { } public async start(options: Readonly) { + this.log.debug('starting basepath proxy server'); + const serverOptions = getServerOptions(this.httpConfig); this.server = createServer(serverOptions); @@ -79,21 +81,22 @@ export class BasePathProxyServer { this.setupRoutes(options); + await this.server.start(); + this.log.info( - `starting basepath proxy server at ${this.server.info.uri}${this.httpConfig.basePath}` + `basepath proxy server running at ${this.server.info.uri}${this.httpConfig.basePath}` ); - - await this.server.start(); } public async stop() { - this.log.info('stopping basepath proxy server'); - - if (this.server !== undefined) { - await this.server.stop(); - this.server = undefined; + if (this.server === undefined) { + return; } + this.log.debug('stopping basepath proxy server'); + await this.server.stop(); + this.server = undefined; + if (this.httpsAgent !== undefined) { this.httpsAgent.destroy(); this.httpsAgent = undefined; diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 7b4481cdfd248f7..68c9c5f2346db5f 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -45,6 +45,8 @@ export class HttpServer { } public async start(config: HttpConfig) { + this.log.debug('starting http server'); + const serverOptions = getServerOptions(config); this.server = createServer(serverOptions); @@ -70,10 +72,10 @@ export class HttpServer { await this.server.start(); - this.log.info( - `Server running at ${this.server.info.uri}${config.rewriteBasePath ? config.basePath : ''}`, - // The "legacy" Kibana will output log records with `listening` tag even if `quiet` logging mode is enabled. - { tags: ['listening'] } + this.log.debug( + `http server running at ${this.server.info.uri}${ + config.rewriteBasePath ? config.basePath : '' + }` ); } @@ -82,7 +84,7 @@ export class HttpServer { return; } - this.log.info('stopping http server'); + this.log.debug('stopping http server'); await this.server.stop(); this.server = undefined; } diff --git a/src/core/server/http/https_redirect_server.ts b/src/core/server/http/https_redirect_server.ts index 124760bb463207e..b2664c5e24b550c 100644 --- a/src/core/server/http/https_redirect_server.ts +++ b/src/core/server/http/https_redirect_server.ts @@ -30,6 +30,8 @@ export class HttpsRedirectServer { constructor(private readonly log: Logger) {} public async start(config: HttpConfig) { + this.log.debug('starting http --> https redirect server'); + if (!config.ssl.enabled || config.ssl.redirectHttpFromPort === undefined) { throw new Error( 'Redirect server cannot be started when [ssl.enabled] is set to `false`' + @@ -37,10 +39,6 @@ export class HttpsRedirectServer { ); } - this.log.info( - `starting HTTP --> HTTPS redirect server [${config.host}:${config.ssl.redirectHttpFromPort}]` - ); - // Redirect server is configured in the same way as any other HTTP server // within the platform with the only exception that it should always be a // plain HTTP server, so we just ignore `tls` part of options. @@ -65,6 +63,7 @@ export class HttpsRedirectServer { try { await this.server.start(); + this.log.debug(`http --> https redirect server running at ${this.server.info.uri}`); } catch (err) { if (err.code === 'EADDRINUSE') { throw new Error( @@ -83,7 +82,7 @@ export class HttpsRedirectServer { return; } - this.log.info('stopping HTTP --> HTTPS redirect server'); + this.log.debug('stopping http --> https redirect server'); await this.server.stop(); this.server = undefined; } diff --git a/src/core/server/legacy_compat/legacy_service.ts b/src/core/server/legacy_compat/legacy_service.ts index ca05bd9c367ca9c..b185ebf9023e638 100644 --- a/src/core/server/legacy_compat/legacy_service.ts +++ b/src/core/server/legacy_compat/legacy_service.ts @@ -19,16 +19,7 @@ import { Server, ServerOptions } from 'hapi-latest'; import { CLUSTER_MANAGER_PATH, REPL_PATH } from '../../cli/installation_features'; -import { - $combineLatest, - BehaviorSubject, - filter, - first, - k$, - map, - Subscription, - toPromise, -} from '../../lib/kbn_observable'; +import { $combineLatest, first, k$, map, Subscription, toPromise } from '../../lib/kbn_observable'; import { CoreService } from '../../types/core_service'; import { ConfigService, Env, RawConfig } from '../config'; import { DevConfig } from '../dev'; @@ -40,27 +31,22 @@ interface LegacyKbnServer { applyLoggingConfiguration: (settings: Readonly>) => void; listen: () => Promise; close: () => Promise; - ready: () => Promise; - server: { - /** - * Forwards log request to the legacy platform. - * @param tags A string or array of strings used to briefly identify the event. - * @param [data] Optional string or object to log with the event. - * @param [timestamp] Timestamp value associated with the log record. - */ - log: (tags: string | string[], data?: string | Error, timestamp?: Date) => void; - }; } interface LegacyServiceOptions { isDevClusterMaster: boolean; } +interface HttpConnection { + server: Server; + options: ServerOptions; +} + export class LegacyService implements CoreService { private readonly log: Logger; private kbnServer?: LegacyKbnServer; private rawConfigSubscription?: Subscription; - private readonly connection$: BehaviorSubject = new BehaviorSubject(null); + private connection?: HttpConnection; constructor( private readonly env: Env, @@ -74,7 +60,7 @@ export class LegacyService implements CoreService { } public async start() { - this.log.debug('Starting legacy service'); + this.log.debug('starting legacy service'); this.rawConfigSubscription = this.configService.getConfig$().subscribe({ next: rawConfig => { @@ -101,7 +87,7 @@ export class LegacyService implements CoreService { } public async stop() { - this.log.debug('Stopping legacy service'); + this.log.debug('stopping legacy service'); if (this.rawConfigSubscription !== undefined) { this.rawConfigSubscription.unsubscribe(); @@ -115,51 +101,46 @@ export class LegacyService implements CoreService { } private setupConnectionListener() { - this.env.legacy.once( - 'connection', - ({ server, options }: { server: Server; options: ServerOptions }) => { - const legacyProxifier = new LegacyPlatformProxifier( - this.logger.get('legacy', 'proxy'), - server.listener - ); - - // We register Kibana proxy middleware right before we start server to allow - // all new platform plugins register their routes, so that `legacyProxifier` - // handles only requests that aren't handled by the new platform. - server.route({ - path: '/{p*}', - method: '*', - options: { payload: { output: 'stream', parse: false, timeout: false } }, - handler: async ({ raw: { req, res } }, responseToolkit) => { - if (this.kbnServer === undefined) { - this.log.debug(`Kibana server is not ready yet ${req.method}:${req.url}.`); - - // If legacy server is not ready yet (e.g. it's still in optimization phase), - // we should let client know that and ask to retry after 30 seconds. - return responseToolkit - .response('Kibana server is not ready yet') - .code(503) - .header('Retry-After', '30'); - } - - this.log.debug(`Request will be handled by proxy ${req.method}:${req.url}.`); - - // Forward request and response objects to the legacy platform. This method - // is used whenever new platform doesn't know how to handle the request. - legacyProxifier.emit('request', req, res); - - return responseToolkit.abandon; - }, - }); - - this.connection$.next({ - ...options, - port: undefined, - autoListen: false, - listener: legacyProxifier as any, - }); - } - ); + this.env.legacy.once('connection', ({ server, options }: HttpConnection) => { + const legacyProxifier = new LegacyPlatformProxifier( + this.logger.get('legacy', 'proxy'), + server.listener + ); + + // We register Kibana proxy middleware right before we start server to allow + // all new platform plugins register their routes, so that `legacyProxifier` + // handles only requests that aren't handled by the new platform. + server.route({ + path: '/{p*}', + method: '*', + options: { payload: { output: 'stream', parse: false, timeout: false } }, + handler: async ({ raw: { req, res } }, responseToolkit) => { + if (this.kbnServer === undefined) { + this.log.debug(`Kibana server is not ready yet ${req.method}:${req.url}.`); + + // If legacy server is not ready yet (e.g. it's still in optimization phase), + // we should let client know that and ask to retry after 30 seconds. + return responseToolkit + .response('Kibana server is not ready yet') + .code(503) + .header('Retry-After', '30'); + } + + this.log.debug(`Request will be handled by proxy ${req.method}:${req.url}.`); + + // Forward request and response objects to the legacy platform. This method + // is used whenever new platform doesn't know how to handle the request. + legacyProxifier.emit('request', req, res); + + return responseToolkit.abandon; + }, + }); + + this.connection = { + server, + options: { ...options, listener: legacyProxifier as any }, + }; + }); } private async createClusterManager(rawConfig: RawConfig) { @@ -178,14 +159,7 @@ export class LegacyService implements CoreService { : undefined ); - return { - close() { - // noop - }, - listen() { - // noop - }, - } as LegacyKbnServer; + return undefined; } private async createKbnServer(rawConfig: RawConfig) { @@ -194,16 +168,13 @@ export class LegacyService implements CoreService { toPromise() ); - // If `server.autoListen` is set to `false`, then HTTP service won't be started - // and connection options won't be passed to the "legacy" Kibana that will block - // its initialization, so we should make "legacy" Kibana aware of that case and - // let it continue initialization without waiting for connection options. - const connection = !httpConfig.autoListen - ? { autoListen: false } - : await k$(this.connection$)(filter(conn => conn !== null), first(), toPromise()); - const KbnServer = require('../../../server/kbn_server'); - const kbnServer: LegacyKbnServer = new KbnServer(rawConfig.getRaw(), { connection }); + const kbnServer: LegacyKbnServer = new KbnServer(rawConfig.getRaw(), { + // If we haven't received connection from the core that means that server wasn't + // supposed to start automatically (e.g. when `--server.autoListen=false`) and + // we should make sure that "legacy" Kibana is aware of that. + connection: this.connection === undefined ? { autoListen: false } : this.connection.options, + }); // The kbnWorkerType check is necessary to prevent the repl // from being started multiple times in different processes. @@ -212,11 +183,13 @@ export class LegacyService implements CoreService { require(REPL_PATH).startRepl(this.kbnServer); } - try { - await kbnServer.listen(); - } catch (err) { - await kbnServer.close(); - throw err; + if (httpConfig.autoListen) { + try { + await kbnServer.listen(); + } catch (err) { + await kbnServer.close(); + throw err; + } } return kbnServer; diff --git a/src/server/kbn_server.js b/src/server/kbn_server.js index 5772b122dd1c169..120199993361810 100644 --- a/src/server/kbn_server.js +++ b/src/server/kbn_server.js @@ -141,7 +141,13 @@ export default class KbnServer { process.send(['WORKER_LISTENING']); } - return this.server; + const { server, config } = this; + server.log(['listening', 'info'], `Server running at ${server.info.uri}${ + config.get('server.rewriteBasePath') + ? config.get('server.basePath') + : '' + }`); + return server; } async close() {