1use Mojo::Base -strict; 2 3use Test::More; 4use Mojo::Transaction::WebSocket; 5use Mojo::WebSocket qw(WS_BINARY WS_CLOSE WS_CONTINUATION WS_PING WS_PONG WS_TEXT), qw(build_frame parse_frame); 6 7# Simple text frame roundtrip 8my $bytes = build_frame 0, 1, 0, 0, 0, WS_TEXT, 'whatever'; 9is $bytes, "\x81\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 10my $frame = parse_frame \(my $dummy = $bytes), 262144; 11is $frame->[0], 1, 'fin flag is set'; 12is $frame->[1], 0, 'rsv1 flag is not set'; 13is $frame->[2], 0, 'rsv2 flag is not set'; 14is $frame->[3], 0, 'rsv3 flag is not set'; 15is $frame->[4], 1, 'text frame'; 16is $frame->[5], 'whatever', 'right payload'; 17is build_frame(0, 1, 0, 0, 0, 1, 'whatever'), $bytes, 'frames are equal'; 18 19# Simple ping frame roundtrip 20$bytes = build_frame 0, 1, 0, 0, 0, WS_PING, 'whatever'; 21is $bytes, "\x89\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 22$frame = parse_frame \($dummy = $bytes), 262144; 23is $frame->[0], 1, 'fin flag is set'; 24is $frame->[1], 0, 'rsv1 flag is not set'; 25is $frame->[2], 0, 'rsv2 flag is not set'; 26is $frame->[3], 0, 'rsv3 flag is not set'; 27is $frame->[4], 9, 'ping frame'; 28is $frame->[5], 'whatever', 'right payload'; 29is build_frame(0, 1, 0, 0, 0, 9, 'whatever'), $bytes, 'frames are equal'; 30 31# Simple pong frame roundtrip 32$bytes = build_frame 0, 1, 0, 0, 0, WS_PONG, 'whatever'; 33is $bytes, "\x8a\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 34$frame = parse_frame \($dummy = $bytes), 262144; 35is $frame->[0], 1, 'fin flag is set'; 36is $frame->[1], 0, 'rsv1 flag is not set'; 37is $frame->[2], 0, 'rsv2 flag is not set'; 38is $frame->[3], 0, 'rsv3 flag is not set'; 39is $frame->[4], 10, 'pong frame'; 40is $frame->[5], 'whatever', 'right payload'; 41is build_frame(0, 1, 0, 0, 0, 10, 'whatever'), $bytes, 'frames are equal'; 42 43# Simple text frame roundtrip with all flags set 44$bytes = build_frame 0, 1, 1, 1, 1, 1, 'whatever'; 45is $bytes, "\xf1\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 46$frame = parse_frame \($dummy = $bytes), 262144; 47is $frame->[0], 1, 'fin flag is set'; 48is $frame->[1], 1, 'rsv1 flag is set'; 49is $frame->[2], 1, 'rsv2 flag is set'; 50is $frame->[3], 1, 'rsv3 flag is set'; 51is $frame->[4], 1, 'text frame'; 52is $frame->[5], 'whatever', 'right payload'; 53is build_frame(0, 1, 1, 1, 1, 1, 'whatever'), $bytes, 'frames are equal'; 54 55# Simple text frame roundtrip without FIN bit 56$bytes = build_frame 0, 0, 0, 0, 0, 1, 'whatever'; 57is $bytes, "\x01\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 58$frame = parse_frame \($dummy = $bytes), 262144; 59is $frame->[0], 0, 'fin flag is not set'; 60is $frame->[1], 0, 'rsv1 flag is not set'; 61is $frame->[2], 0, 'rsv2 flag is not set'; 62is $frame->[3], 0, 'rsv3 flag is not set'; 63is $frame->[4], 1, 'text frame'; 64is $frame->[5], 'whatever', 'right payload'; 65is build_frame(0, 0, 0, 0, 0, 1, 'whatever'), $bytes, 'frames are equal'; 66 67# Simple text frame roundtrip with RSV1 flags set 68$bytes = build_frame(0, 1, 1, 0, 0, 1, 'whatever'); 69is $bytes, "\xc1\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 70$frame = parse_frame \($dummy = $bytes), 262144; 71is $frame->[0], 1, 'fin flag is set'; 72is $frame->[1], 1, 'rsv1 flag is set'; 73is $frame->[2], 0, 'rsv2 flag is not set'; 74is $frame->[3], 0, 'rsv3 flag is not set'; 75is $frame->[4], 1, 'text frame'; 76is $frame->[5], 'whatever', 'right payload'; 77is build_frame(0, 1, 1, 0, 0, 1, 'whatever'), $bytes, 'frames are equal'; 78 79# Simple continuation frame roundtrip with RSV2 flags set 80$bytes = build_frame(0, 1, 0, 1, 0, WS_CONTINUATION, 'whatever'); 81is $bytes, "\xa0\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 82$frame = parse_frame \($dummy = $bytes), 262144; 83is $frame->[0], 1, 'fin flag is set'; 84is $frame->[1], 0, 'rsv1 flag is not set'; 85is $frame->[2], 1, 'rsv2 flag is set'; 86is $frame->[3], 0, 'rsv3 flag is not set'; 87is $frame->[4], 0, 'continuation frame'; 88is $frame->[5], 'whatever', 'right payload'; 89is build_frame(0, 1, 0, 1, 0, 0, 'whatever'), $bytes, 'frames are equal'; 90 91# Simple text frame roundtrip with RSV3 flags set 92$bytes = build_frame(0, 1, 0, 0, 1, 1, 'whatever'); 93is $bytes, "\x91\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame'; 94$frame = parse_frame \($dummy = $bytes), 262144; 95is $frame->[0], 1, 'fin flag is set'; 96is $frame->[1], 0, 'rsv1 flag is not set'; 97is $frame->[2], 0, 'rsv2 flag is not set'; 98is $frame->[3], 1, 'rsv3 flag is set'; 99is $frame->[4], 1, 'text frame'; 100is $frame->[5], 'whatever', 'right payload'; 101is build_frame(0, 1, 0, 0, 1, 1, 'whatever'), $bytes, 'frames are equal'; 102 103# Simple binary frame roundtrip 104$bytes = build_frame(0, 1, 0, 0, 0, WS_BINARY, 'works'); 105is $bytes, "\x82\x05\x77\x6f\x72\x6b\x73", 'right frame'; 106$frame = parse_frame \($dummy = $bytes), 262144; 107is $frame->[0], 1, 'fin flag is set'; 108is $frame->[1], 0, 'rsv1 flag is not set'; 109is $frame->[2], 0, 'rsv2 flag is not set'; 110is $frame->[3], 0, 'rsv3 flag is not set'; 111is $frame->[4], 2, 'binary frame'; 112is $frame->[5], 'works', 'right payload'; 113is $bytes = build_frame(0, 1, 0, 0, 0, 2, 'works'), $bytes, 'frames are equal'; 114 115# Masked text frame roundtrip 116$bytes = build_frame 1, 1, 0, 0, 0, 1, 'also works'; 117$frame = parse_frame \($dummy = $bytes), 262144; 118is $frame->[0], 1, 'fin flag is set'; 119is $frame->[1], 0, 'rsv1 flag is not set'; 120is $frame->[2], 0, 'rsv2 flag is not set'; 121is $frame->[3], 0, 'rsv3 flag is not set'; 122is $frame->[4], 1, 'text frame'; 123is $frame->[5], 'also works', 'right payload'; 124isnt(build_frame(0, 1, 0, 0, 0, 2, 'also works'), $bytes, 'frames are not equal'); 125 126# Masked binary frame roundtrip 127$bytes = build_frame(1, 1, 0, 0, 0, 2, 'just works'); 128$frame = parse_frame \($dummy = $bytes), 262144; 129is $frame->[0], 1, 'fin flag is set'; 130is $frame->[1], 0, 'rsv1 flag is not set'; 131is $frame->[2], 0, 'rsv2 flag is not set'; 132is $frame->[3], 0, 'rsv3 flag is not set'; 133is $frame->[4], 2, 'binary frame'; 134is $frame->[5], 'just works', 'right payload'; 135isnt(build_frame(0, 1, 0, 0, 0, 2, 'just works'), $bytes, 'frames are not equal'); 136 137# One-character text frame roundtrip 138$bytes = build_frame(0, 1, 0, 0, 0, 1, 'a'); 139is $bytes, "\x81\x01\x61", 'right frame'; 140$frame = parse_frame \($dummy = $bytes), 262144; 141is $frame->[0], 1, 'fin flag is set'; 142is $frame->[1], 0, 'rsv1 flag is not set'; 143is $frame->[2], 0, 'rsv2 flag is not set'; 144is $frame->[3], 0, 'rsv3 flag is not set'; 145is $frame->[4], 1, 'text frame'; 146is $frame->[5], 'a', 'right payload'; 147is build_frame(0, 1, 0, 0, 0, 1, 'a'), $bytes, 'frames are equal'; 148 149# One-byte binary frame roundtrip 150$bytes = build_frame(0, 1, 0, 0, 0, 2, 'a'); 151is $bytes, "\x82\x01\x61", 'right frame'; 152$frame = parse_frame \($dummy = $bytes), 262144; 153is $frame->[0], 1, 'fin flag is set'; 154is $frame->[1], 0, 'rsv1 flag is not set'; 155is $frame->[2], 0, 'rsv2 flag is not set'; 156is $frame->[3], 0, 'rsv3 flag is not set'; 157is $frame->[4], 2, 'binary frame'; 158is $frame->[5], 'a', 'right payload'; 159is $bytes = build_frame(0, 1, 0, 0, 0, 2, 'a'), $bytes, 'frames are equal'; 160 161# 16-bit text frame roundtrip 162$bytes = build_frame(0, 1, 0, 0, 0, 1, 'hi' x 10000); 163is $bytes, "\x81\x7e\x4e\x20" . ("\x68\x69" x 10000), 'right frame'; 164$frame = parse_frame \($dummy = $bytes), 262144; 165is $frame->[0], 1, 'fin flag is set'; 166is $frame->[1], 0, 'rsv1 flag is not set'; 167is $frame->[2], 0, 'rsv2 flag is not set'; 168is $frame->[3], 0, 'rsv3 flag is not set'; 169is $frame->[4], 1, 'text frame'; 170is $frame->[5], 'hi' x 10000, 'right payload'; 171is build_frame(0, 1, 0, 0, 0, 1, 'hi' x 10000), $bytes, 'frames are equal'; 172 173# 64-bit text frame roundtrip 174$bytes = build_frame(0, 1, 0, 0, 0, 1, 'hi' x 200000); 175is $bytes, "\x81\x7f\x00\x00\x00\x00\x00\x06\x1a\x80" . ("\x68\x69" x 200000), 'right frame'; 176$frame = parse_frame \($dummy = $bytes), 500000; 177is $frame->[0], 1, 'fin flag is set'; 178is $frame->[1], 0, 'rsv1 flag is not set'; 179is $frame->[2], 0, 'rsv2 flag is not set'; 180is $frame->[3], 0, 'rsv3 flag is not set'; 181is $frame->[4], 1, 'text frame'; 182is $frame->[5], 'hi' x 200000, 'right payload'; 183is build_frame(0, 1, 0, 0, 0, 1, 'hi' x 200000), $bytes, 'frames are equal'; 184 185# Empty text frame roundtrip 186$bytes = build_frame(0, 1, 0, 0, 0, 1, ''); 187is $bytes, "\x81\x00", 'right frame'; 188$frame = parse_frame \($dummy = $bytes), 262144; 189is $frame->[0], 1, 'fin flag is set'; 190is $frame->[1], 0, 'rsv1 flag is not set'; 191is $frame->[2], 0, 'rsv2 flag is not set'; 192is $frame->[3], 0, 'rsv3 flag is not set'; 193is $frame->[4], 1, 'text frame'; 194is $frame->[5], '', 'no payload'; 195is build_frame(0, 1, 0, 0, 0, 1, ''), $bytes, 'frames are equal'; 196 197# Empty close frame roundtrip 198$bytes = build_frame(0, 1, 0, 0, 0, WS_CLOSE, ''); 199is $bytes, "\x88\x00", 'right frame'; 200$frame = parse_frame \($dummy = $bytes), 262144; 201is $frame->[0], 1, 'fin flag is set'; 202is $frame->[1], 0, 'rsv1 flag is not set'; 203is $frame->[2], 0, 'rsv2 flag is not set'; 204is $frame->[3], 0, 'rsv3 flag is not set'; 205is $frame->[4], 8, 'close frame'; 206is $frame->[5], '', 'no payload'; 207is build_frame(0, 1, 0, 0, 0, 8, ''), $bytes, 'frames are equal'; 208 209# Masked empty binary frame roundtrip 210$bytes = build_frame(1, 1, 0, 0, 0, 2, ''); 211$frame = parse_frame \($dummy = $bytes), 262144; 212is $frame->[0], 1, 'fin flag is set'; 213is $frame->[1], 0, 'rsv1 flag is not set'; 214is $frame->[2], 0, 'rsv2 flag is not set'; 215is $frame->[3], 0, 'rsv3 flag is not set'; 216is $frame->[4], 2, 'binary frame'; 217is $frame->[5], '', 'no payload'; 218isnt(build_frame(0, 1, 0, 0, 0, 2, ''), $bytes, 'frames are not equal'); 219 220# Size limit 221$bytes = build_frame(0, 1, 0, 0, 0, WS_BINARY, 'works'); 222is $bytes, "\x82\x05\x77\x6f\x72\x6b\x73", 'right frame'; 223$frame = parse_frame \($dummy = $bytes), 4; 224ok $frame, 'true'; 225ok !ref $frame, 'not a reference'; 226 227# Incomplete frame 228is parse_frame(\($dummy = "\x82\x05\x77\x6f\x72\x6b"), 262144), undef, 'incomplete frame'; 229 230# Fragmented message 231my $fragmented = Mojo::Transaction::WebSocket->new; 232my $text; 233$fragmented->on(text => sub { $text = pop }); 234$fragmented->parse_message([0, 0, 0, 0, WS_TEXT, 'wo']); 235ok !$text, 'text event has not been emitted yet'; 236$fragmented->parse_message([0, 0, 0, 0, WS_CONTINUATION, 'r']); 237ok !$text, 'text event has not been emitted yet'; 238$fragmented->parse_message([1, 0, 0, 0, WS_CONTINUATION, 'ks!']); 239is $text, 'works!', 'right payload'; 240 241# Compressed binary message 242my $compressed = Mojo::Transaction::WebSocket->new({compressed => 1}); 243$frame = $compressed->build_message({binary => 'just works'}); 244is $frame->[0], 1, 'fin flag is set'; 245is $frame->[1], 1, 'rsv1 flag is set'; 246is $frame->[2], 0, 'rsv2 flag is not set'; 247is $frame->[3], 0, 'rsv3 flag is not set'; 248is $frame->[4], WS_BINARY, 'binary frame'; 249ok $frame->[5], 'has payload'; 250my $payload = $compressed->build_message({binary => 'just works'})->[5]; 251isnt $frame->[5], $payload, 'different payload'; 252ok length $frame->[5] > length $payload, 'payload is smaller'; 253my $uncompressed = Mojo::Transaction::WebSocket->new; 254my $frame2 = $uncompressed->build_message({binary => 'just works'}); 255is $frame2->[0], 1, 'fin flag is set'; 256is $frame2->[1], 0, 'rsv1 flag is not set'; 257is $frame2->[2], 0, 'rsv2 flag is not set'; 258is $frame2->[3], 0, 'rsv3 flag is not set'; 259is $frame2->[4], WS_BINARY, 'binary frame'; 260ok $frame2->[5], 'has payload'; 261isnt $frame->[5], $frame2->[5], 'different payload'; 262is $frame2->[5], $uncompressed->build_message({binary => 'just works'})->[5], 'same payload'; 263 264# Compressed fragmented message 265my $fragmented_compressed = Mojo::Transaction::WebSocket->new({compressed => 1}); 266$text = undef; 267$fragmented_compressed->on(message => sub { $text = pop }); 268my $compressed_payload = $fragmented_compressed->build_message({text => 'just works'})->[5]; 269ok !$text, 'message event has not been emitted yet'; 270$fragmented_compressed->parse_message([0, 1, 0, 0, WS_TEXT, substr($compressed_payload, 0, 3)]); 271ok !$text, 'message event has not been emitted yet'; 272$fragmented_compressed->parse_message([0, 0, 0, 0, WS_CONTINUATION, substr($compressed_payload, 3, 3)]); 273ok !$text, 'message event has not been emitted yet'; 274$fragmented_compressed->parse_message([1, 0, 0, 0, WS_CONTINUATION, substr($compressed_payload, 6)]); 275is $text, 'just works', 'decoded correctly'; 276 277done_testing(); 278