File Coverage

blib/lib/Net/Async/IRC/Protocol.pm
Criterion Covered Total %
statement 100 109 91.7
branch 27 34 79.4
condition 2 6 33.3
subroutine 23 24 95.8
pod 10 11 90.9
total 162 184 88.0


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