File Coverage

blib/lib/Net/Async/WebSocket/Server.pm
Criterion Covered Total %
statement 41 46 89.1
branch 4 6 66.6
condition 2 3 66.6
subroutine 10 11 90.9
pod 4 4 100.0
total 61 70 87.1


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::Server 0.14;
7              
8 3     3   284135 use v5.14;
  3         11  
9 3     3   16 use warnings;
  3         6  
  3         189  
10 3     3   21 use base qw( IO::Async::Listener );
  3         6  
  3         1717  
11              
12 3     3   50082 use Carp;
  3         11  
  3         195  
13              
14 3     3   670 use Net::Async::WebSocket::Protocol;
  3         9  
  3         113  
15              
16 3     3   1566 use Protocol::WebSocket::Handshake::Server;
  3         26897  
  3         1344  
17              
18             =head1 NAME
19              
20             C - serve WebSocket clients using C
21              
22             =head1 SYNOPSIS
23              
24             use Future::AsyncAwait;
25              
26             use IO::Async::Loop;
27             use Net::Async::WebSocket::Server;
28              
29             my $server = Net::Async::WebSocket::Server->new(
30             on_client => sub {
31             my ( undef, $client ) = @_;
32              
33             $client->configure(
34             on_text_frame => sub {
35             my ( $self, $frame ) = @_;
36             $self->send_text_frame( $frame );
37             },
38             );
39             }
40             );
41              
42             my $loop = IO::Async::Loop->new;
43             $loop->add( $server );
44              
45             await $server->listen(
46             service => 3000,
47             );
48              
49             $loop->run;
50              
51             =head1 DESCRIPTION
52              
53             This subclass of L accepts WebSocket connections. When a
54             new connection arrives it will perform an initial handshake, and then pass the
55             connection on to the continuation callback or method.
56              
57             =cut
58              
59             =head1 EVENTS
60              
61             The following events are invoked, either using subclass methods or CODE
62             references in parameters:
63              
64             =head2 on_client
65              
66             $self->on_client( $client );
67             $on_client->( $self, $client );
68              
69             Invoked when a new client connects and completes its initial handshake.
70              
71             It will be passed a new instance of a L
72             object, wrapping the client connection.
73              
74             =head2 on_handshake
75              
76             Invoked when a handshake has been requested.
77              
78             $self->on_handshake( $client, $hs, $continue );
79             $on_handshake->( $self, $client, $hs, $continue );
80              
81             Calling C<$continue> with a true value will complete the handshake, false will
82             drop the connection.
83              
84             This is useful for filtering on origin, for example:
85              
86             on_handshake => sub {
87             my ( $self, $client, $hs, $continue ) = @_;
88              
89             $continue->( $hs->req->origin eq "http://localhost" );
90             }
91              
92             =cut
93              
94             sub new
95             {
96 2     2 1 484534 my $class = shift;
97 2         34 return $class->SUPER::new(
98             handle_class => "Net::Async::WebSocket::Protocol",
99             @_,
100             );
101             }
102              
103             sub on_accept
104             {
105 2     2 1 540 my $self = shift;
106 2         7 my ( $client ) = @_;
107              
108 2         34 my $hs = Protocol::WebSocket::Handshake::Server->new;
109              
110             $client->configure(
111             on_read => sub {
112 2     2   1974 my ( $client, $buffref, $closed ) = @_;
113              
114 2         16 $hs->parse( $$buffref ); # modifies $$buffref
115              
116 2 50       1685 if( $hs->is_done ) {
117             my $on_handshake = $self->can_event( "on_handshake" ) ||
118 2   66     38 sub { $_[3]->( 1 ) };
119              
120             $on_handshake->( $self, $client, $hs, sub {
121 2         19 my ( $ok ) = @_;
122              
123 2 50       27 unless( $ok ) {
124 0         0 $self->remove_child( $client );
125 0         0 return;
126             }
127              
128 2         20 $client->configure( on_read => undef );
129 2         178 $client->write( $hs->to_string );
130              
131 2         1494 $client->debug_printf( "HANDSHAKE done" );
132 2         18 $self->invoke_event( on_client => $client );
133 2         58 } );
134             }
135              
136 2         296 return 0;
137             },
138 2         33 );
139              
140 2         224 $self->add_child( $client );
141             }
142              
143             =head1 PARAMETERS
144              
145             The following named parameters may be passed to C or C:
146              
147             =over 8
148              
149             =item on_client => CODE
150              
151             =item on_handshake => CODE
152              
153             CODE references for event handlers.
154              
155             =back
156              
157             =cut
158              
159             sub configure
160             {
161 2     2 1 76 my $self = shift;
162 2         7 my %params = @_;
163              
164 2         7 foreach (qw( on_client on_handshake )) {
165 4 100       18 $self->{$_} = delete $params{$_} if exists $params{$_};
166             }
167              
168 2         27 $self->SUPER::configure( %params );
169             }
170              
171             sub listen
172             {
173 0     0 1   my $self = shift;
174 0           my %params = @_;
175              
176 0           $self->SUPER::listen(
177             socktype => 'stream',
178             %params,
179             );
180             }
181              
182             =head1 AUTHOR
183              
184             Paul Evans
185              
186             =cut
187              
188             0x55AA;