Comparing version 11.7.0 to 11.8.0
@@ -141,2 +141,4 @@ /* | ||
opts.headers = opts.headers || {} | ||
synthetics.assignHeadersToOutgoingRequest(agent.config, transaction, outboundHeaders) | ||
@@ -143,0 +145,0 @@ maybeAddDtCatHeaders(agent, transaction, outboundHeaders, opts?.headers) |
@@ -8,4 +8,2 @@ /* | ||
/* eslint sonarjs/cognitive-complexity: ["error", 42] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */ | ||
const shimmer = require('../../shimmer') | ||
@@ -39,4 +37,2 @@ const logger = require('../../logger').child({ component: 'http' }) | ||
const transport = isHTTPS ? 'HTTPS' : 'HTTP' | ||
let serverPort = null | ||
return tracer.transactionProxy(function wrappedHandler(evnt, request, response) { | ||
@@ -96,11 +92,3 @@ const transaction = tracer.getTransaction() | ||
// store the port on which this transaction runs | ||
if (this.address instanceof Function) { | ||
const address = this.address() | ||
if (address) { | ||
serverPort = address.port | ||
} | ||
} | ||
transaction.port = serverPort | ||
transaction.port = parsePort(this) | ||
// need to set any config-driven names early for RUM | ||
@@ -120,64 +108,98 @@ logger.trace( | ||
if (agent.config.distributed_tracing.enabled) { | ||
// Node http headers are automatically lowercase | ||
transaction.acceptDistributedTraceHeaders(transport, request.headers) | ||
} else if (agent.config.cross_application_tracer.enabled) { | ||
const { id, transactionId } = cat.extractCatHeaders(request.headers) | ||
const { externalId, externalTransaction } = cat.parseCatData( | ||
id, | ||
transactionId, | ||
agent.config.encoding_key | ||
) | ||
cat.assignCatToTransaction(externalId, externalTransaction, transaction) | ||
maybeAddDtCatHeaders({ transaction, request, transport, agent }) | ||
response.once('finish', instrumentedFinish.bind(response, segment, transaction)) | ||
response.once('close', instrumentedFinish.bind(response, segment, transaction)) | ||
return tracer.bindFunction(emit, segment).apply(this, arguments) | ||
}) | ||
} | ||
/** | ||
* Gets the port from the Server object | ||
* | ||
* @param {object} server http(s) server | ||
* @returns {number|null} parsed port | ||
*/ | ||
function parsePort(server) { | ||
let serverPort = null | ||
// store the port on which this transaction runs | ||
if (server.address instanceof Function) { | ||
const address = server.address() | ||
if (address) { | ||
serverPort = address.port | ||
} | ||
} | ||
return serverPort | ||
} | ||
function instrumentedFinish() { | ||
// Remove listeners so this doesn't get called twice. | ||
response.removeListener('finish', instrumentedFinish) | ||
response.removeListener('close', instrumentedFinish) | ||
function maybeAddDtCatHeaders({ agent, request, transaction, transport }) { | ||
if (agent.config.distributed_tracing.enabled) { | ||
// Node http headers are automatically lowercase | ||
transaction.acceptDistributedTraceHeaders(transport, request.headers) | ||
} else if (agent.config.cross_application_tracer.enabled) { | ||
const { id, transactionId } = cat.extractCatHeaders(request.headers) | ||
const { externalId, externalTransaction } = cat.parseCatData( | ||
id, | ||
transactionId, | ||
agent.config.encoding_key | ||
) | ||
cat.assignCatToTransaction(externalId, externalTransaction, transaction) | ||
} | ||
} | ||
// Naming must happen before the segment and transaction are ended, | ||
// because metrics recording depends on naming's side effects. | ||
transaction.finalizeNameFromUri(transaction.parsedUrl, response.statusCode) | ||
/** | ||
* Adds instrumentation to response on finish/close. | ||
* It will add `http.statusCode`, `http.statusText` | ||
* to the transaction trace and span. | ||
* It will also assign the response headers to the transaction | ||
* | ||
* @param {TraceSegment} segment active segment | ||
* @param {Transaction} transaction active transaction | ||
*/ | ||
function instrumentedFinish(segment, transaction) { | ||
// Remove listeners so this doesn't get called twice. | ||
this.removeListener('finish', instrumentedFinish) | ||
this.removeListener('close', instrumentedFinish) | ||
if (response) { | ||
if (response.statusCode != null) { | ||
const responseCode = String(response.statusCode) | ||
// Naming must happen before the segment and transaction are ended, | ||
// because metrics recording depends on naming's side effects. | ||
transaction.finalizeNameFromUri(transaction.parsedUrl, this.statusCode) | ||
if (/^\d+$/.test(responseCode)) { | ||
transaction.trace.attributes.addAttribute( | ||
DESTS.TRANS_COMMON, | ||
'http.statusCode', | ||
responseCode | ||
) | ||
if (this) { | ||
const { statusCode, statusMessage } = this | ||
segment.addSpanAttribute('http.statusCode', responseCode) | ||
} | ||
} | ||
if (statusCode != null) { | ||
const responseCode = String(statusCode) | ||
if (response.statusMessage !== undefined) { | ||
transaction.trace.attributes.addAttribute( | ||
DESTS.TRANS_COMMON, | ||
'http.statusText', | ||
response.statusMessage | ||
) | ||
if (/^\d+$/.test(responseCode)) { | ||
transaction.trace.attributes.addAttribute( | ||
DESTS.TRANS_COMMON, | ||
'http.statusCode', | ||
responseCode | ||
) | ||
segment.addSpanAttribute('http.statusText', response.statusMessage) | ||
} | ||
const headers = response.getHeaders() | ||
if (headers) { | ||
headerAttributes.collectResponseHeaders(headers, transaction) | ||
} | ||
segment.addSpanAttribute('http.statusCode', responseCode) | ||
} | ||
} | ||
// And we are done! End the segment and transaction. | ||
segment.end() | ||
transaction.end() | ||
if (statusMessage !== undefined) { | ||
transaction.trace.attributes.addAttribute( | ||
DESTS.TRANS_COMMON, | ||
'http.statusText', | ||
statusMessage | ||
) | ||
segment.addSpanAttribute('http.statusText', statusMessage) | ||
} | ||
response.once('finish', instrumentedFinish) | ||
response.once('close', instrumentedFinish) | ||
return tracer.bindFunction(emit, segment).apply(this, arguments) | ||
}) | ||
const headers = this.getHeaders() | ||
if (headers) { | ||
headerAttributes.collectResponseHeaders(headers, transaction) | ||
} | ||
} | ||
// And we are done! End the segment and transaction. | ||
segment.end() | ||
transaction.end() | ||
} | ||
@@ -348,24 +370,38 @@ | ||
/** | ||
* http.request and http.get signatures vary. This function | ||
* will parse the options and callback | ||
* | ||
* @param {*} input first arg of http.request and http.get | ||
* @param {*} options request opts of callback | ||
* @param {Function} cb if present it is the callback | ||
* @returns {Array} [options, cb] | ||
*/ | ||
function parseRequest(input, options, cb) { | ||
// If the first argument is a URL, merge it into the options object. | ||
// This code is copied from Node internals. | ||
if (typeof input === 'string') { | ||
const urlStr = input | ||
input = urlToOptions(new URL(urlStr)) | ||
} else if (input.constructor && input.constructor.name === 'URL') { | ||
input = urlToOptions(input) | ||
} else { | ||
cb = options | ||
options = input | ||
input = null | ||
} | ||
if (typeof options === 'function') { | ||
cb = options | ||
options = input || {} | ||
} else { | ||
options = Object.assign(input || {}, options) | ||
} | ||
return [options, cb] | ||
} | ||
function wrapRequest(agent, request) { | ||
return function wrappedRequest(input, options, cb) { | ||
// If the first argument is a URL, merge it into the options object. | ||
// This code is copied from Node internals. | ||
if (typeof input === 'string') { | ||
const urlStr = input | ||
input = urlToOptions(new URL(urlStr)) | ||
} else if (input.constructor && input.constructor.name === 'URL') { | ||
input = urlToOptions(input) | ||
} else { | ||
cb = options | ||
options = input | ||
input = null | ||
} | ||
if (typeof options === 'function') { | ||
cb = options | ||
options = input || {} | ||
} else { | ||
options = Object.assign(input || {}, options) | ||
} | ||
;[options, cb] = parseRequest(input, options, cb) | ||
const reqArgs = [options, cb] | ||
@@ -372,0 +408,0 @@ |
@@ -54,4 +54,4 @@ /* | ||
const attrs = transaction?.trace?.custom.get(DESTINATIONS.TRANS_SCOPE) | ||
return attrs?.conversation_id | ||
return attrs?.['llm.conversation_id'] | ||
} | ||
} |
@@ -8,4 +8,2 @@ /* | ||
/* eslint sonarjs/cognitive-complexity: ["error", 30] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */ | ||
const logger = require('../logger').child({ component: 'PromiseShim' }) | ||
@@ -16,2 +14,14 @@ const Shim = require('./shim') | ||
/** | ||
* Checks if function is actually not a function | ||
* or it is wrapped | ||
* | ||
* @param {Shim} shim instance of shim | ||
* @param {Function} fn function to wrap | ||
* @returns {boolean} is already wrapped or not | ||
*/ | ||
function isWrapped(shim, fn) { | ||
return shim.isFunction(fn) === false || shim.isWrapped(fn) | ||
} | ||
/** | ||
* A helper class for wrapping promise modules. | ||
@@ -32,3 +42,2 @@ * | ||
* @param {string} pkgVersion version of module | ||
* @param shimName | ||
* @see Shim | ||
@@ -47,2 +56,3 @@ */ | ||
* @private | ||
* @returns {Contextualizer} contextualizer class | ||
*/ | ||
@@ -148,3 +158,3 @@ static get Contextualizer() { | ||
return this.wrap(nodule, properties, function executorWrapper(shim, caller) { | ||
if (!shim.isFunction(caller) || shim.isWrapped(caller)) { | ||
if (isWrapped(shim, caller)) { | ||
return | ||
@@ -206,3 +216,3 @@ } | ||
return this.wrap(nodule, properties, function castWrapper(shim, cast) { | ||
if (!shim.isFunction(cast) || shim.isWrapped(cast)) { | ||
if (isWrapped(shim, cast)) { | ||
return | ||
@@ -291,3 +301,3 @@ } | ||
return this.wrap(nodule, properties, function promisifyWrapper(shim, promisify) { | ||
if (!shim.isFunction(promisify) || shim.isWrapped(promisify)) { | ||
if (isWrapped(shim, promisify)) { | ||
return | ||
@@ -308,3 +318,5 @@ } | ||
/** | ||
* Returns wrapped promise that binds the active segment accordingly | ||
* | ||
* @returns {Promise} promise bound to active segment | ||
*/ | ||
@@ -330,4 +342,4 @@ function __NR_wrappedPromisified() { | ||
/** | ||
* @param shim | ||
* @param args | ||
* @param {object} shim instance of shim | ||
* @param {Array} args arguments passed to executor function | ||
* @private | ||
@@ -347,4 +359,4 @@ */ | ||
* | ||
* @param resolve | ||
* @param reject | ||
* @param {Function} resolve function of promise | ||
* @param {Function} reject function of promise | ||
*/ | ||
@@ -360,4 +372,5 @@ function contextExporter(resolve, reject) { | ||
/** | ||
* @param context | ||
* @param fn | ||
* @param {object} context context object | ||
* @param {Function} fn function that is wrapped | ||
* @returns {Function} wrapped function | ||
* @private | ||
@@ -376,11 +389,56 @@ */ | ||
/** | ||
* @param shim | ||
* @param fn | ||
* @param name | ||
* @param useAllParams | ||
* | ||
* @param {object} params object passed to wrapHandler | ||
* @param {Function} params.handler to wrap | ||
* @param {number} params.index index of argument | ||
* @param {number} params.argsLength length of args | ||
* @param {boolean} params.useAllParams flag to use all params | ||
* @param {object} params.ctx context passed in to store the next handler and isWrapped | ||
* @param {object} params.shim shim instance | ||
* @returns {Function} wrapped function | ||
*/ | ||
function wrapHandler({ handler, index, argsLength, useAllParams, ctx, shim }) { | ||
if ( | ||
isWrapped(shim, handler) || | ||
(!useAllParams && index !== argsLength - 1) // Don't want all and not last | ||
) { | ||
ctx.isWrapped = shim.isWrapped(handler) | ||
return handler | ||
} | ||
return function __NR_wrappedThenHandler() { | ||
if (!ctx.handler || !ctx.handler[symbols.context]) { | ||
return handler.apply(this, arguments) | ||
} | ||
let promSegment = ctx.handler[symbols.context].getSegment() | ||
const segment = promSegment || shim.getSegment() | ||
if (segment && segment !== promSegment) { | ||
ctx.handler[symbols.context].setSegment(segment) | ||
promSegment = segment | ||
} | ||
let ret = null | ||
try { | ||
ret = shim.applySegment(handler, promSegment, true, this, arguments) | ||
} finally { | ||
if (ret && typeof ret.then === 'function') { | ||
ret = ctx.handler[symbols.context].continueContext(ret) | ||
} | ||
} | ||
return ret | ||
} | ||
} | ||
/** | ||
* @param {object} shim instance of shim | ||
* @param {Function} fn function that is wrapped | ||
* @param {string} _name function name(unused) | ||
* @param {boolean} useAllParams flag to indicate if all params of function are used | ||
* @returns {Function|undefined} wrapped function | ||
* @private | ||
*/ | ||
function _wrapThen(shim, fn, name, useAllParams) { | ||
function _wrapThen(shim, fn, _name, useAllParams) { | ||
// Don't wrap non-functions. | ||
if (shim.isWrapped(fn) || !shim.isFunction(fn)) { | ||
if (isWrapped(shim, fn)) { | ||
return | ||
@@ -397,55 +455,23 @@ } | ||
// Wrap up the arguments and execute the real then. | ||
let isWrapped = false | ||
// store isWrapped and current handler on context object to be passed into wrapHandler | ||
const ctx = { isWrapped: false, handler: null } | ||
const args = new Array(arguments.length) | ||
for (let i = 0; i < arguments.length; ++i) { | ||
args[i] = wrapHandler(arguments[i], i, arguments.length) | ||
args[i] = wrapHandler({ | ||
shim, | ||
handler: arguments[i], | ||
index: i, | ||
argsLength: arguments.length, | ||
useAllParams, | ||
ctx | ||
}) | ||
} | ||
const next = fn.apply(this, args) | ||
ctx.handler = fn.apply(this, args) | ||
// If we got a promise (which we should have), link the parent's context. | ||
if (!isWrapped && next instanceof shim._class && next !== promise) { | ||
Contextualizer.link(promise, next, thenSegment) | ||
if (!ctx.isWrapped && ctx.handler instanceof shim._class && ctx.handler !== promise) { | ||
Contextualizer.link(promise, ctx.handler, thenSegment) | ||
} | ||
return next | ||
/** | ||
* | ||
* @param handler | ||
* @param i | ||
* @param length | ||
*/ | ||
function wrapHandler(handler, i, length) { | ||
if ( | ||
!shim.isFunction(handler) || // Not a function | ||
shim.isWrapped(handler) || // Already wrapped | ||
(!useAllParams && i !== length - 1) // Don't want all and not last | ||
) { | ||
isWrapped = shim.isWrapped(handler) | ||
return handler | ||
} | ||
return function __NR_wrappedThenHandler() { | ||
if (!next || !next[symbols.context]) { | ||
return handler.apply(this, arguments) | ||
} | ||
let promSegment = next[symbols.context].getSegment() | ||
const segment = promSegment || shim.getSegment() | ||
if (segment && segment !== promSegment) { | ||
next[symbols.context].setSegment(segment) | ||
promSegment = segment | ||
} | ||
let ret = null | ||
try { | ||
ret = shim.applySegment(handler, promSegment, true, this, arguments) | ||
} finally { | ||
if (ret && typeof ret.then === 'function') { | ||
ret = next[symbols.context].continueContext(ret) | ||
} | ||
} | ||
return ret | ||
} | ||
} | ||
return ctx.handler | ||
} | ||
@@ -478,2 +504,41 @@ } | ||
/** | ||
* Iterate over the children and assign parent/child relationship | ||
* via parentIdx and idx of contextualizer. | ||
* | ||
* The first child needs to be updated to have its own branch as well. And | ||
* each of that child's children must be updated with the new parent index. | ||
* This is the only non-constant-time action for linking, but it only | ||
* happens with branching promise chains specifically when the 2nd branch | ||
* is added. | ||
* | ||
* Note: This does not account for branches of branches. That may result | ||
* in improperly parented segments. | ||
* | ||
* @param {Contextualizer} ctxlzr instance of contextualizer | ||
*/ | ||
static buildContextTree(ctxlzr) { | ||
// When the branch-point is the 2nd through nth link in the chain, it is | ||
// necessary to track its segment separately so the branches can parent | ||
// their segments on the branch-point. | ||
if (ctxlzr.parentIdx !== -1) { | ||
ctxlzr.idx = ctxlzr.context.branch() | ||
} | ||
let parent = ctxlzr | ||
let child = ctxlzr.child | ||
const branchIdx = ctxlzr.context.branch() | ||
do { | ||
child.parentIdx = parent.idx | ||
child.idx = branchIdx | ||
parent = child | ||
child = child.child | ||
} while (child) | ||
// We set the child to something falsey that isn't `null` so we can | ||
// distinguish between having no child, having one child, and having | ||
// multiple children. | ||
ctxlzr.child = false | ||
} | ||
static link(prev, next, segment) { | ||
@@ -488,31 +553,3 @@ let ctxlzr = prev && prev[symbols.context] | ||
if (ctxlzr.child) { | ||
// When the branch-point is the 2nd through nth link in the chain, it is | ||
// necessary to track its segment separately so the branches can parent | ||
// their segments on the branch-point. | ||
if (ctxlzr.parentIdx !== -1) { | ||
ctxlzr.idx = ctxlzr.context.branch() | ||
} | ||
// The first child needs to be updated to have its own branch as well. And | ||
// each of that child's children must be updated with the new parent index. | ||
// This is the only non-constant-time action for linking, but it only | ||
// happens with branching promise chains specifically when the 2nd branch | ||
// is added. | ||
// | ||
// Note: This does not account for branches of branches. That may result | ||
// in improperly parented segments. | ||
let parent = ctxlzr | ||
let child = ctxlzr.child | ||
const branchIdx = ctxlzr.context.branch() | ||
do { | ||
child.parentIdx = parent.idx | ||
child.idx = branchIdx | ||
parent = child | ||
child = child.child | ||
} while (child) | ||
// We set the child to something falsey that isn't `null` so we can | ||
// distinguish between having no child, having one child, and having | ||
// multiple children. | ||
ctxlzr.child = false | ||
Contextualizer.buildContextTree(ctxlzr) | ||
} | ||
@@ -519,0 +556,0 @@ |
@@ -15,7 +15,7 @@ /* | ||
const comment = /(?:#|--).*?(?=\r|\n|$)/ | ||
const multilineComment = /\/\*(?:[^/]|\/[^*])*?(?:\*\/|\/\*.*)/ | ||
const multilineComment = /\/\*(?:[^\/]|\/[^*])*?(?:\*\/|\/\*.*)/ | ||
const uuid = /\{?(?:[0-9a-f]\-*){32}\}?/ | ||
const hex = /0x[0-9a-f]+/ | ||
const boolean = /true|false|null/ | ||
const number = /\b-?(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?/ | ||
const boolean = /\b(?:true|false|null)\b/ | ||
const number = /-?\b(?:[0-9]+\.)?[0-9]+(e[+-]?[0-9]+)?/ | ||
@@ -33,3 +33,3 @@ const dialects = (obfuscate.dialects = Object.create(null)) | ||
), | ||
unmatchedPairs(/'|\/\*|\*\/|(?:\$(?!\?))/) | ||
unmatchedPairs(/'|\/\*|\*\/|\$(?!\?)/) | ||
] | ||
@@ -47,2 +47,7 @@ | ||
dialects.sqlite = [ | ||
replacer(join([singleQuote, comment, multilineComment, hex, boolean, number], 'gi')), | ||
unmatchedPairs(/'|\/\*|\*\//) | ||
] | ||
dialects.default = dialects.mysql | ||
@@ -49,0 +54,0 @@ |
{ | ||
"name": "newrelic", | ||
"version": "11.7.0", | ||
"version": "11.8.0", | ||
"author": "New Relic Node.js agent team <nodejs@newrelic.com>", | ||
@@ -188,3 +188,3 @@ "license": "Apache-2.0", | ||
"@newrelic/koa": "^8.0.1", | ||
"@newrelic/security-agent": "0.5.0", | ||
"@newrelic/security-agent": "^0.6.0", | ||
"@newrelic/superagent": "^7.0.1", | ||
@@ -217,3 +217,2 @@ "@tyriar/fibonacci-heap": "^2.0.7", | ||
"c8": "^8.0.1", | ||
"chai": "^4.1.2", | ||
"clean-jsdoc-theme": "^4.2.4", | ||
@@ -220,0 +219,0 @@ "commander": "^7.0.0", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2098295
41
226
35624
+ Added@newrelic/security-agent@0.6.0(transitive)
+ Addedaxios@1.7.7(transitive)
- Removed@newrelic/security-agent@0.5.0(transitive)
- Removedaxios@1.6.0(transitive)