dd-trace 2.2.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/index.d.ts +57 -0
  2. package/package.json +2 -2
  3. package/packages/datadog-instrumentations/index.js +9 -0
  4. package/packages/datadog-instrumentations/src/amqp10.js +70 -0
  5. package/packages/datadog-instrumentations/src/amqplib.js +58 -0
  6. package/packages/datadog-instrumentations/src/cassandra-driver.js +191 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +118 -0
  8. package/packages/datadog-instrumentations/src/elasticsearch.js +9 -4
  9. package/packages/datadog-instrumentations/src/helpers/instrument.js +3 -3
  10. package/packages/datadog-instrumentations/src/ioredis.js +8 -5
  11. package/packages/datadog-instrumentations/src/mocha.js +122 -0
  12. package/packages/datadog-instrumentations/src/mongodb-core.js +179 -0
  13. package/packages/datadog-instrumentations/src/pg.js +75 -0
  14. package/packages/datadog-instrumentations/src/redis.js +8 -8
  15. package/packages/datadog-instrumentations/src/rhea.js +224 -0
  16. package/packages/datadog-instrumentations/src/tedious.js +66 -0
  17. package/packages/datadog-plugin-amqp10/src/index.js +79 -122
  18. package/packages/datadog-plugin-amqplib/src/index.js +77 -142
  19. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  20. package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
  21. package/packages/datadog-plugin-cucumber/src/index.js +85 -128
  22. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +5 -3
  23. package/packages/datadog-plugin-mocha/src/index.js +96 -207
  24. package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
  25. package/packages/datadog-plugin-pg/src/index.js +32 -69
  26. package/packages/datadog-plugin-rhea/src/index.js +59 -225
  27. package/packages/datadog-plugin-tedious/src/index.js +38 -86
  28. package/packages/dd-trace/lib/version.js +1 -1
  29. package/packages/dd-trace/src/appsec/recommended.json +137 -116
  30. package/packages/dd-trace/src/config.js +13 -1
  31. package/packages/dd-trace/src/noop/tracer.js +4 -0
  32. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
  33. package/packages/dd-trace/src/opentracing/tracer.js +1 -1
  34. package/packages/dd-trace/src/plugins/log_plugin.js +10 -5
  35. package/packages/dd-trace/src/plugins/util/git.js +1 -1
  36. package/packages/dd-trace/src/proxy.js +4 -0
  37. package/packages/dd-trace/src/span_processor.js +22 -7
  38. package/packages/dd-trace/src/tracer.js +16 -0
  39. package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
  40. package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
  41. package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
