|
18 | 18 | */ |
19 | 19 |
|
20 | 20 | import { Server } from 'hapi'; |
21 | | -import { notFound } from 'boom'; |
22 | | -import { map, sample } from 'lodash'; |
23 | | -import { map as promiseMap, fromNode } from 'bluebird'; |
24 | | -import { Agent as HttpsAgent } from 'https'; |
25 | | -import { readFileSync } from 'fs'; |
26 | | - |
27 | | -import { setupConnection } from '../../server/http/setup_connection'; |
28 | | -import { registerHapiPlugins } from '../../server/http/register_hapi_plugins'; |
| 21 | +import { createBasePathProxy } from '../../core'; |
29 | 22 | import { setupLogging } from '../../server/logging'; |
30 | 23 |
|
31 | | -const alphabet = 'abcdefghijklmnopqrztuvwxyz'.split(''); |
| 24 | +export async function configureBasePathProxy(config) { |
| 25 | + // New platform forwards all logs to the legacy platform so we need HapiJS server |
| 26 | + // here just for logging purposes and nothing else. |
| 27 | + const server = new Server(); |
| 28 | + setupLogging(server, config); |
| 29 | + |
| 30 | + const basePathProxy = createBasePathProxy({ server, config }); |
| 31 | + |
| 32 | + await basePathProxy.configure({ |
| 33 | + isKibanaPath: (path) => { |
| 34 | + const isApp = path.startsWith('app/'); |
| 35 | + const isKnownShortPath = ['login', 'logout', 'status'].includes(path); |
| 36 | + |
| 37 | + return isApp || isKnownShortPath; |
| 38 | + }, |
| 39 | + |
| 40 | + blockUntil: () => { |
| 41 | + // Wait until `serverWorker either crashes or starts to listen. |
| 42 | + // The `serverWorker` property should be set by the ClusterManager |
| 43 | + // once it creates the worker. |
| 44 | + const serverWorker = basePathProxy.serverWorker; |
| 45 | + if (serverWorker.listening || serverWorker.crashed) { |
| 46 | + return Promise.resolve(); |
| 47 | + } |
32 | 48 |
|
33 | | -export default class BasePathProxy { |
34 | | - constructor(clusterManager, config) { |
35 | | - this.clusterManager = clusterManager; |
36 | | - this.server = new Server(); |
| 49 | + return new Promise((resolve) => { |
| 50 | + const done = () => { |
| 51 | + serverWorker.removeListener('listening', done); |
| 52 | + serverWorker.removeListener('crashed', done); |
37 | 53 |
|
38 | | - this.targetPort = config.get('dev.basePathProxyTarget'); |
39 | | - this.basePath = config.get('server.basePath'); |
| 54 | + resolve(); |
| 55 | + }; |
40 | 56 |
|
41 | | - const sslEnabled = config.get('server.ssl.enabled'); |
42 | | - if (sslEnabled) { |
43 | | - this.proxyAgent = new HttpsAgent({ |
44 | | - key: readFileSync(config.get('server.ssl.key')), |
45 | | - passphrase: config.get('server.ssl.keyPassphrase'), |
46 | | - cert: readFileSync(config.get('server.ssl.certificate')), |
47 | | - ca: map(config.get('server.ssl.certificateAuthorities'), readFileSync), |
48 | | - rejectUnauthorized: false |
| 57 | + serverWorker.on('listening', done); |
| 58 | + serverWorker.on('crashed', done); |
49 | 59 | }); |
50 | 60 | } |
| 61 | + }); |
51 | 62 |
|
52 | | - if (!this.basePath) { |
53 | | - this.basePath = `/${sample(alphabet, 3).join('')}`; |
54 | | - config.set('server.basePath', this.basePath); |
55 | | - } |
56 | | - |
57 | | - const ONE_GIGABYTE = 1024 * 1024 * 1024; |
58 | | - config.set('server.maxPayloadBytes', ONE_GIGABYTE); |
59 | | - |
60 | | - setupLogging(this.server, config); |
61 | | - setupConnection(this.server, config); |
62 | | - registerHapiPlugins(this.server, config); |
63 | | - |
64 | | - this.setupRoutes(); |
65 | | - } |
66 | | - |
67 | | - setupRoutes() { |
68 | | - const { clusterManager, server, basePath, targetPort } = this; |
69 | | - |
70 | | - server.route({ |
71 | | - method: 'GET', |
72 | | - path: '/', |
73 | | - handler(req, reply) { |
74 | | - return reply.redirect(basePath); |
75 | | - } |
76 | | - }); |
77 | | - |
78 | | - server.route({ |
79 | | - method: '*', |
80 | | - path: `${basePath}/{kbnPath*}`, |
81 | | - config: { |
82 | | - pre: [ |
83 | | - (req, reply) => { |
84 | | - promiseMap(clusterManager.workers, worker => { |
85 | | - if (worker.type === 'server' && !worker.listening && !worker.crashed) { |
86 | | - return fromNode(cb => { |
87 | | - const done = () => { |
88 | | - worker.removeListener('listening', done); |
89 | | - worker.removeListener('crashed', done); |
90 | | - cb(); |
91 | | - }; |
92 | | - |
93 | | - worker.on('listening', done); |
94 | | - worker.on('crashed', done); |
95 | | - }); |
96 | | - } |
97 | | - }) |
98 | | - .return(undefined) |
99 | | - .nodeify(reply); |
100 | | - } |
101 | | - ], |
102 | | - }, |
103 | | - handler: { |
104 | | - proxy: { |
105 | | - passThrough: true, |
106 | | - xforward: true, |
107 | | - agent: this.proxyAgent, |
108 | | - protocol: server.info.protocol, |
109 | | - host: server.info.host, |
110 | | - port: targetPort, |
111 | | - } |
112 | | - } |
113 | | - }); |
114 | | - |
115 | | - server.route({ |
116 | | - method: '*', |
117 | | - path: `/{oldBasePath}/{kbnPath*}`, |
118 | | - handler(req, reply) { |
119 | | - const { oldBasePath, kbnPath = '' } = req.params; |
120 | | - |
121 | | - const isGet = req.method === 'get'; |
122 | | - const isBasePath = oldBasePath.length === 3; |
123 | | - const isApp = kbnPath.startsWith('app/'); |
124 | | - const isKnownShortPath = ['login', 'logout', 'status'].includes(kbnPath); |
125 | | - |
126 | | - if (isGet && isBasePath && (isApp || isKnownShortPath)) { |
127 | | - return reply.redirect(`${basePath}/${kbnPath}`); |
128 | | - } |
129 | | - |
130 | | - return reply(notFound()); |
131 | | - } |
132 | | - }); |
133 | | - } |
134 | | - |
135 | | - async listen() { |
136 | | - await fromNode(cb => this.server.start(cb)); |
137 | | - this.server.log(['listening', 'info'], `basePath Proxy running at ${this.server.info.uri}${this.basePath}`); |
138 | | - } |
139 | | - |
| 63 | + return basePathProxy; |
140 | 64 | } |
0 commit comments