You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lib/src/http/kernel/HTTPKernel.ts

151 lines
5.5 KiB

import {Inject, Instantiable, Singleton} from "@extollo/di"
import {Collection, HTTPStatus} from "@extollo/util"
import {HTTPKernelModule} from "./HTTPKernelModule";
import {Logging} from "../../service/Logging";
import {AppClass} from "../../lifecycle/AppClass";
import {Request} from "../lifecycle/Request";
import {http} from "../response/HTTPErrorResponseFactory";
/**
* Interface for fluently registering kernel modules into the kernel.
*/
export interface ModuleRegistrationFluency {
before: (other?: Instantiable<HTTPKernelModule>) => HTTPKernel,
after: (other?: Instantiable<HTTPKernelModule>) => HTTPKernel,
first: () => HTTPKernel,
last: () => HTTPKernel,
core: () => HTTPKernel,
}
/**
* Error thrown when a kernel module is requested that does not exist w/in the kernel.
* @extends Error
*/
export class KernelModuleNotFoundError extends Error {
constructor(name: string) {
super(`The kernel module ${name} is not registered with the kernel.`)
}
}
@Singleton()
export class HTTPKernel extends AppClass {
@Inject()
protected readonly logging!: Logging;
/**
* Collection of preflight modules to apply.
* @type Collection<HTTPKernelModule>
*/
protected preflight: Collection<HTTPKernelModule> = new Collection<HTTPKernelModule>()
/**
* Module considered to be the main handler.
* @type HTTPKernelModule
*/
protected inflight?: HTTPKernelModule
/**
* Collection of postflight modules to apply.
* @type Collection<HTTPKernelModule>
*/
protected postflight: Collection<HTTPKernelModule> = new Collection<HTTPKernelModule>()
/**
* Handle the incoming request, applying the preflight modules, inflight module, then postflight modules.
* @param {Request} request
* @return Promise<Request>
*/
public async handle(request: Request): Promise<Request> {
try {
for (const module of this.preflight.toArray()) {
this.logging.verbose(`Applying pre-flight HTTP kernel module: ${module.constructor.name}`)
request = await module.apply(request)
}
if (this.inflight) {
this.logging.verbose(`Applying core HTTP kernel module: ${this.inflight.constructor.name}`)
request = await this.inflight.apply(request)
}
for (const module of this.postflight.toArray()) {
this.logging.verbose(`Applying post-flight HTTP kernel module: ${module.constructor.name}`)
request = await module.apply(request)
}
} catch (e: any) {
this.logging.error(e)
await http(HTTPStatus.REQUEST_TIMEOUT).write(request)
}
this.logging.verbose('Finished kernel lifecycle')
return request
}
/**
* Get a fluent interface for registering the given kernel module.
* @param {Instantiable<HTTPKernelModule>} module
* @return ModuleRegistrationFluency
*/
public register(module: Instantiable<HTTPKernelModule>): ModuleRegistrationFluency {
this.logging.verbose(`Registering HTTP kernel module: ${module.name}`)
return {
before: (other?: Instantiable<HTTPKernelModule>): HTTPKernel => {
if ( !other ) {
this.preflight = this.preflight.push(this.app().make(module))
return this
}
let found_index = this.preflight.find((mod: HTTPKernelModule) => mod instanceof other)
if ( typeof found_index !== 'undefined' ) {
this.preflight = this.preflight.put(found_index, this.app().make(module))
return this
} else {
found_index = this.postflight.find((mod: HTTPKernelModule) => mod instanceof other)
}
if ( typeof found_index !== 'undefined' ) {
this.postflight = this.postflight.put(found_index, this.app().make(module))
} else {
throw new KernelModuleNotFoundError(other.name)
}
return this
},
after: (other?: Instantiable<HTTPKernelModule>): HTTPKernel => {
if ( !other ) {
this.postflight = this.postflight.push(this.app().make(module))
return this
}
let found_index = this.preflight.find((mod: HTTPKernelModule) => mod instanceof other)
if ( typeof found_index !== 'undefined' ) {
this.preflight = this.preflight.put(found_index + 1, this.app().make(module))
return this
} else {
found_index = this.postflight.find((mod: HTTPKernelModule) => mod instanceof other)
}
if ( typeof found_index !== 'undefined' ) {
this.postflight = this.postflight.put(found_index + 1, this.app().make(module))
} else {
throw new KernelModuleNotFoundError(other.name)
}
return this
},
first: (): HTTPKernel => {
this.preflight = this.preflight.put(0, this.app().make(module))
return this
},
last: (): HTTPKernel => {
this.postflight = this.postflight.push(this.app().make(module))
return this
},
core: (): HTTPKernel => {
this.inflight = this.app().make(module)
return this
},
}
}
}