Skip to content

WebSocket doesn't dispatch error event when underlying socket is unexpectedly destroyed #4487

@auvred

Description

@auvred

Bug Description

After a WebSocket connection is established, the error event is not dispatched on network failures; only the close event is emitted.

Reproducible By

const { WebSocketServer } = require('ws')
const { setGlobalDispatcher, Agent, WebSocket } = require('undici')

let socket
// extract the underlying socket, so we can destroy it later
setGlobalDispatcher(new (class foo extends Agent {
  dispatch(opts, handler) {
    return super.dispatch(opts, {
      ...handler,
      onUpgrade(statusCode, headers, _socket) {
        socket = _socket
        return handler.onUpgrade(statusCode, headers, _socket)
      }
    })
  }
})())

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

const ws = new WebSocket(`ws://localhost:${server.address().port}`)

ws.onopen = () => {
  console.log('open')
  setTimeout(() => {
    console.log('destroying socket')
    socket.destroy(new Error('foo'))
  }, 1000)
}
ws.onerror = () => console.log('error')
ws.onclose = e => console.log('close', e.code)

Expected Behavior

According to WHATWG WebSockets Standard section 4. Feedback from the protocol:

When the WebSocket connection is closed, possibly cleanly, the user agent must queue a task to run the following substeps:

  1. Change the ready state to CLOSED (3).
  2. If the user agent was required to fail the WebSocket connection, or if the WebSocket connection was closed after being flagged as full, fire an event named error at the WebSocket object. [WSP]
  3. Fire an event named close at the WebSocket object, [...]

Note the 2. If the user agent was required to fail the WebSocket connection, [...], fire an event named error.

According to RFC 6455 section 7.1.7. Client-Initiated Closure:

If at any point the underlying transport layer connection is unexpectedly lost, the client MUST _Fail the WebSocket Connection_.

A net.Socket is destroyed with an ErrnoException when the network connection is lost. Therefore, the WebSocket client should fail the connection and dispatch an error event.

Logs & Screenshots

Running the reproduction:

$ node index.js
open
destroying socket
close 1006

^ Notice that no error message is logged.

I tried this in Firefox: connected to the remote WebSocket and then brought the network interface down. In that case, the error listener was called after the connection was lost.

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingwebsocketPull requests or issues related to websocket and its standard

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions