File Coverage

blib/lib/IO/Socket/Telnet/HalfDuplex.pm
Criterion Covered Total %
statement 58 61 95.0
branch 9 18 50.0
condition 3 5 60.0
subroutine 7 7 100.0
pod 2 3 66.6
total 79 94 84.0


line stmt bran cond sub pod time code
1 7     7   201925 use strict;
  7         16  
  7         260  
2 7     7   36 use warnings;
  7         15  
  7         420  
3             package IO::Socket::Telnet::HalfDuplex;
4             our $VERSION = '0.02';
5              
6 7     7   43 use base 'IO::Socket::Telnet';
  7         18  
  7         7871  
7              
8             =head1 NAME
9              
10             IO::Socket::Telnet::HalfDuplex - more reliable telnet communication
11              
12             =head1 VERSION
13              
14             version 0.02
15              
16             =head1 SYNOPSIS
17              
18             use IO::Socket::Telnet::HalfDuplex;
19             my $socket = IO::Socket::Telnet::HalfDuplex->new(PeerAddr => 'localhost');
20             while (1) {
21             $socket->send(scalar <>);
22             print $socket->read;
23             }
24              
25             =head1 DESCRIPTION
26              
27             A common issue when communicating over a network is deciding when input is done
28             being received. If the communication is a fixed protocol, the protocol should
29             define this clearly, but this isn't always the case; in particular, interactive
30             telnet sessions provide no way to tell whether or not the data that has been
31             sent is the full amount of data that the server wants to send, or whether that
32             was just a single packet which should be combined with future packets to form
33             the full message. This module attempts to alleviate this somewhat by providing
34             a way to estimate how much time you should wait before assuming that all the
35             data has arrived.
36              
37             The method used is a slight abuse of the telnet out-of-band option
38             negotiation - most telnet servers, when told to DO an option that they don't
39             understand, will respond that they WONT do that option, and will continue to do
40             so every time (this is not guaranteed by the telnet spec, however - if this
41             isn't the case, L is the only option). We can use this
42             method to get an estimate of how long we should wait for the data. This module
43             sends a ping in the out-of-band data before reading, with the assumption that
44             by the time it gets to the server, all the output that has been generated by
45             your most recent C will already be queued up in the server's output
46             buffer. This would be guaranteed if we were just communicating with the telnet
47             server directly, but typically we are communicating with a subprocess spawned
48             by the telnet server, which means that the telnet server can respond to the
49             ping while the subprocess is continuing to send data, making this not failsafe.
50             It's generally a safe assumption for interactive programs across a network,
51             though, since interactive programs tend to respond quickly, relative to network
52             latency. After sending the ping, we just read as much as we can until we get
53             the pong. This process is all wrapped up in the L method provided by
54             this module; the rest of the interface is just inherited from
55             L.
56              
57             =cut
58              
59             =head1 CONSTRUCTOR
60              
61             =head2 new(PARAMHASH)
62              
63             The constructor takes mostly the same arguments as L, but
64             also accepts the key C, which takes an integer between 40 and 239
65             to use for the ping/pong mechanism. This defaults to 99 if not specified.
66              
67             =cut
68              
69             sub new {
70 3     3 1 3004456 my $class = shift;
71 3         148 my %args = @_;
72 3   100     196 my $ping = delete $args{PingOption} || 99;
73 3 50 33     547 die "Invalid option: $ping (must be 40-239)" if $ping < 40 || $ping >= 240;
74 3         324 my $self = $class->SUPER::new(@_);
75 3         13304 ${*{$self}}{ping_option} = $ping;
  3         8  
  3         18  
76 3         75 $self->IO::Socket::Telnet::telnet_simple_callback(\&_telnet_negotiation);
77 3         61 return $self;
78             }
79              
80             sub telnet_simple_callback {
81 2     2 0 78 my $self = shift;
82 2 50       16 ${*$self}{halfduplex_simple_cb} = $_[0] if @_;
  2         41  
83 2         11 ${*$self}{halfduplex_simple_cb};
  2         13  
84             }
85              
86             =head1 METHODS
87              
88             =cut
89              
90             =head2 read()
91              
92             Performs a (hopefully) full read on the socket. Returns the data read. Throws an exception if the connection ends before all data is read.
93              
94             =cut
95              
96             sub read {
97 12     12 1 20977 my $self = shift;
98 12         26 my $buffer;
99              
100 12         25 $self->do(chr(${*{$self}}{ping_option}));
  12         27  
  12         181  
101 12         1418 ${*{$self}}{got_pong} = 0;
  12         22  
  12         56  
102              
103 12         50 eval {
104 12         95 local $SIG{__DIE__};
105              
106 12         33 while (1) {
107 102         101 my $b;
108 102 50       296 defined $self->recv($b, 4096, 0) and do {
109 102         18294 $buffer .= $b;
110 102 100       163 die "got pong\n" if ${*{$self}}{got_pong};
  102         120  
  102         419  
111 90         197 next;
112             };
113 0 0       0 die "Disconnected from server: $!" unless $!{EINTR};
114             }
115             };
116              
117 12 50       91 die $@ if $@ !~ /^got pong\n/;
118              
119 12         413 return $buffer;
120             }
121              
122             sub _telnet_negotiation {
123 12     12   2719 my $self = shift;
124 12         21 my $option = shift;
125              
126 12         17 my $external_callback = ${*{$self}}{halfduplex_simple_cb};
  12         22  
  12         55  
127 12         26 my $ping = ${*{$self}}{ping_option};
  12         22  
  12         30  
128 12 50       162 if ($option =~ / $ping$/) {
129 12         18 ${*{$self}}{got_pong} = 1;
  12         22  
  12         36  
130 12 100       59 return '' unless $external_callback;
131 2         8 return $self->$external_callback($option);
132             }
133              
134 0 0         return unless $external_callback;
135 0           return $self->$external_callback($option);
136             }
137              
138             =head1 CAVEATS
139              
140             This is not actually guaranteed half-duplex communication - that's not possible
141             in general over a telnet connection without specifying a protocol in advance.
142             This module just does its best to get as close as possible, and tends to do
143             reasonably well in practice.
144              
145             =head1 BUGS
146              
147             No known bugs.
148              
149             Please report any bugs through RT: email
150             C, or browse to
151             L.
152              
153             =head1 SEE ALSO
154              
155             L, L, L, L
156              
157             L
158              
159             =head1 CREDITS
160              
161             This algorithm (and most of the implementation) is due to Shawn Moore (L) for projects such as L and L.
162              
163             =head1 SUPPORT
164              
165             You can find this documentation for this module with the perldoc command.
166              
167             perldoc IO::Socket::Telnet::HalfDuplex
168              
169             You can also look for information at:
170              
171             =over 4
172              
173             =item * AnnoCPAN: Annotated CPAN documentation
174              
175             L
176              
177             =item * CPAN Ratings
178              
179             L
180              
181             =item * RT: CPAN's request tracker
182              
183             L
184              
185             =item * Search CPAN
186              
187             L
188              
189             =back
190              
191             =head1 AUTHOR
192              
193             Jesse Luehrs
194              
195             =head1 COPYRIGHT AND LICENSE
196              
197             This software is copyright (c) 2009 by Jesse Luehrs.
198              
199             This is free software; you can redistribute it and/or modify it under
200             the same terms as perl itself.
201              
202             =cut
203              
204             1;