Add support for request timeouts; timeout helper
This commit is contained in:
parent
48ead77765
commit
3991c9b4bb
4
TODO.txt
4
TODO.txt
@ -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
|
|
||||||
|
@ -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',
|
||||||
|
48
lib/src/support/timeout.ts
Normal file
48
lib/src/support/timeout.ts
Normal 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
|
||||||
|
}
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user