vti / protocol-websocket

Protocol::WebSocket
Other
40 stars 27 forks source link

Server drops connection randomly #59

Closed cc32d9 closed 5 years ago

cc32d9 commented 5 years ago

I wrote this test script that starts a WS server and dumps the JSON messages coming in: https://github.com/EOSChronicleProject/eos-chronicle/blob/master/testing/chronicle-ws-dumper.pl

The problem is, it starts working properly, and then closes the connection for no apparent reason. Net::WebSocket::Server reports protocol error, so probably some options in frame encoding are not understood by the Protocol::Websocket library.

I will attach a packet capture. The Perl server is running on localhost:8800. Wireshark does not show any errors.

cc32d9 commented 5 years ago

ws.pcap.zip

cc32d9 commented 5 years ago

the websocket connection is configured as binary.

lighterowl commented 5 years ago

The client seems to be sending a lot of continuation frames - there's still no FIN frame even by the time the server is already dead, i.e. the RST frame is sent at the end of the capture. Perhaps you've hit some kind of internal message length limit?

cc32d9 commented 5 years ago

I'm sending messages with Boost library, calling write() or async_write()

https://www.boost.org/doc/libs/1_67_0/libs/beast/doc/html/beast/using_websocket/send_and_receive_messages.html

It's supposed to divide messages into smaller frames internally.

cc32d9 commented 5 years ago

oh, wait. Net::WebSocket::Server::Connection sets the maximum payload size to 64k:

https://metacpan.org/source/Net::WebSocket::Server::Connection

I'll check with a bigger value now.

cc32d9 commented 5 years ago

actually here, line 162: https://metacpan.org/source/Net::WebSocket::Server::Connection#L162

it creates a new frame with default parameters, and that object has max payload size of 64k. So I;ll need to set the package variable $MAX_PAYLOAD_SIZE for that.

lighterowl commented 5 years ago

This might be it, since exceeding max_frame_size causes next_bytes to die, which would've caused the code to break out the while loop and return 1002 in the connection close frame, since $@ would be set to the string that the evaled function died with. Incidentally, 1002 is what the server returned according to the capture.

cc32d9 commented 5 years ago

I set $Protocol::WebSocket::Frame::MAX_PAYLOAD_SIZE = 100*1024*1024; , but it still the same behavior. I keep digging.

lighterowl commented 5 years ago

You could also just modify your local copy of Net::WebSocket::Server::Connection to print $@ inside that if, that would certainly help a lot. It might have also failed due to too many continuation frames.

cc32d9 commented 5 years ago

yes, that's what I did. I get:

Too many fragments at /usr/local/share/perl/5.26.2/Protocol/WebSocket/Frame.pm line 231

you set maximum number of fragments to 128. Is there a way to increase that? Does the protocol allow it?

lighterowl commented 5 years ago

No, the protocol does not place any restrictions on the number of frames. This is purely an implementation limit that you're free to raise. In fact, the RFC itself says (under "implementation note") the payload of fragmented frames may be delivered to the client (i.e. a client using a WebSocket API in a program) without waiting for the last fragment. Perhaps this (c|sh)ould be implemented here (or in Net::WebSocket::Server) to encourage such usage.

cc32d9 commented 5 years ago

In my use-case, I need the whole message, and it may be quite large, even multiple megabytes.

Could you maybe define another package variable instead of hard-coding 128 fragments? That would help me a lot.

lighterowl commented 5 years ago

Sure, I guess that would work. I'll prepare a PR shortly.

cc32d9 commented 5 years ago

Thanks. In the meanwhile I set the hardcoded value to 12800, and it runs beautifully :)