NAME

JRPC::CGI - JSON-RPC 2.0 Processing for CGI and HTTP::Server::Simple::CGI

DESCRIPTION

This package provides JSON-RPC 2.0 services processor for 2 runtimes based on:

  • CGI (CGI.pm) Plain old CGI scripting (or mod_perl ModPerl::Registry mode)

  • HTTP::Server::Simple::CGI - a fast and lightweight runtime with a Perl embedded httpd (web server) module.

HTTP::Server::Simple::CGI is especially interesting for doing distributed computation over the http.

METHODS

Because of the rudimentary nature of CGI (in both good and bad), the JRPC::CGI::handle_cgi($cgi) is to be called explicitly in code (as CGI is not hosted by sophisticated server).

The service method JRPC::CGI::handle_simple_server_cgi($server, $cgi); for HTTP::Server::Simple::CGI can be aliased to local package's handle_request method, which is the request handling method for HTTP::Server::Simple framework (similar to mod_perl's and Nginx's handler($r) method).

JRPC::CGI::handle_cgi($cgi)

Traditional CGI Handler for JRPC. Example CGI wrapper:

#!/usr/bin/perl
use CGI;
use CGI::Carp qw/fatalsToBrowser warningsToBrowser/;
use JRPC::CGI;
use SvcTest; # Load Service package
my $cgi = CGI->new();
# Process request. Reports all errors to Client as a JSON-RPC error (fault) response.
JRPC::CGI::handle_cgi($cgi);
exit(0);

# This "Service Package" could (and should) be in a separate file (SvcTest.pm).
# It will be called back by JRPC.
package SvcTest;
use Scalar::Util ('reftype');
# Simpliest possible service:
# - reflect/echo 'params' (of request) to 'result' (of response)
# - Framework will take care of request parsing and response serialization
# - On validation errors, Framework will turn a Perl exception to a JSON-RPC fault.
# Call this by: ..., "method": "Test.echo", ...
sub echo {
   my ($p, $jrpc) = @_;
   # Validate, require $p to be HASH (ref).
   # Framework will convert exceptions to JSON-RPC Fault
   if (reftype($p) ne 'HASH') {die("param was not found to be a JSON Object");}
   return($p);
}
1;

JRPC::CGI::handle_simple_server_cgi($server, $cgi);

Wrapper for intercepting a request to HTTP::Server::Simple::CGI.
Alias this as a handle_request() in your package implementing
HTTP::Server::Simple::CGI. Example:

  #!/usr/bin/perl
  {
  package MyJRPC;
  use HTTP::Server::Simple::CGI;
  use base 'HTTP::Server::Simple::CGI';
  # Reuse handle_simple_server_cgi, assign as local alias.
  *handle_request = \&JRPC::CGI::handle_simple_server_cgi;
  }
  my $port = $ENV{'HTTP_SIMPLE_PORT'} || 8080;
  my $pid = MyWebServer->new($port);
  #my $pid = MyWebServer->new($port)->background();
  
  print "Use 'kill $pid' to stop server (on port $port).\n";

RUNNING SERVER IN THREAD

To be able to run server in thread and to be able to terminate the thread, use the following idiom:

# Server thread as anonymous sub. Pass port to run at.
my $runmyserver = sub {
  my ($port) = @_;
  # Use signaling to kill thread
  $SIG{'KILL'} = sub { threads->exit(); };
  # Run in the same process, NOT spawning a sub process.
  MyServer->new($port)->run();
};

my $thr = threads->create($runmyserver, $port);
# ...
# Much later ... terminate server as no more needed.
$thr->kill('KILL')->detach();
# This main thread should continue / survive beyond this point ...

HINTS

JSON-RPC is not a domain for obsessed print(); debugging folks. Printing to STDOUT messes up the JSON-RPC response output. The returned data structure gets automatically converted to a successful JSON-RPC Response (data goes into 'result' member). Any fatal errors thrown as Perl exceptions get automatically converted to a valid JSON-RPC exception / fault (member 'error', and optionally to logs). Any diagnostic messaging goes to response or logs (or both), NOT STDOUT.

TODO

  • Private package (file) for ServerSimple (with direct default handler handle_request())?

  • In private package use HTTP::Server::Simple::CGI (and inherit from it)