Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion docs/docs/api/DiagnosticsChannel.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,38 @@ This message is published after the client has successfully connected to a serve
```js
import diagnosticsChannel from 'diagnostics_channel'

diagnosticsChannel.channel('undici:websocket:open').subscribe(({ address, protocol, extensions, websocket }) => {
diagnosticsChannel.channel('undici:websocket:open').subscribe(({
address, // { address: string, family: string, port: number }
protocol, // string - negotiated subprotocol
extensions, // string - negotiated extensions
websocket, // WebSocket - the WebSocket instance
handshakeResponse // object - HTTP response that upgraded the connection
}) => {
console.log(address) // address, family, and port
console.log(protocol) // negotiated subprotocols
console.log(extensions) // negotiated extensions
console.log(websocket) // the WebSocket instance

// Handshake response details
console.log(handshakeResponse.status) // 101 for successful WebSocket upgrade
console.log(handshakeResponse.statusText) // 'Switching Protocols'
console.log(handshakeResponse.headers) // Object containing response headers
})
```

### Handshake Response Object

The `handshakeResponse` object contains the HTTP response that upgraded the connection to WebSocket:

- `status` (number): The HTTP status code (101 for successful WebSocket upgrade)
- `statusText` (string): The HTTP status message ('Switching Protocols' for successful upgrade)
- `headers` (object): The HTTP response headers from the server, including:
- `upgrade: 'websocket'`
- `connection: 'upgrade'`
- `sec-websocket-accept` and other WebSocket-related headers

This information is particularly useful for debugging and monitoring WebSocket connections, as it provides access to the initial HTTP handshake response that established the WebSocket connection.

## `undici:websocket:close`

This message is published after the connection has closed.
Expand Down
9 changes: 8 additions & 1 deletion lib/web/websocket/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,11 +482,18 @@ class WebSocket extends EventTarget {
fireEvent('open', this)

if (channels.open.hasSubscribers) {
// Convert headers to a plain object for the event
const headers = response.headersList.entries
channels.open.publish({
address: response.socket.address(),
protocol: this.#protocol,
extensions: this.#extensions,
websocket: this
websocket: this,
handshakeResponse: {
status: response.status,
statusText: response.statusText,
headers
}
})
}
}
Expand Down
51 changes: 51 additions & 0 deletions test/websocket/diagnostics-channel-handshake-response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict'

const { test } = require('node:test')
const dc = require('node:diagnostics_channel')
const { WebSocketServer } = require('ws')
const { WebSocket } = require('../..')
const { tspl } = require('@matteo.collina/tspl')

test('diagnostics channel - undici:websocket:open includes handshake response', async (t) => {
const { equal, ok, completed } = tspl(t, { plan: 11 })

const server = new WebSocketServer({ port: 0 })
const { port } = server.address()

server.on('connection', (ws) => {
setTimeout(() => {
ws.close(1000, 'test')
}, 50)
})

const openListener = (data) => {
// Verify handshake response data
ok(data.handshakeResponse, 'handshakeResponse should be defined')
equal(data.handshakeResponse.status, 101, 'status should be 101')
equal(data.handshakeResponse.statusText, 'Switching Protocols', 'statusText should be correct')
// Check handshake response headers
const headers = data.handshakeResponse.headers
ok(headers, 'headers should be defined')
ok(typeof headers === 'object', 'headers should be an object')
ok('upgrade' in headers, 'upgrade header should be present')
ok('connection' in headers, 'connection header should be present')
ok('sec-websocket-accept' in headers, 'sec-websocket-accept header should be present')
// Optionally, check values
equal(headers.upgrade.toLowerCase(), 'websocket', 'upgrade header should be websocket')
equal(headers.connection.toLowerCase(), 'upgrade', 'connection header should be upgrade')
ok(typeof headers['sec-websocket-accept'] === 'string', 'sec-websocket-accept header should be a string')
}

dc.channel('undici:websocket:open').subscribe(openListener)

t.after(() => {
server.close()
dc.channel('undici:websocket:open').unsubscribe(openListener)
})

// Create WebSocket connection
// eslint-disable-next-line no-unused-vars
const _ws = new WebSocket(`ws://localhost:${port}`)

await completed
})
Loading