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

When Mojo WebSocket enables permessage-deflate, it causes abnormal data transmission. #2235

Open
hbasrc opened this issue Mar 5, 2025 · 3 comments

Comments

@hbasrc
Copy link

hbasrc commented Mar 5, 2025

This issue occurs only with certain WebSocket server communications. When permessage-deflate is enabled, the first time the payload is sent, it works normally. However, when sending again the second time, it is found that the compressed payload generated by build_message is already corrupted, resulting in a 1006 disconnection. My solution is:

In Mojo::Transaction::WebSocket:

sub build_message {
  my ($self, $frame) = @_;
  ...
  $deflate->flush($out, Z_SYNC_FLUSH);
  $deflate->deflateReset();
}

But I believe this is not the final or best fix.

Here is the debug info after enabling permessage-deflate:

-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
send heartbeat.
-- Building frame (1, 1, 0, 0, 1)
-- Small payload (52)
"\252VJL.\311\314\317S\262R\312HM,*IJM,Q\322QJ\316\311L\315+\211/)V\2622471\2604\260436300\3203\250\5\0"  <---- Payload is correct at this time
-- Parsing frame (1, 1, 0, 0, 1)
-- Small payload (66)
"\0<\0\303\377{\"channel\":\"heartbeat\",\"data\":[{\"server_ts\":1740909635305}]}\0"
\ {
    channel   "heartbeat",
    data      [
        [0] {
            server_ts   1740909635305
        }
    ]
}
-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
send heartbeat.
-- Building frame (1, 1, 0, 0, 1)
-- Small payload (8)
"\252&Z\207)T\a\0"                   <--------- payload was wrong the second time
-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
WebSocket closed with status 1006:
Attempting to websocket reconnect in 5 seconds...
Terminating on signal SIGINT(2)

Here is without permessage-deflate, it works fine:

-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
send heartbeat.
-- Building frame (1, 0, 0, 0, 1)
-- Small payload (50)
"{\"action\":\"heartbeat\",\"client_ts\":1740909589000.0}"
-- Parsing frame (1, 0, 0, 0, 1)
-- Small payload (60)
"{\"channel\":\"heartbeat\",\"data\":[{\"server_ts\":1740909588668}]}"
\ {
    channel   "heartbeat",
    data      [
        [0] {
            server_ts   1740909588668
        }
    ]
}
-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
send heartbeat.
-- Building frame (1, 0, 0, 0, 1)
-- Small payload (50)
"{\"action\":\"heartbeat\",\"client_ts\":1740909609000.0}"
-- Parsing frame (1, 0, 0, 0, 1)
-- Small payload (60)
"{\"channel\":\"heartbeat\",\"data\":[{\"server_ts\":1740909608669}]}"
\ {
    channel   "heartbeat",
    data      [
        [0] {
            server_ts   1740909608669
        }
    ]
}
Terminating on signal SIGINT(2)

@kraih
Copy link
Member

kraih commented Mar 5, 2025

There is context here missing. What client/server are involved here? Could they have a broken permessage-deflate implementation?

@hbasrc
Copy link
Author

hbasrc commented Mar 5, 2025

The client is tested with Mojolicious versions 8.71 to 9.39, and the systems include both Linux and Windows, with the same results. The server is opaque, so it is unknown, but it is known to have passed through Cloudflare WAF. I also tried implementing the same code in Python and JavaScript , and the results were normal; only the Perl Mojo implementation is not functioning correctly.

@hbasrc
Copy link
Author

hbasrc commented Mar 5, 2025

I also tried injecting debug code into build_message:

use Data::Printer;
sub build_message {
  my ($self, $frame) = @_;
  ...

say "Before deflate: ", length($frame->[5]);
p $frame->[5];
  my $deflate = $self->{deflate}
    ||= Compress::Raw::Zlib::Deflate->new(AppendOutput => 1, MemLevel => 8, WindowBits => -15);
  $deflate->deflate($frame->[5], my $out);
  $deflate->flush($out, Z_SYNC_FLUSH);
say "After deflate: ", length($out);
p $out;

Clearly, payload was not flushed to $out, result is as follows:

send heartbeat.
Before deflate: 50
"{"action":"heartbeat","client_ts":1741187284000.0}"
After deflate: 56
"猇JL.商蟂睷蔋M,*IJM,Q襋J紊L??)V?4714?7?100??1187284\0\0\0"
-- Building frame (1, 1, 0, 0, 1)
-- Small payload (52)
"\252VJL.\311\314\317S\262R\312HM,*IJM,Q\322QJ\316\311L\315+\211/)V\26224714\26407\2620100\3203\250\5\0"
-- Parsing frame (1, 1, 0, 0, 1)
-- Small payload (66)
"\0<\0\303\377{\"channel\":\"heartbeat\",\"data\":[{\"server_ts\":1741187283102}]}\0"
\ {
    channel   "heartbeat",
    data      [
        [0] {
            server_ts   1741187283102
        }
    ]
}
-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
-- Parsing frame (1, 0, 0, 0, 9)
-- Small payload (4)
"ping"
-- Building frame (1, 0, 0, 0, 10)
-- Small payload (4)
"ping"
send heartbeat.
Before deflate: 50
"{"action":"heartbeat","client_ts":1741187304000.0}"
After deflate: 13
"?V嚤�Td\0\0\0"
-- Building frame (1, 1, 0, 0, 1)
-- Small payload (9)
"\252&V\207\261\1T\a\0"
WebSocket closed with status 1006:
Attempting to websocket reconnect in 5 seconds...
Terminating on signal SIGINT(2)

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

No branches or pull requests

2 participants