File Coverage

blib/lib/Net/WebSocket/Server/Connection.pm
Criterion Covered Total %
statement 97 129 75.1
branch 30 68 44.1
condition 6 15 40.0
subroutine 17 31 54.8
pod 15 15 100.0
total 165 258 63.9


line stmt bran cond sub pod time code
1             package Net::WebSocket::Server::Connection;
2              
3 2     2   42 use 5.006;
  2         6  
4 2     2   13 use strict;
  2         5  
  2         84  
5 2     2   14 use warnings FATAL => 'all';
  2         6  
  2         86  
6              
7 2     2   12 use Carp;
  2         4  
  2         151  
8 2     2   1171 use Protocol::WebSocket::Handshake::Server;
  2         36235  
  2         58  
9 2     2   15 use Protocol::WebSocket::Frame;
  2         4  
  2         59  
10 2     2   32 use Socket qw(IPPROTO_TCP TCP_NODELAY);
  2         5  
  2         647  
11 2     2   17 use Encode;
  2         6  
  2         4832  
12              
13             sub new {
14 1     1 1 3 my $class = shift;
15              
16 1         4 my %params = @_;
17              
18             my $self = {
19             socket => undef,
20             server => undef,
21             nodelay => 1,
22             max_send_size => eval { Protocol::WebSocket::Frame->new->{max_payload_size} } || 65536,
23             max_recv_size => eval { Protocol::WebSocket::Frame->new->{max_payload_size} } || 65536,
24       0     on_handshake => sub{},
25       0     on_ready => sub{},
26       0     on_disconnect => sub{},
27       0     on_utf8 => sub{},
28       0     on_pong => sub{},
29       0     on_binary => sub{},
30 1   50     3 };
      50        
31              
32 1         65 while (my ($key, $value) = each %params ) {
33 2 50       12 croak "Invalid $class parameter '$key'" unless exists $self->{$key};
34 2 50 33     8 croak "$class parameter '$key' expects a coderef" if ref $self->{$key} eq 'CODE' && ref $value ne 'CODE';
35 2         8 $self->{$key} = $value;
36             }
37              
38 1         3 croak "$class construction requires '$_'" for grep { !defined $self->{$_} } qw(socket server);
  2         7  
39              
40 1         44 $self->{handshake} = new Protocol::WebSocket::Handshake::Server();
41 1         72 $self->{disconnecting} = 0;
42 1         15 $self->{ip} = $self->{socket}->peerhost;
43 1         126 $self->{port} = $self->{socket}->peerport;
44              
45             # only attempt to start SSL if this is an IO::Socket::SSL-like socket that also has not completed its SSL handshake (SSL_startHandshake => 0)
46 1 50 33     41 $self->{needs_ssl} = 1 if $self->{socket}->can("accept_SSL") && !$self->{socket}->opened;
47              
48 1         5 bless $self, $class;
49             }
50              
51             sub on {
52 1     1 1 78 my $self = shift;
53 1         22 my %params = @_;
54              
55 1         8 while (my ($key, $value) = each %params ) {
56 6 50       23 croak "Invalid event '$key'" unless exists $self->{"on_$key"};
57 6 50       26 croak "Expected a coderef for event '$key'" unless ref $value eq 'CODE';
58 6         32 $self->{"on_$key"} = $value;
59             }
60             }
61              
62              
63             ### accessors
64              
65 0     0 1 0 sub server { $_[0]->{server} }
66              
67 0     0 1 0 sub socket { $_[0]->{socket} }
68              
69 2     2 1 42 sub is_ready { !$_[0]->{handshake} }
70              
71 0     0 1 0 sub ip { $_[0]{ip} }
72              
73 0     0 1 0 sub port { $_[0]{port} }
74              
75             sub nodelay {
76 0     0 1 0 my $self = shift;
77 0 0       0 if (@_) {
78 0         0 $self->{nodelay} = $_[0];
79 0 0       0 setsockopt($self->{socket}, IPPROTO_TCP, TCP_NODELAY, $self->{nodelay} ? 1 : 0) unless $self->{handshake};
    0          
80             }
81 0         0 return $self->{nodelay};
82             }
83              
84             sub max_send_size {
85 0     0 1 0 my $self = shift;
86 0 0       0 $self->{max_send_size} = $_[0] if @_;
87 0         0 return $self->{max_send_size};
88             }
89              
90             sub max_recv_size {
91 0     0 1 0 my $self = shift;
92 0 0       0 if (@_) {
93 0 0       0 croak "Cannot change max_recv_size; handshake is already complete" if $self->{parser};
94 0         0 $self->{max_recv_size} = $_[0];
95             }
96 0         0 return $self->{max_recv_size};
97             }
98              
99              
100             ### methods
101              
102             sub disconnect {
103 2     2 1 14 my ($self, $code, $reason) = @_;
104 2 100       19 return if $self->{disconnecting};
105 1         4 $self->{disconnecting} = 1;
106              
107 1         10 $self->_event('on_disconnect', $code, $reason);
108              
109 1         4 my $data = '';
110 1 50 33     9 if (defined $code || defined $reason) {
111 1   50     7 $code ||= 1000;
112 1 50       35 $reason = '' unless defined $reason;
113 1         12 $data = pack("na*", $code, $reason);
114             }
115 1 50       10 $self->send(close => $data) unless $self->{handshake};
116              
117 1         15 $self->{server}->disconnect($self->{socket});
118             }
119              
120             sub send_binary {
121 14     14 1 224 $_[0]->send(binary => $_[1]);
122             }
123              
124             sub send_utf8 {
125 5     5 1 541 $_[0]->send(text => Encode::encode('UTF-8', $_[1]));
126             }
127              
128             sub send {
129 20     20 1 666 my ($self, $type, $data) = @_;
130              
131 20 50       87 if ($self->{handshake}) {
132 0         0 carp "tried to send data before finishing handshake";
133 0         0 return 0;
134             }
135              
136 20         104 my $frame = new Protocol::WebSocket::Frame(type => $type, max_payload_size => $self->{max_send_size});
137 20 50       1238 $frame->append($data) if defined $data;
138              
139 20         396 my $bytes = eval { $frame->to_bytes };
  20         78  
140 20 50       1397 if (!defined $bytes) {
141 0 0       0 carp "error while building message: $@" if $@;
142 0         0 return;
143             }
144              
145 20         2003 syswrite($self->{socket}, $bytes);
146             }
147              
148             sub recv {
149 19     19 1 53 my ($self) = @_;
150              
151 19 50       66 if ($self->{needs_ssl}) {
152 0         0 my $ssl_done = $self->{socket}->accept_SSL;
153 0 0       0 if ($self->{socket}->errstr) {
154 0         0 $self->disconnect;
155 0         0 return;
156             }
157 0 0       0 return unless $ssl_done;
158 0         0 $self->{needs_ssl} = 0;
159             }
160              
161 19         60 my ($len, $data) = (0, "");
162 19 50       434 if (!($len = sysread($self->{socket}, $data, 8192))) {
163 0         0 $self->disconnect();
164 0         0 return;
165             }
166              
167             # read remaining data
168 19         110441 $len = sysread($self->{socket}, $data, 8192, length($data)) while $len >= 8192;
169              
170 19 100       98 if ($self->{handshake}) {
171 1         6 $self->{handshake}->parse($data);
172 1 50       1435 if ($self->{handshake}->error) {
    50          
173 0         0 $self->disconnect(1002);
174             } elsif ($self->{handshake}->is_done) {
175 1         35 $self->_event(on_handshake => $self->{handshake});
176 1 50   0   16 return unless do { local $SIG{__WARN__} = sub{}; $self->{socket}->connected };
  1         7  
  1         20  
177              
178 1         29 syswrite($self->{socket}, $self->{handshake}->to_string);
179 1         692 delete $self->{handshake};
180              
181 1         5 $self->{parser} = new Protocol::WebSocket::Frame(max_payload_size => $self->{max_recv_size});
182 1 50       48 setsockopt($self->{socket}, IPPROTO_TCP, TCP_NODELAY, 1) if $self->{nodelay};
183 1         4 $self->_event('on_ready');
184             }
185 1         18 return;
186             }
187              
188 18         131 $self->{parser}->append($data);
189              
190 18         615 my $bytes;
191 18         61 while (defined ($bytes = eval { $self->{parser}->next_bytes })) {
  35         209  
192 18 100       3053 if ($self->{parser}->is_binary) {
    100          
    100          
    50          
193 6         74 $self->_event(on_binary => $bytes);
194             } elsif ($self->{parser}->is_text) {
195 5         169 $self->_event(on_utf8 => Encode::decode('UTF-8', $bytes));
196             } elsif ($self->{parser}->is_pong) {
197 6         154 $self->_event(on_pong => $bytes);
198             } elsif ($self->{parser}->is_close) {
199 1 50       80 $self->disconnect(length $bytes ? unpack("na*",$bytes) : ());
200 1         155 return;
201             }
202             }
203              
204 17 50       567 if ($@) {
205 0         0 $self->disconnect(1002);
206 0         0 return;
207             }
208             }
209              
210             ### internal methods
211              
212             sub _event {
213 20     20   1138 my ($self, $event, @args) = @_;
214 20         101 $self->{$event}($self, @args);
215             }
216              
217             1; # End of Net::WebSocket::Server
218              
219             __END__