@@ -0,0 +1,122 @@
1
+ const { addHook, channel, AsyncResource } = require('./helpers/instrument')
2
+ const shimmer = require('../../datadog-shimmer')
3
+
4
+ const testStartCh = channel('ci:mocha:test:start')
5
+ const errorCh = channel('ci:mocha:test:error')
6
+ const skipCh = channel('ci:mocha:test:skip')
7
+ const testEndCh = channel('ci:mocha:test:end')
8
+ const testAsyncEndCh = channel('ci:mocha:test:async-end')
9
+ const suiteEndCh = channel('ci:mocha:suite:end')
10
+ const hookErrorCh = channel('ci:mocha:hook:error')
11
+ const parameterizedTestCh = channel('ci:mocha:test:parameterize')
12
+ const testRunEndCh = channel('ci:mocha:run:end')
13
+
14
+ function isRetry (test) {
15
+ return test._currentRetry !== undefined && test._currentRetry !== 0
16
+ }
17
+
18
+ function getAllTestsInSuite (root) {
19
+ const tests = []
20
+ function getTests (suiteOrTest) {
21
+ suiteOrTest.tests.forEach(test => {
22
+ tests.push(test)
23
+ })
24
+ suiteOrTest.suites.forEach(suite => {
25
+ getTests(suite)
26
+ })
27
+ }
28
+ getTests(root)
29
+ return tests
30
+ }
31
+
32
+ addHook({
33
+ name: 'mocha',
34
+ versions: ['>=5.2.0'],
35
+ file: 'lib/runner.js'
36
+ }, (Runner) => {
37
+ shimmer.wrap(Runner.prototype, 'runTest', runTest => function () {
38
+ if (!testStartCh.hasSubscribers) {
39
+ return runTest.apply(this, arguments)
40
+ }
41
+
42
+ if (!isRetry(this.test)) {
43
+ testStartCh.publish(this.test)
44
+ }
45
+
46
+ this.once('test end', AsyncResource.bind(() => {
47
+ let status
48
+
49
+ if (this.test.pending) {
50
+ status = 'skipped'
51
+ } else if (this.test.state !== 'failed' && !this.test.timedOut) {
52
+ status = 'pass'
53
+ } else {
54
+ status = 'fail'
55
+ }
56
+
57
+ testAsyncEndCh.publish(status)
58
+ }))
59
+
60
+ this.once('fail', AsyncResource.bind((test, err) => {
61
+ errorCh.publish(err)
62
+ }))
63
+
64
+ this.once('pending', AsyncResource.bind((test) => {
65
+ skipCh.publish(test)
66
+ }))
67
+
68
+ try {
69
+ return runTest.apply(this, arguments)
70
+ } catch (err) {
71
+ errorCh.publish(err)
72
+ throw err
73
+ } finally {
74
+ testEndCh.publish(undefined)
75
+ }
76
+ })
77
+
78
+ shimmer.wrap(Runner.prototype, 'runTests', runTests => function () {
79
+ if (!suiteEndCh.hasSubscribers) {
80
+ return runTests.apply(this, arguments)
81
+ }
82
+ this.once('end', AsyncResource.bind(() => {
83
+ testRunEndCh.publish(undefined)
84
+ }))
85
+ runTests.apply(this, arguments)
86
+ const suite = arguments[0]
87
+ // We call `getAllTestsInSuite` with the root suite so every skipped test
88
+ // should already have an associated test span.
89
+ const tests = getAllTestsInSuite(suite)
90
+ suiteEndCh.publish(tests)
91
+ })
92
+
93
+ shimmer.wrap(Runner.prototype, 'fail', fail => function (hook, error) {
94
+ if (!hookErrorCh.hasSubscribers) {
95
+ return fail.apply(this, arguments)
96
+ }
97
+ if (error && hook.ctx && hook.ctx.currentTest) {
98
+ error.message = `${hook.title}: ${error.message}`
99
+ hookErrorCh.publish({ test: hook.ctx.currentTest, error })
100
+ }
101
+ return fail.apply(this, arguments)
102
+ })
103
+
104
+ return Runner
105
+ })
106
+
107
+ addHook({
108
+ name: 'mocha-each',
109
+ versions: ['>=2.0.1']
110
+ }, (mochaEach) => {
111
+ return shimmer.wrap(mochaEach, function () {
112
+ const [params] = arguments
113
+ const { it, ...rest } = mochaEach.apply(this, arguments)
114
+ return {
115
+ it: function (name) {
116
+ parameterizedTestCh.publish({ name, params })
117
+ it.apply(this, arguments)
118
+ },
119
+ ...rest
120
+ }
121
+ })
122
+ })
@@ -0,0 +1,179 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook,
6
+ AsyncResource
7
+ } = require('./helpers/instrument')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ const startCh = channel(`apm:mongodb:query:start`)
11
+ const endCh = channel(`apm:mongodb:query:end`)
12
+ const asyncEndCh = channel(`apm:mongodb:query:async-end`)
13
+ const errorCh = channel(`apm:mongodb:query:error`)
14
+
15
+ addHook({ name: 'mongodb-core', versions: ['2 - 3.1.9'] }, Server => {
16
+ const serverProto = Server.Server.prototype
17
+ shimmer.wrap(serverProto, 'command', command => wrapCommand(command, 'command'))
18
+ shimmer.wrap(serverProto, 'insert', insert => wrapCommand(insert, 'insert', 'insert'))
19
+ shimmer.wrap(serverProto, 'update', update => wrapCommand(update, 'update', 'update'))
20
+ shimmer.wrap(serverProto, 'remove', remove => wrapCommand(remove, 'remove', 'remove'))
21
+
22
+ const cursorProto = Server.Cursor.prototype
23
+ shimmer.wrap(cursorProto, '_getmore', _getmore => wrapCursor(_getmore, 'getMore', 'getMore'))
24
+ shimmer.wrap(cursorProto, '_find', _find => wrapQuery(_find, '_find'))
25
+ shimmer.wrap(cursorProto, 'kill', kill => wrapCursor(kill, 'killCursors', 'killCursors'))
26
+ return Server
27
+ })
28
+
29
+ addHook({ name: 'mongodb', versions: ['>=4'], file: 'lib/cmap/connection.js' }, Connection => {
30
+ const proto = Connection.Connection.prototype
31
+ shimmer.wrap(proto, 'command', command => wrapConnectionCommand(command, 'command'))
32
+ shimmer.wrap(proto, 'query', query => wrapConnectionCommand(query, 'query'))
33
+ return Connection
34
+ })
35
+
36
+ addHook({ name: 'mongodb', versions: ['>=3.3 <4'], file: 'lib/core/wireprotocol/index.js' }, wp => wrapWp(wp))
37
+
38
+ addHook({ name: 'mongodb-core', versions: ['>=3.2'], file: 'lib/wireprotocol/index.js' }, wp => wrapWp(wp))
39
+
40
+ addHook({ name: 'mongodb-core', versions: ['~3.1.10'], file: 'lib/wireprotocol/3_2_support.js' }, WireProtocol => {
41
+ shimmer.wrap(WireProtocol.prototype, 'command', command => wrapUnifiedCommand(command, 'command'))
42
+ return WireProtocol
43
+ })
44
+
45
+ addHook({ name: 'mongodb-core', versions: ['~3.1.10'], file: 'lib/wireprotocol/2_6_support.js' }, WireProtocol => {
46
+ shimmer.wrap(WireProtocol.prototype, 'command', command => wrapUnifiedCommand(command, 'command'))
47
+ return WireProtocol
48
+ })
49
+
50
+ addHook({ name: 'mongodb', versions: ['>=3.5.4'], file: 'lib/utils.js' }, util => {
51
+ shimmer.wrap(util, 'maybePromise', maybePromise => function (parent, callback, fn) {
52
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
53
+ const callbackIndex = arguments.length - 2
54
+
55
+ callback = arguments[callbackIndex]
56
+
57
+ if (typeof callback === 'function') {
58
+ arguments[callbackIndex] = asyncResource.bind(callback)
59
+ }
60
+
61
+ return maybePromise.apply(this, arguments)
62
+ })
63
+ return util
64
+ })
65
+
66
+ function wrapWp (wp) {
67
+ shimmer.wrap(wp, 'command', command => wrapUnifiedCommand(command, 'command'))
68
+ shimmer.wrap(wp, 'insert', insert => wrapUnifiedCommand(insert, 'insert', 'insert'))
69
+ shimmer.wrap(wp, 'update', update => wrapUnifiedCommand(update, 'update', 'update'))
70
+ shimmer.wrap(wp, 'remove', remove => wrapUnifiedCommand(remove, 'remove', 'remove'))
71
+ shimmer.wrap(wp, 'query', query => wrapUnifiedCommand(query, 'query'))
72
+ shimmer.wrap(wp, 'getMore', getMore => wrapUnifiedCommand(getMore, 'getMore', 'getMore'))
73
+ shimmer.wrap(wp, 'killCursors', killCursors => wrapUnifiedCommand(killCursors, 'killCursors', 'killCursors'))
74
+ return wp
75
+ }
76
+
77
+ function wrapUnifiedCommand (command, operation, name) {
78
+ const wrapped = function (server, ns, ops) {
79
+ if (!startCh.hasSubscribers) {
80
+ return command.apply(this, arguments)
81
+ }
82
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
83
+ return instrument(asyncResource, operation, command, this, arguments, server, ns, ops, { name })
84
+ }
85
+ return shimmer.wrap(command, wrapped)
86
+ }
87
+
88
+ function wrapConnectionCommand (command, operation, name) {
89
+ const wrapped = function (ns, ops) {
90
+ if (!startCh.hasSubscribers) {
91
+ return command.apply(this, arguments)
92
+ }
93
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
94
+ const hostParts = typeof this.address === 'string' ? this.address.split(':') : ''
95
+ const options = hostParts.length === 2
96
+ ? { host: hostParts[0], port: hostParts[1] }
97
+ : {} // no port means the address is a random UUID so no host either
98
+ const topology = { s: { options } }
99
+
100
+ ns = `${ns.db}.${ns.collection}`
101
+ return instrument(asyncResource, operation, command, this, arguments, topology, ns, ops, { name })
102
+ }
103
+ return shimmer.wrap(command, wrapped)
104
+ }
105
+
106
+ function wrapQuery (query, operation, name) {
107
+ const wrapped = function () {
108
+ if (!startCh.hasSubscribers) {
109
+ return query.apply(this, arguments)
110
+ }
111
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
112
+ const pool = this.server.s.pool
113
+ const ns = this.ns
114
+ const ops = this.cmd
115
+ return instrument(asyncResource, operation, query, this, arguments, pool, ns, ops)
116
+ }
117
+
118
+ return shimmer.wrap(query, wrapped)
119
+ }
120
+
121
+ function wrapCursor (cursor, operation, name) {
122
+ const wrapped = function () {
123
+ if (!startCh.hasSubscribers) {
124
+ return cursor.apply(this, arguments)
125
+ }
126
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
127
+ const pool = this.server.s.pool
128
+ const ns = this.ns
129
+ return instrument(asyncResource, operation, cursor, this, arguments, pool, ns, {}, { name })
130
+ }
131
+ return shimmer.wrap(cursor, wrapped)
132
+ }
133
+
134
+ function wrapCommand (command, operation, name) {
135
+ const wrapped = function (ns, ops) {
136
+ if (!startCh.hasSubscribers) {
137
+ return command.apply(this, arguments)
138
+ }
139
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
140
+ return instrument(asyncResource, operation, command, this, arguments, this, ns, ops, { name })
141
+ }
142
+ return shimmer.wrap(command, wrapped)
143
+ }
144
+
145
+ function instrument (ar, operation, command, ctx, args, server, ns, ops, options = {}) {
146
+ const name = options.name || (ops && Object.keys(ops)[0])
147
+ const index = args.length - 1
148
+ let callback = args[index]
149
+
150
+ if (typeof callback !== 'function') return command.apply(ctx, args)
151
+
152
+ callback = ar.bind(callback)
153
+
154
+ const serverInfo = server && server.s && server.s.options
155
+
156
+ startCh.publish({ ns, ops, options: serverInfo, name })
157
+
158
+ args[index] = AsyncResource.bind(function (err, res) {
159
+ if (err) {
160
+ errorCh.publish(err)
161
+ }
162
+
163
+ asyncEndCh.publish(undefined)
164
+
165
+ if (callback) {
166
+ return callback.apply(this, arguments)
167
+ }
168
+ })
169
+
170
+ try {
171
+ return command.apply(ctx, args)
172
+ } catch (err) {
173
+ errorCh.publish(err)
174
+
175
+ throw err
176
+ } finally {
177
+ endCh.publish(undefined)
178
+ }
179
+ }
@@ -0,0 +1,75 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook,
6
+ AsyncResource
7
+ } = require('./helpers/instrument')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ const startCh = channel('apm:pg:query:start')
11
+ const asyncEndCh = channel('apm:pg:query:async-end')
12
+ const endCh = channel('apm:pg:query:end')
13
+ const errorCh = channel('apm:pg:query:error')
14
+
15
+ addHook({ name: 'pg', versions: ['>=4'] }, pg => {
16
+ shimmer.wrap(pg.Client.prototype, 'query', query => wrapQuery(query))
17
+ return pg
18
+ })
19
+
20
+ addHook({ name: 'pg', file: 'lib/native/index.js', versions: ['>=4'] }, Client => {
21
+ shimmer.wrap(Client.prototype, 'query', query => wrapQuery(query))
22
+ return Client
23
+ })
24
+
25
+ function wrapQuery (query) {
26
+ return function () {
27
+ if (!startCh.hasSubscribers) {
28
+ return query.apply(this, arguments)
29
+ }
30
+
31
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
32
+
33
+ const retval = query.apply(this, arguments)
34
+
35
+ const queryQueue = this.queryQueue || this._queryQueue
36
+ const activeQuery = this.activeQuery || this._activeQuery
37
+ const pgQuery = queryQueue[queryQueue.length - 1] || activeQuery
38
+
39
+ if (!pgQuery) {
40
+ return retval
41
+ }
42
+ const statement = pgQuery.text
43
+
44
+ startCh.publish({ params: this.connectionParameters, statement })
45
+
46
+ const finish = AsyncResource.bind(function (error) {
47
+ if (error) {
48
+ errorCh.publish(error)
49
+ }
50
+ asyncEndCh.publish(undefined)
51
+ })
52
+
53
+ if (pgQuery.callback) {
54
+ const originalCallback = asyncResource.bind(pgQuery.callback)
55
+ pgQuery.callback = function (err, res) {
56
+ finish(err)
57
+ return originalCallback.apply(this, arguments)
58
+ }
59
+ } else if (pgQuery.once) {
60
+ pgQuery
61
+ .once('error', finish)
62
+ .once('end', () => finish())
63
+ } else {
64
+ pgQuery.then(() => finish(), finish)
65
+ }
66
+
67
+ try {
68
+ return retval
69
+ } catch (err) {
70
+ errorCh.publish(err)
71
+ } finally {
72
+ endCh.publish(undefined)
73
+ }
74
+ }
75
+ }
@@ -21,14 +21,14 @@ addHook({ name: '@node-redis/client', file: 'dist/lib/client/commands-queue.js',
21
21
  const name = command[0]
22
22
  const args = command.slice(1)
23
23
 
24
- startSpan(this, name, args)
24
+ start(this, name, args)
25
25
 
26
26
  const res = addCommand.apply(this, arguments)
27
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
28
+ const onResolve = asyncResource.bind(() => finish(asyncEndCh, errorCh))
29
+ const onReject = asyncResource.bind(err => finish(asyncEndCh, errorCh, err))
27
30
 
28
- res.then(
29
- () => finish(asyncEndCh, errorCh),
30
- err => finish(asyncEndCh, errorCh, err)
31
- )
31
+ res.then(onResolve, onReject)
32
32
  endCh.publish(undefined)
33
33
  return res
34
34
  })
