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

Sanic 24.6 does not support websocket latest version 14.2 #3031

Open
1 task done
banniford opened this issue Jan 21, 2025 · 8 comments
Open
1 task done

Sanic 24.6 does not support websocket latest version 14.2 #3031

banniford opened this issue Jan 21, 2025 · 8 comments
Labels

Comments

@banniford
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Sanic 24.6 will install websocket 14.2 released on January 20th during automatic dependency installation. This dependency will cause websocket to fail to connect and report the error "websocket Unrecognized frame opcode: 13". It is necessary to manually specify the websocket version as 14.1

Code snippet

No response

Expected Behavior

No response

How do you run Sanic?

Sanic CLI

Operating System

Linux

Sanic Version

24.6

Additional context

No response

@banniford banniford added the bug label Jan 21, 2025
@banniford
Copy link
Author

sanic 24.6 在自动安装依赖时会安装一月二十号发布的 websockets 14.2,该依赖会导致 websocket 无法连接并报错 ”websocket Unrecognized frame opcode: 13“,需要手动指定 websockets 版本为 14.1

websockets==14.1

@klausmyrseth
Copy link

klausmyrseth commented Jan 21, 2025

I experienced the same yesterday (think this goes for more then only latest version), also when downgrading sanic it still kept websocket 14.2 as a dependency and failed.

Workaround if you have pipenv for instance is to force version 14.1 for websocket as library and it works fine.

Adding note to verify observation and subscribe to bug as we want to remove the version force as soon as this is fixed!

erhuabushuo added a commit to erhuabushuo/sanic that referenced this issue Jan 24, 2025
erhuabushuo added a commit to erhuabushuo/sanic that referenced this issue Jan 24, 2025
@dhensen
Copy link

dhensen commented Feb 5, 2025

Omg this took me so much time to find... I kept getting a websocket code 1006. I blamed it on my own doing, but it was the 14.2. Good work reporting this 👍

banniford added a commit to banniford/sanic that referenced this issue Feb 6, 2025
@juliocesar-io
Copy link

I can confirm this issue.

When using Sanic 24.6, the installer automatically pulls in websockets==14.2 (released on January 19, 2025). This version causes WebSocket connections to fail with the error:

websocket Unrecognized frame opcode: 13

To work around this, I manually force websockets==14.1 as commented by @banniford

However, I’m using uv as package manager , and adding websockets==14.1 breaks the entire build. I’m not sure whether the root cause is in “uv,” changes introduced by websockets==14.2, or sub-dependency resolution within “uv.”

Is working well for now. 👍

Environment & Testing

  • Operating Systems: Ubuntu, macOS
  • Python Versions: 3.9, 3.11, 3.12
  • Sanic Versions: 22.12.0, 24.6

Summary

  • Bug: Sanic 24.6 + websockets==14.2 causes WebSocket connections to fail.
  • Workaround: Pin the dependency to websockets==14.1 forcing with pip

I hope this clarifies the issue for anyone experiencing the same problem and leads to a permanent fix.

@alenart91
Copy link

Currently running Sanic v24.12.0 and was getting error code 1006 (invalid frame header in Brave browser).

pip uninstall websockets
pip install websockets==14.1

Worked perfectly for me

@jamesj-steam
Copy link

Same here running Sanic v24.12.0, returns an invalid frame header exception in Chrome and IE.

Forcing websockets==14.1 fixed it in our Docker.

@andershol
Copy link

If you take the server and client samples currently on https://websockets.readthedocs.io/en/stable/ and run them together it works. But if you replace the server with a minimal Sanic sample, from the "Websockets" tab in the bottom of https://sanic.dev/en/ (with the missing "app = Sanic("App")" and "app.run()" added, and the client changed to use port 8000), you get the problem with "13 is not a valid Opcode" (you might comment out the two places in the websockets library where ProtocolError is caught, to have it be caught as a standard exception for more verbose debug output).

If you look at the conversation using Wireshark you see that the client sends a GET request with an "upgrade" header, the server responds with 101 "switching protocol" and then the client closes the connection with status code 1011. So it is the client that seem to be unhappy.

If you add logging of self.reader.buffer inside parse() in protocol.py (e.g. just before Frame.parse is called) you see that the buffer contains b'\r\n\r\n' when it fails which looks like to line breaks but apparently is interpreted as a frame with opcode 13 (\r is char 13).

Looking a bit more at the Wireshark dump, you see that the "switching protocol" response from the sanic-server ends with "0d 0a 0d 0a 0d 0a 0d 0a" after the last http-header, i.e. not just the double line-break that signify the end of the http-header and start of the http-body, but an additional double line break as the body. I would guess that it is this double line break body that is interpreted as an invalid websocket-frame in the new version of websockets and ignored in the previous version. I guess would one would have to look into if this is a protocol violation to decide if this is a bug in sanic or websockets (and even if it is a bug in sanic, websockets might still be changed to just ignore it).

@andershol
Copy link

As I read rfc 6455 section 4.2.2 it doesn't explicitly say that the server can not send content in the http body, but it says, after describing the http header fields, "This completes the server's handshake. [...] At this point, the server may begin sending (and receiving) data." And since tcp is stream based and the http response send by sanic do not contain a content-length field, there would be no way for the client to know when a http body ended. So it seems that the double line-break in the body is invalid.

Looking a the Sanic code it seems the extra line-break is added here. That construction just looks wrong as there do not seem to be a reason to add a line-break after the body in http. The line-break appears to only be added if resp.body is not None, but resp can only come from accept() (in websockets) and it returns a Response where body defaults to b"" (not None), so this is why we see the extra line-break even though the body is empty. Therefore, removing the rbody += b"\r\n\r\n" would seem to fix the problem for the success path in accept() (and a quick test shows that it indeed does fix it). Looking at the failure paths in accept() it seems they add a failure message to the body, but in this failure message they use LF-line-breaks, so it again doesn't make much sense to add a CRLF-line-break after such a message.

So the fix for this issue seems to be just removing the rbody += b"\r\n\r\n" in sanic/server/protocols/websocket_protocol.py .

altersun added a commit to altersun/d-stack that referenced this issue Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants