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   871 use strict;
  21         42  
  21         534  
4 21     21   95 use warnings;
  21         49  
  21         542  
5              
6 21     21   104 use base 'Protocol::WebSocket::Stateful';
  21         40  
  21         8249  
7              
8 21     21   178 use Scalar::Util qw(readonly);
  21         40  
  21         26685  
9             require Digest::MD5;
10              
11             our $MAX_MESSAGE_SIZE = 10 * 2048;
12              
13             sub new {
14 112     112 1 3827 my $class = shift;
15 112 50       300 $class = ref $class if ref $class;
16              
17 112         326 my $self = {@_};
18 112         202 bless $self, $class;
19              
20 112   100     579 $self->{version} ||= '';
21              
22 112         209 $self->{buffer} = '';
23              
24 112   100     461 $self->{fields} ||= {};
25              
26 112   33     418 $self->{max_message_size} ||= $MAX_MESSAGE_SIZE;
27              
28 112   100     392 $self->{cookies} ||= [];
29              
30 112         450 $self->state('first_line');
31              
32 112         324 return $self;
33             }
34              
35 277 100   277 1 1128 sub secure { @_ > 1 ? $_[0]->{secure} = $_[1] : $_[0]->{secure} }
36              
37 704     704 1 2361 sub fields { shift->{fields} }
38              
39             sub field {
40 704     704 1 965 my $self = shift;
41 704         1128 my $name = lc shift;
42              
43 704 100       1423 return $self->fields->{$name} unless @_;
44              
45 218         468 $self->fields->{$name} = $_[0];
46              
47 218         380 return $self;
48             }
49              
50             sub error {
51 529     529 1 691 my $self = shift;
52              
53 529 100       1468 return $self->{error} unless @_;
54              
55 11         19 my $error = shift;
56 11         30 $self->{error} = $error;
57 11         34 $self->state('error');
58              
59 11         29 return $self;
60             }
61              
62             sub subprotocol {
63 368 100   368 1 1112 @_ > 1 ? $_[0]->{subprotocol} = $_[1] : $_[0]->{subprotocol};
64             }
65              
66 496 100   496 1 1291 sub host { @_ > 1 ? $_[0]->{host} = $_[1] : $_[0]->{host} }
67 637 100   637 1 1720 sub origin { @_ > 1 ? $_[0]->{origin} = $_[1] : $_[0]->{origin} }
68              
69 505 100   505 1 1756 sub version { @_ > 1 ? $_[0]->{version} = $_[1] : $_[0]->{version} }
70              
71 19 100   19 1 47 sub number1 { @_ > 1 ? $_[0]->{number1} = $_[1] : $_[0]->{number1} }
72 19 100   19 1 53 sub number2 { @_ > 1 ? $_[0]->{number2} = $_[1] : $_[0]->{number2} }
73 70 100   70 1 257 sub challenge { @_ > 1 ? $_[0]->{challenge} = $_[1] : $_[0]->{challenge} }
74              
75             sub checksum {
76 28     28 1 52 my $self = shift;
77              
78 28 100       66 if (@_) {
79 9         18 $self->{checksum} = $_[0];
80 9         22 return $self;
81             }
82              
83 19 100       51 return $self->{checksum} if defined $self->{checksum};
84              
85 15 50       38 Carp::croak(qq/number1 is required/) unless defined $self->number1;
86 15 50       46 Carp::croak(qq/number2 is required/) unless defined $self->number2;
87 15 50       47 Carp::croak(qq/challenge is required/) unless defined $self->challenge;
88              
89 15         26 my $checksum = '';
90 15         36 $checksum .= pack 'N' => $self->number1;
91 15         38 $checksum .= pack 'N' => $self->number2;
92 15         36 $checksum .= $self->challenge;
93 15         84 $checksum = Digest::MD5::md5($checksum);
94              
95 15   33     103 return $self->{checksum} ||= $checksum;
96             }
97              
98             sub parse {
99 265     265 1 499 my $self = shift;
100              
101 265 100       614 return 1 unless defined $_[0];
102              
103 254 50       553 return if $self->error;
104              
105 254 100       606 return unless $self->_append(@_);
106              
107 252   100     661 while (!$self->is_state('body') && defined(my $line = $self->_get_line)) {
108 284 100       620 if ($self->state eq 'first_line') {
    100          
109 48 100       147 return unless defined $self->_parse_first_line($line);
110              
111 42         150 $self->state('fields');
112             }
113             elsif ($line ne '') {
114 197 50       447 return unless defined $self->_parse_field($line);
115             }
116             else {
117 39         124 $self->state('body');
118 39         75 last;
119             }
120             }
121              
122 246 100       552 return 1 unless $self->is_state('body');
123              
124 49         163 my $rv = $self->_parse_body;
125 49 100       120 return unless defined $rv;
126              
127             # Need more data
128 48 100       165 return $rv unless ref $rv;
129              
130 44 100 100     240 $_[0] = $self->{buffer} unless readonly $_[0] || ref $_[0];
131 44         215 return $self->done;
132             }
133              
134             sub _extract_number {
135 22     22   37 my $self = shift;
136 22         86 my $key = shift;
137              
138 22         210 my $number = join '' => $key =~ m/\d+/g;
139 22         124 my $spaces = $key =~ s/ / /g;
140              
141 22 50       67 return if $spaces == 0;
142              
143 22         127 return int($number / $spaces);
144             }
145              
146             sub _append {
147 254     254   352 my $self = shift;
148              
149 254 50       412 return if $self->error;
150              
151 254 100       527 if (ref $_[0]) {
152 8         46 $_[0]->read(my $buf, $self->{max_message_size});
153 8         186 $self->{buffer} .= $buf;
154             }
155             else {
156 246         579 $self->{buffer} .= $_[0];
157 246 100       774 $_[0] = '' unless readonly $_[0];
158             }
159              
160 254 100       585 if (length $self->{buffer} > $self->{max_message_size}) {
161 2         11 $self->error('Message is too long');
162 2         9 return;
163             }
164              
165 252         520 return $self;
166             }
167              
168             sub _get_line {
169 481     481   701 my $self = shift;
170              
171 481 100       2505 if ($self->{buffer} =~ s/^(.*?)\x0d?\x0a//) {
172 284         1312 return $1;
173             }
174              
175 197         582 return;
176             }
177              
178 2     2   5 sub _parse_first_line {shift}
179              
180             sub _parse_field {
181 197     197   273 my $self = shift;
182 197         280 my $line = shift;
183              
184 197         866 my ($name, $value) = split /:\s*/ => $line => 2;
185 197 50 33     715 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         519 $self->field($name => $value);
192              
193 197 100       455 if ($name =~ m/^x-forwarded-proto$/i) {
194 1         6 $self->secure(1);
195             }
196              
197 197         707 return $self;
198             }
199              
200 2     2   4 sub _parse_body {shift}
201              
202             1;
203             __END__