File Coverage

blib/lib/Net/Async/WebSocket/Protocol.pm
Criterion Covered Total %
statement 48 48 100.0
branch 8 8 100.0
condition 2 3 66.6
subroutine 16 16 100.0
pod 4 8 50.0
total 78 83 93.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2010-2017 -- leonerd@leonerd.org.uk
5              
6             package Net::Async::WebSocket::Protocol;
7              
8 4     4   908 use strict;
  4         8  
  4         101  
9 4     4   15 use warnings;
  4         7  
  4         97  
10 4     4   14 use base qw( IO::Async::Stream );
  4         7  
  4         1658  
11              
12 4     4   106644 use Carp;
  4         9  
  4         213  
13              
14             our $VERSION = '0.12';
15              
16 4     4   1602 use Protocol::WebSocket::Frame;
  4         8005  
  4         118  
17 4     4   1503 use Sub::Util 1.40 qw( set_subname );
  4         1024  
  4         1844  
18              
19             my %FRAMETYPES = (
20             1 => "text",
21             2 => "binary",
22             0x8 => "close",
23             0x9 => "ping",
24             0xa => "pong",
25             );
26              
27             my @ON_TYPE_FRAMES = map { "on_${_}_frame" } values %FRAMETYPES;
28              
29             =head1 NAME
30              
31             C - send and receive WebSocket frames
32              
33             =head1 DESCRIPTION
34              
35             This subclass of L implements an established WebSocket
36             connection, that has already completed its setup handshaking and is ready to
37             pass frames.
38              
39             Objects of this type would not normally be constructed directly. For WebSocket
40             clients, see L, which is a subclass of this.
41             For WebSocket servers, see L, which constructs
42             objects in this class when it accepts a new connection and passes it to its
43             event handler.
44              
45             =cut
46              
47             =head1 EVENTS
48              
49             The following events are invoked, either using subclass methods or CODE
50             references in parameters:
51              
52             =head2 on_text_frame
53              
54             $self->on_text_frame( $text )
55             $on_text_frame->( $self, $text )
56              
57             Invoked when a text frame is received. It is passed a Unicode character string
58             formed by decoding the received UTF-8 bytes.
59              
60             =head2 on_frame
61              
62             $self->on_frame( $text )
63             $on_frame->( $self, $text )
64              
65             A synonym for C, provided for backward compatibility.
66              
67             This may be removed in a later version.
68              
69             =head2 on_binary_frame, on_ping_frame, on_pong_frame, on_close_frame
70              
71             $self->on_..._frame( $bytes )
72             $on_..._frame->( $self, $bytes )
73              
74             Invoked when other types of frame are received. These will be passed plain
75             byte strings.
76              
77             =head2 on_raw_frame
78              
79             $self->on_raw_frame( $frame, $bytes )
80             $on_raw_frame->( $self, $frame, $bytes )
81              
82             Invoked when a frame is received that does not have a specific handler defined
83             of one of the above types. C<$frame> will be an instance of
84             L.
85              
86             =cut
87              
88             sub _init
89             {
90 4     4   4766 my $self = shift;
91 4         27 $self->SUPER::_init;
92              
93 4         109 $self->{framebuffer} = Protocol::WebSocket::Frame->new;
94             }
95              
96             =head1 PARAMETERS
97              
98             The following named parameters may be passed to C or C:
99              
100             =over 8
101              
102             =item on_frame => CODE
103              
104             =item on_text_frame => CODE
105              
106             =item on_binary_frame, on_ping_frame, on_pong_frame, on_close_frame => CODE
107              
108             =item on_raw_frame => CODE
109              
110             CODE references for event handlers.
111              
112             =item masked => BOOL
113              
114             Whether frames constructed and sent by this instance will be masked.
115              
116             =back
117              
118             =cut
119              
120             sub configure
121             {
122 19     19 1 1739 my $self = shift;
123 19         53 my %params = @_;
124              
125 19         51 foreach (qw( on_frame on_raw_frame masked ), @ON_TYPE_FRAMES ) {
126 152 100       283 $self->{$_} = delete $params{$_} if exists $params{$_};
127             }
128              
129 19         96 $self->SUPER::configure( %params );
130             }
131              
132             sub on_read
133             {
134 7     7 1 9447 my $self = shift;
135 7         19 my ( $buffref, $closed ) = @_;
136              
137 7         15 my $framebuffer = $self->{framebuffer};
138              
139 7         30 $framebuffer->append( $$buffref ); # modifies $$buffref
140              
141 7         77 while( defined( my $bytes = $framebuffer->next_bytes ) ) {
142 7         595 my $type = $FRAMETYPES{$framebuffer->opcode};
143 7         76 $self->debug_printf( "FRAME $type" );
144              
145 7 100       39 my $text = $framebuffer->is_text ? Encode::decode_utf8( $bytes ) : undef;
146              
147 7 100 66     351 $self->maybe_invoke_event( "on_${type}_frame" => $text // $bytes )
148             or $self->maybe_invoke_event( on_raw_frame => $framebuffer, $bytes );
149              
150 7 100       156 $self->maybe_invoke_event( on_frame => $text ) if $framebuffer->is_text;
151             }
152              
153 7         244 return 0;
154             }
155              
156             =head1 METHODS
157              
158             The following methods documented with a trailing call to C<< ->get >> return
159             L instances.
160              
161             =cut
162              
163             =head2 send_frame
164              
165             $self->send_frame( @args )->get
166              
167             Sends a frame to the peer containing containing the given string. The
168             arguments are passed to L's C method.
169              
170             This method is discouraged in favour of the more specific ones listed below,
171             and is only provided for back-compatibility or for sending new frame types not
172             recognised by the specific methods.
173              
174             =cut
175              
176             sub send_frame
177             {
178 2     2 1 1189 my $self = shift;
179              
180 2         8 $self->write( Protocol::WebSocket::Frame->new( @_ )->to_bytes );
181             }
182              
183             =head2 send_text_frame
184              
185             $self->send_text_frame( $text, %params )->get
186              
187             Sends a text frame to the peer. The given string will be treated as a Unicode
188             character string, and sent as UTF-8 encoded bytes.
189              
190             Any additional arguments will be passed as parameters to the underlying
191             L call.
192              
193             =head2 send_I_frame
194              
195             $self->send_binary_frame( $bytes, %params )->get
196              
197             $self->send_ping_frame( $bytes, %params )->get
198              
199             $self->send_pong_frame( $bytes, %params )->get
200              
201             $self->send_close_frame( $bytes, %params )->get
202              
203             Sends a frame of the given type to the peer.
204              
205             Any additional arguments will be passed as parameters to the underlying
206             L call.
207              
208             =cut
209              
210             sub send_text_frame
211             {
212 3     3 1 509 my $self = shift;
213 3         9 my ( $text, %params ) = @_;
214              
215             # Protocol::WebSocket::Frame will UTF-8 encode this for us
216             $self->write(
217             Protocol::WebSocket::Frame->new(
218             type => "text",
219             buffer => $text,
220             masked => $self->{masked},
221 3         19 )->to_bytes,
222             %params
223             );
224             }
225              
226             foreach my $type ( values %FRAMETYPES ) {
227             next if $type eq "text";
228             my $method = "send_${type}_frame";
229             my $code = sub {
230 1     1 0 1953 my $self = shift;
        1 0    
        1 0    
        1 0    
231 1         3 my ( $bytes, %params ) = @_;
232              
233             $self->write(
234             Protocol::WebSocket::Frame->new(
235             type => $type,
236             buffer => $bytes,
237             masked => $self->{masked},
238 1         5 )->to_bytes,
239             %params
240             );
241             };
242              
243 4     4   24 no strict 'refs';
  4         7  
  4         218  
244             *$method = set_subname $method => $code;
245             }
246              
247             =head1 AUTHOR
248              
249             Paul Evans
250              
251             =cut
252              
253             0x55AA;