File Coverage

blib/lib/Net/Async/IRC/Protocol.pm
Criterion Covered Total %
statement 88 110 80.0
branch 21 34 61.7
condition 2 12 16.6
subroutine 22 25 88.0
pod 9 12 75.0
total 142 193 73.5


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-2014 -- leonerd@leonerd.org.uk
5              
6             package Net::Async::IRC::Protocol;
7              
8 5     5   28 use strict;
  5         11  
  5         153  
9 5     5   25 use warnings;
  5         22  
  5         204  
10              
11             our $VERSION = '0.10';
12              
13 5     5   21 use base qw( IO::Async::Stream Protocol::IRC );
  5         8  
  5         4053  
14              
15 5     5   96138 use Carp;
  5         11  
  5         361  
16              
17 5     5   28 use Protocol::IRC::Message;
  5         11  
  5         124  
18              
19 5     5   27 use Encode qw( find_encoding );
  5         9  
  5         215  
20 5     5   29 use Time::HiRes qw( time );
  5         12  
  5         50  
21              
22 5     5   5771 use IO::Async::Timer::Countdown;
  5         10730  
  5         5617  
23              
24             =head1 NAME
25              
26             C - send and receive IRC messages
27              
28             =head1 DESCRIPTION
29              
30             This subclass of L implements an established
31             IRC connection that has already completed its inital login sequence and is
32             ready to send and receive IRC messages. It handles base message sending and
33             receiving, and implements ping timers.
34              
35             Objects of this type would not normally be constructed directly. For IRC
36             clients, see L which is a subclass of it. All the events,
37             parameters, and methods documented below are relevant there.
38              
39             =cut
40              
41             =head1 EVENTS
42              
43             The following events are invoked, either using subclass methods or C
44             references in parameters:
45              
46             =head2 $handled = on_message
47              
48             =head2 $handled = on_message_MESSAGE
49              
50             Invoked on receipt of a valid IRC message. See C below.
51              
52             =head2 on_irc_error $err
53              
54             Invoked on receipt of an invalid IRC message if parsing fails. C<$err> is the
55             error message text. If left unhandled, any parse error will result in the
56             connection being immediataely closed, followed by the exception being
57             re-thrown.
58              
59             =head2 on_ping_timeout
60              
61             Invoked if the peer fails to respond to a C message within the given
62             timeout.
63              
64             =head2 on_pong_reply $lag
65              
66             Invoked when the peer successfully sends a C reply response to a C
67             message. C<$lag> is the response time in (fractional) seconds.
68              
69             =cut
70              
71             =head1 PARAMETERS
72              
73             The following named parameters may be passed to C or C:
74              
75             =over 8
76              
77             =item on_message => CODE
78              
79             =item on_message_MESSAGE => CODE
80              
81             =item on_irc_error => CODE
82              
83             =item on_ping_timeout => CODE
84              
85             =item on_pong_reply => CODE
86              
87             C references for event handlers.
88              
89             =item pingtime => NUM
90              
91             Amount of quiet time, in seconds, after a message is received from the peer,
92             until a C will be sent to check it is still alive.
93              
94             =item pongtime => NUM
95              
96             Timeout, in seconds, after sending a C message, to wait for a C
97             response.
98              
99             =item encoding => STRING
100              
101             If supplied, sets an encoding to use to encode outgoing messages and decode
102             incoming messages.
103              
104             =back
105              
106             =cut
107              
108             =head1 CONSTRUCTOR
109              
110             =cut
111              
112             =head2 $irc = Net::Async::IRC::Protocol->new( %args )
113              
114             Returns a new instance of a C object. This object
115             represents a IRC connection to a peer. As it is a subclass of
116             C its constructor takes any arguments for
117             that class, in addition to the parameters named below.
118              
119             =cut
120              
121             sub new
122             {
123 5     5 1 11 my $class = shift;
124 5         22 my %args = @_;
125              
126 5         16 my $on_closed = delete $args{on_closed};
127              
128             return $class->SUPER::new(
129             %args,
130              
131             on_closed => sub {
132 0     0   0 my $self = shift;
133              
134 0         0 my $loop = $self->get_loop;
135              
136 0         0 $self->{pingtimer}->stop;
137 0         0 $self->{pongtimer}->stop;
138              
139 0 0       0 $on_closed->( $self ) if $on_closed;
140              
141 0         0 undef $self->{connect_f};
142 0         0 undef $self->{login_f};
143             },
144 5         83 );
145             }
146              
147             sub _init
148             {
149 5     5   11 my $self = shift;
150 5         40 $self->SUPER::_init( @_ );
151              
152 5         149 my $pingtime = 60;
153 5         12 my $pongtime = 10;
154              
155             $self->{pingtimer} = IO::Async::Timer::Countdown->new(
156             delay => $pingtime,
157              
158             on_expire => sub {
159 2     2   4002849 my $now = time();
160              
161 2         71 $self->send_message( "PING", undef, "$now" );
162              
163 2         400 $self->{ping_send_time} = $now;
164              
165 2         15 $self->{pongtimer}->start;
166             },
167 5         71 );
168 5         500 $self->add_child( $self->{pingtimer} );
169              
170             $self->{pongtimer} = IO::Async::Timer::Countdown->new(
171             delay => $pongtime,
172              
173             on_expire => sub {
174 1 50   1   1001750 $self->{on_ping_timeout}->( $self ) if $self->{on_ping_timeout};
175             },
176 5         169 );
177 5         246 $self->add_child( $self->{pongtimer} );
178             }
179              
180             # for Protocol::IRC
181             sub encoder
182             {
183 25     25 1 29 my $self = shift;
184 25         105 return $self->{encoder};
185             }
186              
187             sub configure
188             {
189 6     6 1 14 my $self = shift;
190 6         19 my %args = @_;
191              
192 6         66 $self->{$_} = delete $args{$_} for grep m/^on_message/, keys %args;
193              
194 6         20 for (qw( on_ping_timeout on_pong_reply on_irc_error )) {
195 18 100       69 $self->{$_} = delete $args{$_} if exists $args{$_};
196             }
197              
198 6 100       21 if( exists $args{pingtime} ) {
199 1         5 $self->{pingtimer}->configure( delay => delete $args{pingtime} );
200             }
201              
202 6 100       47 if( exists $args{pongtime} ) {
203 1         4 $self->{pongtimer}->configure( delay => delete $args{pongtime} );
204             }
205              
206 6 50       55 if( exists $args{encoding} ) {
207 0         0 my $encoding = delete $args{encoding};
208 0         0 my $obj = find_encoding( $encoding );
209 0 0       0 defined $obj or croak "Cannot handle an encoding of '$encoding'";
210 0         0 $self->{encoder} = $obj;
211             }
212              
213 6         45 $self->SUPER::configure( %args );
214             }
215              
216             sub setup_transport
217             {
218 0     0 0 0 my $self = shift;
219 0         0 $self->SUPER::setup_transport( @_ );
220              
221 0         0 $self->{connect_f} = Future->new->done( $self->transport->read_handle );
222 0 0 0     0 $self->{pingtimer}->start if $self->{pingtimer} and $self->get_loop;
223             }
224              
225             sub teardown_transport
226             {
227 0     0 0 0 my $self = shift;
228              
229 0         0 undef $self->{connect_f};
230 0         0 undef $self->{login_f};
231 0 0 0     0 $self->{pingtimer}->stop if $self->{pingtimer} and $self->get_loop;
232              
233 0         0 $self->SUPER::teardown_transport( @_ );
234             }
235              
236             =head1 METHODS
237              
238             =cut
239              
240             =head2 $connect = $irc->is_connected
241              
242             Returns true if a connection to the peer is established. Note that even
243             after a successful connection, the connection may not yet logged in to. See
244             also the C method.
245              
246             =cut
247              
248             sub is_connected
249             {
250 6     6 1 2418 my $self = shift;
251 6 100       26 return 0 unless my $connect_f = $self->{connect_f};
252 5   33     17 return $connect_f->is_ready && !$connect_f->failure;
253             }
254              
255             =head2 $loggedin = $irc->is_loggedin
256              
257             Returns true if the full login sequence has been performed on the connection
258             and it is ready to use.
259              
260             =cut
261              
262             sub is_loggedin
263             {
264 2     2 1 775 my $self = shift;
265 2 100       10 return 0 unless my $login_f = $self->{login_f};
266 1   33     4 return $login_f->is_ready && !$login_f->failure;
267             }
268              
269             sub on_read
270             {
271 14     14 1 2023932 my $self = shift;
272 14         27 my ( $buffref, $eof ) = @_;
273              
274 14         31 my $pingtimer = $self->{pingtimer};
275              
276 14 100       65 $pingtimer->is_running ? $pingtimer->reset : $pingtimer->start;
277              
278 14 100       1429 eval {
279 14         85 $self->Protocol::IRC::on_read( $$buffref );
280 13         98 1;
281             } and return 0;
282              
283 1         3 my $e = "$@"; chomp $e;
  1         8  
284              
285 1 50       4 $self->maybe_invoke_event( on_irc_error => $e )
286             and return 0;
287              
288 0         0 $self->close_now;
289 0         0 die "$e\n";
290             }
291              
292             =head2 $nick = $irc->nick
293              
294             Returns the current nick in use by the connection.
295              
296             =cut
297              
298             sub _set_nick
299             {
300 9     9   16 my $self = shift;
301 9         26 ( $self->{nick} ) = @_;
302 9         69 $self->{nick_folded} = $self->casefold_name( $self->{nick} );
303             }
304              
305             sub nick
306             {
307 4     4 1 769 my $self = shift;
308 4         28 return $self->{nick};
309             }
310              
311             =head2 $nick_folded = $irc->nick_folded
312              
313             Returns the current nick in use by the connection, folded by C
314             for convenience.
315              
316             =cut
317              
318             sub nick_folded
319             {
320 9     9 1 12 my $self = shift;
321 9         54 return $self->{nick_folded};
322             }
323              
324             =head1 MESSAGE HANDLING
325              
326             A message with a command of C will try handlers in following places:
327              
328             =over 4
329              
330             =item 1.
331              
332             A CODE ref in a parameter called C
333              
334             $on_message_COMMAND->( $irc, $message, \%hints )
335              
336             =item 2.
337              
338             A method called C
339              
340             $irc->on_message_COMMAND( $message, \%hints )
341              
342             =item 3.
343              
344             A CODE ref in a parameter called C
345              
346             $on_message->( $irc, 'COMMAND', $message, \%hints )
347              
348             =item 4.
349              
350             A method called C
351              
352             $irc->on_message( 'COMMAND', $message, \%hints )
353              
354             =back
355              
356             Certain commands are handled internally by methods on the base
357             C class itself. These may cause other hints hash
358             keys to be created, or to invoke other handler methods. These are documented
359             below.
360              
361             =cut
362              
363             sub invoke
364             {
365 34     34 1 45 my $self = shift;
366 34 100       109 my $retref = $self->maybe_invoke_event( @_ ) or return undef;
367 21         456 return $retref->[0];
368             }
369              
370             sub on_message_PONG
371             {
372 1     1 0 16 my $self = shift;
373 1         2 my ( $message, $hints ) = @_;
374              
375 1 50       3 return 1 unless $self->{pongtimer}->is_running;
376              
377 1         8 my $lag = time - $self->{ping_send_time};
378              
379 1         3 $self->{current_lag} = $lag;
380 1 50       6 $self->{on_pong_reply}->( $self, $lag ) if $self->{on_pong_reply};
381              
382 1         6 $self->{pongtimer}->stop;
383              
384 1         31 return 1;
385             }
386              
387             =head1 AUTHOR
388              
389             Paul Evans
390              
391             =cut
392              
393             0x55AA;