Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closing WebSockets from the server results in 'Close received after close' error #21642

Open
jespertheend opened this issue Dec 19, 2023 · 6 comments · May be fixed by denoland/fastwebsockets#72 or #22684

Comments

@jespertheend
Copy link
Contributor

Version: Deno 1.39.0

This is similar to #20948 except that in this case WebSocket.close() is called from Deno's end.
While #20948 was fixed in #20518, this issue still seems to be reproducible.
This too seems to have regressed somewhere between 1.32.3 and 1.32.4.

To reproduce, run the following with deno run --unstable -A foo.js:

Deno.serve({hostname: "0.0.0.0", port: 8000}, (request) => {
	const url = new URL(request.url);
	if (url.pathname == "/") {
		return new Response(`
			<script>
				const url = new URL(location.href);
				url.pathname = "/ws";
				url.protocol = "ws:";
				const ws = new WebSocket(url.href);
			</script>
			<button onclick="ws.send('close')">close</button>
		`, {
			headers: {
				"Content-Type": "text/html",
			}
		});
	} else if (url.pathname == "/ws") {
		const { socket, response } = Deno.upgradeWebSocket(request);
		socket.addEventListener("open", () => {
			console.log("open");
		});
		socket.addEventListener("message", () => {
			console.log("close");
			socket.close();
		});
		return response;
	}
	return new Response("not found", {status: 404})
});

Then visit http://localhost:8000/ and click the 'close' button.

The browser console logs:

WebSocket connection to 'ws://localhost:8000/ws' failed: Close received after close
@chefnaphtha
Copy link

can repro - note that this only happens on chromium, not firefox

@chefnaphtha
Copy link

client workaround:

ws.addEventListener('error', () => {
	if (ws.readyState == ws.CLOSED) return onSuccess()
	onError()
})

@jespertheend
Copy link
Contributor Author

That workaround doesn't work for me, it still logs the message in the browser console

@chefnaphtha
Copy link

still logs, but that's not important in my case, in my case i was showing an error screen and stopping the process as soon as an error event was fired. close received after close fires that error event, which made the process always fail

@jespertheend
Copy link
Contributor Author

I found only one Chromium bug that mentions the error message: https://crbug.com/40718979
A Chromium developer mentioned that the message means that the server has sent two close frames.

The only thing I was able to find is that the following code is fired right after sending the close frame:

deno/ext/websocket/lib.rs

Lines 752 to 755 in 942b2aa

let mut sender = move |frame| {
let writer = writer.clone();
async move { writer.borrow_mut().await.write_frame(frame).await }
};

But I don't know nearly enough about this and for all I know this might be intentional.

@jespertheend jespertheend linked a pull request Mar 3, 2024 that will close this issue
@daem-on
Copy link

daem-on commented Mar 6, 2024

I want to add that the CloseEvent that is generated by this close always has a code of 1006 and an empty reason. This makes it impossible to communicate any information with a websocket close in Chromium browsers - so it's not just about the console error log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants