File Coverage

blib/lib/Net/WebSocket/Server/Connection.pm
Criterion Covered Total %
statement 93 123 75.6
branch 28 60 46.6
condition 4 16 25.0
subroutine 16 29 55.1
pod 13 13 100.0
total 154 241 63.9


line stmt bran cond sub pod time code
1             package Net::WebSocket::Server::Connection;
2              
3 2     2   51 use 5.006;
  2         7  
  2         77  
4 2     2   11 use strict;
  2         2  
  2         63  
5 2     2   10 use warnings FATAL => 'all';
  2         11  
  2         86  
6              
7 2     2   13 use Carp;
  2         3  
  2         118  
8 2     2   1168 use Protocol::WebSocket::Handshake::Server;
  2         27710  
  2         55  
9 2     2   16 use Protocol::WebSocket::Frame;
  2         3  
  2         42  
10 2     2   9 use Socket qw(IPPROTO_TCP TCP_NODELAY);
  2         2  
  2         708  
11 2     2   13 use Encode;
  2         3  
  2         2545  
12              
13             sub new {
14 1     1 1 2 my $class = shift;
15              
16 1         2 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 0     0   0 on_handshake => sub{},
24 0     0   0 on_ready => sub{},
25 0     0   0 on_disconnect => sub{},
26 0     0   0 on_utf8 => sub{},
27 0     0   0 on_pong => sub{},
28 0     0   0 on_binary => sub{},
29 1   50     4 };
30              
31 1         89 while (my ($key, $value) = each %params ) {
32 2 50       8 croak "Invalid $class parameter '$key'" unless exists $self->{$key};
33 2 50 33     7 croak "$class parameter '$key' expects a coderef" if ref $self->{$key} eq 'CODE' && ref $value ne 'CODE';
34 2         6 $self->{$key} = $value;
35             }
36              
37 1         3 croak "$class construction requires '$_'" for grep { !defined $self->{$_} } qw(socket server);
  2         7  
