Skip to content

Commit 95d706e

Browse files
committed
feat: add synchronous hooks to HTTP server
synchronous hooks can be used to decorate the HTTP context and bind values into the container resolver specific for that given request
1 parent 0d5f667 commit 95d706e

File tree

3 files changed

+67
-28
lines changed

3 files changed

+67
-28
lines changed

benchmarks/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,4 @@ async function fastifyRun() {
7272
console.log('Completed')
7373
}
7474

75-
fastifyRun().then(coolOff).then(adonisRun)
75+
adonisRun().then(coolOff).then(fastifyRun)

src/server/main.ts

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ import { middlewareHandler } from './factories/middleware_handler.js'
3535
* registered routes.
3636
*/
3737
export class Server {
38+
/**
39+
* Collection of synchronous request hooks
40+
*/
41+
#requestHooks: Set<(ctx: HttpContext) => void> = new Set()
42+
3843
/**
3944
* Registered error handler (if any)
4045
*/
@@ -131,21 +136,15 @@ export class Server {
131136
}
132137

133138
/**
134-
* Creates an instance of the HTTP context for the current
135-
* request
139+
* Handles the HTTP request
136140
*/
137-
#createContext(req: IncomingMessage, res: ServerResponse, resolver: ContainerResolver<any>) {
138-
const request = new Request(req, res, this.#encryption, this.#config, this.#qsParser)
139-
const response = new Response(
140-
req,
141-
res,
142-
this.#encryption,
143-
this.#config,
144-
this.#router,
145-
this.#qsParser
146-
)
147-
148-
return new HttpContext(request, response, this.#app.logger.child({}), resolver)
141+
#handleRequest(ctx: HttpContext, resolver: ContainerResolver<any>) {
142+
return this.#serverMiddlewareStack!.runner()
143+
.finalHandler(finalHandler(this.#router!, resolver, ctx))
144+
.run(middlewareHandler(resolver, ctx))
145+
.then(useReturnValue(ctx))
146+
.catch((error) => this.#resolvedErrorHandler.handle(error, ctx))
147+
.finally(writeResponse(ctx))
149148
}
150149

151150
/**
@@ -202,18 +201,6 @@ export class Server {
202201
}
203202
}
204203

205-
/**
206-
* Handles the HTTP request
207-
*/
208-
#handleRequest(ctx: HttpContext, resolver: ContainerResolver<any>) {
209-
return this.#serverMiddlewareStack!.runner()
210-
.finalHandler(finalHandler(this.#router!, resolver, ctx))
211-
.run(middlewareHandler(resolver, ctx))
212-
.then(useReturnValue(ctx))
213-
.catch((error) => this.#resolvedErrorHandler.handle(error, ctx))
214-
.finally(writeResponse(ctx))
215-
}
216-
217204
/**
218205
* Set the HTTP server instance used to listen for requests.
219206
*/
@@ -237,12 +224,39 @@ export class Server {
237224
return this.#router
238225
}
239226

227+
/**
228+
* Synchronous hooks to get notified everytime a new HTTP request comes
229+
*/
230+
onRequest(callback: (ctx: HttpContext) => void) {
231+
this.#requestHooks.add(callback)
232+
return this
233+
}
234+
240235
/**
241236
* Handle request
242237
*/
243238
handle(req: IncomingMessage, res: ServerResponse) {
239+
/**
240+
* Creating essential instances
241+
*/
244242
const resolver = this.#app.container.createResolver()
245-
const ctx = this.#createContext(req, res, resolver)
243+
const request = new Request(req, res, this.#encryption, this.#config, this.#qsParser)
244+
const response = new Response(
245+
req,
246+
res,
247+
this.#encryption,
248+
this.#config,
249+
this.#router,
250+
this.#qsParser
251+
)
252+
const ctx = new HttpContext(request, response, this.#app.logger.child({}), resolver)
253+
254+
/**
255+
* Invoking synchronous hooks
256+
*/
257+
for (let hook of this.#requestHooks) {
258+
hook(ctx)
259+
}
246260

247261
if (this.usingAsyncLocalStorage) {
248262
return asyncLocalStorage.storage!.run(ctx, () => this.#handleRequest(ctx, resolver))

tests/server.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,3 +846,28 @@ test.group('Server | force content negotiation', () => {
846846
})
847847
})
848848
})
849+
850+
test.group('Server | synchronous hooks', () => {
851+
test('register synchronous hooks', async ({ assert }) => {
852+
assert.plan(1)
853+
854+
let stack: any[] = []
855+
856+
const app = new AppFactory().create()
857+
await app.init()
858+
859+
const server = new ServerFactory().merge({ app }).create()
860+
const httpServer = createServer(server.handle.bind(server))
861+
862+
server.onRequest((ctx) => {
863+
stack.push(ctx)
864+
})
865+
866+
server.getRouter().get('/', async (ctx) => {
867+
assert.deepEqual(stack[0], ctx)
868+
})
869+
await server.boot()
870+
871+
await supertest(httpServer).get('/').expect(200)
872+
})
873+
})

0 commit comments

Comments
 (0)