Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/IMQRequestContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*!
* IMQRequestContext implementation
*
* I'm Queue Software Project
* Copyright (C) 2025 imqueue.com <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* If you want to use this code in a closed source (commercial) project, you can
* purchase a proprietary commercial license. Please contact us at
* <[email protected]> to get commercial licensing options.
*/
import { AsyncLocalStorage } from 'node:async_hooks';
import { IMQMetadata } from './IMQMetadata';
import { IMQRPCRequest } from './IMQRPCRequest';

const storage = new AsyncLocalStorage<IMQRPCRequest>();

/**
* Runs the given function with `request` bound to the current async execution
* context, so any code reached from it (and its asynchronous continuations) can
* access the in-flight request's metadata via `currentMetadata()` without
* threading it through call signatures.
*
* The binding is scoped to the function: it is established for the duration of
* the call and automatically removed afterwards, which keeps concurrent
* requests isolated from one another.
*
* @param {IMQRPCRequest} request - the request to bind for this execution
* @param {() => T} fn - the work to run within the bound context
* @return {T}
*/
export function runWithRequest<T>(request: IMQRPCRequest, fn: () => T): T {
return storage.run(request, fn);
}

/**
* Returns the metadata of the in-flight IMQ request for the current async
* execution, if any. Returns `undefined` outside of a `runWithRequest()` scope.
* The transport carries metadata as an opaque bag; callers interpret its fields.
*
* @return {IMQMetadata | undefined}
*/
export function currentMetadata(): IMQMetadata | undefined {
return storage.getStore()?.metadata;
}
8 changes: 8 additions & 0 deletions src/IMQService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
import * as os from 'os';
import { ArgDescription } from './IMQRPCDescription';
import { IMQBeforeCall, IMQAfterCall } from './IMQRPCOptions';
import { runWithRequest } from './IMQRequestContext';
import * as http from 'node:http';

const cluster: any = require('cluster');
Expand Down Expand Up @@ -170,6 +171,13 @@ export abstract class IMQService {
private async handleRequest(
request: IMQRPCRequest,
id: string,
): Promise<string> {
return runWithRequest(request, () => this.processRequest(request, id));
}

private async processRequest(
request: IMQRPCRequest,
id: string,
): Promise<string> {
const logger = this.options.logger || console;
const method = request.method;
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ export * from './IMQLock';
export * from './IMQService';
export * from './IMQClient';
export * from './IMQMetadata';
export * from './IMQRequestContext';
Loading