File Coverage

blib/lib/XPC/Daemon.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             #
3             # Daemon.pm - XPC Daemon
4             #
5             # TODO: Make this a subclass of HTTP::Daemon and override
6             # &product_tokens() to return "XPC::Daemon" + version number
7             # so that the Server HTTP header won't say libwww*.
8             #
9             # Copyright (C) 2001 Gregor N. Purdy.
10             # All rights reserved.
11             #
12             # This is free software; you can redistribute it and/or modify it under
13             # the same terms as Perl itself.
14             #
15              
16              
17 1     1   2531 use strict;
  1         3  
  1         57  
18              
19             package XPC::Daemon;
20              
21 1     1   832 use HTTP::Daemon;
  1         100166  
  1         13  
22 1     1   893 use HTTP::Status;
  1         3  
  1         394  
23 1     1   1262 use Data::Dumper;
  1         15480  
  1         95  
24              
25 1     1   755 use XPC;
  0            
  0            
26              
27              
28             #
29             # new()
30             #
31              
32             sub new
33             {
34             my $class = shift;
35              
36             my $self = bless { PROCEDURES => { } }, $class;
37              
38             $self->{DAEMON} = new HTTP::Daemon;
39             $self->{DEBUG} = 0;
40              
41             return $self;
42             }
43              
44              
45             #
46             # debug()
47             #
48              
49             sub debug
50             {
51             my $self = shift;
52              
53             $self->{DEBUG} = ($_[0] ? 1 : 0) if @_;
54              
55             return $self->{DEBUG};
56             }
57              
58              
59             #
60             # url()
61             #
62              
63             sub url
64             {
65             my $self = shift;
66              
67             return $self->daemon->url . "XPC";
68             }
69              
70              
71             #
72             # run()
73             #
74              
75             sub run
76             {
77             my $self = shift;
78              
79             die "$0: No procedures defined!" unless keys %{$self->{PROCEDURES}};
80              
81             while (my $c = $self->daemon->accept) {
82             while (my $r = $c->get_request) {
83             if ($r->method eq 'POST' and $r->url->path eq "/XPC") {
84             my @response = $self->process_request($r);
85             my $xpc = XPC->new;
86             map { $xpc->add_response($_); } @response;
87             my $http_res = new HTTP::Response;
88             $http_res->content($xpc->as_string);
89             $http_res->code(200);
90             $c->send_response($http_res);
91             } else {
92             $c->send_error(RC_FORBIDDEN)
93             }
94             }
95             $c->close;
96             undef($c);
97             }
98             }
99              
100              
101              
102             #
103             # add_procedure()
104             #
105              
106             sub add_procedure
107             {
108             my $self = shift;
109             my ($procedure, $callback) = @_;
110              
111             $self->{PROCEDURES}{$procedure} = $callback;
112              
113             print "&XPC::Daemon::add_procedure(): Added procedure '$procedure'.\n" if $self->debug();
114             }
115              
116              
117             #
118             # callback()
119             #
120              
121             sub callback
122             {
123             my ($self, $procedure) = @_;
124              
125             return $self->{PROCEDURES}{$procedure};
126             }
127              
128              
129             #
130             # daemon()
131             #
132              
133             sub daemon
134             {
135             my $self = shift;
136             return $self->{DAEMON};
137             }
138              
139              
140             #
141             # process_request()
142             #
143              
144             sub process_request
145             {
146             my $self = shift;
147             my $r = shift;
148              
149             my @response;
150             my $id_required;
151              
152             if ($self->debug()) {
153             print "&XPC::Daemon::process_request(): Recieved request:\n";
154             print $r->content;
155             print "\n";
156              
157             print "&XPC::Daemon::process_request(): Parsing request...\n";
158             }
159              
160             my $xml = $r->content;
161             my $xpc;
162              
163             eval { $xpc = XPC->new($xml); };
164              
165             if ($@ or !defined $xpc) {
166             print "&XPC::Daemon::process_request(): Unable to parse XPC request:\n";
167             print $xml;
168             print "\n";
169             push @response, make_fault(7, "Unable to parse request!");
170             return @response;
171             }
172              
173             print "&XPC::Daemon::process_request(): Request parses as class ", ref $xpc, ".\n" if $self->debug;
174              
175             $xpc = $xpc->[0];
176              
177             if ($self->debug()) {
178             print "&XPC::Daemon::process_request(): Class is ", ref $xpc, ".\n";
179             print Dumper($xpc);
180             print "\n";
181             }
182              
183              
184             my @requests = grep { ref $_ ne 'XPC::Characters' } @{$xpc->{Kids}};
185              
186             print "&XPC::Daemon::process_request(): Processing queries and calls...\n" if $self->debug();
187              
188             foreach my $req (@requests) {
189             if (@requests > 1 and not $req->id) {
190             @response = ( make_fault(3, "Every request of a multi-request must set 'id'!") );
191             last;
192              
193             # TODO: We really should scan them first so we don't cause any side-effects.
194             }
195              
196             if (ref $req eq 'XPC::call') {
197             push @response, $self->process_call($req);
198             } elsif (ref $req eq 'XPC::query') {
199             push @response, make_fault(1, "<query>s are not supported!");
200             } elsif (ref $req eq 'XPC::result') {
201             push @response, make_fault(5, "<result>s are not requests!");
202             } elsif (ref $req eq 'XPC::fault') {
203             push @response, make_fault(6, "<fault>s are not requests!");
204             } else {
205             push @response, make_fault(4, sprintf("Unknown request type '%s'!", ref $req));
206             }
207             }
208              
209             return @response;
210             }
211              
212              
213             #
214             # process_call()
215             #
216              
217             sub process_call
218             {
219             my $self = shift;
220             my $call = shift;
221              
222             my $procedure = $call->procedure;
223              
224             print "&XPC::Daemon::process_call(): Processing call to '$procedure'...\n" if $self->debug();
225              
226             my $callback = $self->callback($procedure);
227              
228             if ($callback) {
229             return make_result(scalar(&$callback()));
230             } else {
231             return make_fault(2, sprintf("<call> to unknown procedure '%s'!", $procedure));
232             }
233             }
234              
235              
236             ##############################################################################
237             ##
238             ## UTILITIES:
239             ##
240             ##############################################################################
241              
242              
243             #
244             # make_fault()
245             #
246              
247             sub make_fault
248             {
249             return new XPC::fault(@_);
250             }
251              
252              
253             #
254             # make_result()
255             #
256              
257             sub make_result
258             {
259             return XPC::result->new_scalar(@_);
260             }
261              
262              
263             1;
264              
265              
266             =head1 NAME
267              
268             XPC::Daemon - XML Procedure Call daemon class
269              
270              
271             =head1 SYNOPSIS
272              
273             use XPC::Daemon;
274             my $daemon = new XPC::Daemon;
275             $daemon->add_procedure('localtime', sub { localtime });
276             my $pid = fork;
277             die "$0: Unable to fork!\n" unless defined $pid;
278            
279             if ($pid) {
280             print STDOUT $daemon->url, "\n";
281             print STDERR "$0: Forked child $pid.\n";
282             exit 0;
283             } else {
284             $daemon->run;
285             exit 0;
286             }
287              
288              
289             =head1 DESCRIPTION
290              
291             This class is a generic XPC-over-HTTP server daemon. Use the C
292             method to give it specific functionality.
293              
294              
295             =head1 AUTHOR
296              
297             Gregor N. Purdy Egregor@focusresearch.comE
298              
299              
300             =head1 COPYRIGHT
301              
302             Copyright (C) 2001 Gregor N. Purdy.
303             All rights reserved.
304              
305             This is free software; you can redistribute it and/or modify it under
306             the same terms as Perl itself.
307