NAME Sub::Spec::HTTP::Server - Serve subroutine calls via HTTP/HTTPS VERSION version 0.01 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/.pid. error_log_path => STR Location of error log. Default is /var/log/-error.log. It will be opened in append mode. access_log_path => STR Location of access log. Default is /var/log/-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 Sub::Spec Sub::Spec::HTTP::Client AUTHOR Steven Haryanto 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.