File Coverage

blib/lib/XAS/Lib/Net/Client.pm
Criterion Covered Total %
statement 12 124 9.6
branch 0 38 0.0
condition n/a
subroutine 4 16 25.0
pod 10 11 90.9
total 26 189 13.7


line stmt bran cond sub pod time code
1             package XAS::Lib::Net::Client;
2              
3             our $VERSION = '0.03';
4              
5 1     1   4 use IO::Socket;
  1         2  
  1         8  
6 1     1   919 use IO::Select;
  1         1141  
  1         38  
7 1     1   6 use Errno ':POSIX';
  1         1  
  1         405  
8              
9             use XAS::Class
10 1         15 debug => 0,
11             version => $VERSION,
12             base => 'XAS::Base',
13             mixin => 'XAS::Lib::Mixins::Bufops XAS::Lib::Mixins::Keepalive',
14             utils => ':validation dotid trim',
15             accessors => 'handle select attempts',
16             mutators => 'timeout',
17             import => 'class',
18             vars => {
19             PARAMS => {
20             -port => 1,
21             -host => 1,
22             -tcp_keepalive => { optional => 1, default => 0 },
23             -timeout => { optional => 1, default => 60 },
24             -eol => { optional => 1, default => "\015\012" },
25             },
26             ERRNO => 0,
27             ERRSTR => '',
28             }
29 1     1   5 ;
  1         1  
