python-websockets / websockets

Library for building WebSocket servers and clients in Python
https://websockets.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
5.23k stars 519 forks source link

websocket over stomp received Session closed error #1474

Closed chj113 closed 4 months ago

chj113 commented 5 months ago

I connected springboot websocket server by python client(javascript client stomp.js on chrome is ok), but receive Session closed ERROR...,I need help,thanks..

python:3.8 websockets:13.0

python log

DEBUG:websockets.client:> GET /websocket HTTP/1.1
DEBUG:websockets.client:> Host: xxx.com
DEBUG:websockets.client:> Upgrade: websocket
DEBUG:websockets.client:> Connection: Upgrade
DEBUG:websockets.client:> Sec-WebSocket-Key: jAjJP5WRdLlo2EVBLgrncQ==
DEBUG:websockets.client:> Sec-WebSocket-Version: 13
DEBUG:websockets.client:> Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
DEBUG:websockets.client:> User-Agent: Python/3.8 websockets/12.0
DEBUG:websockets.client:< HTTP/1.1 101
DEBUG:websockets.client:< Server: nginx
DEBUG:websockets.client:< Date: Fri, 28 Jun 2024 09:39:51 GMT
DEBUG:websockets.client:< Connection: upgrade
DEBUG:websockets.client:< Vary: Origin
DEBUG:websockets.client:< Vary: Access-Control-Request-Method
DEBUG:websockets.client:< Vary: Access-Control-Request-Headers
DEBUG:websockets.client:< Upgrade: websocket
DEBUG:websockets.client:< Sec-WebSocket-Accept: j7eoOgoDCJ5Zw9lP7dt5vKB1ff0=
DEBUG:websockets.client:< Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
DEBUG:websockets.client:< X-Content-Type-Options: nosniff
DEBUG:websockets.client:< X-XSS-Protection: 1; mode=block
DEBUG:websockets.client:= connection is OPEN
DEBUG:websockets.client:> TEXT 'CONNECT\naccept-version:1.2,1.1,1.0\nheart-beat...Z5Zj_dHBLqaenQg\n\n\x00' [258 bytes]
DEBUG:websockets.client:< TEXT 'CONNECTED\nversion:1.2\nheart-beat:10000,4000\n...-111.222.111.222\n\n\x00' [91 bytes]
Connected to STOMP server
DEBUG:websockets.client:% sending keepalive ping
DEBUG:websockets.client:> PING 7e 7e 1d 92 [binary, 4 bytes]
DEBUG:websockets.client:< PONG 7e 7e 1d 92 [binary, 4 bytes]
DEBUG:websockets.client:% received keepalive pong
DEBUG:websockets.client:% sending keepalive ping
DEBUG:websockets.client:> PING c4 f5 ce 9a [binary, 4 bytes]
DEBUG:websockets.client:< TEXT '\n' [1 byte]
DEBUG:websockets.client:< PONG c4 f5 ce 9a [binary, 4 bytes]
<<<

DEBUG:websockets.client:% received keepalive pong
DEBUG:websockets.client:< TEXT 'ERROR\nmessage:Session closed.\ncontent-length:0\n\n\x00' [49 bytes]
DEBUG:websockets.client:< TEXT '\n' [1 byte]
DEBUG:websockets.client:< CLOSE 1002 (protocol error) [2 bytes]
DEBUG:websockets.client:= connection is CLOSING
DEBUG:websockets.client:> CLOSE 1002 (protocol error) [2 bytes]
<<< ERROR
message:Session closed.
content-length:0

python code

import logging
import websockets
from websockets.legacy.client import connect

logging.basicConfig()
logger = logging.getLogger('websockets')
logger.setLevel(logging.DEBUG)

websockets_logger = logging.getLogger('websockets.protocol')
websockets_logger.setLevel(logging.DEBUG)

url = 'wss:.....'
token = '...'

async def wss_connect():
    async for websocket in connect(uri=url,ping_interval=5):
        try:
            connect_frame = (
                'CONNECT\n'
                'accept-version:1.2,1.1,1.0\n'
                'heart-beat:4000,10000\n'
                f'Authorization:{token}\n'
                '\n'
                '\x00'
            )
            await websocket.send(connect_frame)
            response = await websocket.recv()
            if response.startswith('CONNECTED'):
                print("Connected to STOMP server")
                while True:
                    message = await websocket.recv()
                    print(f"<<< {message}")
            else:
                print("Failed to connect to STOMP server")
        except websockets.ConnectionClosed as error:
            print("error", error)
            print("Connection closed, attempting to reconnect...")
            await asyncio.sleep(5)

if __name__ == "__main__":
    asyncio.run(wss_connect())

springboot server:

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(1);
        taskScheduler.setThreadNamePrefix("wss-heartbeat-thread-");
        taskScheduler.initialize();
        registry.enableSimpleBroker("/stream")
                .setTaskScheduler(taskScheduler)
                .setHeartbeatValue(new long[]{10000, 4000});
    }
aaugustin commented 5 months ago

The server is closing the connection, as shown here in the log:

DEBUG:websockets.client:< CLOSE 1002 (protocol error) [2 bytes]

Maybe the server logs say why it's closing the connection?

chj113 commented 4 months ago

No error found in server logs.Other stomp clients like stompjs connect to the same server work well. After I turned off the server's heartbeat, this error no longer exists.but the connection closed after a few minites.

chj113 commented 4 months ago

The server is closing the connection, as shown here in the log:

DEBUG:websockets.client:< CLOSE 1002 (protocol error) [2 bytes]

Maybe the server logs say why it's closing the connection?

server log like this: LoggingWebSocketHandlerDecorator closed with CloseStatus[code=1002, reason=null] It looks like a protocol error. https://stackoverflow.com/questions/65269411/websocket-closes-with-protocol-error-1002 Can y give me a demo that websockets works over stomp?thanks...

aaugustin commented 4 months ago

Try adding ping_interval=None instead of ping_interval=5. If that works, then the problem could be that the server attempts to UTF-8-decode the payload of pings, which is incorrect — nothing says that ping payloads must be valid UTF-8 — but I've seen that before.

The logs that you posted don't corroborate this theory fully. The first ping payload becomes invalid UTF-8 in the fourth byte, while the second ping payload is invalid at the first byte. Still, the fact that the error happens at a different point might cause different behavior.

>>> b"\x7e\x7e\x1d\x92".decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x92 in position 3: invalid start byte
>>> b"\xc4\xf5\xce\x9a".decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc4 in position 0: invalid continuation byte

Can y give me a demo that websockets works over stomp?

I'm not familiar with STOMP. What I see in the logs is a completely valid WebSocket session. From the perspective of the WebSocket RFC, websockets does the right thing. Really the question is "what does the server dislike?"

chj113 commented 4 months ago

@aaugustin Thanks for your help! When I modified it to the following code, it started working properly. I hope it will be helpful to others. heart-beat:4000,10000 -> heart-beat:0,0

connect_frame = (
                'CONNECT\n'
                'accept-version:1.2,1.1,1.0\n'
                'heart-beat:0,0\n'
                f'Authorization:{token}\n'
                '\n'
                '\x00'
            )