File Coverage

blib/lib/Net/SeedServe/Server.pm
Criterion Covered Total %
statement 72 76 94.7
branch 10 16 62.5
condition n/a
subroutine 13 13 100.0
pod 6 6 100.0
total 101 111 90.9


line stmt bran cond sub pod time code
1             package Net::SeedServe::Server;
2              
3 6     6   5830 use strict;
  6         10  
  6         148  
4 6     6   30 use warnings;
  6         13  
  6         164  
5              
6 6     6   31 use Net::SeedServe;
  6         6  
  6         105  
7 6     6   29 use IO::All;
  6         47  
  6         38  
8 6     6   5335 use Time::HiRes qw(usleep);
  6         9237  
  6         24  
9              
10             =head1 NAME
11              
12             Net::SeedServe::Server - Perl module for implementing a seed server.
13              
14             =head1 DESCRIPTION
15              
16             None yet. Consult the code, and the examples in the tests directory.
17              
18             =head1 METHODS
19              
20             =head2 $obj = Net::SeedServe::Server->new(status_file => $status_filename);
21              
22             Initialises a new object with the status filename.
23              
24             =cut
25              
26             sub new
27             {
28 7     7 1 130 my $class = shift;
29 7         19 my $self = {};
30 7         21 bless $self, $class;
31 7         42 $self->_init(@_);
32 7         17 return $self;
33             }
34              
35             sub _init
36             {
37 7     7   15 my $self = shift;
38 7         31 my %args = (@_);
39 7 50       59 $self->{'status_file'} = $args{'status_file'} or
40             die "Unknown status file!";
41              
42 7         18 return 0;
43             }
44              
45             =head2 $server->start()
46              
47             Starts the server on a port starting from port 3,000. Returns a hash ref
48             containing the port.
49              
50             =cut
51              
52             sub start
53             {
54 6     6 1 31 my $self = shift;
55 6         18 my $status_file = $self->{'status_file'};
56              
57 6         15 for(my $port = 3000; ; $port++)
58             {
59 10         1288 unlink($status_file);
60 10         11914 my $fork_pid = fork();
61 10 50       542 if (!defined($fork_pid))
62             {
63 0         0 die "Fork was not successful!";
64             }
65 10 100       455 if (! $fork_pid)
66             {
67             # The child will start the service.
68 3         618 my $server =
69             Net::SeedServe->new(
70             'status_file' => $status_file,
71             'port' => $port,
72             );
73              
74             eval
75 3         62 {
76 3         40 $server->loop();
77             };
78 3 50       655 if ($@)
79             {
80 3         584 exit(-1);
81             }
82             }
83             else
84             {
85             # The parent will try to find the child's status
86 7         442 while (! -f $status_file)
87             {
88 62         322966 usleep(5000);
89             }
90 7         282 my $text = io()->file($status_file)->getline();
91 7 100       71296 if ($text eq "Status:Success\tPort:$port\tPID:$fork_pid\n")
92             {
93             # The game is on - the service is running and everything's OK.
94 3         311 $self->{'port'} = $port;
95 3         28 $self->{'server_pid'} = $fork_pid;
96 3         83 return +{ 'port' => $port };
97             }
98             else
99             {
100 4         1037364 waitpid($fork_pid, 0);
101             }
102             }
103             }
104             }
105              
106             =head2 $server->connect(status_file => $status_filename)
107              
108             Connects to an existing Server whose status file is $status_filename.
109              
110             =cut
111              
112             sub connect
113             {
114 1     1 1 8 my $self = shift;
115              
116 1         57 my $status_file = $self->{'status_file'};
117              
118 1         9 my $text = io()->file($status_file)->getline();
119              
120 1 50       466 if ($text !~ /^Status:Success\tPort:(\d+)\tPID:(\d+)$/)
121             {
122 0         0 die "Invalid status file.";
123             }
124              
125 1         91 my $port = $1;
126 1         13 $self->{'server_pid'} = $2;
127             # TODO ?
128             # Add sanity checks.
129              
130 1         3 $self->{'port'} = $port;
131              
132 1         4 return { 'port' => $port, };
133             }
134              
135             =head2 $server->stop()
136              
137             Stops the service by killing the listening process.
138              
139             =cut
140              
141             sub stop
142             {
143 3     3 1 61249 my $self = shift;
144              
145 3         56 my $pid = $self->{'server_pid'};
146 3         4692 kill("TERM", $pid);
147              
148 3         1386 waitpid($pid, 0);
149             }
150              
151             sub _ok_transact
152             {
153 10     10   20 my $self = shift;
154 10         27 my $msg = shift;
155 10         26 my $port = $self->{'port'};
156 10         46 my $conn = io("localhost:$port");
157 10         1524 $conn->print("$msg\n");
158 10         8876 my $response = $conn->getline();
159 10 50       1292 if ($response eq "OK\n")
160             {
161 10         35 return 0;
162             }
163             else
164             {
165 0         0 die "Invalid response - $response.";
166             }
167             }
168              
169             =head2 $server->clear();
170              
171             Sends a clear transaction that clears the seeds of the seed server.
172              
173             =cut
174              
175             sub clear
176             {
177 2     2 1 42135 my $self = shift;
178 2         8 return $self->_ok_transact("CLEAR");
179             }
180              
181             =head2 $server->enqueue(\@seeds);
182              
183             Enqueues several seeds in the server to be served next.
184              
185             =cut
186              
187             sub enqueue
188             {
189 8     8 1 47324 my $self = shift;
190 8         20 my $seeds = shift;
191 8 50       20 if (grep { $_ !~ /^\d+$/ } @$seeds)
  14         86  
192             {
193 0         0 die "Invalid seed.";
194             }
195 8         72 return $self->_ok_transact("ENQUEUE " . join("", map { "$_," } @$seeds));
  14         58  
196             }
197              
198             1;
199              
200             __END__