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