File Coverage

blib/lib/JRPC/Apache2.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1              
2             package JRPC::Apache2;
3 1     1   860 use JRPC;
  1         2  
  1         32  
4 1     1   4 use strict;
  1         2  
  1         25  
5 1     1   3 use warnings;
  1         2  
  1         17  
6 1     1   218 use Apache2::RequestRec ();
  0            
  0            
7             use Apache2::RequestIO ();
8             # qw(OK)
9             use Apache2::Const -compile => qw(:common); # 'OK', 'DECLINED'
10             use APR::Table ();
11             use JSON::XS;
12             use JRPC; # Import ... ?
13             *jdie = JRPC::createfault;
14             #use Storable (); # Would import store - do not want that.
15             # For Global Request (PerlOptions +GlobalRequest)
16             #use Apache2::RequestUtil;
17              
18             =head1 NAME
19              
20             JRPC::Apache2 - JSON-RPC Services in Apache2 / mod_perl runtime
21              
22             =head1 DESCRIPTION
23              
24             This package is a mod_perl JSON-RPC handler / dispatcher. It only contains the conventional mod_perl handler($r) callback method
25             (see mod_perl documentation for details: http://perl.apache.org/docs/2.0/user/config/config.html ).
26             Do not call the handler() method directly, but assign it to be used as a mod_perl handler (Servicpackage "Math" is used here for an example):
27              
28             # Load Service Package (for JRPC::Apache2 to use)
29             PerlModule Math;
30             # Assign directly to a URL Location / path by
31            
32             SetHandler modperl
33             PerlResponseHandler JRPC::Apache2
34            
35              
36             =cut
37             # Parse and handle JSON-RPC Request.
38             # reads POST body by $r->read($buffer, $len).
39             # if/else dispatching (of 3 meth) gives slight edge compared to
40             # non-cached brute force method resolution (~770 vs. ~740)
41             #if ($m eq 'add') {$resp = add($p);}
42             #elsif ($m eq 'store') {$resp = store($p);}
43             #elsif ($m eq 'multiply') {$resp = multiply($p);}
44              
45             sub handler {
46             my ($r) = @_;
47             if ($r->method() ne 'POST') {return(Apache2::Const::DECLINED);} # 1 illegal
48             my $hdrs_in = $r->headers_in();
49             my $hdrs_out = $r->headers_out();
50             my $buffer = '';my $len = 8000; # TODO: Grab _actual_ content-length
51             my $cnt = $r->read($buffer, $len);
52             #NA:my $hdlr = $r->handler(); # Returns String ("modperl")
53             $r->content_type('text/plain');
54             #####################################
55             my $jresp = {'id' => $$, 'jsonrpc' => '2.0', }; # Set up dummy
56            
57             eval {
58             if (!$cnt) {die("JSON-RPC Request body is Empty (-32700)");}
59             # Parse Request
60             my $j = eval { JRPC::parse($buffer); }; #
61             if ($@) {die("Error Parsing JSON-RPC Request: $@");}
62             if (defined($JRPC::prelogger) && (ref($JRPC::prelogger) eq 'CODE')) {$JRPC::prelogger->($j);}
63             my $p = $j->{'params'};
64             my $m = $j->{'method'};
65             $jresp->{'id'} = $j->{'id'};
66             #my $res = {}; # Result
67             my $f;my $mid = 0;
68             if ($m =~ /\./) {$f = JRPC::methresolve_dotnot($r, $m);$mid=1;}
69             else {my $f = JRPC::methresolve($r, $m);}
70             if (!$f) {die("method '$m' not resolved (-32601) mid=$mid");}
71             ##### reqinit
72             #if (my $f = $pkg->can('reqinit')) {$f->($p, $j);}
73             ##### Execute, store result
74             my $res = eval { $f->($p); }; # Dispatch (catching any exceptions)
75             if ($@) {die("Error in processing JRPC method '$m' (-32603): $@");}
76             # Definite Success - serialize response ?
77             $jresp->{'result'} = $res;
78             ###### Respond
79             #my $clen = $hdrs_in->get('content-length'); # Double verify read()
80             # 'clen' => $clen, 'postread' => $cnt,
81             # There could be blessed nodes in $res ('result' now) that do not serialize
82             # well. Be ready to encounter exceptions here.
83             DEBUG: $jresp->{'APAOK'} = Apache2::Const::OK;
84             my $out = eval { encode_json($jresp); }; # Serialize as a separate step to know length
85             if ($@) {die("Error Forming the response: $@");}
86             ##########################
87             $hdrs_out->{'content-length'} = length($out); # Raw assignment ?
88             $r->print($out);
89             };
90             if ($@) {
91             $r->print( JRPC::createfault($jresp, $@, 500) );
92             }
93             return Apache2::Const::OK;
94             }
95             #$r->err_headers_out->add('Set-Cookie' => $cookie);
96             #$r->print(encode_json($j));
97             1;
98