diff --git a/README.md b/README.md index 42e0a5c..6c51471 100644 --- a/README.md +++ b/README.md @@ -155,12 +155,15 @@ interceptor.close(); It's possible to set some simple **synchronous** functions as hooks: -- `onClientRequest(req)` -- `onClientResponse(req, res)` -- `onClientError(req, res, error)` -- `onServerRequest(req)` +- `onServerRequest(req) - `onServerResponse(req, res)` - `onServerError(req, res, error)` +- `onClientRequest(req, clientCtx)` +- `onClientResponse(req, res, clientCtx)` +- `onClientError(req, res, clientCtx, error)` + +The `clientCtx` is used to pass through hooks calls objects which cannot be set on request +(which is then sent through `postMessage`, so it might be not serializable) #### Client hooks diff --git a/index.js b/index.js index 5bf71bb..8222c00 100644 --- a/index.js +++ b/index.js @@ -61,7 +61,9 @@ function createThreadInterceptor (opts) { delete newOpts.dispatcher - hooks.fireOnClientRequest(newOpts) + // We use it as client context where hooks can add non-serializable properties + const clientCtx = {} + hooks.fireOnClientRequest(newOpts, clientCtx) if (newOpts.body?.[Symbol.asyncIterator]) { collectBodyAndDispatch(newOpts, handler).then(() => { @@ -89,11 +91,11 @@ function createThreadInterceptor (opts) { clearTimeout(handle) if (err) { - hooks.fireOnClientError(newOpts, res, err) + hooks.fireOnClientError(newOpts, res, clientCtx, err) handler.onError(err) return } - hooks.fireOnClientResponse(newOpts, res) + hooks.fireOnClientResponse(newOpts, res, clientCtx) const headers = [] for (const [key, value] of Object.entries(res.headers)) { diff --git a/lib/hooks.js b/lib/hooks.js index 59fb85c..8195754 100644 --- a/lib/hooks.js +++ b/lib/hooks.js @@ -6,7 +6,8 @@ const supportedHooks = [ 'onServerError', 'onClientRequest', 'onClientResponse', - 'onClientError'] + 'onClientError' +] class Hooks { onServerRequest = [] diff --git a/test/hooks.test.js b/test/hooks.test.js index c79d462..c5fa032 100644 --- a/test/hooks.test.js +++ b/test/hooks.test.js @@ -175,7 +175,7 @@ test('hooks - onClientError', async (t) => { const interceptor = createThreadInterceptor({ domain: '.local', - onClientError: (_req, _rep, error) => { + onClientError: (_req, _rep, _ctx, error) => { hookCalled = error } }) @@ -198,11 +198,11 @@ test('hooks - multiple onClientErrors', async (t) => { t.after(() => worker.terminate()) const hookCalled = [] - const onClientError1 = (_req, _rep, error) => { + const onClientError1 = (_req, _rep, _ctx, error) => { hookCalled.push({ error1: error.message }) } - const onClientError2 = (_req, _rep, error) => { + const onClientError2 = (_req, _rep, _ctx, error) => { hookCalled.push({ error2: error.message }) } @@ -347,3 +347,29 @@ test('hooks - request propagation between onClientRequest and onClientResponse', strictEqual(statusCode, 200) deepStrictEqual(hookCalledClient, 'propagated') }) + +test('hooks - context propagation between onClientRequest and onClientResponse', async (t) => { + const worker = new Worker(join(__dirname, 'fixtures', 'worker1.js')) + t.after(() => worker.terminate()) + let hookCalledClient + + const interceptor = createThreadInterceptor({ + domain: '.local', + onClientRequest: (_req, ctx) => { + ctx.data = 'propagated' + }, + onClientResponse: (req, _res, ctx) => { + hookCalledClient = ctx.data + } + }) + interceptor.route('myserver', worker) + + const agent = new Agent().compose(interceptor) + + const { statusCode } = await request('http://myserver.local', { + dispatcher: agent, + }) + + strictEqual(statusCode, 200) + deepStrictEqual(hookCalledClient, 'propagated') +})