1 | {"version":3,"file":"Server.js","sourceRoot":"","sources":["../../../src/lib/Server.ts"],"names":[],"mappings":"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","sourcesContent":["import { json, urlencoded } from 'body-parser';\nimport express from 'express';\nimport { Server as HttpServer } from 'http';\nimport { Socket } from 'net';\nimport WebSocket from 'ws';\nimport { Handle } from '@theintern/common';\n\nimport { pullFromArray } from './common/util';\nimport { isErrnoException } from './node/util';\nimport Node from './executors/Node';\nimport { Message } from './channels/Base';\n\nimport instrument from './middleware/instrument';\nimport unhandled from './middleware/unhandled';\nimport finalError from './middleware/finalError';\nimport resolveSuites from './middleware/resolveSuites';\nimport post from './middleware/post';\nimport filterUrl from './middleware/filterUrl';\n\nexport interface Context {\n readonly stopped: boolean;\n readonly basePath: string;\n readonly executor: Node;\n handleMessage(message: Message): Promise<any>;\n}\n\nexport default class Server implements ServerProperties {\n /** Executor managing this Server */\n readonly executor!: Node;\n\n /** Base path to resolve file requests against */\n basePath!: string;\n\n /** Port to use for HTTP connections */\n port!: number;\n\n /**\n * If true, wait for emit handlers to complete before responding to a\n * message\n */\n runInSync!: boolean;\n\n /** Port to use for WebSocket connections */\n socketPort!: number;\n\n get stopped() {\n return !this._httpServer;\n }\n\n protected _app: express.Express | undefined;\n protected _httpServer: HttpServer | undefined;\n protected _sessions:\n | { [id: string]: { listeners: ServerListener[] } }\n | undefined;\n protected _wsServer: WebSocket.Server | undefined;\n protected _wsPingTimers: NodeJS.Timer[];\n\n constructor(options: ServerOptions) {\n Object.assign(\n this,\n {\n basePath: '.',\n runInSync: false,\n },\n options\n );\n this._wsPingTimers = [];\n }\n\n start() {\n let startupError: Error;\n let wsServer: WebSocket.Server;\n let httpServer: HttpServer;\n\n return new Promise<void>((resolve, reject) => {\n const app = (this._app = express());\n this._sessions = {};\n\n this.executor.log(\n 'Listening for WebSocket connections on port',\n this.socketPort\n );\n\n let clientCount = 0;\n wsServer = new WebSocket.Server({ port: this.socketPort });\n wsServer.on('connection', (client) => {\n clientCount++;\n this.executor.log(`WebSocket client ${clientCount} connected`);\n this._handleWebSocket(client, clientCount);\n });\n wsServer.on('error', (error) => {\n if (isErrnoException(error) && error.code === 'EADDRINUSE') {\n const err: NodeJS.ErrnoException = new Error(\n `Something is already listening on the websocket server port (${this.socketPort})`\n );\n err.code = error.code;\n err.errno = error.errno;\n reject(err);\n } else if (!this._wsServer) {\n reject(error);\n } else {\n this.executor.emit('error', error);\n }\n });\n wsServer.on('close', () => {\n for (const timer of this._wsPingTimers) {\n clearInterval(timer);\n }\n });\n\n const context = Object.create(null, {\n stopped: {\n enumerable: true,\n get: () => this.stopped,\n },\n basePath: {\n enumerable: true,\n get: () => this.basePath,\n },\n executor: {\n enumerable: true,\n get: () => this.executor,\n },\n handleMessage: {\n enumerable: false,\n writable: false,\n configurable: false,\n value: (message: Message) => this._handleMessage(message),\n },\n });\n\n // Add \"intern\" object to both request and response objects\n Object.defineProperty(app.request, 'intern', {\n enumerable: true,\n get: () => context,\n });\n Object.defineProperty(app.response, 'intern', {\n enumerable: true,\n get: () => context,\n });\n\n app.use(filterUrl());\n\n // Handle JSON and form-encoded request bodies\n app.use(json({ limit: '1mb' }), urlencoded({ extended: true }));\n\n // Log all requests\n app.use((request, _response, next) => {\n this.executor.log(`${request.method} request for ${request.url}`);\n return next();\n });\n\n const internPath = this.executor.config.internPath;\n\n // Allow resolution using both __intern and node_modules/intern.\n // Note that internPath will always end with a '/'.\n app.use(\n [`/${internPath}__resolveSuites__`, '/__intern/__resolveSuites__'],\n resolveSuites(context)\n );\n\n // Map __intern to config.internPath\n app.use('/__intern', express.static(internPath, { fallthrough: false }));\n\n // TODO: Allow user to add middleware here\n\n app.use(\n instrument(context),\n express.static(this.basePath),\n post(context),\n unhandled(),\n finalError()\n );\n\n httpServer = app.listen(this.port, resolve);\n httpServer.on('error', (error) => {\n if (isErrnoException(error) && error.code === 'EADDRINUSE') {\n const err: NodeJS.ErrnoException = new Error(\n `Something is already listening on the server port (${this.port})`\n );\n err.code = error.code;\n err.errno = error.errno;\n reject(err);\n } else if (!this._httpServer) {\n reject(error);\n } else {\n this.executor.emit('error', error);\n }\n });\n\n const sockets: Socket[] = [];\n\n // If sockets are not manually destroyed then Node.js will keep\n // itself running until they all expire\n httpServer.on('close', () => {\n let socket: Socket | undefined;\n while ((socket = sockets.pop())) {\n socket.destroy();\n }\n });\n\n httpServer.on('connection', (socket) => {\n sockets.push(socket);\n this.executor.log(\n 'HTTP connection opened,',\n sockets.length,\n 'open connections'\n );\n\n socket.on('close', () => {\n let index = sockets.indexOf(socket);\n index !== -1 && sockets.splice(index, 1);\n this.executor.log(\n 'HTTP connection closed,',\n sockets.length,\n 'open connections'\n );\n });\n });\n })\n .then(() => {\n this._wsServer = wsServer;\n this._httpServer = httpServer;\n })\n .catch((error) => {\n startupError = error;\n try {\n wsServer.close();\n } catch (_error) {}\n try {\n httpServer.close();\n } catch (_error) {}\n })\n .then(() => {\n if (startupError) {\n throw startupError;\n }\n });\n }\n\n stop() {\n this.executor.log('Stopping server...');\n const promises: Promise<any>[] = [];\n\n if (this._app && this._httpServer) {\n promises.push(\n new Promise((resolve) => {\n this._httpServer!.close(resolve);\n }).then(() => {\n this.executor.log('Stopped http server');\n this._app = this._httpServer = undefined;\n })\n );\n }\n\n if (this._wsServer) {\n promises.push(\n new Promise((resolve) => {\n this._wsServer!.close(resolve);\n }).then(() => {\n this.executor.log('Stopped ws server');\n this._wsServer = undefined;\n })\n );\n }\n\n return Promise.all(promises);\n }\n\n /**\n * Listen for all events for a specific session\n */\n subscribe(sessionId: string, listener: ServerListener): Handle {\n const listeners = this._getSession(sessionId).listeners;\n listeners.push(listener);\n return {\n destroy: function (this: any) {\n this.destroy = function () {};\n pullFromArray(listeners, listener);\n },\n };\n }\n\n private _getSession(sessionId: string) {\n let session = this._sessions![sessionId];\n if (!session) {\n session = this._sessions![sessionId] = { listeners: [] };\n }\n return session;\n }\n\n private _handleMessage(message: Message): Promise<any> {\n this.executor.log(\n 'Processing message [',\n message.id,\n '] for',\n message.sessionId,\n ':',\n message.name\n );\n\n const promise = this._publish(message);\n if (getShouldWait(this.runInSync, message)) {\n return promise;\n }\n\n // If we're not returning the promise, catch any errors to avoid\n // unhandled rejections\n promise.catch((error) => {\n this.executor.emit('error', error);\n });\n\n return resolvedPromise;\n }\n\n private _handleWebSocket(client: WebSocket, id: number) {\n let isAlive = true;\n\n // Send a ping every 30 seconds to check that the client is alive, and to\n // keep the connection alive\n const timer = setInterval(() => {\n if (!isAlive) {\n client.terminate();\n clearInterval(timer);\n }\n isAlive = false;\n this.executor.log('Sending ping to remote', id);\n client.ping();\n }, 30000);\n\n // Keep track of the heartbeat timer so it can be cleared if the server is\n // stopped.\n this._wsPingTimers.push(timer);\n\n // When a response is received from the client, flag it as still alive\n client.on('pong', () => {\n this.executor.log('Received pong from remote', id);\n isAlive = true;\n });\n\n client.on('message', (data) => {\n this.executor.log('Received WebSocket message from', id);\n const message: Message = JSON.parse(data.toString());\n this._handleMessage(message)\n .catch((error) => this.executor.emit('error', error))\n .then(() => {\n this.executor.log('Sending ack for [', message.id, '] to', id);\n client.send(JSON.stringify({ id: message.id }), (error) => {\n if (error) {\n this.executor.emit(\n 'error',\n new Error(\n `Error sending ack for [ ${message.id} ] to ${id}: ${error.message}`\n )\n );\n }\n });\n });\n });\n\n client.on('error', (error) => {\n this.executor.log(`WebSocket client error for ${id}:`, error);\n this.executor.emit('error', error);\n clearInterval(timer);\n });\n }\n\n private _publish(message: Message) {\n const listeners = this._getSession(message.sessionId).listeners;\n return Promise.all(\n listeners.map((listener) => listener(message.name, message.data))\n );\n }\n}\n\nexport interface ServerProperties {\n basePath: string;\n executor: Node;\n port: number;\n runInSync: boolean;\n socketPort: number;\n}\n\nexport interface ServerListener {\n (name: string, data?: any): void;\n}\n\nexport type ServerOptions = Partial<ServerProperties> & { executor: Node };\n\nconst resolvedPromise = Promise.resolve();\n\n/**\n * Indicate whether Server should wait for an event to process before sending an\n * acknowlegement.\n */\nfunction getShouldWait(waitMode: string | boolean, message: Message) {\n let eventName = message.name;\n\n // never wait for runEnd\n if (eventName === 'runEnd') {\n return false;\n }\n\n let shouldWait = false;\n\n if (waitMode === 'fail') {\n if (\n (eventName === 'testEnd' && message.data.error) ||\n (eventName === 'suiteEnd' && message.data.error) ||\n eventName === 'error'\n ) {\n shouldWait = true;\n }\n } else if (waitMode === true) {\n shouldWait = true;\n } else if (Array.isArray(waitMode) && waitMode.indexOf(eventName) !== -1) {\n shouldWait = true;\n }\n\n return shouldWait;\n}\n"]} |