File Coverage

blib/lib/Net/Async/WebSocket/Client.pm
Criterion Covered Total %
statement 69 78 88.4
branch 8 16 50.0
condition 3 12 25.0
subroutine 16 20 80.0
pod 3 3 100.0
total 99 129 76.7


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::Client;
7              
8 3     3   222003 use strict;
  3         16  
  3         85  
9 3     3   12 use warnings;
  3         4  
  3         64  
10 3     3   20 use base qw( Net::Async::WebSocket::Protocol );
  3         10  
  3         833  
11 3     3   40 use 5.010; # //
  3         10  
12              
13             IO::Async::Notifier->VERSION( '0.63' ); # ->adopt_future
14              
15 3     3   15 use Carp;
  3         4  
  3         152  
16              
17 3     3   15 use Scalar::Util qw( blessed );
  3         5  
  3         101  
18              
19 3     3   1334 use URI;
  3         12563  
  3         304  
20              
21             BEGIN {
22             eval {
23 3         388 require URI::wss;
24 3 50   3   9 } or do {
25             # In case URI doesn't know that ws:// and wss:// URIs use host/port
26 3         1068 require URI::_server;
27              
28 3         15227 @URI::ws::ISA = qw( URI::_server );
29 3     0   20 *URI::ws::default_port = sub { 80 };
  0         0  
30              
31 3         48 @URI::wss::ISA = qw( URI::_server );
32 3     0   14 *URI::wss::default_port = sub { 443 };
  0         0  
33 3     0   11 *URI::wss::secure = sub { 1 };
  0         0  
34             };
35              
36             # We also need to support ->resource_name, which the CPAN module does not
37             # understand as of 2017-01-01
38 3     3   16 no warnings 'once';
  3         6  
  3         191  
39             *URI::wss::resource_name = sub {
40             shift->path_query
41 3 50   0   125 } unless URI::wss->can( "resource_name" );
  0         0  
42             }
43              
44             our $VERSION = '0.12';
45              
46 3     3   1134 use Protocol::WebSocket::Handshake::Client;
  3         44642  
  3         1165  
47              
48             =head1 NAME
49              
50             C - connect to a WebSocket server using
51             C
52              
53             =head1 SYNOPSIS
54              
55             use IO::Async::Loop;
56             use Net::Async::WebSocket::Client;
57              
58             my $client = Net::Async::WebSocket::Client->new(
59             on_text_frame => sub {
60             my ( $self, $frame ) = @_;
61             print $frame;
62             },
63             );
64              
65             my $loop = IO::Async::Loop->new;
66             $loop->add( $client );
67              
68             $client->connect(
69             url => "ws://$HOST:$PORT/",
70             )->then( sub {
71             $client->send_text_frame( "Hello, world!\n" );
72             })->get;
73              
74             $loop->run;
75              
76             =head1 DESCRIPTION
77              
78             This subclass of L connects to a WebSocket
79             server to establish a WebSocket connection for passing frames.
80              
81             =cut
82              
83             sub new
84             {
85 2     2 1 4574 my $class = shift;
86 2         26 return $class->SUPER::new(
87             masked => 1,
88             @_,
89             );
90             }
91              
92             =head1 METHODS
93              
94             The following methods documented with a trailing call to C<< ->get >> return
95             L instances.
96              
97             =cut
98              
99             sub _do_handshake
100             {
101 2     2   6 my $self = shift;
102 2         8 my %params = @_;
103              
104             my $hs = Protocol::WebSocket::Handshake::Client->new(
105             url => $params{url},
106 2         22 );
107              
108 2         545 $self->debug_printf( "HANDSHAKE start" );
109 2         14 $self->write( $hs->to_string );
110              
111 2         756 my $f = $self->loop->new_future;
112             $self->SUPER::configure( on_read => sub {
113 2     2   4753 my ( undef, $buffref, $closed ) = @_;
114              
115 2         12 $hs->parse( $$buffref ); # modifies $$buffref
116              
117 2 50       610 if( $hs->is_done ) {
118 2         29 $self->debug_printf( "HANDSHAKE done" );
119 2         14 $self->SUPER::configure( on_read => undef );
120              
121 2         151 $f->done( $self );
122             }
123              
124 2         153 return 0;
125 2         1029 } );
126              
127 2         134 return $f;
128             }
129              
130             =head2 connect
131              
132             $self->connect( %params )->get
133              
134             Connect to a WebSocket server. Takes the following named parameters:
135              
136             =over 8
137              
138             =item url => STRING
139              
140             URL to provide to WebSocket handshake. This is also used to infer the host and
141             service name (port number) if not otherwise supplied.
142              
143             =back
144              
145             The returned L returns the client instance itself, making it useful
146             in chaining constructors.
147              
148             =head2 connect (void)
149              
150             $self->connect( %params )
151              
152             When not returning a C, the following additional parameters provide
153             continuations:
154              
155             =over 8
156              
157             =item on_connected => CODE
158              
159             CODE reference to invoke when the handshaking is complete.
160              
161             =back
162              
163             =cut
164              
165             sub connect
166             {
167 1     1 1 220 my $self = shift;
168 1         5 my %params = @_;
169              
170 1 50       12 if( my $url = $params{url} ) {
171 1 50 33     13 $url = URI->new( $url ) unless blessed $url and $url->isa( "URI" );
172              
173 1   33     249 $params{host} //= $url->host;
174 1   33     3 $params{service} //= $url->port;
175              
176 1 50       6 if( $url->secure ) {
177 0         0 require IO::Async::SSL;
178 0         0 push @{ $params{extensions} }, qw( SSL );
  0         0  
179 0   0     0 $params{SSL_hostname} //= $url->host;
180             }
181             }
182              
183 1         6 my $on_connected = delete $params{on_connected};
184              
185             my $f = $self->SUPER::connect( %params )->then( sub {
186 1     1   440 my ( $self ) = @_;
187              
188 1         8 $self->_do_handshake( %params );
189 1         14 });
190              
191 1 50       20820 $f->on_done( $on_connected ) if $on_connected;
192              
193 1 50       19 return $f if defined wantarray;
194              
195 0         0 $self->adopt_future( $f );
196             }
197              
198             =head2 connect_handle
199              
200             $client->connect_handle( $handle, %params )->get
201              
202             Sets the read and write handles to the IO reference given, then performs the
203             initial handshake using the parameters given. These are as for C.
204              
205             =cut
206              
207             sub connect_handle
208             {
209 1     1 1 942 my $self = shift;
210 1         4 my ( $handle, %params ) = @_;
211              
212 1         6 $self->set_handle( $handle );
213              
214 1         294 $self->_do_handshake( %params );
215             }
216              
217             =head1 AUTHOR
218              
219             Paul Evans
220              
221             =cut
222              
223             0x55AA;