NAME
    Sub::Spec::HTTP::Server - PSGI application to serve remote (HTTP)
    subroutine call requests

VERSION
    version 0.08

SYNOPSIS
    Suppose you want to expose functions in "My::API::Adder" and
    "My::API::Adder::Array" as HTTP API functions, using URL
    http://<host>/api/v1/<module>/<func>:

     package My::API::Adder;
     our %SPEC;
     $SPEC{add} = {args => {a=>["float*"=>{arg_pos=>0}],
                            b=>["float*"=>{arg_pos=>1}]}};
     sub add { my %args=@_; [200, "OK", $args{a}+$args{b}] }
     1;

     package My::API::Adder::Array;
     $SPEC{add_array} = {

         summary => 'Concatenate two arrays together',
         args => {a1=>["array*" => {summary => 'First array'}],
                  a2=>["array*" => {summary => 'Second array'}]},
     };
     sub add { my %args=@_; [200, "OK", [@{$args{a1}}, @{$args{a2}}]] }
     1;

    First, write "app.psgi":

     #!perl
     use Plack::Builder;
     use Plack::Util::SubSpec qw(errpage)
     use Sub::Spec::HTTP::Server::Command qw(
         about call help listmod listsub usage);

     builder {
         # this is the basic composition
         enable "SubSpec::LogAccess";
         enable "SubSpec::ParseRequest"
             uri_pattern => qr!^/api/v1
                               (?:/(?<module>[^?]+)
                                 (?:/(?<sub>[^?/]+)?)
                               )?!x,
             after_parse => sub {
                 my $env = shift;
                 my $m = $env->{"ss.uri_pattern_matches"};
                 if ($m->{module}) {
                     my $mod = "My::API::$m->{module}";
                     $env->{"ss.request"}{uri} = "pm:$mod" .
                         ($m->{sub} ? "/$m->{sub}" : "");
                     $_ = "My::API::$_" unless /^My::API::/;
                 }
             };
         enable "SubSpec::HandleCommand";
     };

    Run the app with PSGI server, e.g. Gepok:

     % plackup -s Gepok --https_ports 5001 \
           --ssl_key_file /path/to/ssl.key --ssl_cert_file /path/to/ssl.crt

    Call your functions over HTTP(S)?:

     % curl http://localhost:5000/api/v1/Adder/add/2/3
     [200,"OK",6]

     % curl -H 'X-SS-Req-Log-Level: trace' \
       'https://localhost:5001/api/v1/Adder/Array/add?a1:j=[1]&a2:j=[2,3]'
     [200,"OK",[1,2,3]]

    Request help/usage information:

     % curl -H 'X-SS-Req-Command: help' \
       'http://localhost:5000/api/v1/Adder/Array/add'
     My::API::Adder::Array::add - Concatenate two arrays together

     Arguments:
       a1   (array, required) First array
       a2   (array, required) Second array

    List available function in a module (request key 'command' given in
    request variable):

     % curl 'http://localhost:5000/api/v1/Adder/Array?-ss-req-command=list_subs'
     ['add_array']

    List available modules:

     % curl -H 'X-SS-Req-Command: list_mods' \
       'http://localhost:5000/api/v1/'
     ['Adder','Adder::Array']

DESCRIPTION
    Sub::Spec::HTTP::*Server* is a PSGI *application* to serve remote (HTTP)
    subroutine call requests. It is suitable for serving remote API. (Sorry
    for the slight confusion between "server" and "application"; this module
    was not originally PSGI-based.)

    As the case with any PSGI application, you can use any *PSGI server* to
    run it with. But you might want to consider Gepok, which has built-in
    HTTPS support.

    This PSGI application is actually a set of modular middlewares
    (Plack::Middleware::SubSpec::*) which you can compose in your app.psgi,
    configuring each one and including only the ones you need. The Synopsis
    shows one such basic composition. There are more middlewares to do
    custom stuffs. See each middleware's documentation for details.

    This module uses Log::Any for logging.

    This module uses Moo for object system.

FAQ
  I don't want to expose my subroutines and module structure!
    Well, isn't exposing functions the whole point of API?

    If you have modules that you do not want to expose as API, simply
    exclude it (e.g. using "allowable_modules" configuration in
    SubSpec::ParseRequest middleware. Or, create a set of wrapper modules to
    expose only the functionalities that you want to expose.

  I want to expose just a single module (e.g. Foo) and provide a simpler API URL (e.g. without having to specify module name).
    You can do something like this:

     enable "SubSpec::ParseRequest"
         uri_pattern => qr!^/api/v1/(?<sub>[^?/]+)?!,
         after_parse => sub {
             my $env = shift;
             $env->{"ss.request"}{uri} = "pm:Foo/".
                 $env->{"ss.uri_pattern_matches"}{sub};
         };

I want to let user specify output format from URI (e.g. /api/v1/json/... or /api/v1/yaml/...)
    You can do something like:

     enable "SubSpec::ParseRequest"
         uri_pattern => qr!^/api/v1/(?<output_format>json|yaml)/
                           (?<module>[^?/]+)?
                           (?:/(?<sub>[^?/]+)?)!x;

    or:

     enable "SubSpec::ParseRequest"
         uri_pattern => qr!^/api/v1/(?<fmt>j|y)/
                           (?<module>[^?/]+)?
                           (?:/(?<sub>[^?/]+)?)!x,
         after_parse => sub {
             my $env = shift;
             my $fmt = $env->{"ss.uri_pattern_matches"}{fmt};
             $env->{"ss.request"}{output_format} = $fmt =~ /j/ ? 'json' : 'yaml';
         };

I want to support another output format (e.g. XML, MessagePack, etc).
    Add a format_<fmtname> method to
    Plack::Middleware::SubSpec::HandleCommand. The method accepts sub
    response and is expected to return a tuplet ($output, $content_type).

    Note that you do not have to modify the
    Plack/Middleware/SubSpec/HandleCommand.pm file itself. You can inject
    the method from another file.

    Also make sure that the output format is allowed (see configuration
    "allowable_output_formats" in the command handler middleware).

I need custom URI syntax
    You can leave "uri_pattern" empty and perform your custom URI parsing in
    "after_parse". For example:

     enable "SubSpec::ParseRequest"
         after_parse => sub {
             my $env = shift;
             # parse $env->{REQUEST_URI} on your own and put the result in
             # $env->{"ss.request"}{uri}
         };

    Or alternatively you can write your own request parser to replace
    ParseRequest.

  I want to automatically reload modules that changed on disk.
    Use one of the module-reloading module on CPAN, e.g.: Module::Reload or
    Module::Reload::Conditional.

  I want to authenticate clients.
    Enable Plack::Middleware::Auth::Basic (or other authen middleware you
    prefer) before SubSpec::ParseRequest.

  I want to authorize clients.
    Take a look at Plack::Middleware::SubSpec::Authz::ACL which allows
    authorization based on various conditions. Normally this is put after
    authentication and before command handling.

  I want to support new commands.
    Write Sub::Spec::HTTP::Server::Command::<cmdname>, and include the
    command in SubSpec::ParseRequest's "allowable_commands" configuration.

    But first consider if that is really what you want. If you want to serve
    static files or do stuffs unrelated to calling subroutines or subroutine
    spec, you ought to put it somewhere else.

SEE ALSO
    Sub::Spec::HTTP

    Gepok

AUTHOR
    Steven Haryanto <stevenharyanto@gmail.com>

COPYRIGHT AND LICENSE
    This software is copyright (c) 2011 by Steven Haryanto.

    This is free software; you can redistribute it and/or modify it under
    the same terms as the Perl 5 programming language system itself.

