deno: Uncatchable `IO error: Broken pipe (os error 32)` when sending to much data through WebSocket
Hi guys !
Tested in Deno 1.20.5
So there are a lot of broken pipe
problems around and a lot of them are workable via try
or Promise.catch
(from other posts I found);
But after further investigation in my own app, I drilled down an error that I couldn’t catch in any way. I identified that it’s happening when there is too much data sent in a WebSocket. Since it’s not catchable I think it’s a bug, but if you expect me to implement the verification then… (but that would be weird regarding the arbitrary limit that has nothing to do with Deno userland => see reprod)
Here is the reprod (check comment to toggle the bug)
// Typical web server
const server = await Deno.listen({ port: 8080 });
// Delaying the client connection to make sure server is up & running
setTimeout(() => {
const sock = new WebSocket("ws://localhost:8080")
sock.onopen = () => {
console.log("clientop")
}
sock.onmessage = () => {
console.log("recv");
Deno.exit(0)
}
}, 1000);
// Wait for a request
for await (const conn of server) {
Deno.serveHttp(conn)
.nextRequest()
.then((event: Deno.RequestEvent | null) => {
if (event) {
const { socket, response } = Deno.upgradeWebSocket(event.request)
// string of length Math.pow(2, 24) + 1 WILL WORK but with Math.pow(2, 24) + 2 we trigger the broken pipe
// I have to admit I don't know why this limit exactly but... found it
const content = new Array(Math.pow(2, 24) + 2).join(":")
socket.onopen = () => {
console.log("open");
// Wait a bit to fullsend...
setTimeout(() => {
console.log("send");
// try won't do anything, and wrapping in Promise to .catch won't either
try {
socket.send(content);
} catch (error) {
console.error("Catch")
}
}, 100);
}
event.respondWith(response);
}
})
}
I didn’t reproduce the bug using only the HTTP without upgrading
Tell me if you need more info, cheers
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 2
- Comments: 15 (6 by maintainers)
I’m not sure I’d feel confident if the errors just went away. This is clearly some timing / size issue in the underlying rust code / network layer. That happens, code’s buggy, pipes break!
The fact that an underlying error cannot be caught by the calling code is concerning. If the calling code could catch these errors, then we could handle them in the application. I think reporting (and ultimately fixing these errors) should still be prioritized, but if underlying network problems leads to system crashes which cannot be avoided, then the platform is unusable.
Here’s a small reproducer that doesn’t depend on message sizes:
I’ve encountered this with a Deno 1.25.0 server, but the snippet reproduces with 1.26 and current
main
as well.I got the same error and am able to narrow it down to following code which reliably crashes opened with chromium, but not with firefox.
This seems to be solved in Deno’s 1.27 release. At least the above reproduction code does no longer throw an error for me after upgrading from 1.25 to 1.27. For reference, I tried this on Manjaro Linux and Ubuntu 20.04.
Yes its that one (https://github.com/denoland/fastwebsockets/commit/93d8aff85020fc95f8a654a97497ddfcd8822990) and relavant commit in Deno: https://github.com/denoland/deno/commit/91dc6fa5f11f5621c21397c783ee899bfd9657ef
Thanks for the report! This was fixed in the latest release (1.36.1). There was a race condition in the recv buffer. I’m going around closing issues related to this but please feel free to reopen if its still happening.
Can confirm that the reproc no longer crashes. (M1, Mac OS 13)
I will see if I can reproduce the error outside of this.
Edit: I was able to reproduce the bug. If you take the first proposed reproc and change the
Math.pow
to 26, it will throw IO error. Wondering if this is intentional or not? If it is, it’s really annoying.I am also experiencing this error. I have a webserver that streams videos, and its practically unusable. I experience this crash whenever I try to skip a part of the video (or even when loading it sometimes). Here is what Deno says when it crashes:
The whole thing is in a try-catch block but it does nothing.