File Coverage

lib/Devel/Trepan/IO/TCPServer.pm
Criterion Covered Total %
statement 42 82 51.2
branch 3 20 15.0
condition 0 6 0.0
subroutine 12 21 57.1
pod 0 11 0.0
total 57 140 40.7


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011-2013 Rocky Bernstein <rocky@cpan.org>
3             # Debugger Server Input/Output interface.
4              
5 14     14   6096 use warnings; use strict;
  14     14   39  
  14         382  
  14         74  
  14         35  
  14         470  
6              
7              
8             package Devel::Trepan::IO::TCPServer;
9              
10 14     14   71 use English qw ( -no_match_vars );
  14         84  
  14         89  
11 14     14   11660 use IO::Socket qw(SOCK_STREAM);
  14         154712  
  14         74  
12              
13             BEGIN {
14 14     14   135 my @OLD_INC = @INC;
15 14     14   3052 use rlib '../../..';
  14         32  
  14         109  
16 14     14   10322 use Devel::Trepan::IO::TCPPack;
  14         43  
  14         823  
17 14     14   711 use Devel::Trepan::Util qw(hash_merge);
  14         37  
  14         690  
18 14         386 @INC = @OLD_INC;
19             }
20              
21 14     14   96 use constant DEFAULT_INIT_OPTS => {open => 1};
  14         37  
  14         1202  
22              
23 14         12493 use constant SERVER_SOCKET_OPTS => {
24             host => '127.0.0.1', # or ::1? or localhost?
25             port => 1954,
26             timeout => 5, # FIXME: not used
27             reuse => 1, # FIXME: not used. Allow port to be resued on close?
28             open => 1,
29             logger => undef # Complaints should be sent here.
30             # Python has: 'posix' == os.name
31 14     14   100 };
  14         36  
32              
33             sub new($;$)
34             {
35 2     2 0 1319 my ($class, $opts) = @_;
36 2         42 $opts = hash_merge($opts, DEFAULT_INIT_OPTS);
37             my $self = {
38             input => undef,
39             output => undef,
40             session => undef,
41             buf => '', # Read buffer
42             state => 'disconnected',
43             logger => $opts->{logger},
44 2         35 line_edit => 0
45             };
46 2         8 bless $self, $class;
47 2 50       43 $self->open($opts) if $opts->{open};
48 1         3 return $self;
49             }
50              
51             sub is_connected($)
52             {
53 0     0 0 0 my $self = shift;
54             $self->{state} = 'connected' if
55 0 0 0     0 $self->{inout} and $self->{inout}->connected;
56 0         0 return $self->{state} eq 'connected';
57             }
58              
59             sub is_interactive($) {
60 0     0 0 0 my $self = shift;
61 0         0 return -t $self->{input};
62             }
63              
64              
65             sub have_term_readline($)
66             {
67 0     0 0 0 return 0;
68             }
69              
70             # Closes server connection.
71             sub close
72             {
73 0     0 0 0 my $self = shift;
74 0         0 $self->{state} = 'closing';
75 0 0       0 if ($self->{inout}) {
76 0         0 close($self->{inout}) ;
77             }
78 0         0 $self->{state} = 'disconnected';
79 0 0       0 print {$self->{logger}} "Disconnected\n" if $self->{logger};
  0         0  
80             }
81              
82             sub open($;$)
83             {
84 2     2 0 8 my ($self, $opts) = @_;
85 2         8 $opts = hash_merge($opts, SERVER_SOCKET_OPTS);
86 2         83 $self->{host} = $opts->{host};
87 2         12 $self->{port} = $opts->{port};
88             $self->{server} =
89             IO::Socket::INET->new(
90             LocalPort => $self->{port},
91             LocalAddr => $self->{host},
92 2         49 Type => SOCK_STREAM,
93             Reuse => 1,
94             Listen => 1 # or SOMAXCONN
95              
96             );
97             die "Can't open socket host $self->{host}, port $self->{port}\n" unless
98 2 100       1374 $self->{server};
99             # @server.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, 5)
100             # # @opts[:timeout])
101 1         7 $self->{state} = 'listening';
102             }
103              
104             sub is_empty($)
105             {
106 0     0 0   my($self) = @_;
107 0           0 == length($self->{buf});
108             }
109              
110             # Read one message unit. It's possible however that
111             # more than one message will be set in a receive, so we will
112             # have to buffer that for the next read.
113             # EOFError will be raised on EOF.
114             sub read_msg($)
115             {
116 0     0 0   my($self) = @_;
117 0 0         $self->wait_for_connect unless $self->is_connected;
118 0           my ($buf, $data, $info);
119 0   0       while (!$self->{buf} || is_empty($self)) {
120 0           $self->{session}->recv($self->{buf}, TCP_MAX_PACKET);
121             }
122 0           eval {
123 0           ($self->{buf}, $data) = unpack_msg($self->{buf});
124             };
125 0 0         if ($EVAL_ERROR) {
126 0           $self->{buf} = '';
127 0           die $EVAL_ERROR;
128             }
129 0           return $data;
130             }
131              
132             sub wait_for_connect
133             {
134 0     0 0   my($self) = @_;
135 0 0         if ($self->{logger}) {
136             my $msg = sprintf("Waiting for a connection on port %d at " .
137             "address %s...",
138 0           $self->{port}, $self->{host});
139 0           print {$self->{logger}} "$msg\n";
  0            
140             }
141             $self->{input} = $self->{output} = $self->{session} =
142 0           $self->{server}->accept;
143 0 0         print {$self->{logger}} "Got connection\n" if $self->{logger};
  0            
144 0           $self->{state} = 'connected';
145             }
146              
147             # This method the debugger uses to write. In contrast to
148             # writeline, no newline is added to the } to `str'. Also
149             # msg doesn't have to be a string.
150             sub write($$)
151             {
152 0     0 0   my($self, $msg) = @_;
153 0 0         $self->wait_for_connect unless $self->is_connected;
154             # FIXME: do we have to check the size of msg and split output?
155 0           $self->{session}->print(pack_msg($msg));
156             }
157              
158             sub writeline($$)
159             {
160 0     0 0   my($self, $msg) = @_;
161 0           $self->write($msg . "\n");
162             }
163              
164             # Demo
165             unless (caller) {
166             my $server = Devel::Trepan::IO::TCPServer->new(
167             { open => 1,
168             port => 1027,
169             });
170             if (scalar @ARGV) {
171             printf "Listening for connection...\n";
172             my $line = $server->read_msg;
173             while (defined($line)) {
174             chomp $line;
175             print "Got: $line\n";
176             last if $line eq 'quit';
177             $line = $server->read_msg;
178             }
179             # $server->open;
180             # Thread.new do
181             # while 1 do
182             # begin
183             # line = server.read_msg.chomp
184             # puts "got #{line}"
185             # rescue EOFError
186             # puts 'Got EOF'
187             # break
188             # }
189             # }
190             # }
191             # threads << Thread.new do
192             # t = TCPSocket.new('localhost', 1027)
193             # while 1 do
194             # begin
195             # print "input? "
196             # line = STDIN.gets
197             # break if !line || line.chomp == 'quit'
198             # t.puts(pack_msg(line))
199             # rescue EOFError
200             # puts "Got EOF"
201             # break
202             # rescue Exception => e
203             # puts "Got #{e}"
204             # break
205             # }
206             # }
207             # t.close
208             # }
209             # threads.each {|t| t.join }
210             $server->close;
211             }
212             }
213              
214             1;