UNPKG

@workablehr/riviere

Version:

log inbound/outbound HTTP traffic

891 lines (875 loc) 23.9 kB
const defaultAdapter = require('../../../../lib/adapters/default'); const sinon = require('sinon'); const sandbox = sinon.createSandbox(); describe('#defaultAdapter', () => { let clock; before(() => { clock = sinon.useFakeTimers(new Date(2011, 9, 1).getTime()); }); after(() => { clock.restore(); }); describe('.requestProxy()', () => { it('should pass', () => { defaultAdapter.requestProxy(); }); it('should pass when incomingMessage.url exists', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { request: { enabled: true }, headersRegex: new RegExp('^X-.*', 'i'), hostFieldName: 'myHost' } }); const options = { method: 'GET', port: '8080', headers: { test: 'ok' }, protocol: 'http:', path: '/some', pathname: '/some', query: 'some=something', hostname: 'some-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; const httpRequest = () => { return { on: (on, cb) => { const res = { headers: { 'x-foo': 'foo' } }; cb(res); logger.info.callCount.should.equal(2); logger.info.args[0][0].should.eql({ method: 'GET', protocol: 'http', port: '8080', path: '/some', query: 'some=something', requestId: 'ok', href: 'http://some-host:8080/some', myHost: 'some-host', metaBody: {}, metaHeaders: {}, contentLength: 0, log_tag: 'outbound_request' }); logger.info.args[1][0].should.eql({ method: 'GET', path: '/some', myHost: 'some-host', duration: 0, query: 'some=something', href: 'http://some-host:8080/some', status: undefined, protocol: 'http', requestId: 'ok', contentLength: 0, userAgent: '', log_tag: 'inbound_response', metaHeaders: { request: {}, headers: { 'x-foo': 'foo' } } }); } }; }; http.request = new Proxy(httpRequest, requestProxy); http.request(options); logger.info.callCount.should.eql(2); }); it('should pass when no protocol', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { request: { enabled: true }, hostFieldName: 'myHost' } }); const options = { method: 'GET', port: '8080', headers: { test: 'ok' }, protocol: undefined, path: '/some', pathname: '/some', query: 'some=something', host: 'some-host' }; const http = { request: function() { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; const httpRequest = function() { arguments[0].should.eql({ method: 'GET', port: '8080', headers: { test: 'ok' }, protocol: undefined, path: '/some', pathname: '/some', query: 'some=something', host: 'some-host' }); return { on: () => { throw new Error('should not be called'); } }; }; http.request = new Proxy(httpRequest, requestProxy); http.request(options); logger.info.callCount.should.eql(0); }); it('should pass but not log when path is in blacklisted paths', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { blacklistedPathRegex: new RegExp('^/v4.0/traces$'), request: { enabled: true }, hostFieldName: 'myHost' } }); const options = { method: 'PUT', port: '8080', headers: { test: 'ok' }, protocol: 'https:', path: '/v4.0/traces', pathname: '/v4.0/traces', hostname: 'test-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(0); }); it('should pass when incomingMessage.url does not exist', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { request: { enabled: true }, hostFieldName: 'myHost' } }); const options = { method: 'GET', port: '8080', headers: { test: 'ok' }, protocol: 'https:', path: '/some?somequery=query', pathname: '/some?somequery=query', hostname: 'test-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(2); logger.info.args[0][0].should.eql({ method: 'GET', port: '8080', path: '/some?somequery=query', query: undefined, requestId: 'ok', myHost: 'test-host', protocol: 'https', href: 'https://test-host:8080/some%3Fsomequery=query', metaBody: {}, metaHeaders: {}, contentLength: 0, log_tag: 'outbound_request' }); logger.info.args[1][0].should.eql({ method: 'GET', path: '/some?somequery=query', status: 200, myHost: 'test-host', protocol: 'https', href: 'https://test-host:8080/some%3Fsomequery=query', duration: 0, query: undefined, requestId: 'ok', contentLength: 0, userAgent: '', log_tag: 'inbound_response', metaHeaders: { request: {} } }); }); it('should pass when requestId is not given', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { headersRegex: new RegExp('test', 'i'), request: { enabled: true }, hostFieldName: 'myHost' } }); const options = { method: 'GET', port: '8080', headers: {}, protocol: 'https:', path: '/some?somequery=query', pathname: '/some?somequery=query', hostname: 'test-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200, headers: {} }); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(2); logger.info.args[0][0].metaHeaders.headers.should.have.property('test'); logger.info.args[1][0].metaHeaders.request.headers.should.have.property('test'); logger.info.args[0][0].should.containEql({ method: 'GET', protocol: 'https', myHost: 'test-host', port: '8080', path: '/some?somequery=query', query: undefined, href: 'https://test-host:8080/some%3Fsomequery=query', metaBody: {}, log_tag: 'outbound_request' }); logger.info.args[1][0].should.containEql({ method: 'GET', myHost: 'test-host', path: '/some?somequery=query', status: 200, duration: 0, query: undefined, protocol: 'https', log_tag: 'inbound_response' }); }); it('should pass when requestId does not exist', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { request: { enabled: true } } }); const options = { method: 'GET', port: '8080', headers: { [traceHeaderName]: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2' }, protocol: 'https:', path: '/some?somequery=query', pathname: '/some?somequery=query', hostname: 'test-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(2); logger.info.args[0][0].should.eql({ method: 'GET', protocol: 'https', host: 'test-host', port: '8080', path: '/some?somequery=query', query: undefined, href: 'https://test-host:8080/some%3Fsomequery=query', requestId: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', metaBody: {}, metaHeaders: {}, contentLength: 0, log_tag: 'outbound_request' }); logger.info.args[1][0].should.eql({ method: 'GET', host: 'test-host', path: '/some?somequery=query', status: 200, duration: 0, query: undefined, href: 'https://test-host:8080/some%3Fsomequery=query', protocol: 'https', requestId: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', contentLength: 0, userAgent: '', log_tag: 'inbound_response', metaHeaders: { request: {} } }); }); it('should pass when requestId key is given', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, traceHeaderName, serialize, level: 'info', opts: { request: { enabled: true }, hostFieldName: 'myHost' } }); const options = { method: 'GET', port: '8080', headers: { [traceHeaderName]: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2' }, protocol: 'https:', path: '/some?somequery=query', pathname: '/some?somequery=query', host: 'test-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(2); logger.info.args[0][0].should.eql({ method: 'GET', protocol: 'https', myHost: 'test-host', port: '8080', path: '/some?somequery=query', query: undefined, href: 'https://test-host/some%3Fsomequery=query', requestId: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', metaBody: {}, metaHeaders: {}, contentLength: 0, log_tag: 'outbound_request' }); logger.info.args[1][0].should.eql({ method: 'GET', path: '/some?somequery=query', status: 200, duration: 0, href: 'https://test-host/some%3Fsomequery=query', myHost: 'test-host', query: undefined, protocol: 'https', requestId: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', contentLength: 0, userAgent: '', log_tag: 'inbound_response', metaHeaders: { request: {} } }); }); it('should pass if the proxy throws an error before proxying', () => { const logger = { info: sandbox.spy(), error: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, opts: { request: { enabled: true }, hostFieldName: 'myHost' } }); const options = null; const http = { request: () => { return { on: () => { throw new Error('should not throw'); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(0); }); it('should pass if the proxy throws an error after proxying', () => { const logger = { info: sandbox.spy(), error: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { request: { enabled: true }, hostFieldName: 'myHost' } }); const options = { method: 'GET', port: '8080', headers: { test: 'ok' }, protocol: 'http:', path: '/some', pathname: '/some', query: 'some=something', hostname: 'some-host' }; const http = { request: () => { return { on: (event, fn) => { fn(null); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(1); logger.info.args[0][0].should.eql({ method: 'GET', protocol: 'http', myHost: 'some-host', href: 'http://some-host:8080/some', port: '8080', path: '/some', query: 'some=something', requestId: 'ok', metaBody: {}, metaHeaders: {}, contentLength: 0, log_tag: 'outbound_request' }); }); it('should not throw if the request has no headers', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { request: { enabled: true }, hostFieldName: 'myHost' } }); const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; const options = { method: 'GET', port: '8080', protocol: 'http:', path: '/some', pathname: '/some', query: 'some=something', host: 'some-host' }; const httpRequest = () => { return { on: (on, cb) => { const res = {}; cb(res); logger.info.callCount.should.equal(2); logger.info.args[0][0].should.containEql({ method: 'GET', protocol: 'http', port: '8080', path: '/some', query: 'some=something', href: 'http://some-host/some', myHost: 'some-host', metaBody: {}, log_tag: 'outbound_request' }); logger.info.args[1][0].should.containEql({ method: 'GET', path: '/some', myHost: 'some-host', duration: 0, query: 'some=something', status: undefined, protocol: 'http', log_tag: 'inbound_response' }); } }; }; http.request = new Proxy(httpRequest, requestProxy); http.request(options); }); it('should pass and log only outbound if request.enabled=false', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, serialize, traceHeaderName, level: 'info', opts: { request: { enabled: false }, hostFieldName: 'myHost' } }); const options = { method: 'GET', port: '8080', headers: { test: 'ok' }, protocol: 'http:', path: '/some', pathname: '/some', query: 'some=something', hostname: 'some-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; const httpRequest = () => { return { on: (on, cb) => { const res = {}; cb(res); logger.info.callCount.should.equal(1); logger.info.args[0][0].should.eql({ method: 'GET', path: '/some', myHost: 'some-host', duration: 0, query: 'some=something', href: 'http://some-host:8080/some', status: undefined, protocol: 'http', requestId: 'ok', contentLength: 0, userAgent: '', log_tag: 'inbound_response', metaHeaders: { request: {} } }); } }; }; http.request = new Proxy(httpRequest, requestProxy); http.request(options); logger.info.callCount.should.eql(1); }); context('when request contains the header from obfuscateHrefIfHeaderExists value', () => { it('should pass and obfuscate URL path when request is enabled', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, traceHeaderName, serialize, level: 'info', opts: { request: { enabled: true }, hostFieldName: 'myHost', obfuscateHrefIfHeaderExists: 'X-Riviere-obfuscate' } }); const options = { method: 'GET', port: '8080', headers: { [traceHeaderName]: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', 'X-Riviere-obfuscate': true }, protocol: 'https:', path: '/some?somequery=query', pathname: '/some?somequery=query', host: 'test-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200 }); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(2); logger.info.args[0][0].should.eql({ method: 'GET', protocol: 'https', myHost: 'test-host', port: '8080', path: '/***', query: undefined, href: 'https://test-host/***', requestId: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', metaBody: {}, metaHeaders: {}, contentLength: 0, log_tag: 'outbound_request' }); logger.info.args[1][0].should.eql({ method: 'GET', path: '/***', status: 200, duration: 0, href: 'https://test-host/***', myHost: 'test-host', query: undefined, protocol: 'https', requestId: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', contentLength: 0, userAgent: '', log_tag: 'inbound_response', metaHeaders: { request: {} } }); }); it('should pass and obfuscate URL path from response when request is disabled', () => { const logger = { info: sandbox.spy() }; const serialize = a => a; const traceHeaderName = 'test'; const requestProxy = defaultAdapter.requestProxy({ logger, traceHeaderName, serialize, level: 'info', opts: { request: { enabled: false }, hostFieldName: 'myHost', obfuscateHrefIfHeaderExists: 'X-Riviere-obfuscate', headersRegex: new RegExp('^X-.*$') } }); const options = { method: 'GET', port: '8080', headers: { [traceHeaderName]: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', 'X-Riviere-obfuscate': true }, protocol: 'https:', path: '/some?somequery=query', pathname: '/some?somequery=query', host: 'test-host' }; const http = { request: () => { return { on: (event, fn) => { fn({ statusCode: 200, headers: [] }); } }; } }; http.request = new Proxy(http.request, requestProxy); http.request(options); logger.info.callCount.should.eql(1); logger.info.args[0][0].should.eql({ method: 'GET', path: '/***', status: 200, duration: 0, href: 'https://test-host/***', myHost: 'test-host', query: undefined, protocol: 'https', requestId: 'cff07fc2-4ef6-42b6-9a74-ba3abf8b31a2', contentLength: 0, userAgent: '', log_tag: 'inbound_response', metaHeaders: { request: { headers: { 'X-Riviere-obfuscate': true } } } }); }); }); }); });