Skip to content
Open
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
31 changes: 28 additions & 3 deletions lib/web/websocket/stream/websocketstream.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class WebSocketStream {
onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
onMessage: (opcode, data) => this.#onMessage(opcode, data),
onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),
onParserDrain: () => this.#handler.socket.resume(),
onParserDrain: () => this.#resumeSocketIfNeeded(),
onSocketData: (chunk) => {
if (!this.#parser.write(chunk)) {
this.#handler.socket.pause()
Expand Down Expand Up @@ -117,7 +117,7 @@ class WebSocketStream {
this.#closedPromise = Promise.withResolvers()

// 7. Apply backpressure to the WebSocket.
// TODO
// This is done when the socket becomes available.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong and could possibly lead to dropped messages.

  • user does new WebSocketStream(...)
  • await ws.opened
  • sets up the ReadableStream idk, 10 seconds later

any messages between the connection opening and reading messages will get "dropped" (stream will pull them even if the user isn't)


// 8. If options [" signal "] exists ,
if (options.signal != null) {
Expand Down Expand Up @@ -195,6 +195,22 @@ class WebSocketStream {

// 3. Close the WebSocket with this , code , and reason .
closeWebSocketConnection(this.#handler, code, reason, true)
this.#resumeSocketIfNeeded()
}

#resumeSocketIfNeeded () {
const socket = this.#handler.socket

if (socket == null) {
return
}

if (
this.#handler.readyState === states.CLOSING ||
(this.#readableStreamController?.desiredSize > 0 && !this.#parser.writableNeedDrain)
) {
socket.resume()
}
}

#write (chunk) {
Expand Down Expand Up @@ -257,6 +273,7 @@ class WebSocketStream {
/** @type {import('../websocket').Handler['onConnectionEstablished']} */
#onConnectionEstablished (response, parsedExtensions) {
this.#handler.socket = response.socket
this.#handler.socket.pause()

// Get options from dispatcher options
const maxFragments = this.#handler.controller.dispatcher?.webSocketOptions?.maxFragments
Expand Down Expand Up @@ -291,6 +308,7 @@ class WebSocketStream {
start: (controller) => {
this.#readableStreamController = controller
},
pull: () => this.#resumeSocketIfNeeded(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" // 5. Let pullAlgorithm be an action that pulls bytes from stream ."

#resumeSocketIfNeeded does not do that

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't it make sense to handle backpressure in the WHATWG stream instead of the node socket? This implementation doesn't look correct to me

cancel: (reason) => this.#cancel(reason)
})

Expand All @@ -301,7 +319,10 @@ class WebSocketStream {
// 13. Set up writable with writeAlgorithm , closeAlgorithm , and abortAlgorithm .
const writable = new WritableStream({
write: (chunk) => this.#write(chunk),
close: () => closeWebSocketConnection(this.#handler, null, null),
close: () => {
closeWebSocketConnection(this.#handler, null, null)
this.#resumeSocketIfNeeded()
},
abort: (reason) => this.#closeUsingReason(reason)
})

Expand Down Expand Up @@ -350,6 +371,9 @@ class WebSocketStream {
this.#readableStreamController.enqueue(chunk)

// 4. Apply backpressure to the WebSocket.
if (this.#readableStreamController.desiredSize <= 0) {
this.#handler.socket.pause()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto wrt node socket vs whatwg stream.

}
}

/** @type {import('../websocket').Handler['onSocketClose']} */
Expand Down Expand Up @@ -441,6 +465,7 @@ class WebSocketStream {
// 4. Close the WebSocket with stream , code , and reasonString . If this throws an exception,
// discard code and reasonString and close the WebSocket with stream .
closeWebSocketConnection(this.#handler, code, reasonString)
this.#resumeSocketIfNeeded()
}

// To cancel a WebSocketStream stream given reason , close using reason giving stream and reason .
Expand Down
3 changes: 1 addition & 2 deletions test/web-platform-tests/expectation.json
Original file line number Diff line number Diff line change
Expand Up @@ -39163,8 +39163,7 @@
"cases": [
{
"name": "backpressure should be applied to received messages",
"success": false,
"message": "assert_greater_than_equal: data send should have taken at least 2 seconds expected a number greater than or equal to 1.8 but got 0.0826730728149414"
"success": true
}
]
},
Expand Down
Loading