Skip to content

Commit 658e78b

Browse files
authored
fix: Added defensive code in lib/subscribers/middleware-wrapper.js to prevent crash when attempting to associate an error on an incoming http request (#3697)
1 parent 0051697 commit 658e78b

File tree

2 files changed

+94
-22
lines changed

2 files changed

+94
-22
lines changed

lib/subscribers/middleware-wrapper.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ class MiddlewareWrapper {
161161
errorWare = true
162162
route = null
163163
}
164-
const txInfo = request?.raw?.[transactionInfo] || request?.[transactionInfo]
164+
165+
// fallback to empty object, this is purely defensive code and will not correlate
166+
// the error with the http request
167+
// see: https://github.com/newrelic/node-newrelic/issues/3696
168+
const txInfo = request?.raw?.[transactionInfo] || request?.[transactionInfo] || {}
165169
if (errorWare) {
166170
txInfo.errorHandled = errorWare
167171
}

test/unit/lib/subscribers/middleare-wrapper.test.js

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,28 @@ const test = require('node:test')
99
const loggerMock = require('../../mocks/logger')
1010
const MwWrapper = require('#agentlib/subscribers/middleware-wrapper.js')
1111
const helper = require('#testlib/agent_helper.js')
12+
const { transactionInfo } = require('#agentlib/symbols.js')
1213

1314
test.beforeEach((ctx) => {
1415
const agent = helper.loadMockedAgent()
1516
const logger = loggerMock()
1617
const wrapper = new MwWrapper({ agent, logger, system: 'UnitTest' })
1718
function handler (arg1, arg2, arg3) {
18-
return `${arg1}, ${arg2}, ${arg3}`
19-
}
20-
const txInfo = {}
21-
wrapper.extractTxInfo = function() {
22-
const request = {}
23-
return { txInfo, request, errorWare: false }
19+
return `${arg2}, ${arg3}`
2420
}
21+
const origExtractTxInfo = wrapper.extractTxInfo
2522
ctx.nr = {
2623
agent,
2724
handler,
2825
logger,
29-
txInfo,
3026
wrapper
3127
}
28+
29+
wrapper.extractTxInfo = function() {
30+
const data = origExtractTxInfo.apply(this, arguments)
31+
ctx.nr.txInfo = data.txInfo
32+
return data
33+
}
3234
})
3335

3436
test.afterEach((ctx) => {
@@ -53,19 +55,20 @@ test('should wrap handler if it is a function', function (t) {
5355
test('should not run wrapped handler in context if transaction not present', function (t) {
5456
const { handler, wrapper } = t.nr
5557
const wrapped = wrapper.wrap({ handler })
56-
const result = wrapped('one', 'two', 'three')
57-
assert.equal(result, 'one, two, three')
58+
const result = wrapped({}, 'one', 'two')
59+
assert.equal(result, 'one, two')
5860
})
5961

6062
test('should run wrapped handler in context if transaction present, and properly name segment', function (t, end) {
6163
const { agent, handler, wrapper } = t.nr
6264
const route = '/test/url'
6365
const wrapped = wrapper.wrap({ handler, route })
66+
const request = {}
6467
helper.runInTransaction(agent, function (tx) {
6568
tx.type = 'web'
6669
tx.url = route
67-
const result = wrapped('one', 'two', 'three')
68-
assert.equal(result, 'one, two, three')
70+
const result = wrapped(request, 'one', 'two')
71+
assert.equal(result, 'one, two')
6972

7073
const [segment] = tx.trace.getChildren(tx.trace.root.id)
7174
assert.equal(segment.name, 'Nodejs/Middleware/UnitTest/handler//test/url')
@@ -77,59 +80,124 @@ test('should run wrapped handler in context if transaction present, and properly
7780
})
7881

7982
test('should handle error when passed in to done handler', function (t, end) {
80-
const { agent, txInfo, wrapper } = t.nr
83+
const { agent, wrapper } = t.nr
8184
const error = new Error('test error')
8285
function handler (req, res, next) {
8386
next(error)
8487
}
8588
const route = '/test/url'
8689
const wrapped = wrapper.wrap({ handler, route })
90+
const request = {}
8791
helper.runInTransaction(agent, function (tx) {
8892
tx.type = 'web'
8993
tx.url = route
90-
wrapped('one', 'two', function() {})
91-
assert.deepEqual(txInfo.error, error)
92-
assert.equal(txInfo.errorHandled, false)
94+
wrapped(request, 'one', function() {})
95+
assert.deepEqual(t.nr.txInfo.error, error)
96+
assert.equal(t.nr.txInfo.errorHandled, false)
97+
98+
tx.end()
99+
end()
100+
})
101+
})
93102

103+
test('should handle error when passed in to done handler `request.raw`', function (t, end) {
104+
const { agent, wrapper } = t.nr
105+
const error = new Error('test error')
106+
function handler (req, res, next) {
107+
next(error)
108+
}
109+
const route = '/test/url'
110+
const wrapped = wrapper.wrap({ handler, route })
111+
const request = { raw: { [transactionInfo]: {} } }
112+
helper.runInTransaction(agent, function (tx) {
113+
tx.type = 'web'
114+
tx.url = route
115+
wrapped(request, 'one', function() {})
116+
assert.deepEqual(t.nr.txInfo.error, error)
117+
assert.equal(t.nr.txInfo.errorHandled, false)
118+
assert.deepEqual(request.raw[transactionInfo], t.nr.txInfo)
119+
tx.end()
120+
end()
121+
})
122+
})
123+
124+
test('should handle error when passed in to done handler `request`', function (t, end) {
125+
const { agent, wrapper } = t.nr
126+
const error = new Error('test error')
127+
function handler (req, res, next) {
128+
next(error)
129+
}
130+
const route = '/test/url'
131+
const wrapped = wrapper.wrap({ handler, route })
132+
const request = { [transactionInfo]: {} }
133+
helper.runInTransaction(agent, function (tx) {
134+
tx.type = 'web'
135+
tx.url = route
136+
wrapped(request, 'one', function() {})
137+
assert.deepEqual(t.nr.txInfo.error, error)
138+
assert.equal(t.nr.txInfo.errorHandled, false)
139+
assert.deepEqual(request[transactionInfo], t.nr.txInfo)
140+
tx.end()
141+
end()
142+
})
143+
})
144+
145+
test('should handle error when passed in to done handler but mark error handled when error ware', function (t, end) {
146+
const { agent, wrapper } = t.nr
147+
const error = new Error('test error')
148+
function handler (err, req, res, next) {
149+
next(err)
150+
}
151+
const route = '/test/url'
152+
const wrapped = wrapper.wrap({ handler, route })
153+
const request = {}
154+
helper.runInTransaction(agent, function (tx) {
155+
tx.type = 'web'
156+
tx.url = route
157+
wrapped(error, request, 'one', function() {})
158+
assert.deepEqual(t.nr.txInfo.error, error)
159+
assert.equal(t.nr.txInfo.errorHandled, true)
94160
tx.end()
95161
end()
96162
})
97163
})
98164

99165
test('should not handle error when no error is passed to done handler', function (t, end) {
100-
const { agent, txInfo, wrapper } = t.nr
166+
const { agent, wrapper } = t.nr
101167
function handler (req, res, next) {
102168
next()
103169
}
104170
const route = '/test/url'
171+
const request = {}
105172
const wrapped = wrapper.wrap({ handler, route })
106173
helper.runInTransaction(agent, function (tx) {
107174
tx.type = 'web'
108175
tx.url = route
109-
wrapped('one', 'two', function() {})
110-
assert.deepEqual(txInfo, {})
176+
wrapped(request, 'one', function() {})
177+
assert.deepEqual(t.nr.txInfo, {})
111178

112179
tx.end()
113180
end()
114181
})
115182
})
116183

117184
test('should not handle error when isError is not using default handler', function (t, end) {
118-
const { agent, txInfo, wrapper } = t.nr
185+
const { agent, wrapper } = t.nr
119186
const error = new Error('test error')
120187
function handler (req, res, next) {
121188
next(error)
122189
}
123190
const route = '/test/url'
191+
const request = {}
124192
const wrapped = wrapper.wrap({ handler, route })
125193
wrapper.isError = function(err) {
126194
return err.message !== 'test error'
127195
}
128196
helper.runInTransaction(agent, function (tx) {
129197
tx.type = 'web'
130198
tx.url = route
131-
wrapped('one', 'two', function() {})
132-
assert.deepEqual(txInfo, {})
199+
wrapped(request, 'one', function() {})
200+
assert.deepEqual(t.nr.txInfo, {})
133201
tx.end()
134202
end()
135203
})

0 commit comments

Comments
 (0)