NAME
WWW::Suffit::Server - The Suffit API web-server class
SYNOPSIS
use Mojo::File qw/ path /;
my $root = path()->child('test')->to_string;
my $app = MyApp->new(
project_name => 'MyApp',
project_version => '0.01',
moniker => 'myapp',
debugmode => 1,
loglevel => 'debug',
max_history_size => 25,
# System
uid => 1000,
gid => 1000,
# Dirs and files
homedir => path($root)->child('share')->make_path->to_string,
datadir => path($root)->child('var')->make_path->to_string,
tempdir => path($root)->child('tmp')->make_path->to_string,
documentroot => path($root)->child('www')->make_path->to_string,
logfile => path($root)->child('log')->make_path->child('myapp.log')->to_string,
pidfile => path($root)->child('run')->make_path->child('myapp.pid')->to_string,
# Server
server_addr => '*',
server_port => 8080,
server_url => 'http://127.0.0.1:8080',
trustedproxies => ['127.0.0.1'],
accepts => 10000,
clients => 1000,
requests => 100,
workers => 4,
spare => 2,
reload_sig => 'USR2',
no_daemonize => 1,
# Security
mysecret => 'Eph9Ce$quo.p2@oW3',
rsa_keysize => 2048,
private_key => undef, # Auto
public_key => undef, # Auto
# Initialization options
all_features => 'no',
config_opts => {
file => path($root)->child('etc')->make_path->child('myapp.conf')->to_string,
defaults => {foo => 'bar'},
},
);
# Run preforked application
$app->preforked_run( 'start' );
1;
package MyApp;
use Mojo::Base 'WWW::Suffit::Server';
sub init { shift->routes->any('/' => {text => 'Hello World!'}) }
1;
DESCRIPTION
This module provides API web-server functionality
OPTIONS
sub startup {
my $self = shift->SUPER::startup( OPTION_NAME => VALUE, ... );
# ...
}
Options passed as arguments to the startup function allow you to customize the initialization of plugins at the level of your descendant class, and options are considered to have higher priority than attributes of the same name.
List of allowed options (pairs of name-value):
admin_routes_opts
admin_routes_opts => {
prefix_path => "/admin",
prefix_name => "admin",
}
- prefix_name
-
prefix_name => "admin"
This option defines prefix of admin api route name
Default: 'admin'
- prefix_path
-
prefix_path => "/admin"
This option defines prefix of admin api route
Default: '/admin'
all_features
all_features => 'on'
This option enables all of the init_* options, which are described bellow
Default: off
api_routes_opts
api_routes_opts => {
prefix_path => "/api",
prefix_name => "api",
}
- prefix_name
-
prefix_name => "api"
This option defines prefix of api route name
Default: 'api'
- prefix_path
-
prefix_path => "/api"
This option defines prefix of api route
Default: '/api'
authdb_opts
authdb_opts => {
uri => "sqlite://<PATH_TO_DB_FILE>?sqlite_unicode=1",
cachedconnection => 'on',
cacheexpiration => 300,
cachemaxkeys => 1024*1024,
sourcefile => '/tmp/authdb.json',
}
- uri, url
-
uri => "sqlite:///tmp/auth.db?sqlite_unicode=1",
Default: See config
AuthDBURL
orAuthDBURI
directive - cachedconnection
-
cachedconnection => 'on',
Default: See config
AuthDBCachedConnection
directive. Default: on - cacheexpire, cacheexpiration
-
cacheexpiration => 300,
Default: See config
AuthDBCacheExpire
orAuthDBCacheExpiration
directive. Default: 300 - cachemaxkeys
-
cachemaxkeys => 1024*1024,
Default: See config
AuthDBCacheMaxKeys
directive. Default: 1024*1024 - sourcefile
-
sourcefile => '/tmp/authdb.json',
Default: See config
AuthDBSourceFile
directive
config_opts
config_opts => { ... }
This option sets Mojolicious::Plugin::ConfigGeneral plugin options
Default:
`noload => 1` if $self->configobj exists
`defaults => $self->config` if $self->config is not void
init_admin_routes
init_admin_routes => 'on'
This option enables Admin Suffit API routes
Default: off
init_authdb
init_authdb => 'on'
This option enables AuthDB initialize
Default: off
init_api_routes
init_api_routes => 'on'
This option enables Suffit API routes
Default: off
init_rsa_keys
init_rsa_keys => 'on'
This option enables RSA keys initialize
Default: off
init_user_routes
init_user_routes => 'on'
This option enables User Suffit API routes
Default: off
syslog_opts
syslog_opts => { ... }
This option sets WWW::Suffit::Plugin::Syslog plugin options
Default:
`enable => 1` if the `Log` config directive is "syslog"
user_routes_opts
user_routes_opts => {
prefix_path => "/user",
prefix_name => "user",
}
- prefix_name
-
prefix_name => "user"
This option defines prefix of user api route name
Default: 'user'
- prefix_path
-
prefix_path => "/user"
This option defines prefix of user api route
Default: '/user'
ATTRIBUTES
This class implements the following attributes
accepts
accepts => 0,
Maximum number of connections a worker is allowed to accept, before stopping gracefully and then getting replaced with a newly started worker, passed along to "max_accepts" in Mojo::IOLoop
Default: 10000
See "accepts" in Mojo::Server::Prefork
cache
The WWW::Suffit::Cache object
clients
clients => 0,
Maximum number of accepted connections this server is allowed to handle concurrently, before stopping to accept new incoming connections, passed along to "max_connections" in Mojo::IOLoop
Default: 1000
See "max_clients" in Mojo::Server::Daemon
configobj
The Config::General object or undef
acruxconfig
The Acrux::Config object or undef
datadir
datadir => '/var/lib/myapp',
The sharedstate data directory (data dir)
Default: /var/lib/<MONIKER>
debugmode
debugmode => 0,
If this attribute is enabled then this server is no daemonize performs
documentroot
documentroot => '/var/www/myapp',
Document root directory
Default: /var/www/<MONIKER>
gid
gid => 1000,
gid => getgrnam( 'anonymous' ),
This attribute pass GID to set the real group identifier and the effective group identifier for this process
homedir
homedir => '/usr/share/myapp',
The Project home directory
Default: /usr/share/<MONIKER>
logfile
logfile => '/var/log/myapp.log',
The log file
Default: /var/log/<MONIKER>.log
loglevel
loglevel => 'warn',
This attribute performs set the log level
Default: warn
max_history_size
max_history_size => 25,
Maximum number of logged messages to store in "history"
Default: 25
moniker
moniker => 'myapp',
Project name in lowercase notation, project nickname, moniker. This value often used as default filename for configuration files and the like
Default: decamelizing the application class
mysecret
mysecret => 'dgdfg',
Default secret string
Default: <DEFAULT_SECRET>
no_daemonize
no_daemonize => 1,
This attribute disables the daemonize process
Default: 0
pidfile
pidfile => '/var/run/myapp.pid',
The pid file
Default: /tmp/prefork.pid
See "pid_file" in Mojo::Server::Prefork
project_name
project_name => 'MyApp',
The project name. For example: MyApp
Default: current class name
private_key
private_key => '...'
Private RSA key
project_version
project_version => '0.01'
The project version. For example: 1.00
NOTE! This is required attribute!
public_key
public_key => '...',
Public RSA key
requests
requests => 0,
Maximum number of keep-alive requests per connection
Default: 100
See "max_requests" in Mojo::Server::Daemon
reload_sig
reload_sig => 'USR2',
reload_sig => 'HUP',
The signal name that will be used to receive reload commands from the system
Default: USR2
rsa_keysize
rsa_keysize => 2048
RSA key size
See RSA_KeySize
configuration directive
Default: 2048
server_addr
server_addr => '*',
Main listener address (host)
Default: * (::0, 0:0:0:0)
server_port
server_port => 8080,
Main listener port
Default: 8080
server_url
server_url => 'http://127.0.0.1:8080',
Main real listener URL
See ListenAddr
and ListenPort
configuration directives
Default: http://127.0.0.1:8080
spare
spare => 0,
Temporarily spawn up to this number of additional workers if there is a need.
Default: 2
See "spare" in Mojo::Server::Prefork
tempdir
tempdir => '/tmp/myapp',
The temp directory
Default: /tmp/<MONIKER>
trustedproxies
List of trusted proxies
Default: none
uid
uid => 1000,
uid => getpwnam( 'anonymous' ),
This attribute pass UID to set the real user identifier and the effective user identifier for this process
workers
workers => 0,
Number of worker processes
Default: 4
See "workers" in Mojo::Server::Prefork
METHODS
This class inherits all methods from Mojolicious and implements the following new ones
init
$app->init;
This is your main hook into the Suffit application, it will be called at application startup immediately after calling the Mojolicious startup hook. Meant to be overloaded in a your subclass
listeners
This method returns server listeners as list of URLs
$prefork->listen( $app->listeners );
preforked_run
$app->preforked_run( COMMAND );
$app->preforked_run( COMMAND, ...OPTIONS... );
$app->preforked_run( COMMAND, { ...OPTIONS... } );
$app->preforked_run( 'start' );
$app->preforked_run( 'start', prerun => sub { ... } );
$app->preforked_run( 'stop' );
$app->preforked_run( 'restart', prerun => sub { ... } );
$app->preforked_run( 'status' );
$app->preforked_run( 'reload' );
This method runs your application using a command that is passed as the first argument
Options:
- prerun
-
prerun => sub { my ($app, $prefork) = @_; $prefork->on(finish => sub { # Finish my $this = shift; # Prefork object my $graceful = shift; $this->app->log->debug($graceful ? 'Graceful server shutdown' : 'Server shutdown' ); }); }
This option defines callback function that performs operations with prefork instance Mojo::Server::Prefork befor demonize and server running
raise
$app->raise("Mask %s", "val");
$app->raise("val");
Prints error message to STDERR and exit with errorlevel = 1
NOTE! For internal use only
reload
The reload hook
startup
Main "startup" in Mojolicious hook
HELPERS
This class implements the following helpers
authdb
This is access method to the AuthDB object (state object)
clientid
my $clientid = $app->clientid;
This helper returns client ID that calculates from User-Agent
and Remote-Address
headers:
md5(User-Agent . Remote-Address)
gen_cachekey
my $cachekey = $app->gen_cachekey;
my $cachekey = $app->gen_cachekey(16);
This helper helps generate the new CacheKey for caching user data that was got from authorization database
gen_rsakeys
my %keysdata = $app->gen_rsakeys;
my %keysdata = $app->gen_rsakeys( 2048 );
This helper generates RSA keys pair and returns structure as hash:
private_key => '...',
public_key => '...',
key_size => 2048,
error => '...',
jwt
This helper makes JWT object with RSA keys and returns it
token
This helper performs get of current token from HTTP Request headers
CONFIGURATION
This class supports the following configuration directives
GENERAL DIRECTIVES
- Log
-
Log Syslog Log File
This directive defines the log provider. Supported providers:
File
,Syslog
Default: File
- LogFile
-
LogFile /var/log/myapp.log
This directive sets the path to logfile
Default: /var/log/<MONIKER>.log
- LogLevel
-
LogLevel warn
This directive defines log level.
Available log levels are
trace
,debug
,info
,warn
,error
andfatal
, in that order.Default: warn
SERVER DIRECTIVES
- ListenURL
-
ListenURL http://127.0.0.1:8008 ListenURL http://127.0.0.1:8009 ListenURL 'https://*:3000?cert=/x/server.crt&key=/y/server.key&ca=/z/ca.crt'
Directives that specify additional listening addresses in URL form
NOTE! This is a multiple directive
Default: none
- ListenAddr
-
ListenAddr * ListenAddr 0.0.0.0 ListenAddr 127.0.0.1
This directive sets the master listen address
Default: * (0.0.0.0)
- ListenPort
-
ListenPort 8080 ListenPort 80 ListenPort 443
This directive sets the master listen port
Default: 8080
- Accepts
-
Accepts 0
Maximum number of connections a worker is allowed to accept, before stopping gracefully and then getting replaced with a newly started worker, defaults to the value of "accepts" in Mojo::Server::Prefork. Setting the value to 0 will allow workers to accept new connections indefinitely
Default: 0
- Clients
-
Clients 1000
Maximum number of accepted connections each worker process is allowed to handle concurrently, before stopping to accept new incoming connections, defaults to 100. Note that high concurrency works best with applications that perform mostly non-blocking operations, to optimize for blocking operations you can decrease this value and increase "workers" instead for better performance
Default: 1000
- Requests
-
Requests 100
Maximum number of keep-alive requests per connection
Default: 100
- Spare
-
Spare 2
Temporarily spawn up to this number of additional workers if there is a need, defaults to 2. This allows for new workers to be started while old ones are still shutting down gracefully, drastically reducing the performance cost of worker restarts
Default: 2
- Workers
-
Workers 4
Number of worker processes, defaults to 4. A good rule of thumb is two worker processes per CPU core for applications that perform mostly non-blocking operations, blocking operations often require more and benefit from decreasing concurrency with "clients" (often as low as 1)
Default: 4
- TrustedProxy
-
TrustedProxy 127.0.0.1 TrustedProxy 10.0.0.0/8 TrustedProxy 172.16.0.0/12 TrustedProxy 192.168.0.0/16 TrustedProxy fc00::/7
Trusted reverse proxies, addresses or networks in
CIDR
form. The real IP address takes fromX-Forwarded-For
headerNOTE! This is a multiple directive
Default: All reverse proxies will be passed
- Reload_Sig
-
Reload_Sig USR2 Reload_Sig HUP
This directive sets the dafault signal name that will be used to receive reload commands from the system
Default: USR2
SSL/TLS SERVER DIRECTIVES
- TLS
-
TLS enabled
This directive enables or disables the TLS (https) listening
Default: disabled
- TLS_CA, TLS_Cert, TLS_Key
-
TLS_CA certs/ca.crt TLS_Cert certs/server.crt TLS_Key certs/server.key
Paths to TLS files. Absolute or relative paths (started from /etc/<MONIKER>)
TLS_CA - Path to TLS certificate authority file used to verify the peer certificate. TLS_Cert - Path to the TLS cert file, defaults to a built-in test certificate. TLS_Key - Path to the TLS key file, defaults to a built-in test key
Default: none
- TLS_Ciphers, TLS_Verify, TLS_Version
-
TLS_Version TLSv1_2 TLS_Ciphers AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH TLS_Verify 0x00
Directives for setting TLS extra data
TLS cipher specification string. For more information about the format see https://www.openssl.org/docs/manmaster/man1/ciphers.html/CIPHER-STRINGS. TLS_Verify - TLS verification mode. TLS_Version - TLS protocol version.
Default: none
- TLS_FD, TLS_Reuse, TLS_Single_Accept
-
TLS_FD - File descriptor with an already prepared listen socket. TLS_Reuse - Allow multiple servers to use the same port with the
SO_REUSEPORT
socket option. TLS_Single_Accept - Only accept one connection at a time.
SECURITY DIRECTIVES
- PrivateKeyFile, PublicKeyFile
-
PrivateKeyFile /var/lib/myapp/rsa-private.key PublicKeyFile /var/lib/myapp/rsa-public.key
Private and Public RSA key files If not possible to read files by the specified paths, they will be created automatically
Defaults:
PrivateKeyFile /var/lib/E<lt>MONIKERE<gt>/rsa-private.key PublicKeyFile /var/lib/E<lt>MONIKERE<gt>/rsa-public.key
- RSA_KeySize
-
RSA_KeySize 2048
RSA Key size. This is size (length) of the RSA Key. Allowed key sizes in bits:
512
,1024
,2048
,3072
,4096
Default: 2048
- Secret
-
Secret "My$ecretPhr@se!"
HMAC secret passphrase
Default: md5(rsa_private_file)
ATHORIZATION DIRECTIVES
- AuthDBURL, AuthDBURI
-
AuthDBURI "mysql://user:pass@mysql.example.com/authdb \ ?mysql_auto_reconnect=1&mysql_enable_utf8=1" AuthDBURI "sqlite:///var/lib/myapp/auth.db?sqlite_unicode=1"
Authorization database connect string (Data Source URI) This directive written in the URI form
Default: "sqlite:///var/lib/<MONIKER>/auth.db?sqlite_unicode=1"
- AuthDBCachedConnection
-
AuthDBCachedConnection 1 AuthDBCachedConnection Yes AuthDBCachedConnection On AuthDBCachedConnection Enable
This directive defines status of caching while establishing of connection to database
See "cached" in WWW::Suffit::AuthDB
Default: false (no caching connection)
- AuthDBCacheExpire, AuthDBCacheExpiration
-
AuthDBCacheExpiration 300
The expiration time
See "expiration" in WWW::Suffit::AuthDB
Default: 300 (5 min)
- AuthDBCacheMaxKeys
-
AuthDBCacheMaxKeys 1024
The maximum keys number in cache
See "max_keys" in WWW::Suffit::AuthDB
Default: 1024*1024 (1`048`576 keys max)
- AuthDBSourceFile
-
AuthDBSourceFile /var/lib/myapp/authdb.json
Authorization database source file path. This is simple JSON file that contains three blocks: users, groups and realms.
Default: /var/lib/<MONIKER>/authdb.json
- Token
-
Token ed23...3c0a
Development token directive This development directive allows authorization without getting real
Authorization
header from the client requestDefault: none
EXAMPLE
Example of well-structured simplified web application
# mkdir lib
# touch lib/MyApp.pm
# chmod 644 lib/MyApp.pm
We will start by MyApp.pm
that contains main application class and controller class
package MyApp;
use Mojo::Base 'WWW::Suffit::Server';
our $VERSION = '1.00';
sub init {
my $self = shift;
my $r = $self->routes;
$r->any('/' => {text => 'Your test server is working!'})->name('index');
$r->get('/test')->to('example#test')->name('test');
}
1;
package MyApp::Controller::Example;
use Mojo::Base 'Mojolicious::Controller';
sub test {
my $self = shift;
$self->render(text => 'Hello World!');
}
1;
The init
method gets called right after instantiation and is the place where the whole your application gets set up
# mkdir bin
# touch bin/myapp.pl
# chmod 644 bin/myapp.pl
myapp.pl
itself can now be created as simplified application script to allow running tests.
#!/usr/bin/perl -w
use strict;
use warnings;
use Mojo::File qw/ curfile path /;
use lib curfile->dirname->sibling('lib')->to_string;
use Mojo::Server;
my $root = curfile->dirname->child('test')->to_string;
Mojo::Server->new->build_app('MyApp',
debugmode => 1,
loglevel => 'debug',
homedir => path($root)->child('www')->make_path->to_string,
datadir => path($root)->child('var')->make_path->to_string,
tempdir => path($root)->child('tmp')->make_path->to_string,
config_opts => {
noload => 1, # force disable loading config from file
defaults => {
foo => 'bar',
},
},
)->start();
Now try to run it
# perl bin/myapp.pl daemon -l http://*:8080
Now let's get to simplified testing
# mkdir t
# touch t/myapp.t
# chmod 644 t/myapp.t
Full Mojolicious applications are easy to test, so t/myapp.t
can be containts:
use strict;
use warnings;
use Test::More;
use Test::Mojo;
use Mojo::File qw/ path /;
use MyApp;
my $root = path()->child('test')->to_string;
my $t = Test::Mojo->new(MyApp->new(
homedir => path($root)->child('www')->make_path->to_string,
datadir => path($root)->child('var')->make_path->to_string,
tempdir => path($root)->child('tmp')->make_path->to_string,
config_opts => {
noload => 1, # force disable loading config from file
defaults => {
foo => 'bar',
},
},
));
subtest 'Test workflow' => sub {
$t->get_ok('/')
->status_is(200)
->content_like(qr/working!/, 'right content by GET /');
$t->post_ok('/' => form => {'_' => time})
->status_is(200)
->content_like(qr/working!/, 'right content by POST /');
$t->get_ok('/test')
->status_is(200)
->content_like(qr/World/, 'right content by GET /test');
};
done_testing();
Now try to test
# prove -lv t/myapp.t
And our final directory structure should be looking like this
MyApp
+- bin
| +- myapp.pl
+- lib
| +- MyApp.pm
+- t
| +- myapp.t
+- test
+- tmp
+- var
+- www
Test-driven development takes a little getting used to, but can be a very powerful tool
HISTORY
See Changes
file
TO DO
See TODO
file
SEE ALSO
Mojolicious, WWW::Suffit, WWW::Suffit::RSA, WWW::Suffit::JWT, WWW::Suffit::API, WWW::Suffit::AuthDB
AUTHOR
Serż Minus (Sergey Lepenkov) https://www.serzik.com <abalama@cpan.org>
COPYRIGHT
Copyright (C) 1998-2024 D&D Corporation. All Rights Reserved
LICENSE
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See LICENSE
file and https://dev.perl.org/licenses/