30              
31              
32             #use Data::Hexdumper;
33              
34             # ----------------------------------------------------------------------
35             # Public Methods
36             # ----------------------------------------------------------------------
37              
38             sub connect {
39 0     0 1   my $self = shift;
40              
41 0           $self->class->var('ERRNO', 0);
42 0           $self->class->var('ERRSTR', '');
43              
44             $self->{'handle'} = IO::Socket::INET->new(
45             Proto => 'tcp',
46             PeerPort => $self->port,
47             PeerAddr => $self->host,
48             Timeout => $self->timeout,
49 0 0         ) or do {
50              
51 0           my $errno = $! + 0;
52 0           my $errstr = $!;
53              
54 0           $self->class->var('ERRNO', $errno);
55 0           $self->class->var('ERRSTR', $errstr);
56              
57 0           $self->throw_msg(
58             dotid($self->class) . '.connect.noconnect',
59             'net_client_noconnect',
60             $self->host,
61             $self->port,
62             $errstr
63             );
64              
65             };
66              
67 0 0         if ($self->tcp_keepalive) {
68              
69 0           $self->log->debug("keepalive activated");
70              
71 0           $self->enable_keepalive($self->handle);
72              
73             }
74              
75 0           $self->handle->blocking(0);
76 0           $self->{'select'} = IO::Select->new($self->handle);
77              
78             }
79              
80             sub pending {
81 0     0 1   my $self = shift;
82              
83 0           return length($self->{'buffer'});
84              
85             }
86              
87             sub disconnect {
88 0     0 1   my $self = shift;
89              
90 0 0         if ($self->handle->connected) {
91              
92 0           $self->handle->close();
93              
94             }
95              
96             }
97              
98             sub get {
99 0     0 1   my $self = shift;
100 0           my ($length) = validate_params(\@_, [
101             { optional => 1, default => 512 }
102             ]);
103              
104 0           my $output;
105              
106 0 0         if ($self->pending > $length) {
107              
108 0           $output = $self->buf_slurp(\$self->{'buffer'}, $length);
109              
110             } else {
111              
112 0           $self->_fill_buffer();
113              
114 0 0         my $l = ($self->pending > $length) ? $length : $self->pending;
115 0           $output = $self->buf_slurp(\$self->{'buffer'}, $l);
116              
117             }
118              
119 0           return $output;
120              
121             }
122              
123             sub gets {
124 0     0 1   my $self = shift;
125              
126 0           my $buffer;
127 0           my $output = '';
128              
129 0           while (my $buf = $self->get()) {
130              
131 0           $buffer .= $buf;
132              
133 0 0         if ($output = $self->buf_get_line(\$buffer, $self->eol)) {
134              
135 0           $self->{'buffer'} = $buffer . $self->{'buffer'};
136 0           last;
137              
138             }
139              
140             }
141              
142 0           return trim($output);
143              
144             }
145              
146             sub put {
147 0     0 1   my $self = shift;
148 0           my ($buffer) = validate_params(\@_, [1]);
149              
150 0           my $counter = 0;
151 0           my $working = 1;
152 0           my $written = 0;
153 0           my $timeout = $self->timeout;
154 0           my $bufsize = length($buffer);
155              
156 0           $self->class->var('ERRNO', 0);
157 0           $self->class->var('ERRSTR', '');
158              
159 0           while ($working) {
160              
161 0           $self->handle->clearerr();
162              
163 0 0         if ($self->select->can_write($timeout)) {
164              
165 0 0         if (my $bytes = $self->handle->syswrite($buffer, $bufsize)) {
166              
167 0           $written += $bytes;
168 0           $buffer = substr($buffer, $bytes);
169 0 0         $working = 0 if ($written >= $bufsize);
170              
171             } else {
172              
173 0 0         if ($self->handle->error) {
174              
175 0           my $errno = $! + 0;
176 0           my $errstr = $!;
177              
178 0 0         if ($errno == EAGAIN) {
179              
180 0           $counter++;
181 0 0         $working = 0 if ($counter > $self->attempts);
182              
183             } else {
184              
185 0           $self->class->var('ERRNO', $errno);
186 0           $self->class->var('ERRSTR', $errstr);
187              
188 0           $self->throw_msg(
189             dotid($self->class) . '.put',
190             'net_client_network',
191             $errstr
192             );
193              
194             }
195              
196             }
197              
198             }
199              
200             } else {
201              
202 0           $working = 0;
203              
204             }
205              
206             }
207              
208 0           return $written;
209              
210             }
211              
212             sub puts {
213 0     0 1   my $self = shift;
214 0           my ($buffer) = validate_params(\@_, [1]);
215              
216 0           my $data = sprintf("%s%s", trim($buffer), $self->eol);
217 0           my $written = $self->put($data);
218              
219 0           return $written;
220              
221             }
222              
223             sub errno {
224 0     0 1   my $class = shift;
225 0           my ($value) = validate_params(\@_, [
226             { optional => 1, default => undef }
227             ]);
228              
229 0 0         class->var('ERRNO', $value) if (defined($value));
230              
231 0           return class->var('ERRNO');
232              
233             }
234              
235             sub errstr {
236 0     0 1   my $class = shift;
237 0           my ($value) = validate_params(\@_, [
238             { optional => 1, default => undef }
239             ]);
240              
241 0 0         class->var('ERRSTR', $value) if (defined($value));
242              
243 0           return class->var('ERRSTR');
244              
245             }
246              
247             sub setup {
248 0     0 0   my $self = shift;
249              
250             }
251              
252             # ----------------------------------------------------------------------
253             # Private Methods
254             # ----------------------------------------------------------------------
255              
256             sub init {
257 0     0 1   my $class = shift;
258              
259 0           my $self = $class->SUPER::init(@_);
260              
261 0           $self->{'attempts'} = 5;
262 0           $self->{'buffer'} = '';
263              
264 0           $self->init_keepalive(); # init tcp keepalive definations
265              
266 0           return $self;
267              
268             }
269              
270             sub _fill_buffer {
271 0     0     my $self = shift;
272              
273 0           my $counter = 0;
274 0           my $working = 1;
275 0           my $read = 0;
276 0           my $timeout = $self->timeout;
277              
278 0           $self->class->var('ERRNO', 0);
279 0           $self->class->var('ERRSTR', '');
280              
281 0           $self->handle->blocking(0);
282              
283 0           while ($working) {
284              
285 0           my $buf;
286              
287 0           $self->handle->clearerr();
288              
289 0 0         if ($self->select->can_read($timeout)) {
290              
291 0 0         if (my $bytes = $self->handle->sysread($buf, 512)) {
292              
293 0           $self->{'buffer'} .= $buf;
294 0           $read += $bytes;
295              
296             } else {
297              
298 0 0         if ($self->handle->error) {
299              
300 0           my $errno = $! + 0;
301 0           my $errstr = $!;
302              
303 0           $self->log->debug("fill_buffer: errno = $errno");
304              
305 0 0         if ($errno == EAGAIN) {
306              
307 0           $counter++;
308 0 0         $working = 0 if ($counter > $self->attempts);
309              
310             } else {
311              
312 0           $self->class->var('ERRNO', $errno);
313 0           $self->class->var('ERRSTR', $errstr);
314              
315 0           $self->throw_msg(
316             dotid($self->class) . '.fill_buffer',
317             'net_client_network',
318             $errstr
319             );
320              
321             }
322              
323             }
324              
325             }
326              
327             } else {
328              
329 0           $working = 0;
330              
331             }
332              
333             }
334              
335 0           $self->handle->blocking(1);
336              
337 0           return $read;
338              
339             }
340              
341             1;
342              
343             __END__
344              
345             =head1 NAME
346              
347             XAS::Lib::Net::Client - The network client interface for the XAS environment
348              
349             =head1 SYNOPSIS
350              
351             my $rpc = XAS::Lib::Net::Client->new(
352             -port => 9505,
353             -host => 'localhost',
354             };
355              
356             =head1 DESCRIPTION
357              
358             This module implements a simple text orientated network protocol. All "packets"
359             will have an explicit "\015\012" appended. This delineates the "packets" and is
360             network neutral. No attempt is made to decipher these "packets".
361              
362             =head1 METHODS
363              
364             =head2 new
365              
366             This initializes the module and can take these parameters. It doesn't actually
367             make a network connection.
368              
369             =over 4
370              
371             =item B<-port>
372              
373             The port number to attach too.
374              
375             =item B<-host>
376              
377             The host to use for the connection. This can be an IP address or
378             a host name.
379              
380             =item B<-timeout>
381              
382             An optional timeout, it defaults to 60 seconds.
383              
384             =item B<-eol>
385              
386             An optional eol. The default is "\015\012". Which is network netural.
387              
388             =item B<-tcp_keeplive>
389              
390             Turns on TCP keepalive for each connection.
391              
392             =back
393              
394             =head2 connect
395              
396             Connect to the defined socket.
397              
398             =head2 disconnect
399              
400             Disconnect from the defined socket.
401              
402             =head2 put($buffer)
403              
404             This writes a buffer to the socket. Returns the number of bytes written.
405              
406             =over 4
407              
408             =item B<$buffer>
409              
410             The buffer to send over the socket.
411              
412             =back
413              
414             =head2 puts($buffer)
415              
416             This writes a buffer that is terminated with eol to the socket. Returns the
417             number of bytes written.
418              
419             =over 4
420              
421             =item B<$buffer>
422              
423             The buffer to send over the socket.
424              
425             =back
426              
427             =head2 get($length)
428              
429             This block reads data from the socket. A buffer is returned when it reaches
430             $length or timeout.
431              
432             =over 4
433              
434             =item B<$length>
435              
436             An optional length for the buffer. Defaults to 512 bytes.
437              
438             =back
439              
440             =head2 gets
441              
442             This reads a buffer delimited by the eol from the socket.
443              
444             =head2 pending
445              
446             This returns the size of the internal read buffer.
447              
448             =head2 errno
449              
450             A class method to return the error number.
451              
452             =head2 errstr
453              
454             A class method to return the error string.
455              
456             =head1 SEE ALSO
457              
458             =over 4
459              
460             =item L<XAS::Lib::Net::POE::Client|XAS::Lib::Net::POE::Client>
461              
462             =item L<XAS::Lib::Net::Server|XAS::Lib::Net::Server>
463              
464             =item L<XAS|XAS>
465              
466             =back
467              
468             =head1 AUTHOR
469              
470             Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>
471              
472             Copyright (c) 2012-2015 Kevin L. Esteb
473              
474             This is free software; you can redistribute it and/or modify it under
475             the terms of the Artistic License 2.0. For details, see the full text
476             of the license at http://www.perlfoundation.org/artistic_license_2_0.
477              
478             =cut