File Coverage

blib/lib/Protocol/WebSocket/Message.pm
Criterion Covered Total %
statement 105 107 98.1
branch 61 70 87.1
condition 15 21 71.4
subroutine 24 24 100.0
pod 14 14 100.0
total 219 236 92.8


line stmt bran cond sub pod time code
1             package Protocol::WebSocket::Message;
2              
3 21     21   654 use strict;
  21         41  
  21         533  
4 21     21   93 use warnings;
  21         37  
  21         463  
5              
6 21     21   91 use base 'Protocol::WebSocket::Stateful';
  21         34  
  21         6940  
7              
8 21     21   131 use Scalar::Util qw(readonly);
  21         40  
  21         23587  
9             require Digest::MD5;
10              
11             our $MAX_MESSAGE_SIZE = 10 * 2048;
12              
13             sub new {
14 111     111 1 3620 my $class = shift;
15 111 50       381 $class = ref $class if ref $class;
16              
17 111         263 my $self = {@_};
18 111         211 bless $self, $class;
19              
20 111   100     614 $self->{version} ||= '';
21              
22 111         199 $self->{buffer} = '';
23              
24 111   100     476 $self->{fields} ||= {};
25              
26 111   33     447 $self->{max_message_size} ||= $MAX_MESSAGE_SIZE;
27              
28 111   100     436 $self->{cookies} ||= [];
29              
30 111         428 $self->state('first_line');
31              
32 111         339 return $self;
33             }
34              
35 277 100   277 1 827 sub secure { @_ > 1 ? $_[0]->{secure} = $_[1] : $_[0]->{secure} }
36              
37 701     701 1 2337 sub fields { shift->{fields} }
38              
39             sub field {
40 701     701 1 929 my $self = shift;
41 701         1137 my $name = lc shift;
42              
43 701 100       1399 return $self->fields->{$name} unless @_;
44              
45 218         428 $self->fields->{$name} = $_[0];
46              
47 218         370 return $self;
48             }
49              
50             sub error {
51 525     525 1 683 my $self = shift;
52              
53 525 100       1466 return $self->{error} unless @_;
54              
55 10         20 my $error = shift;
56 10         22 $self->{error} = $error;
57 10         32 $self->state('error');
58              
59 10         134 return $self;
60             }
61              
62             sub subprotocol {
63 368 100   368 1 1102 @_ > 1 ? $_[0]->{subprotocol} = $_[1] : $_[0]->{subprotocol};
64             }
65              
66 496 100   496 1 1317 sub host { @_ > 1 ? $_[0]->{host} = $_[1] : $_[0]->{host} }
67 639 100   639 1 1738 sub origin { @_ > 1 ? $_[0]->{origin} = $_[1] : $_[0]->{origin} }
68              
69 505 100   505 1 1655 sub version { @_ > 1 ? $_[0]->{version} = $_[1] : $_[0]->{version} }
70              
71 19 100   19 1 65 sub number1 { @_ > 1 ? $_[0]->{number1} = $_[1] : $_[0]->{number1} }
72 19 100   19 1 58 sub number2 { @_ > 1 ? $_[0]->{number2} = $_[1] : $_[0]->{number2} }
73 70 100   70 1 213 sub challenge { @_ > 1 ? $_[0]->{challenge} = $_[1] : $_[0]->{challenge} }
74              
75             sub checksum {
76 28     28 1 56 my $self = shift;
77              
78 28 100       65 if (@_) {
79 9         25 $self->{checksum} = $_[0];
80 9         23 return $self;
81             }
82              
83 19 100       62 return $self->{checksum} if defined $self->{checksum};
84              
85 15 50       44 Carp::croak(qq/number1 is required/) unless defined $self->number1;
86 15 50       54 Carp::croak(qq/number2 is required/) unless defined $self->number2;
87 15 50       40 Carp::croak(qq/challenge is required/) unless defined $self->challenge;
88              
89 15         35 my $checksum = '';
90 15         42 $checksum .= pack 'N' => $self->number1;
91 15         40 $checksum .= pack 'N' => $self->number2;
92 15         34 $checksum .= $self->challenge;
93 15         114 $checksum = Digest::MD5::md5($checksum);
94              
95 15   33     92 return $self->{checksum} ||= $checksum;
96             }
97              
98             sub parse {
99 264     264 1 499 my $self = shift;
100              
101 264 100       597 return 1 unless defined $_[0];
102              
103 253 50       567 return if $self->error;
104              
105 253 100       604 return unless $self->_append(@_);
106              
107 251   100     605 while (!$self->is_state('body') && defined(my $line = $self->_get_line)) {
108 283 100       617 if ($self->state eq 'first_line') {
    100          
109 47 100       140 return unless defined $self->_parse_first_line($line);
110              
111 42         129 $self->state('fields');
112             }
113             elsif ($line ne '') {
114 197 50       443 return unless defined $self->_parse_field($line);
115             }
116             else {
117 39         130 $self->state('body');
118 39         77 last;
119             }
120             }
121              
122 246 100       538 return 1 unless $self->is_state('body');
123              
124 49         191 my $rv = $self->_parse_body;
125 49 100       130 return unless defined $rv;
126              
127             # Need more data
128 48 100       138 return $rv unless ref $rv;
129              
130 44 100 100     251 $_[0] = $self->{buffer} unless readonly $_[0] || ref $_[0];
131 44         192 return $self->done;
132             }
133              
134             sub _extract_number {
135 22     22   39 my $self = shift;
136 22         42 my $key = shift;
137              
138 22         165 my $number = join '' => $key =~ m/\d+/g;
139 22         119 my $spaces = $key =~ s/ / /g;
140              
141 22 50       54 return if $spaces == 0;
142              
143 22         140 return int($number / $spaces);
144             }
145              
146             sub _append {
147 253     253   376 my $self = shift;
148              
149 253 50       396 return if $self->error;
150              
151 253 100       519 if (ref $_[0]) {
152 8         40 $_[0]->read(my $buf, $self->{max_message_size});
153 8         137 $self->{buffer} .= $buf;
154             }
155             else {
156 245         566 $self->{buffer} .= $_[0];
157 245 100       775 $_[0] = '' unless readonly $_[0];
158             }
159              
160 253 100       595 if (length $self->{buffer} > $self->{max_message_size}) {
161 2         6 $self->error('Message is too long');
162 2         7 return;
163             }
164              
165 251         550 return $self;
166             }
167              
168             sub _get_line {
169 480     480   704 my $self = shift;
170              
171 480 100       2441 if ($self->{buffer} =~ s/^(.*?)\x0d?\x0a//) {
172 283         1327 return $1;
173             }
174              
175 197         572 return;
176             }
177              
178 2     2   5 sub _parse_first_line {shift}
179              
180             sub _parse_field {
181 197     197   288 my $self = shift;
182 197         275 my $line = shift;
183              
184 197         813 my ($name, $value) = split /:\s*/ => $line => 2;
185 197 50 33     716 unless (defined $name && defined $value) {
186 0         0 $self->error('Invalid field');
187 0         0 return;
188             }
189              
190             #$name =~ s/^Sec-WebSocket-Origin$/Origin/i; # FIXME
191 197         546 $self->field($name => $value);
192              
193 197 100       478 if ($name =~ m/^x-forwarded-proto$/i) {
194 1         6 $self->secure(1);
195             }
196              
197 197         690 return $self;
198             }
199              
200 2     2   3 sub _parse_body {shift}
201              
202             1;
203             __END__