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
31 changes: 23 additions & 8 deletions src/initializers/riviere.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,34 @@ const init = (config, orkaOptions) => {
});
const handler = {
apply: (target, thisArg, argumentsList) => {
const args = [...argumentsList];
try {
const [requestArgs = {}] = argumentsList || [];
requestArgs.headers = requestArgs.headers || {};
const traceHeaderName = config.traceHeaderName && config.traceHeaderName.toLowerCase();
const traceId = getRequestContext()?.get('requestId') || getRequestContext()?.get('correlationId');
appendHeadersFromStore(requestArgs, getRequestContext(), config);
if (!requestArgs.headers[traceHeaderName] && traceId) {
requestArgs.headers[traceHeaderName] = traceId;
// http.request supports two signatures:
// - http.request(options[, callback])
// - http.request(url[, options][, callback])
// Find the options object based on the first argument type
let optionsIndex = 0;
const firstArg = args[0];
if (typeof firstArg === 'string' || firstArg instanceof URL) {
optionsIndex = 1;
if (!args[1] || typeof args[1] === 'function') {
args.splice(1, 0, {});
}
}
const requestArgs = args[optionsIndex] || {};
if (typeof requestArgs === 'object' && requestArgs !== null) {
requestArgs.headers = requestArgs.headers || {};
const traceHeaderName = config.traceHeaderName && config.traceHeaderName.toLowerCase();
const traceId = getRequestContext()?.get('requestId') || getRequestContext()?.get('correlationId');
appendHeadersFromStore(requestArgs, getRequestContext(), config);
if (!requestArgs.headers[traceHeaderName] && traceId) {
requestArgs.headers[traceHeaderName] = traceId;
}
}
} catch (e) {
getLogger('orka.riviere').error(e);
}
return target.apply(thisArg, argumentsList);
return target.apply(thisArg, args);
}
};
http.request = new Proxy(http.request, handler);
Expand Down
68 changes: 68 additions & 0 deletions test/initializers/riviere.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as riviere from '@workablehr/riviere';
import * as log4js from 'log4js';
import * as sinon from 'sinon';
import * as https from 'https';

const sandbox = sinon.createSandbox();

Expand Down Expand Up @@ -95,4 +96,71 @@ describe('riviere', () => {
);
});
});

context('when proxying https.request', () => {
let originalRequest;
let errorLoggerStub;

const baseConfig = {
riviere: {
enabled: true,
outbound: {
enabled: true,
request: { enabled: true }
},
inbound: {
request: { enabled: true }
}
},
traceHeaderName: 'x-request-id'
};

beforeEach(() => {
originalRequest = https.request;
errorLoggerStub = sandbox.stub(log4js.getLogger('orka.riviere').constructor.prototype, 'error');
});

afterEach(() => {
(https as any).request = originalRequest;
});

it('should handle URL string as first argument without error', () => {
orkaRiviereInitializer.default(baseConfig, orkaOptions);

const req = https.request('https://example.com/api/test', { method: 'GET' }, () => { /* noop */ });
req.on('error', () => { /* expected - we're destroying the socket */ });
req.destroy();

const headerError = errorLoggerStub.args.find(
args => args[0]?.message?.includes('Cannot create property')
);
(headerError === undefined).should.be.true();
});

it('should handle URL object as first argument without error', () => {
orkaRiviereInitializer.default(baseConfig, orkaOptions);

const req = https.request(new URL('https://example.com/api/test'), { method: 'GET' }, () => { /* noop */ });
req.on('error', () => { /* expected - we're destroying the socket */ });
req.destroy();

const headerError = errorLoggerStub.args.find(
args => args[0]?.message?.includes('Cannot create property')
);
(headerError === undefined).should.be.true();
});

it('should handle options object as first argument (original behavior)', () => {
orkaRiviereInitializer.default(baseConfig, orkaOptions);

const req = https.request({ hostname: 'example.com', path: '/api/test', method: 'GET' }, () => { /* noop */ });
req.on('error', () => { /* expected - we're destroying the socket */ });
req.destroy();

const headerError = errorLoggerStub.args.find(
args => args[0]?.message?.includes('Cannot create property')
);
(headerError === undefined).should.be.true();
});
});
});