File Coverage

blib/lib/Terse/WebSocket.pm
Criterion Covered Total %
statement 9 83 10.8
branch 0 32 0.0
condition 0 10 0.0
subroutine 3 7 42.8
pod 1 4 25.0
total 13 136 9.5


line stmt bran cond sub pod time code
1             package Terse::WebSocket;
2              
3 13     13   127 use base 'Terse';
  13         31  
  13         2016  
4 13     13   7054 use MIME::Base64;
  13         14275  
  13         1053  
5 13     13   7264 use Protocol::WebSocket::Handshake::Server;
  13         352143  
  13         14487  
6              
7             sub new {
8 0     0 1   my ($class, $t) = @_;
9 0           my $self = $class->SUPER::new();
10 0           my $version = '';
11 0           my $env = $t->request->env;
12 0           $self->psgix = $env->{'psgix.io'};
13 0           $self->handshake = Protocol::WebSocket::Handshake::Server->new_from_psgi($t->request->env);
14 0           $self->handshake->parse();
15 0           return $self;
16             }
17              
18             sub start {
19 0     0 0   my ($self, $t, $cbs, $responder) = @_;
20 0           my $writer = eval { $responder->([101, $self->handshake->res->headers]); };
  0            
21 0 0         $cbs->{($@ ? 'error' : 'connect')}->($self, $responder, $@);
22 0   0       my $reset_rate = $t->websocket_reset_rate ||= 100000;
23 0           eval {
24 0           my $ping_rate = $reset_rate;
25 0           while (1) {
26 0           $ping_rate--;
27 0           my $response;
28 0 0         if ($ping_rate < 0) {
29 0           $ping_rate = $reset_rate;
30 0           syswrite $self->psgix, $self->handshake->build_frame( type => 'ping' )->to_bytes;
31 0   0       $response = $self->recieve() while($ping_rate-- > 0 && !$response);
32 0 0 0       if (!$response || $response ne 'pong') {
33 0           last;
34             }
35 0           $ping_rate = $reset_rate;
36             }
37 0           $response = $self->recieve();
38 0 0         if ($response) {
39 0 0         if ($response =~ m/^invalid_(length|version|host|required_key)$/) {
40 0           $cbs->{error}->($self, $response, $responder);
41 0           last;
42             } else {
43 0           $ping_rate = $reset_rate;
44 0           $cbs->{recieve}->($self, $response, $responder);
45             }
46             }
47             }
48             };
49 0 0         $cbs->{error}->($self, $responder, $@) if ($@);
50 0 0         $cbs->{disconnect}->($self, $responder) if $cbs->{disconnect};
51 0 0         delete $t->{_application}->websockets->{$t->sid->value} if $cbs->{close_delete};
52 0           $responder->([200, []]);
53             }
54              
55             sub send {
56 0     0 0   my ($self, $message) = @_;
57 0           my $frame = $self->handshake->build_frame;
58 0           $frame->append($message);
59 0           my $pg = $self->psgix;
60 0           syswrite $pg, $frame->to_bytes;
61 0           return $self;
62             }
63              
64             sub recieve {
65 0     0 0   my ($self, @ENCODED) = @_;
66 0           my $length;
67 0 0         if (! scalar @ENCODED) {
68 0 0 0       return shift @{ $self->next_frame } if scalar @{ $self->next_frame ||= [] };
  0            
  0            
69 0           my $pg = $self->psgix;
70 0           my $content = "";
71 0           $length = sysread($pg, $content, 8192);
72 0 0         return unless $length;
73 0           $length = sysread($pg, $content, 8192, length($content)) while $length >= 8192;
74 0           @ENCODED = map { unpack "C", $_ } split //, $content;
  0            
75             }
76 0           my @bits = split //, sprintf("%b\n", $ENCODED[0]);
77 0           $self->fin = $bits[0];
78 0           $self->rsv = [@bits[1 .. 3]];
79 0           $self->op = shift @ENCODED;
80 0 0         if ($ENCODED[0] == 254) {
81 0           my @length = splice @ENCODED, 0, 3;
82 0           $length = ((($length[0] + 2) * $length[1]) + $length[2]);
83             } else {
84 0           $length = shift @ENCODED;
85 0           $length -= 128;
86             }
87 0 0         return pack "C*", join("", @ENCODED) if (scalar @ENCODED == $length);
88 0           my @MASK = splice @ENCODED, 0, 4;
89 0 0         if (scalar @ENCODED > $length) {
90 0           my $next = $self->recieve(splice @ENCODED, $length, scalar @ENCODED);
91 0 0         return $next if ($next eq 'invalid_length');
92 0           unshift @{ $self->next_frame }, $next;
  0            
93             }
94 0 0         return 'invalid_length' if (scalar @ENCODED != $length);
95 0           return join "", map { pack "C", ($ENCODED[$_] ^ $MASK[$_ % 4]) } 0 .. $#ENCODED;
  0            
96             }
97              
98             1;
99              
100             __END__;