NAME
Sub::Spec::HTTP::Server - Serve subroutine calls via HTTP/HTTPS
VERSION
version 0.04
SYNOPSIS
In your program:
use Sub::Spec::HTTP::Server;
use My::Module1;
use My::Module2;
my $server = Sub::Spec::HTTP::Server->new(
sock_path => '/var/run/apid.sock', # activate listening to Unix socket
#http_port => 949, # default is 80
#https_port => 1234, # activate https
#ssl_key_file => '/path/to/key.pem', # need this for https
#ssl_cert_file => '/path/to/crt.pem', # need this for https
#max_requests_per_child => 100, # default is 1000
#start_servers => 0, # default is 3, 0 means don't prefork
#daemonize => 0, # do not go to background
);
$server->run;
After running the program, accessing:
http://localhost:949/My/Module2/func?arg1=1&arg2=2
You will be getting a JSON response:
[200,"OK",{"the":"result data"}]
DESCRIPTION
This class is a preforking HTTP (TCP and Unix socket)/HTTPS (TCP) daemon for serving function call requests (usually for API calls). All functions should have Sub::Spec specs.
This module uses Log::Any for logging.
This module uses Moo for object system.
ATTRIBUTES
name => STR
Name of server, for display in process table ('ps ax'). Default is basename of $0.
daemonize => BOOL
Whether to daemonize (go into background). Default is true.
sock_path => STR
Location of Unix socket. Default is none, which means not listening to Unix socket.
pid_path => STR
Location of PID file. Default is /var/run/<name>.pid.
error_log_path => STR
Location of error log. Default is /var/log/<name>-error.log. It will be opened in append mode.
access_log_path => STR
Location of access log. Default is /var/log/<name>-access.log. It will be opened in append mode.
access_log_max_args_len => INT
Maximum number of characters to log args (in JSON format). Default is 1024. Over this length, only the first 1024 characters are logged.
access_log_max_resp_len => INT
Maximum number of characters to log response (in JSON format). Default is 1024. Over this length, only the first 1024 characters are logged.
http_port => INT
Port to listen to HTTP requests. Default is 80. Undef means not listening for HTTP requests. Note that in Unix environment, binding to ports 1024 and below requires superuser privileges.
http_bind_host => STR
If you only want to bind to a specific interface for HTTP, specify it here, for example 'localhost' or '1.2.3.4'. Setting to undef or '' means to bind to all interface ('*'). Default is 'localhost'.
https_port => INT
Port to listen to HTTPS requests. Default is undef. Undef means not listening for HTTPS requests. Note that in Unix environment, binding to ports 1024 and below requires superuser privileges.
https_bind_host => STR
If you only want to bind to a specific interface for HTTPS, specify it here, for example 'localhost' or '1.2.3.4'. Setting to undef or '' means to bind to all interface ('*'). Default is 'localhost'.
ssl_key_file => STR
Path to SSL key file, to be passed to HTTP::Daemon::SSL. If you enable HTTPS, you need to supply this.
ssl_cert_file => STR
Path to SSL cert file, to be passed to HTTP::Daemon::SSL. If you enable HTTPS, you need to supply this.
start_servers
Number of children to fork at the start of run. Default is 3. If you set this to 0, the server becomes a nonforking one.
Tip: You can set start_servers to 0 and 'daemonize' to false for debugging.
max_requests_per_child
Number of requests each child will serve until it exists. Default is 1000.
module_prefix
Prefix for module. Default is none. Affects get_sub_name().
req
The request object, will be set at the start of each request (before handle_request() is run). Currently this is a barebones hash, but will be a proper object.
resp
The response, should be in the form of [HTTP_STATUS_CODE, MESSAGE, DATA].
METHODS
new()
Create a new server object.
$server->stop()
Stop running server.
$server->run()
Run server.
$server->restart()
Restart server.
$server->is_running() => BOOL
Check whether server is running
$server->before_prefork()
Override this hook to do stuffs before preforking. For example, you can preload all modules. This is more efficient than each children loading modules separately.
The default implementation does nothing.
$server->handle_request()
The main routine to handle request, will be called by run(). Below is the order of processing. At any time during the request, you can set $server->resp() and die to exit early and directly go to access_log().
before_parse_http_request()
parse_http_request()
get_sub_name()
get_sub_args()
auth()
get_sub_spec()
authz()
call_sub()
send_http_response()
after_send_http_response()
access_log()
$server->before_parse_http_request()
Override this to add action before HTTP request is parsed.
$server->parse_http_request()
Parse HTTP request (result in $server->req->{http_req}). Will be called by handle_request().
$server->get_sub_name()
Parse sub's fully qualified name from HTTP request object. Result should be put in $server->req->{sub_module} and $server->req->{sub_name}.
You can override this method to provide other URL syntax. The default implementation parses URI using this syntax:
/MODULE/SUBMODULE/FUNCTION
which will result in sub_module being 'MODULE::SUBMODULE' and sub_name 'FUNCTION'. In addition, some options are allowed:
/MODULE/SUBMODULE/FUNCTION;OPTS
OPTS are a string of one or more option letters. 'j' means to ask server to return response in JSON format. 'r' (the default) means return in pretty formatted text (e.g. tables). 'R' means return in non-pretty/plain text. 'y' means return in YAML. 'p' means return in PHP serialization format.
For example:
/My/Module/my_func;j
If 'module_prefix' attribute is set, it will be prepended to $server->req->{sub_module}. For example, if 'module_prefix' is 'Our::Project', then with the above URI, the final sub_module will become 'Our::Project::My::Module'.
$server->get_sub_args()
Parse sub's args from HTTP request object. Result should be put in $server->req->{sub_args}. It should be a hashref.
The default implementation can get args from request body in PHP serialization format (if Content-Type
HTTP request header is set to application/vnd.php.serialized
) or JSON (application/json
) or YAML (text/yaml
).
Alternatively, it can get args from URL query parameters. Each query parameter corresponds to an argument name. If you add ":j" suffix to query parameter name, it means query parameter value is in JSON format. If ":y" suffix, YAML format. If ":p", PHP serialization format.
You can override this method to provide other ways to parse arguments from HTTP request.
$server->get_sub_args()
Get sub's spec. Result should be put in $server->req->{sub_spec}.
The default implementation will simply looks for the spec in %SPEC in the package specified in $server->req->{sub_module}.
$server->auth()
Authenticate client. Override this if needed. The default implementation does nothing. Authenticated client should be put in $server->req->{auth_user}.
$server->authz()
Authorize client. Override this if needed. The default implementation does nothing.
$server->call_sub()
Call function specified in $server->req->{module} and $server->req->{sub}) using arguments specified in $server->req->{args}. Set $server->resp() with the return value of function.
$server->send_http_response()
Send HTTP response to client. Called by handle_request().
$server->after_send_http_response()
Hook to do stuffs before logging. The default implementation does nothing. You can override this e.g. to mask some arguments from being logged or limit its size.
$server->access_log()
Log request. The default implementation logs like this (all in one line):
[Fri Feb 18 22:05:38 2011] "GET /v1/MyModule/my_func;j?arg1=1&arg2=2"
[127.0.0.1:949] [-] [mod MyModule] [sub my_func]
[args 14 {"name":"val"}] [resp 12 [200,"OK",1]] [subt 2.123ms] [reqt 5.947ms]
where subt is time spent in the subroutine, and reqt is time spent for the whole request (from connect until response is sent, which includes reqt).
FAQ
BUGS/TODOS
I would like to use Plack/PSGI, but the current implementation of this module (using HTTP::Daemon + HTTP::Daemon::SSL) conveniently supports HTTPS out of the box.
SEE ALSO
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.