File Coverage

blib/lib/Net/Async/WebSocket/Protocol.pm
Criterion Covered Total %
statement 47 47 100.0
branch 8 8 100.0
condition 2 3 66.6
subroutine 16 16 100.0
pod 4 8 50.0
total 77 82 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-2024 -- leonerd@leonerd.org.uk
5              
6             package Net::Async::WebSocket::Protocol 0.14;
7              
8 4     4   723 use v5.14;
  4         18  
9 4     4   24 use warnings;
  4         8  
  4         275  
10 4     4   40 use base qw( IO::Async::Stream );
  4         8  
  4         2400  
11              
12 4     4   198915 use Carp;
  4         8  
  4         226  
13              
14 4     4   2312 use Protocol::WebSocket::Frame;
  4         11409  
  4         173  
15              
16 4     4   2286 use meta 0.008;
  4         4713  
  4         179  
17 4     4   28 no warnings 'meta::experimental';
  4         7  
  4         2946  
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 5     5   16538 my $self = shift;
91 5         42 $self->SUPER::_init;
92              
93 5         170 $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 22     22 1 2215 my $self = shift;
123 22         84 my %params = @_;
124              
125 22         60 foreach (qw( on_frame on_raw_frame masked ), @ON_TYPE_FRAMES ) {
126 176 100       343 $self->{$_} = delete $params{$_} if exists $params{$_};
127             }
128              
129 22         89 $self->SUPER::configure( %params );
130             }
131              
132             sub on_read
133             {
134 7     7 1 11554 my $self = shift;
135 7         18 my ( $buffref, $closed ) = @_;
136              
137 7         15 my $framebuffer = $self->{framebuffer};
138              
139 7         41 $framebuffer->append( $$buffref ); # modifies $$buffref
140              
141 7         104 while( defined( my $bytes = $framebuffer->next_bytes ) ) {
142 7         666 my $type = $FRAMETYPES{$framebuffer->opcode};
143 7         227 $self->debug_printf( "FRAME $type" );
144              
145 7 100       34 my $text = $framebuffer->is_text ? Encode::decode_utf8( $bytes ) : undef;
146              
147 7 100 66     191 $self->maybe_invoke_event( "on_${type}_frame" => $text // $bytes )
148             or $self->maybe_invoke_event( on_raw_frame => $framebuffer, $bytes );
149              
150 7 100       188 $self->maybe_invoke_event( on_frame => $text ) if $framebuffer->is_text;
151             }
152              
153 7         251 return 0;
154             }
155              
156             =head1 METHODS
157              
158             The following methods documented in an C expression return L
159             instances.
160              
161             =cut
162              
163             =head2 send_frame
164              
165             await $self->send_frame( @args );
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 4097 my $self = shift;
179              
180 2         12 $self->write( Protocol::WebSocket::Frame->new( @_ )->to_bytes );
181             }
182              
183             =head2 send_text_frame
184              
185             await $self->send_text_frame( $text, %params );
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             await $self->send_binary_frame( $bytes, %params );
196              
197             await $self->send_ping_frame( $bytes, %params );
198              
199             await $self->send_pong_frame( $bytes, %params );
200              
201             await $self->send_close_frame( $bytes, %params );
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 429 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             my $metapkg = meta::get_this_package;
227              
228             foreach my $type ( values %FRAMETYPES ) {
229             next if $type eq "text";
230             my $method = "send_${type}_frame";
231             my $code = sub {
232 1     1 0 1768 my $self = shift;
        1 0    
        1 0    
        1 0    
233 1         3 my ( $bytes, %params ) = @_;
234              
235             $self->write(
236             Protocol::WebSocket::Frame->new(
237             type => $type,
238             buffer => $bytes,
239             masked => $self->{masked},
240 1         5 )->to_bytes,
241             %params
242             );
243             };
244              
245             $metapkg->add_named_sub( $method => $code );
246             }
247              
248             =head1 AUTHOR
249              
250             Paul Evans
251              
252             =cut
253              
254             0x55AA;