NAME
Sub::Spec::HTTP::Server - PSGI application to serve remote (HTTP)
subroutine call requests
VERSION
version 0.10
SYNOPSIS
Suppose you want to expose functions in "My::API::Adder" and
"My::API::Adder::Array" as HTTP API functions, using URL
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;
# or just write any normal Perl module without any specs
Then:
$ servepm My::API::Adder My::API::Adder::Array
Then call your functions over HTTP(S)?:
[200,"OK",6]
% curl -H 'X-SS-Req-Log-Level: trace' \
[200,"OK",[1,2,3]]
Request help/usage information:
% curl -H 'X-SS-Req-Command: help' \
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):
['add']
List available modules:
% curl -H 'X-SS-Req-Command: list_mods' \
['My::API::Adder','My::API::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
How can I customize URL?
For example, instead of:
you want:
or perhaps (if you only have one module to expose):
You can do this by customizing uri_pattern when enabling
SubSpec::ParseRequest middleware (see servepm source code). You just
need to make sure that you produce $env->{"ss.request"}{uri} (and other
necessary SS request keys).
I want to let user specify output format from URI (e.g. /api/json/... or /api/v1/yaml/...)
Again, this can be achieved by customizing the SubSpec::ParseRequest
middleware. 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 need even more 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 enable HTTPS.
Supply --https_ports, --ssl_key_file and --ssl_cert_file options in
servepm.
I don't want to expose my subroutines and module structure directly!
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 "allowed_modules" configuration in
SubSpec::ParseRequest middleware. Or, create a set of wrapper modules to
expose only the functionalities that you want to expose.
Or, if you have a custom mapping of "modules" and "subs" that are
different than Perl's, I recommend you create a new Sub::Spec::URI::xxx
implementation.
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
"allowed_output_formats" in the command handler middleware).
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 "allowed_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, e.g.:
my $app = builder {
mount "/api" => builder {
enable "SubSpec::ParseRequest", ...;
...
},
mount "/static" => ...
};
TIPS AND TRICKS
Proxying API server
Not only can you serve local modules ("pm://" URIs), you can serve
proxy for another.
Performance tuning
To be written.
TODO
* Improve performance.
SEE ALSO
Sub::Spec::HTTP
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.