Skip to content

Commit 78dd563

Browse files
authored
fix(ws): handle socket availability gracefully (#6728)
This allows to receive the original error in case e.g., close is called before the connection could be established. Fixes: #6624
1 parent b071433 commit 78dd563

File tree

3 files changed

+35
-4
lines changed

3 files changed

+35
-4
lines changed

packages/datadog-instrumentations/src/ws.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function wrapSend (send) {
4141

4242
const [data, options, cb] = arguments
4343

44-
const ctx = { data, socket: this._sender._socket }
44+
const ctx = { data, socket: this._sender?._socket }
4545

4646
return typeof cb === 'function'
4747
? producerCh.traceCallback(send, undefined, ctx, this, data, options, cb)
@@ -70,7 +70,7 @@ function createWrappedHandler (handler) {
7070
return function wrappedMessageHandler (data, binary) {
7171
const byteLength = dataLength(data)
7272

73-
const ctx = { data, binary, socket: this._sender._socket, byteLength }
73+
const ctx = { data, binary, socket: this._sender?._socket, byteLength }
7474

7575
return receiverCh.traceSync(handler, ctx, this, data, binary)
7676
}
@@ -93,7 +93,7 @@ function wrapClose (close) {
9393
// if both are true then the self is sending the close event
9494
const isPeerClose = this._closeFrameReceived === true && this._closeFrameSent === false
9595

96-
const ctx = { code, data, socket: this._sender._socket, isPeerClose }
96+
const ctx = { code, data, socket: this._sender?._socket, isPeerClose }
9797

9898
return closeCh.traceSync(close, ctx, this, ...arguments)
9999
}

packages/datadog-plugin-ws/src/close.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class WSClosePlugin extends TracingPlugin {
1717
if (!traceWebsocketMessagesEnabled) return
1818

1919
const { code, data, socket, isPeerClose } = ctx
20-
if (!socket.spanContext) return
20+
if (!socket?.spanContext) return
2121

2222
const spanKind = isPeerClose ? 'consumer' : 'producer'
2323
const spanTags = socket.spanContext.spanTags

packages/datadog-plugin-ws/test/index.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
'use strict'
22

3+
const assert = require('node:assert')
4+
const { once } = require('node:events')
5+
36
const { expect } = require('chai')
7+
const { describe, before, after, it } = require('mocha')
8+
49
const { withVersions } = require('../../dd-trace/test/setup/mocha')
510
const agent = require('../../dd-trace/test/plugins/agent')
611

@@ -15,6 +20,32 @@ describe('Plugin', () => {
1520

1621
describe('ws', () => {
1722
withVersions('ws', 'ws', '>=8.0.0', version => {
23+
describe('regression tests', () => {
24+
before(async () => {
25+
await agent.load(['ws'], [{
26+
service: 'some',
27+
traceWebsocketMessagesEnabled: true
28+
}])
29+
WebSocket = require(`../../../versions/ws@${version}`).get()
30+
})
31+
32+
it('should emit original error in case close is called before connection is established', async () => {
33+
const socket = new WebSocket('wss://localhost:12345')
34+
35+
const errorPromise = once(socket, 'error')
36+
socket.close()
37+
38+
const error = await errorPromise
39+
40+
// Some versions emit an array with an error, some directly emit the error
41+
assert.strictEqual(error?.[0]?.message, 'WebSocket was closed before the connection was established')
42+
})
43+
44+
after(async () => {
45+
agent.close({ ritmReset: false, wipe: true })
46+
})
47+
})
48+
1849
describe('when using WebSocket', () => {
1950
route = 'test'
2051
beforeEach(async () => {

0 commit comments

Comments
 (0)