Add support for request timeouts; timeout helper

This commit is contained in:
garrettmills 2020-08-01 13:59:38 -05:00
parent 48ead77765
commit 3991c9b4bb
No known key found for this signature in database
GPG Key ID: 6ACD58D6ADACFC6E
4 changed files with 72 additions and 5 deletions

View File

@ -22,6 +22,4 @@ unit tests
integration tests integration tests
documentation & docs site documentation & docs site
events and observables? events and observables?
no matching route handler error response handler figure out json - requested content type
request timeout
error response handler figure out json

View File

@ -4,6 +4,8 @@ export default {
prefix: '/', prefix: '/',
allow_mount_without_prefix: false, allow_mount_without_prefix: false,
request_timeout: 15000, // 15 seconds
powered_by: { powered_by: {
enable: true, enable: true,
text: 'Daton', text: 'Daton',

View File

@ -0,0 +1,48 @@
export interface TimeoutSubscriber<T> {
on_time: (handler: (arg: T) => any) => TimeoutSubscriber<T>,
late: (handler: (arg: T) => any) => TimeoutSubscriber<T>,
timeout: (handler: () => any) => TimeoutSubscriber<T>,
run: () => Promise<T>,
}
export function withTimeout<T>(timeout: number, promise: Promise<T>) {
let on_time_handler: (arg: T) => any = (arg) => {}
let late_handler: (arg: T) => any = (arg) => {}
let timeout_handler: () => any = () => {}
const sub = {
on_time: handler => {
on_time_handler = handler
return sub
},
late: handler => {
late_handler = handler
return sub
},
timeout: handler => {
timeout_handler = handler
return sub
},
run: async () => {
let expired = false
let resolved = false
setTimeout(() => {
expired = true
if ( !resolved ) timeout_handler()
}, timeout)
const result: T = await promise
resolved = true
if ( !expired ) {
await on_time_handler(result)
} else {
await late_handler(result)
}
return result
}
} as TimeoutSubscriber<T>
return sub
}

View File

@ -4,6 +4,10 @@ import Kernel from '../http/kernel/Kernel.ts'
import {Logging} from '../service/logging/Logging.ts' import {Logging} from '../service/logging/Logging.ts'
import {serve} from '../external/http.ts' import {serve} from '../external/http.ts'
import {Request} from '../http/Request.ts' import {Request} from '../http/Request.ts'
import {withTimeout} from '../support/timeout.ts'
import {http} from '../http/response/helpers.ts'
import {HTTPStatus} from '../const/http.ts'
import Config from './Config.ts'
@Unit() @Unit()
export default class HttpServer extends LifecycleUnit { export default class HttpServer extends LifecycleUnit {
@ -11,6 +15,7 @@ export default class HttpServer extends LifecycleUnit {
constructor( constructor(
protected readonly kernel: Kernel, protected readonly kernel: Kernel,
protected readonly config: Config,
protected readonly logger: Logging, protected readonly logger: Logging,
) { ) {
super() super()
@ -20,10 +25,24 @@ export default class HttpServer extends LifecycleUnit {
this._server = serve({ port: 8000 }) this._server = serve({ port: 8000 })
this.logger.success(`HTTP/S server listening on port 8000!`) this.logger.success(`HTTP/S server listening on port 8000!`)
const request_timeout: number = this.config.get('server.request_timeout', 15000)
for await ( const native_request of this._server ) { for await ( const native_request of this._server ) {
let req: Request = this.make(Request, native_request) let req: Request = this.make(Request, native_request)
req = await this.kernel.handle(req)
req.response.send() await withTimeout(request_timeout, this.kernel.handle(req))
.on_time(output_req => {
output_req.response.send()
})
.late(output_req => {
this.logger.error(`Request timed out. Response was already sent on path: ${req.path}. Consider increasing server.request_timeout.`)
})
.timeout(async () => {
const factory = http(HTTPStatus.REQUEST_TIMEOUT)
const output_req = await factory.write(req)
output_req.response.send()
})
.run()
} }
} }
} }