File Coverage

blib/lib/Net/WebSocket/Handshake/Server.pm
Criterion Covered Total %
statement 59 61 96.7
branch 20 24 83.3
condition n/a
subroutine 14 15 93.3
pod 2 2 100.0
total 95 102 93.1


line stmt bran cond sub pod time code
1             package Net::WebSocket::Handshake::Server;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Net::WebSocket::Handshake::Server
8              
9             =head1 SYNOPSIS
10              
11             my $hsk = Net::WebSocket::Handshake::Server->new(
12              
13             #optional
14             subprotocols => [ 'echo', 'haha' ],
15              
16             #optional
17             extensions => \@extension_objects,
18             );
19              
20             $hsk->valid_method_or_die( $http_method ); #optional
21              
22             $hsk->consume_headers(@headers_kv_pairs);
23              
24             my $resp_hdr = $hsk->to_string();
25              
26             =head1 DESCRIPTION
27              
28             This class implements WebSocket handshake logic for a server.
29             It handles the basics of handshaking and, optionally, subprotocol
30             and extension negotiation.
31              
32             =cut
33              
34 4     4   1113 use strict;
  4         11  
  4         107  
35 4     4   18 use warnings;
  4         5  
  4         107  
36              
37 4     4   17 use parent qw( Net::WebSocket::Handshake );
  4         6  
  4         17  
38              
39 4     4   215 use Call::Context ();
  4         9  
  4         61  
40 4     4   16 use Digest::SHA ();
  4         8  
  4         58  
41              
42 4     4   15 use Net::WebSocket::Constants ();
  4         7  
  4         40  
43 4     4   18 use Net::WebSocket::X ();
  4         6  
  4         70  
44              
45             #no-op
46 4     4   17 use constant _handle_unrecognized_extension => ();
  4         7  
  4         1848  
47              
48             =head2 I->new( %OPTS )
49              
50             Returns an instance of this class. %OPTS is as described in the base class;
51             there are no options specific to this class.
52              
53             =head2 I->valid_protocol_or_die( PROTOCOL )
54              
55             Throws an exception if the given PROTOCOL isn’t the HTTP protocol (HTTP/1.1)
56             that WebSocket requires for all requests.
57              
58             You only need this if if you’re not using a request-parsing interface
59             that’s compatible with L; otherwise,
60             L’s C function
61             will do this (and other niceties) for you.
62              
63             =cut
64              
65             sub valid_protocol_or_die {
66 2     2 1 59 my ($self, $protocol) = @_;
67              
68 2 100       6 if ($protocol ne Net::WebSocket::Constants::REQUIRED_REQUEST_PROTOCOL()) {
69 1         10 die Net::WebSocket::X->create('BadRequestProtocol', $protocol);
70             }
71              
72 1         3 return;
73             }
74              
75             =head2 I->valid_method_or_die( METHOD )
76              
77             Throws an exception if the given METHOD isn’t the HTTP method (GET) that
78             WebSocket requires for all requests.
79              
80             As with C, L might
81             call this method for you.
82              
83             =cut
84              
85             sub valid_method_or_die {
86 2     2 1 748 my ($self, $method) = @_;
87              
88 2 100       6 if ($method ne Net::WebSocket::Constants::REQUIRED_HTTP_METHOD()) {
89 1         4 die Net::WebSocket::X->create('BadHTTPMethod', $method);
90             }
91              
92 1         2 return;
93             }
94              
95             sub _consume_peer_header {
96 13     13   27 my ($self, $name => $value) = @_;
97              
98 13         19 $name =~ tr; #case insensitive
99              
100 13 100       40 if ($name eq 'sec-websocket-version') {
    100          
    100          
101 3 100       9 if ( $value ne Net::WebSocket::Constants::PROTOCOL_VERSION() ) {
102 1         5 die Net::WebSocket::X->create('UnsupportedProtocolVersion', $value);
103             }
104              
105 2         3 $self->{'_version_ok'} = 1;
106             }
107             elsif ($name eq 'sec-websocket-key') {
108 2         6 $self->{'key'} = $value;
109             }
110             elsif ($name eq 'sec-websocket-protocol') {
111 1         4 Module::Load::load('Net::WebSocket::HTTP');
112              
113 1         69 for my $token ( Net::WebSocket::HTTP::split_tokens($value) ) {
114 3 100       7 if (!defined $self->{'_subprotocol'}) {
115 2         3 ($self->{'_subprotocol'}) = grep { $_ eq $token } @{ $self->{'subprotocols'} };
  6         11  
  2         4  
116             }
117             }
118             }
119             else {
120 7         23 $self->_consume_generic_header($name => $value);
121             }
122              
123 12         39 return;
124             }
125              
126             #Send only those extensions that we’ve deduced the client can actually use.
127             sub _should_include_extension_in_headers {
128 0     0   0 my ($self, $xtn) = @_;
129              
130 0         0 return $xtn->ok_to_use();
131             }
132              
133             sub _encode_subprotocols {
134 2     2   4 my ($self) = @_;
135              
136 2 100       12 local $self->{'subprotocols'} = defined($self->{'_subprotocol'}) ? [ $self->{'_subprotocol'} ] : undef if $self->{'_no_use_legacy'};
    50          
137              
138 2         10 return $self->SUPER::_encode_subprotocols();
139             }
140              
141             sub _valid_headers_or_die {
142 2     2   4 my ($self) = @_;
143              
144 2         10 my @needed = $self->_missing_generic_headers();
145              
146 2 50       6 push @needed, 'Sec-WebSocket-Version' if !$self->{'_version_ok'};
147 2 50       10 push @needed, 'Sec-WebSocket-Key' if !$self->{'key'};
148              
149 2 50       8 die "Need: [@needed]" if @needed;
150              
151 2         4 return;
152             }
153              
154             sub _create_header_lines {
155 2     2   4 my ($self) = @_;
156              
157 2         10 Call::Context::must_be_list();
158              
159             return (
160 2         43 'HTTP/1.1 101 Switching Protocols',
161              
162             #For now let’s assume no one wants any other Upgrade:
163             #or Connection: values than the ones WebSocket requires.
164             'Upgrade: websocket',
165             'Connection: Upgrade',
166              
167             'Sec-WebSocket-Accept: ' . $self->get_accept(),
168              
169             $self->_encode_subprotocols(),
170              
171             $self->_encode_extensions(),
172             );
173             }
174              
175             #----------------------------------------------------------------------
176              
177             =head1 LEGACY INTERFACE: SYNOPSIS
178              
179             #...Parse the request’s headers yourself...
180              
181             my $hsk = Net::WebSocket::Handshake::Server->new(
182              
183             #base 64, gotten from request
184             key => '..',
185              
186             #optional - same as in non-legacy interface
187             subprotocols => [ 'echo', 'haha' ],
188              
189             #optional, instances of Net::WebSocket::Handshake::Extension
190             extensions => \@extension_objects,
191             );
192              
193             #Note the need to conclude the header text manually.
194             print $hsk->create_header_text() . "\x0d\x0a";
195              
196             =cut
197              
198             *get_accept = __PACKAGE__->can('_get_accept');
199              
200             1;