/usr/share/perl5/Mojo/WebSocket.pm is in libmojolicious-perl 7.59+dfsg-1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | package Mojo::WebSocket;
use Mojo::Base -strict;
use Config;
use Exporter 'import';
use Mojo::Util qw(b64_encode dumper sha1_bytes xor_encode);
use constant DEBUG => $ENV{MOJO_WEBSOCKET_DEBUG} || 0;
# Unique value from RFC 6455
use constant GUID => '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
# Perl with support for quads
use constant MODERN =>
(($Config{use64bitint} // '') eq 'define' || $Config{longsize} >= 8);
# Opcodes
use constant {
WS_CONTINUATION => 0x0,
WS_TEXT => 0x1,
WS_BINARY => 0x2,
WS_CLOSE => 0x8,
WS_PING => 0x9,
WS_PONG => 0xa
};
our @EXPORT_OK = (
qw(WS_BINARY WS_CLOSE WS_CONTINUATION WS_PING WS_PONG WS_TEXT),
qw(build_frame challenge client_handshake parse_frame server_handshake)
);
sub build_frame {
my ($masked, $fin, $rsv1, $rsv2, $rsv3, $op, $payload) = @_;
warn "-- Building frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
# Head
my $head = $op + ($fin ? 128 : 0);
$head |= 0b01000000 if $rsv1;
$head |= 0b00100000 if $rsv2;
$head |= 0b00010000 if $rsv3;
my $frame = pack 'C', $head;
# Small payload
my $len = length $payload;
if ($len < 126) {
warn "-- Small payload ($len)\n@{[dumper $payload]}" if DEBUG;
$frame .= pack 'C', $masked ? ($len | 128) : $len;
}
# Extended payload (16-bit)
elsif ($len < 65536) {
warn "-- Extended 16-bit payload ($len)\n@{[dumper $payload]}" if DEBUG;
$frame .= pack 'Cn', $masked ? (126 | 128) : 126, $len;
}
# Extended payload (64-bit with 32-bit fallback)
else {
warn "-- Extended 64-bit payload ($len)\n@{[dumper $payload]}" if DEBUG;
$frame .= pack 'C', $masked ? (127 | 128) : 127;
$frame .= MODERN ? pack('Q>', $len) : pack('NN', 0, $len & 0xffffffff);
}
# Mask payload
if ($masked) {
my $mask = pack 'N', int(rand 9 x 7);
$payload = $mask . xor_encode($payload, $mask x 128);
}
return $frame . $payload;
}
sub challenge {
my $tx = shift;
# "permessage-deflate" extension
my $headers = $tx->res->headers;
$tx->compressed(1)
if ($headers->sec_websocket_extensions // '') =~ /permessage-deflate/;
return _challenge($tx->req->headers->sec_websocket_key) eq
$headers->sec_websocket_accept;
}
sub client_handshake {
my $tx = shift;
my $headers = $tx->req->headers;
$headers->upgrade('websocket') unless $headers->upgrade;
$headers->connection('Upgrade') unless $headers->connection;
$headers->sec_websocket_version(13) unless $headers->sec_websocket_version;
# Generate 16 byte WebSocket challenge
my $challenge = b64_encode sprintf('%16u', int(rand 9 x 16)), '';
$headers->sec_websocket_key($challenge) unless $headers->sec_websocket_key;
return $tx;
}
sub parse_frame {
my ($buffer, $max) = @_;
# Head
return undef unless length $$buffer >= 2;
my ($first, $second) = unpack 'C2', $$buffer;
# FIN
my $fin = ($first & 0b10000000) == 0b10000000 ? 1 : 0;
# RSV1-3
my $rsv1 = ($first & 0b01000000) == 0b01000000 ? 1 : 0;
my $rsv2 = ($first & 0b00100000) == 0b00100000 ? 1 : 0;
my $rsv3 = ($first & 0b00010000) == 0b00010000 ? 1 : 0;
# Opcode
my $op = $first & 0b00001111;
warn "-- Parsing frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
# Small payload
my ($hlen, $len) = (2, $second & 0b01111111);
if ($len < 126) { warn "-- Small payload ($len)\n" if DEBUG }
# Extended payload (16-bit)
elsif ($len == 126) {
return undef unless length $$buffer > 4;
$hlen = 4;
$len = unpack 'x2n', $$buffer;
warn "-- Extended 16-bit payload ($len)\n" if DEBUG;
}
# Extended payload (64-bit with 32-bit fallback)
elsif ($len == 127) {
return undef unless length $$buffer > 10;
$hlen = 10;
$len = MODERN ? unpack('x2Q>', $$buffer) : unpack('x2x4N', $$buffer);
warn "-- Extended 64-bit payload ($len)\n" if DEBUG;
}
# Check message size
return 1 if $len > $max;
# Check if whole packet has arrived
$len += 4 if my $masked = $second & 0b10000000;
return undef if length $$buffer < ($hlen + $len);
substr $$buffer, 0, $hlen, '';
# Payload
my $payload = $len ? substr($$buffer, 0, $len, '') : '';
$payload = xor_encode($payload, substr($payload, 0, 4, '') x 128) if $masked;
warn dumper $payload if DEBUG;
return [$fin, $rsv1, $rsv2, $rsv3, $op, $payload];
}
sub server_handshake {
my $tx = shift;
my $headers = $tx->res->headers;
$headers->upgrade('websocket')->connection('Upgrade');
$headers->sec_websocket_accept(
_challenge($tx->req->headers->sec_websocket_key));
return $tx;
}
sub _challenge { b64_encode(sha1_bytes(($_[0] || '') . GUID), '') }
1;
=encoding utf8
=head1 NAME
Mojo::WebSocket - The WebSocket protocol
=head1 SYNOPSIS
use Mojo::WebSocket qw(WS_TEXT build_frame parse_frame);
my $bytes = build_frame 0, 1, 0, 0, 0, WS_TEXT, 'Hello World!';
my $frame = parse_frame \$bytes, 262144;
=head1 DESCRIPTION
L<Mojo::WebSocket> implements the WebSocket protocol as described in
L<RFC 6455|http://tools.ietf.org/html/rfc6455>. Note that 64-bit frames require
a Perl with support for quads or they are limited to 32-bit.
=head1 FUNCTIONS
L<Mojo::WebSocket> implements the following functions, which can be imported
individually.
=head2 build_frame
my $bytes = build_frame $masked, $fin, $rsv1, $rsv2, $rsv3, $op, $payload;
Build WebSocket frame.
# Masked binary frame with FIN bit and payload
say build_frame 1, 1, 0, 0, 0, WS_BINARY, 'Hello World!';
# Text frame with payload but without FIN bit
say build_frame 0, 0, 0, 0, 0, WS_TEXT, 'Hello ';
# Continuation frame with FIN bit and payload
say build_frame 0, 1, 0, 0, 0, WS_CONTINUATION, 'World!';
# Close frame with FIN bit and without payload
say build_frame 0, 1, 0, 0, 0, WS_CLOSE, '';
# Ping frame with FIN bit and payload
say build_frame 0, 1, 0, 0, 0, WS_PING, 'Test 123';
# Pong frame with FIN bit and payload
say build_frame 0, 1, 0, 0, 0, WS_PONG, 'Test 123';
=head2 challenge
my $bool = challenge Mojo::Transaction::WebSocket->new;
Check WebSocket handshake challenge.
=head2 client_handshake
my $tx = client_handshake Mojo::Transaction::HTTP->new;
Perform WebSocket handshake client-side.
=head2 parse_frame
my $frame = parse_frame \$bytes, $limit;
Parse WebSocket frame.
# Parse single frame and remove it from buffer
my $frame = parse_frame \$buffer, 262144;
say "FIN: $frame->[0]";
say "RSV1: $frame->[1]";
say "RSV2: $frame->[2]";
say "RSV3: $frame->[3]";
say "Opcode: $frame->[4]";
say "Payload: $frame->[5]";
=head2 server_handshake
my $tx = server_handshake Mojo::Transaction::HTTP->new;
Perform WebSocket handshake server-side.
=head1 CONSTANTS
L<Mojo::WebSocket> implements the following constants, which can be imported
individually.
=head2 WS_BINARY
Opcode for C<Binary> frames.
=head2 WS_CLOSE
Opcode for C<Close> frames.
=head2 WS_CONTINUATION
Opcode for C<Continuation> frames.
=head2 WS_PING
Opcode for C<Ping> frames.
=head2 WS_PONG
Opcode for C<Pong> frames.
=head2 WS_TEXT
Opcode for C<Text> frames.
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
=cut
|