File Coverage

blib/lib/KSx/Remote/SearchServer.pm
Criterion Covered Total %
statement 25 93 26.8
branch 0 18 0.0
condition 0 3 0.0
subroutine 9 17 52.9
pod 2 7 28.5
total 36 138 26.0


line stmt bran cond sub pod time code
1 2     2   109020 use strict;
  2         6  
  2         108  
2 2     2   16 use warnings;
  2         6  
  2         195  
3              
4             package KSx::Remote::SearchServer;
5 2     2   100 BEGIN { our @ISA = qw( KinoSearch::Object::Obj ) }
6 2     2   19 use Carp;
  2         6  
  2         296  
7 2     2   16 use Storable qw( nfreeze thaw );
  2         109  
  2         251  
8 2     2   17 use bytes;
  2         5  
  2         21  
9 2     2   62 no bytes;
  2         7  
  2         113  
10              
11             # Inside-out member vars.
12             our %searcher;
13             our %port;
14             our %password;
15             our %sock;
16              
17 2     2   144 use IO::Socket::INET;
  2         3  
  2         25  
18 2     2   56447 use IO::Select;
  2         4330  
  2         2541  
19              
20             sub new {
21 0     0 1   my ( $either, %args ) = @_;
22 0           my $searcher = delete $args{searcher};
23 0           my $password = delete $args{password};
24 0           my $port = delete $args{port};
25 0           my $self = $either->SUPER::new(%args);
26 0           $searcher{$$self} = $searcher;
27 0 0         confess("Missing required param 'password'") unless defined $password;
28 0           $password{$$self} = $password;
29              
30             # Establish a listening socket.
31 0           $port{$$self} = $port;
32 0 0         confess("Invalid port: $port") unless $port =~ /^\d+$/;
33 0           my $sock = IO::Socket::INET->new(
34             LocalPort => $port,
35             Proto => 'tcp',
36             Listen => SOMAXCONN,
37             Reuse => 1,
38             );
39 0 0         confess("No socket: $!") unless $sock;
40 0           $sock->autoflush(1);
41 0           $sock{$$self} = $sock;
42              
43 0           return $self;
44             }
45              
46             sub DESTROY {
47 0     0     my $self = shift;
48 0           delete $searcher{$$self};
49 0           delete $port{$$self};
50 0           delete $password{$$self};
51 0           delete $sock{$$self};
52 0           $self->SUPER::DESTROY;
53             }
54              
55             my %dispatch = (
56             doc_max => \&do_doc_max,
57             doc_freq => \&do_doc_freq,
58             top_docs => \&do_top_docs,
59             fetch_doc => \&do_fetch_doc,
60             fetch_doc_vec => \&do_fetch_doc_vec,
61             terminate => undef,
62             );
63              
64             sub serve {
65 0     0 1   my $self = shift;
66 0           my $main_sock = $sock{$$self};
67 0           my $read_set = IO::Select->new($main_sock);
68              
69 0           while ( my @ready = $read_set->can_read ) {
70 0           for my $readhandle (@ready) {
71             # If this is the main handle, we have a new client, so accept.
72 0 0         if ( $readhandle == $main_sock ) {
73 0           my $client_sock = $main_sock->accept;
74              
75             # Verify password.
76 0           my $pass = <$client_sock>;
77 0 0         chomp($pass) if defined $pass;
78 0 0 0       if ( defined $pass && $pass eq $password{$$self} ) {
79 0           $read_set->add($client_sock);
80 0           print $client_sock "accepted\n";
81             }
82             else {
83 0           print $client_sock "password incorrect\n";
84             }
85             }
86             # Otherwise it's a client sock, so process the request.
87             else {
88 0           my $client_sock = $readhandle;
89 0           my ( $check_val, $buf, $len, $method, $args );
90 0           chomp( $method = <$client_sock> );
91              
92             # If "done", the client's closing.
93 0 0         if ( $method eq 'done' ) {
    0          
    0          
94 0           $read_set->remove($client_sock);
95 0           $client_sock->close;
96 0           next;
97             }
98             # Remote signal to close the server.
99             elsif ( $method eq 'terminate' ) {
100 0           $read_set->remove($client_sock);
101 0           $client_sock->close;
102 0           $main_sock->close;
103 0           return;
104             }
105             # Sanity check the method name.
106             elsif ( !$dispatch{$method} ) {
107 0           print $client_sock "ERROR: Bad method name: $method\n";
108 0           next;
109             }
110              
111             # Process the method call.
112 0           read( $client_sock, $buf, 4 );
113 0           $len = unpack( 'N', $buf );
114 0           read( $client_sock, $buf, $len );
115 0           my $response = $dispatch{$method}->( $self, thaw($buf) );
116 0           my $frozen = nfreeze($response);
117 0           my $packed_len = pack( 'N', bytes::length($frozen) );
118 0           print $client_sock $packed_len . $frozen;
119             }
120             }
121             }
122             }
123              
124             sub do_doc_freq {
125 0     0 0   my ( $self, $args ) = @_;
126 0           return { retval => $searcher{$$self}->doc_freq(%$args) };
127             }
128              
129             sub do_top_docs {
130 0     0 0   my ( $self, $args ) = @_;
131 0           my $top_docs = $searcher{$$self}->top_docs(%$args);
132 0           return { retval => $top_docs };
133             }
134              
135             sub do_doc_max {
136 0     0 0   my ( $self, $args ) = @_;
137 0           my $doc_max = $searcher{$$self}->doc_max;
138 0           return { retval => $doc_max };
139             }
140              
141             sub do_fetch_doc {
142 0     0 0   my ( $self, $args ) = @_;
143 0           my $doc = $searcher{$$self}->fetch_doc( $args->{doc_id} );
144 0           return { retval => $doc };
145             }
146              
147             sub do_fetch_doc_vec {
148 0     0 0   my ( $self, $args ) = @_;
149 0           my $doc_vec = $searcher{$$self}->fetch_doc_vec( $args->{doc_id} );
150 0           return { retval => $doc_vec };
151             }
152              
153             1;
154              
155             __END__