From 574ddbe9cb08eb22aed4d6db532917814ffdecfe Mon Sep 17 00:00:00 2001 From: garrettmills Date: Mon, 12 Apr 2021 11:43:06 -0500 Subject: [PATCH] make HTTP server unit more configurable --- .../module/ParseIncomingBodyHTTPModule.ts | 10 +++++++++- src/http/lifecycle/Request.ts | 3 +++ src/service/HTTPServer.ts | 20 +++++++++++++++---- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/http/kernel/module/ParseIncomingBodyHTTPModule.ts b/src/http/kernel/module/ParseIncomingBodyHTTPModule.ts index 506a7d7..f4027c4 100644 --- a/src/http/kernel/module/ParseIncomingBodyHTTPModule.ts +++ b/src/http/kernel/module/ParseIncomingBodyHTTPModule.ts @@ -32,7 +32,7 @@ export class ParseIncomingBodyHTTPModule extends HTTPKernelModule { public async apply(request: Request): Promise { const contentType = request.getHeader('content-type') const contentTypes = (Array.isArray(contentType) ? contentType : [contentType]) - .filter(Boolean).map(x => x!.toLowerCase()) + .filter(Boolean).map(x => x!.toLowerCase().split(';')[0]) if ( !contentType ) return request if ( @@ -49,6 +49,10 @@ export class ParseIncomingBodyHTTPModule extends HTTPKernelModule { return request } + /** + * Parse the request body as JSON. + * @param request + */ public async applyJSON(request: Request): Promise { await new Promise((res, rej) => { let data = '' @@ -74,6 +78,10 @@ export class ParseIncomingBodyHTTPModule extends HTTPKernelModule { return request } + /** + * Parse the request body using Busboy. This assumes the request contents are multipart. + * @param request + */ public async applyBusboy(request: Request): Promise { const config = this.config.get('server.uploads', {}) diff --git a/src/http/lifecycle/Request.ts b/src/http/lifecycle/Request.ts index 7fc0bd8..225acfd 100644 --- a/src/http/lifecycle/Request.ts +++ b/src/http/lifecycle/Request.ts @@ -90,6 +90,9 @@ export class Request extends ScopedContainer { /** Files parsed from the request. */ public readonly uploadedFiles: {[key: string]: UniversalPath} = {} + /** If true, the response lifecycle will not time out and send errors. */ + public bypassTimeout: boolean = false + constructor( /** The native Node.js request. */ protected clientRequest: IncomingMessage, diff --git a/src/service/HTTPServer.ts b/src/service/HTTPServer.ts index 3335a7f..b72cc17 100644 --- a/src/service/HTTPServer.ts +++ b/src/service/HTTPServer.ts @@ -15,6 +15,7 @@ import {error} from "../http/response/ErrorResponseFactory"; import {ExecuteResolvedRoutePreflightHTTPModule} from "../http/kernel/module/ExecuteResolvedRoutePreflightHTTPModule"; import {ExecuteResolvedRoutePostflightHTTPModule} from "../http/kernel/module/ExecuteResolvedRoutePostflightHTTPModule"; import {ParseIncomingBodyHTTPModule} from "../http/kernel/module/ParseIncomingBodyHTTPModule"; +import {Config} from "./Config"; /** * Application unit that starts the HTTP/S server, creates Request and Response objects @@ -25,6 +26,9 @@ export class HTTPServer extends Unit { @Inject() protected readonly logging!: Logging + @Inject() + protected readonly config!: Config + @Inject() protected readonly kernel!: HTTPKernel @@ -32,7 +36,7 @@ export class HTTPServer extends Unit { protected server?: Server public async up() { - const port = 8000 + const port = this.config.get('server.port', 8000) // TODO register these by config PoweredByHeaderInjectionHTTPModule.register(this.kernel) @@ -68,18 +72,26 @@ export class HTTPServer extends Unit { } public get handler() { + const timeout = this.config.get('server.timeout', 10000) + return async (request: IncomingMessage, response: ServerResponse) => { const extolloReq = new Request(request, response) - // FIXME make timeout configurable - withTimeout(10000, extolloReq.response.sent$.toPromise()) + withTimeout(timeout, extolloReq.response.sent$.toPromise()) .onTime(req => { this.logging.verbose(`Request lifecycle finished on time. (Path: ${extolloReq.path})`) }) .late(req => { - this.logging.warn(`Request lifecycle finished late, so an error response was returned! (Path: ${extolloReq.path})`) + 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.'