38              
39 1         18 $self->{handshake} = new Protocol::WebSocket::Handshake::Server();
40 1         12 $self->{disconnecting} = 0;
41              
42 1         5 bless $self, $class;
43             }
44              
45             sub on {
46 1     1 1 31 my $self = shift;
47 1         10 my %params = @_;
48              
49 1         7 while (my ($key, $value) = each %params ) {
50 6 50       14 croak "Invalid event '$key'" unless exists $self->{"on_$key"};
51 6 50       16 croak "Expected a coderef for event '$key'" unless ref $value eq 'CODE';
52 6         25 $self->{"on_$key"} = $value;
53             }
54             }
55              
56              
57             ### accessors
58              
59 0     0 1 0 sub server { $_[0]->{server} }
60              
61 0     0 1 0 sub socket { $_[0]->{socket} }
62              
63             sub ip {
64 0     0 1 0 my $sock = $_[0]->{socket};
65 0 0 0     0 return $sock && $sock->connected ? $sock->peerhost : "0.0.0.0";
66             }
67              
68             sub port {
69 0     0 1 0 my $sock = $_[0]->{socket};
70 0 0 0     0 return $sock && $sock->connected ? $sock->peerport : 0;
71             }
72              
73             sub nodelay {
74 0     0 1 0 my $self = shift;
75 0 0       0 if (@_) {
76 0         0 $self->{nodelay} = $_[0];
77 0 0       0 setsockopt($self->{socket}, IPPROTO_TCP, TCP_NODELAY, $self->{nodelay} ? 1 : 0) unless $self->{handshake};
    0          
78             }
79 0         0 return $self->{nodelay};
80             }
81              
82             sub max_send_size {
83 0     0 1 0 my $self = shift;
84 0 0       0 $self->{max_send_size} = $_[0] if @_;
85 0         0 return $self->{max_send_size};
86             }
87              
88              
89             ### methods
90              
91             sub disconnect {
92 2     2 1 5 my ($self, $code, $reason) = @_;
93 2 100       12 return if $self->{disconnecting};
94 1         7 $self->{disconnecting} = 1;
95              
96 1         4 $self->_event('on_disconnect', $code, $reason);
97              
98 1         2 my $data = '';
99 1 50 33     5 if (defined $code || defined $reason) {
100 1   50     4 $code ||= 1000;
101 1 50       5 $reason = '' unless defined $reason;
102 1         9 $data = pack("na*", $code, $reason);
103             }
104 1 50       6 $self->send(close => $data) unless $self->{handshake};
105              
106 1         6 $self->{server}->disconnect($self->{socket});
107             }
108              
109             sub send_binary {
110 14     14 1 202 $_[0]->send(binary => $_[1]);
111             }
112              
113             sub send_utf8 {
114 5     5 1 318 $_[0]->send(text => Encode::encode('UTF-8', $_[1]));
115             }
116              
117             sub send {
118 20     20 1 5747 my ($self, $type, $data) = @_;
119              
120 20 50       71 if ($self->{handshake}) {
121 0         0 carp "tried to send data before finishing handshake";
122 0         0 return 0;
123             }
124              
125 20         93 my $frame = new Protocol::WebSocket::Frame(type => $type, max_payload_size => $self->{max_send_size});
126 20 50       833 $frame->append($data) if defined $data;
127              
128 20         250 my $bytes = eval { $frame->to_bytes };
  20         62  
129 20 50       893 if (!defined $bytes) {
130 0 0       0 carp "error while building message: $@" if $@;
131 0         0 return;
132             }
133              
134 20         803 syswrite($self->{socket}, $bytes);
135             }
136              
137             sub recv {
138 19     19 1 32 my ($self) = @_;
139              
140 19         45 my ($len, $data) = (0, "");
141 19 50       232 if (!($len = sysread($self->{socket}, $data, 8192))) {
142 0         0 $self->disconnect();
143 0         0 return;
144             }
145              
146             # read remaining data
147 19         114531 $len = sysread($self->{socket}, $data, 8192, length($data)) while $len >= 8192;
148              
149 19 100       71 if ($self->{handshake}) {
150 1         6 $self->{handshake}->parse($data);
151 1 50       1063 if ($self->{handshake}->error) {
    50          
152 0         0 $self->disconnect(1002);
153             } elsif ($self->{handshake}->is_done) {
154 1         20 $self->_event(on_handshake => $self->{handshake});
155 1 50   0   15 return unless do { local $SIG{__WARN__} = sub{}; $self->{socket}->connected };
  1         8  
  0         0  
  1         9  
156              
157 1         21 syswrite($self->{socket}, $self->{handshake}->to_string);
158 1         466 delete $self->{handshake};
159              
160 1         6 $self->{parser} = new Protocol::WebSocket::Frame();
161 1 50       32 setsockopt($self->{socket}, IPPROTO_TCP, TCP_NODELAY, 1) if $self->{nodelay};
162 1         3 $self->_event('on_ready');
163             }
164 1         16 return;
165             }
166              
167 18         122 $self->{parser}->append($data);
168              
169 18         352 my $bytes;
170 18         42 while (defined ($bytes = eval { $self->{parser}->next_bytes })) {
  35         144  
171 18 100       2253 if ($self->{parser}->is_binary) {
    100          
    100          
    50          
172 6         65 $self->_event(on_binary => $bytes);
173             } elsif ($self->{parser}->is_text) {
174 5         113 $self->_event(on_utf8 => Encode::decode('UTF-8', $bytes));
175             } elsif ($self->{parser}->is_pong) {
176 6         224 $self->_event(on_pong => $bytes);
177             } elsif ($self->{parser}->is_close) {
178 1 50       39 $self->disconnect(length $bytes ? unpack("na*",$bytes) : ());
179 1         78 return;
180             }
181             }
182              
183 17 50       363 if ($@) {
184 0         0 $self->disconnect(1002);
185 0         0 return;
186             }
187             }
188              
189             ### internal methods
190              
191             sub _event {
192 20     20   1528 my ($self, $event, @args) = @_;
193 20         82 $self->{$event}($self, @args);
194             }
195              
196             1; # End of Net::WebSocket::Server
197              
198             __END__