@@ -45,7 +45,7 @@ addHook({ name: 'redis', versions: ['>=2.6 <4'] }, redis => {
45
45
 
46
46
  const cb = asyncResource.bind(options.callback)
47
47
 
48
- startSpan(this, options.command, options.args)
48
+ start(this, options.command, options.args)
49
49
 
50
50
  options.callback = AsyncResource.bind(wrapCallback(asyncEndCh, errorCh, cb))
51
51
 
@@ -70,7 +70,7 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
70
70
 
71
71
  const asyncResource = new AsyncResource('bound-anonymous-fn')
72
72
 
73
- startSpan(this, command, args)
73
+ start(this, command, args)
74
74
 
75
75
  if (typeof callback === 'function') {
76
76
  const cb = asyncResource.bind(callback)
@@ -95,7 +95,7 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
95
95
  return redis
96
96
  })
97
97
 
98
- function startSpan (client, command, args) {
98
+ function start (client, command, args) {
99
99
  const db = client.selected_db
100
100
  const connectionOptions = client.connection_options || client.connection_option || client.connectionOption || {}
101
101
  startCh.publish({ db, command, args, connectionOptions })
@@ -0,0 +1,224 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook,
6
+ AsyncResource
7
+ } = require('./helpers/instrument')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ const circularBufferConstructor = Symbol('circularBufferConstructor')
11
+ const inFlightDeliveries = Symbol('inFlightDeliveries')
12
+
13
+ const patched = new WeakSet()
14
+ const dispatchCh = channel('apm:rhea:dispatch')
15
+ const errorCh = channel('apm:rhea:error')
16
+ const asyncEndCh = channel('apm:rhea:async-end')
17
+ const endCh = channel('apm:rhea:end')
18
+
19
+ const contexts = new WeakMap()
20
+
21
+ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/link.js' }, obj => {
22
+ const startSendCh = channel('apm:rhea:send:start')
23
+ const startReceiveCh = channel('apm:rhea:receive:start')
24
+
25
+ const Sender = obj.Sender
26
+ const Receiver = obj.Receiver
27
+ shimmer.wrap(Sender.prototype, 'send', send => function (msg, tag, format) {
28
+ if (!canTrace(this)) {
29
+ // we can't handle disconnects or ending spans, so we can't safely instrument
30
+ return send.apply(this, arguments)
31
+ }
32
+
33
+ const { host, port } = getHostAndPort(this.connection)
34
+
35
+ const targetAddress = this.options && this.options.target &&
36
+ this.options.target.address ? this.options.target.address : undefined
37
+
38
+ startSendCh.publish({ targetAddress, host, port, msg })
39
+ const delivery = send.apply(this, arguments)
40
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
41
+ const context = {
42
+ asyncResource
43
+ }
44
+ contexts.set(delivery, context)
45
+
46
+ addToInFlightDeliveries(this.connection, delivery)
47
+ try {
48
+ return delivery
49
+ } catch (err) {
50
+ errorCh.publish(err)
51
+
52
+ throw err
53
+ } finally {
54
+ endCh.publish(undefined)
55
+ }
56
+ })
57
+
58
+ shimmer.wrap(Receiver.prototype, 'dispatch', dispatch => function (eventName, msgObj) {
59
+ if (!canTrace(this)) {
60
+ // we can't handle disconnects or ending spans, so we can't safely instrument
61
+ return dispatch.apply(this, arguments)
62
+ }
63
+
64
+ if (eventName === 'message' && msgObj) {
65
+ startReceiveCh.publish({ msgObj, connection: this.connection })
66
+
67
+ if (msgObj.delivery) {
68
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
69
+ const context = {
70
+ asyncResource
71
+ }
72
+ contexts.set(msgObj.delivery, context)
73
+ msgObj.delivery.update = wrapDeliveryUpdate(msgObj.delivery, msgObj.delivery.update)
74
+ addToInFlightDeliveries(this.connection, msgObj.delivery)
75
+ }
76
+ try {
77
+ return dispatch.apply(this, arguments)
78
+ } catch (err) {
79
+ errorCh.publish(err)
80
+
81
+ throw err
82
+ } finally {
83
+ endCh.publish(undefined)
84
+ }
85
+ }
86
+
87
+ return dispatch.apply(this, arguments)
88
+ })
89
+ return obj
90
+ })
91
+
92
+ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/connection.js' }, Connection => {
93
+ shimmer.wrap(Connection.prototype, 'dispatch', dispatch => function (eventName, obj) {
94
+ if (eventName === 'disconnected') {
95
+ const error = obj.error || this.saved_error
96
+ if (this[inFlightDeliveries]) {
97
+ this[inFlightDeliveries].forEach(delivery => {
98
+ const context = contexts.get(delivery)
99
+ const asyncResource = context.asyncResource
100
+ asyncResource.runInAsyncScope(() => {
101
+ errorCh.publish(error)
102
+ finish(delivery, null)
103
+ })
104
+ })
105
+ }
106
+ }
107
+ return dispatch.apply(this, arguments)
108
+ })
109
+ return Connection
110
+ })
111
+
112
+ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/session.js' }, (Session) => {
113
+ patchCircularBuffer(Session.prototype, Session)
114
+ return Session
115
+ })
116
+
117
+ function canTrace (link) {
118
+ return link.connection && link.session && link.session.outgoing
119
+ }
120
+
121
+ function getHostAndPort (connection) {
122
+ let host
123
+ let port
124
+ if (connection && connection.options) {
125
+ host = connection.options.host
126
+ port = connection.options.port
127
+ }
128
+ return { host, port }
129
+ }
130
+
131
+ function wrapDeliveryUpdate (obj, update) {
132
+ const context = contexts.get(obj)
133
+ const asyncResource = context.asyncResource
134
+ if (obj && asyncResource) {
135
+ const cb = asyncResource.bind(update)
136
+ return AsyncResource.bind(function wrappedUpdate (settled, stateData) {
137
+ const state = getStateFromData(stateData)
138
+ dispatchCh.publish({ state })
139
+ return cb.apply(this, arguments)
140
+ })
141
+ }
142
+ return function wrappedUpdate (settled, stateData) {
143
+ return update.apply(this, arguments)
144
+ }
145
+ }
146
+
147
+ function patchCircularBuffer (proto, Session) {
148
+ Object.defineProperty(proto, 'outgoing', {
149
+ configurable: true,
150
+ get () {},
151
+ set (outgoing) {
152
+ delete proto.outgoing // removes the setter on the prototype
153
+ this.outgoing = outgoing // assigns on the instance, like normal
154
+ if (outgoing) {
155
+ let CircularBuffer
156
+ if (outgoing.deliveries) {
157
+ CircularBuffer = outgoing.deliveries.constructor
158
+ }
159
+ if (CircularBuffer && !patched.has(CircularBuffer.prototype)) {
160
+ shimmer.wrap(CircularBuffer.prototype, 'pop_if', popIf => function (fn) {
161
+ arguments[0] = AsyncResource.bind(function (entry) {
162
+ const context = contexts.get(entry)
163
+ const asyncResource = context.asyncResource
164
+ let shouldPop
165
+ if (asyncResource) {
166
+ fn = asyncResource.bind(fn, this)
167
+ shouldPop = fn(entry)
168
+ if (shouldPop && asyncResource) {
169
+ const remoteState = entry.remote_state
170
+ const state = remoteState && remoteState.constructor
171
+ ? entry.remote_state.constructor.composite_type : undefined
172
+ asyncResource.runInAsyncScope(() => {
173
+ return finish(entry, state)
174
+ })
175
+ }
176
+ }
177
+
178
+ return shouldPop
179
+ })
180
+ return popIf.apply(this, arguments)
181
+ })
182
+ patched.add(CircularBuffer.prototype)
183
+ const Session = proto.constructor
184
+ if (Session) {
185
+ Session[circularBufferConstructor] = CircularBuffer
186
+ }
187
+ }
188
+ }
189
+ }
190
+ })
191
+ }
192
+
193
+ function addToInFlightDeliveries (connection, delivery) {
194
+ let deliveries = connection[inFlightDeliveries]
195
+ if (!deliveries) {
196
+ deliveries = new Set()
197
+ connection[inFlightDeliveries] = deliveries
198
+ }
199
+ deliveries.add(delivery)
200
+ }
201
+
202
+ function finish (delivery, state) {
203
+ const obj = contexts.get(delivery)
204
+ if (obj) {
205
+ if (state) {
206
+ dispatchCh.publish({ state })
207
+ }
208
+ asyncEndCh.publish(undefined)
209
+ if (obj.connection && obj.connection[inFlightDeliveries]) {
210
+ obj.connection[inFlightDeliveries].delete(delivery)
211
+ }
212
+ }
213
+ }
214
+
215
+ function getStateFromData (stateData) {
216
+ if (stateData && stateData.descriptor && stateData.descriptor) {
217
+ switch (stateData.descriptor.value) {
218
+ case 0x24: return 'accepted'
219
+ case 0x25: return 'rejected'
220
+ case 0x26: return 'released'
221
+ case 0x27: return 'modified'
222
+ }
223
+ }
224
+ }