Add foreground service, some cleanup, and start websocket server

This commit is contained in:
2022-07-13 21:35:18 -05:00
parent 4d7769de56
commit 6476416c67
12 changed files with 158 additions and 43 deletions

View File

@@ -8,7 +8,6 @@ import {Inject} from '../di'
import * as nodePath from 'path'
import {Unit} from '../lifecycle/Unit'
import {isCanonicalReceiver} from '../support/CanonicalReceiver'
import {env} from '../lifecycle/Application'
/**
* Interface describing a definition of a single canonical item loaded from the app.

24
src/service/Foreground.ts Normal file
View File

@@ -0,0 +1,24 @@
import {Unit} from '../lifecycle/Unit'
import {Inject} from '../di'
import {Logging} from './Logging'
import * as process from 'process'
export class Foreground extends Unit {
@Inject()
protected readonly logging!: Logging
protected resolver?: () => unknown
public up(): Promise<void> {
return new Promise<void>(res => {
this.resolver = res
this.logging.success(`Application started! Press ^C or send SIGINT to stop.`)
process.stdin.resume()
process.on('SIGINT', res)
})
}
public down(): void {
this.resolver?.()
}
}

View File

@@ -1,5 +1,5 @@
import {Inject, Singleton} from '../di'
import {ErrorWithContext, HTTPStatus, withTimeout} from '../util'
import {ErrorWithContext} from '../util'
import {Unit} from '../lifecycle/Unit'
import {createServer, IncomingMessage, RequestListener, Server, ServerResponse} from 'http'
import {Logging} from './Logging'
@@ -67,10 +67,9 @@ export class HTTPServer extends Unit {
this.server = createServer(this.handler)
this.server.listen(port, () => {
this.logging.success(`Server listening on port ${port}. Press ^C to stop.`)
this.logging.success(`Server listening on port ${port}.`)
res()
})
process.on('SIGINT', res)
})
}
@@ -85,37 +84,19 @@ export class HTTPServer extends Unit {
}
}
public get handler(): RequestListener {
// const timeout = this.config.get('server.timeout', 10000)
// const timeout = 0 // temporarily disable this because it is causing problems
public getServer(): Server {
if ( !this.server ) {
throw new ErrorWithContext('Unable to access server: it has not yet been created')
}
return this.server
}
public get handler(): RequestListener {
return async (request: IncomingMessage, response: ServerResponse) => {
const extolloReq = new Request(request, response)
await this.requestLocalStorage.run(extolloReq, async () => {
/* withTimeout(timeout, extolloReq.response.sent$.toPromise())
.onTime(() => {
this.logging.verbose(`Request lifecycle finished on time. (Path: ${extolloReq.path})`)
})
.late(() => {
if ( !extolloReq.bypassTimeout ) {
this.logging.warn(`Request lifecycle finished late, so an error response was returned! (Path: ${extolloReq.path})`)
}
})
.timeout(() => {
if ( extolloReq.bypassTimeout ) {
this.logging.info(`Request lifecycle has timed out, but bypassRequest was set. (Path: ${extolloReq.path})`)
return
}
this.logging.error(`Request lifecycle has timed out. Will send error response instead. (Path: ${extolloReq.path})`)
extolloReq.response.setStatus(HTTPStatus.REQUEST_TIMEOUT)
extolloReq.response.body = 'Sorry, your request timed out.'
extolloReq.response.send()
})
.run()
.catch(e => this.logging.error(e))*/
this.logging.info(`${extolloReq.method} ${extolloReq.path}`)
try {

View File

@@ -12,7 +12,6 @@ import {PackageDiscovered} from '../support/PackageDiscovered'
import {staticServer} from '../http/servers/static'
import {Bus} from '../support/bus'
import {RequestLocalStorage} from '../http/RequestLocalStorage'
import {env} from '../lifecycle/Application'
/**
* Application unit that loads the various route files from `app/http/routes` and pre-compiles the route handlers.
@@ -106,7 +105,7 @@ export class Routing extends Unit {
* @param method
* @param path
*/
public match(method: HTTPMethod, path: string): Route<unknown, unknown[]> | undefined {
public match(method: 'ws' | HTTPMethod, path: string): Route<unknown, unknown[]> | undefined {
return this.compiledRoutes.firstWhere(route => {
return route.match(method, path)
})

View File

@@ -0,0 +1,50 @@
import {Unit, UnitStatus} from '../lifecycle/Unit'
import {Inject, Singleton} from '../di'
import * as WebSocket from 'ws'
import {HTTPServer} from './HTTPServer'
import {Logging} from './Logging'
import {ErrorWithContext} from '../util'
import {Request} from '../http/lifecycle/Request'
@Singleton()
export class WebsocketServer extends Unit {
@Inject()
protected readonly http!: HTTPServer
@Inject()
protected readonly logging!: Logging
protected server?: WebSocket.Server
public async up(): Promise<void> {
// Make sure the HTTP server is started. Otherwise, this is going to fail anyway
if ( this.http.status !== UnitStatus.Started ) {
throw new ErrorWithContext('Cannot start WebsocketServer without HTTPServer.', {
suggestion: 'Make sure the HTTPServer is registered in your Units.extollo.ts file, and it is listed before the WebsocketServer.',
})
}
// Start the websocket server
this.logging.info('Starting WebSocket server...')
this.server = new WebSocket.Server<WebSocket.WebSocket>({
server: this.http.getServer(),
})
// Register the websocket handler
this.server.on('connection', (ws, request) => {
this.logging.info('Got WebSocket connection! ' + request.method)
const extolloReq = new Request(request)
this.logging.debug(ws)
this.logging.debug(request)
})
}
public down(): Promise<void> {
return new Promise(res => {
// Stop the websocket server, if it exists
if ( this.server ) {
this.server.close(() => res())
}
})
}
}