| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package WWW::Suffit::Server; |
|
2
|
1
|
|
|
1
|
|
134457
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
41
|
|
|
3
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
9
|
|
|
|
1
|
|
|
|
|
63
|
|
|
4
|
1
|
|
|
1
|
|
734
|
use utf8; |
|
|
1
|
|
|
|
|
348
|
|
|
|
1
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=encoding utf8 |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
WWW::Suffit::Server - The Suffit API web-server class |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use Mojo::File qw/ path /; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
my $root = path()->child('test')->to_string; |
|
17
|
|
|
|
|
|
|
my $app = MyApp->new( |
|
18
|
|
|
|
|
|
|
project_name => 'MyApp', |
|
19
|
|
|
|
|
|
|
project_version => '0.01', |
|
20
|
|
|
|
|
|
|
moniker => 'myapp', |
|
21
|
|
|
|
|
|
|
debugmode => 1, |
|
22
|
|
|
|
|
|
|
loglevel => 'debug', |
|
23
|
|
|
|
|
|
|
max_history_size => 25, |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# System |
|
26
|
|
|
|
|
|
|
uid => 1000, |
|
27
|
|
|
|
|
|
|
gid => 1000, |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Dirs and files |
|
30
|
|
|
|
|
|
|
homedir => path($root)->child('share')->make_path->to_string, |
|
31
|
|
|
|
|
|
|
datadir => path($root)->child('var')->make_path->to_string, |
|
32
|
|
|
|
|
|
|
tempdir => path($root)->child('tmp')->make_path->to_string, |
|
33
|
|
|
|
|
|
|
documentroot => path($root)->child('www')->make_path->to_string, |
|
34
|
|
|
|
|
|
|
logfile => path($root)->child('log')->make_path->child('myapp.log')->to_string, |
|
35
|
|
|
|
|
|
|
pidfile => path($root)->child('run')->make_path->child('myapp.pid')->to_string, |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Server |
|
38
|
|
|
|
|
|
|
server_addr => '*', |
|
39
|
|
|
|
|
|
|
server_port => 8080, |
|
40
|
|
|
|
|
|
|
server_url => 'http://127.0.0.1:8080', |
|
41
|
|
|
|
|
|
|
trustedproxies => ['127.0.0.1'], |
|
42
|
|
|
|
|
|
|
accepts => 10000, |
|
43
|
|
|
|
|
|
|
clients => 1000, |
|
44
|
|
|
|
|
|
|
requests => 100, |
|
45
|
|
|
|
|
|
|
workers => 4, |
|
46
|
|
|
|
|
|
|
spare => 2, |
|
47
|
|
|
|
|
|
|
reload_sig => 'USR2', |
|
48
|
|
|
|
|
|
|
no_daemonize => 1, |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# Security |
|
51
|
|
|
|
|
|
|
mysecret => 'Eph9Ce$quo.p2@oW3', |
|
52
|
|
|
|
|
|
|
rsa_keysize => 2048, |
|
53
|
|
|
|
|
|
|
private_key => undef, # Auto |
|
54
|
|
|
|
|
|
|
public_key => undef, # Auto |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# Initialization options |
|
57
|
|
|
|
|
|
|
all_features => 'no', |
|
58
|
|
|
|
|
|
|
config_opts => { |
|
59
|
|
|
|
|
|
|
file => path($root)->child('etc')->make_path->child('myapp.conf')->to_string, |
|
60
|
|
|
|
|
|
|
defaults => {foo => 'bar'}, |
|
61
|
|
|
|
|
|
|
}, |
|
62
|
|
|
|
|
|
|
); |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Run preforked application |
|
65
|
|
|
|
|
|
|
$app->preforked_run( 'start' ); |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
1; |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
package MyApp; |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
use Mojo::Base 'WWW::Suffit::Server'; |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub init { shift->routes->any('/' => {text => 'Hello World!'}) } |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
1; |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
This module provides API web-server functionality |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head1 OPTIONS |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub startup { |
|
84
|
|
|
|
|
|
|
my $self = shift->SUPER::startup( OPTION_NAME => VALUE, ... ); |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# ... |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Options passed as arguments to the startup function allow you to customize |
|
90
|
|
|
|
|
|
|
the initialization of plugins at the level of your descendant class, and |
|
91
|
|
|
|
|
|
|
options are considered to have higher priority than attributes of the same name. |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
List of allowed options (pairs of name-value): |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head2 admin_routes_opts |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
admin_routes_opts => { |
|
98
|
|
|
|
|
|
|
prefix_path => "/admin", |
|
99
|
|
|
|
|
|
|
prefix_name => "admin", |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=over 8 |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=item prefix_name |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
prefix_name => "admin" |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
This option defines prefix of admin api route name |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Default: 'admin' |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=item prefix_path |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
prefix_path => "/admin" |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
This option defines prefix of admin api route |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Default: '/admin' |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=back |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head2 all_features |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
all_features => 'on' |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
This option enables all of the init_* options, which are described bellow |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Default: off |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=head2 api_routes_opts |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
api_routes_opts => { |
|
133
|
|
|
|
|
|
|
prefix_path => "/api", |
|
134
|
|
|
|
|
|
|
prefix_name => "api", |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=over 8 |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=item prefix_name |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
prefix_name => "api" |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
This option defines prefix of api route name |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Default: 'api' |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item prefix_path |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
prefix_path => "/api" |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
This option defines prefix of api route |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Default: '/api' |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=back |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head2 authdb_opts |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
authdb_opts => { |
|
160
|
|
|
|
|
|
|
uri => "sqlite://?sqlite_unicode=1", |
|
161
|
|
|
|
|
|
|
cachedconnection => 'on', |
|
162
|
|
|
|
|
|
|
cacheexpiration => 300, |
|
163
|
|
|
|
|
|
|
cachemaxkeys => 1024*1024, |
|
164
|
|
|
|
|
|
|
sourcefile => '/tmp/authdb.json', |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=over 8 |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item uri, url |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
uri => "sqlite:///tmp/auth.db?sqlite_unicode=1", |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Default: See config C or C directive |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item cachedconnection |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
cachedconnection => 'on', |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Default: See config C directive. Default: on |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=item cacheexpire, cacheexpiration |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
cacheexpiration => 300, |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Default: See config C or C directive. Default: 300 |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=item cachemaxkeys |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
cachemaxkeys => 1024*1024, |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Default: See config C directive. Default: 1024*1024 |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=item sourcefile |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sourcefile => '/tmp/authdb.json', |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
Default: See config C directive |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=back |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head2 config_opts |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
config_opts => { ... } |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
This option sets L plugin options |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Default: |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
`noload => 1` if $self->configobj exists |
|
210
|
|
|
|
|
|
|
`defaults => $self->config` if $self->config is not void |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head2 init_admin_routes |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
init_admin_routes => 'on' |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
This option enables Admin Suffit API routes |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
Default: off |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head2 init_authdb |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
init_authdb => 'on' |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
This option enables AuthDB initialize |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Default: off |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head2 init_api_routes |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
init_api_routes => 'on' |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
This option enables Suffit API routes |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
Default: off |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head2 init_rsa_keys |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
init_rsa_keys => 'on' |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
This option enables RSA keys initialize |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Default: off |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head2 init_user_routes |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
init_user_routes => 'on' |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
This option enables User Suffit API routes |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Default: off |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head2 syslog_opts |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
syslog_opts => { ... } |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
This option sets L plugin options |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
Default: |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
`enable => 1` if the `Log` config directive is "syslog" |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=head2 user_routes_opts |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
user_routes_opts => { |
|
265
|
|
|
|
|
|
|
prefix_path => "/user", |
|
266
|
|
|
|
|
|
|
prefix_name => "user", |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=over 8 |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item prefix_name |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
prefix_name => "user" |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
This option defines prefix of user api route name |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
Default: 'user' |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=item prefix_path |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
prefix_path => "/user" |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
This option defines prefix of user api route |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
Default: '/user' |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=back |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
This class implements the following attributes |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head2 accepts |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
accepts => 0, |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
Maximum number of connections a worker is allowed to accept, before stopping |
|
298
|
|
|
|
|
|
|
gracefully and then getting replaced with a newly started worker, |
|
299
|
|
|
|
|
|
|
passed along to L |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
Default: 10000 |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
See L |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=head2 cache |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
The WWW::Suffit::Cache object |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=head2 clients |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
clients => 0, |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
Maximum number of accepted connections this server is allowed to handle concurrently, |
|
314
|
|
|
|
|
|
|
before stopping to accept new incoming connections, passed along to L |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
Default: 1000 |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
See L |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=head2 configobj |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
The Config::General object or undef |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=head2 acruxconfig |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
The L object or undef |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=head2 datadir |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
datadir => '/var/lib/myapp', |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
The sharedstate data directory (data dir) |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
Default: /var/lib/ |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=head2 debugmode |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
debugmode => 0, |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
If this attribute is enabled then this server is no daemonize performs |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head2 documentroot |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
documentroot => '/var/www/myapp', |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Document root directory |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
Default: /var/www/ |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head2 gid |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
gid => 1000, |
|
353
|
|
|
|
|
|
|
gid => getgrnam( 'anonymous' ), |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
This attribute pass GID to set the real group identifier and the effective group identifier for this process |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=head2 homedir |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
homedir => '/usr/share/myapp', |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
The Project home directory |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Default: /usr/share/ |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head2 logfile |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
logfile => '/var/log/myapp.log', |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
The log file |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Default: /var/log/.log |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=head2 loglevel |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
loglevel => 'warn', |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
This attribute performs set the log level |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Default: warn |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head2 max_history_size |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
max_history_size => 25, |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
Maximum number of logged messages to store in "history" |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Default: 25 |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=head2 moniker |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
moniker => 'myapp', |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
Project name in lowercase notation, project nickname, moniker. |
|
394
|
|
|
|
|
|
|
This value often used as default filename for configuration files and the like |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Default: decamelizing the application class |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
See L |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=head2 mysecret |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
mysecret => 'dgdfg', |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Default secret string |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
Default: |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head2 no_daemonize |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
no_daemonize => 1, |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
This attribute disables the daemonize process |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
Default: 0 |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=head2 pidfile |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
pidfile => '/var/run/myapp.pid', |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
The pid file |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Default: /tmp/prefork.pid |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
See L |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=head2 project_name |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
project_name => 'MyApp', |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
The project name. For example: MyApp |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
Default: current class name |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=head2 private_key |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
private_key => '...' |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
Private RSA key |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=head2 project_version |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
project_version => '0.01' |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
The project version. For example: 1.00 |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
B This is required attribute! |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=head2 public_key |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
public_key => '...', |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Public RSA key |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 requests |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
requests => 0, |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
Maximum number of keep-alive requests per connection |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
Default: 100 |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
See L |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head2 reload_sig |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
reload_sig => 'USR2', |
|
467
|
|
|
|
|
|
|
reload_sig => 'HUP', |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
The signal name that will be used to receive reload commands from the system |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
Default: USR2 |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=head2 rsa_keysize |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
rsa_keysize => 2048 |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
RSA key size |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
See C configuration directive |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Default: 2048 |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head2 server_addr |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
server_addr => '*', |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Main listener address (host) |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
Default: * (::0, 0:0:0:0) |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=head2 server_port |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
server_port => 8080, |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
Main listener port |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Default: 8080 |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=head2 server_url |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
server_url => 'http://127.0.0.1:8080', |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
Main real listener URL |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
See C and C configuration directives |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
Default: http://127.0.0.1:8080 |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=head2 spare |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
spare => 0, |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
Temporarily spawn up to this number of additional workers if there is a need. |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
Default: 2 |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
See L |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=head2 tempdir |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
tempdir => '/tmp/myapp', |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
The temp directory |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
Default: /tmp/ |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head2 trustedproxies |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
List of trusted proxies |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
Default: none |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=head2 uid |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
uid => 1000, |
|
536
|
|
|
|
|
|
|
uid => getpwnam( 'anonymous' ), |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
This attribute pass UID to set the real user identifier and the effective user identifier for this process |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=head2 workers |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
workers => 0, |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
Number of worker processes |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
Default: 4 |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
See L |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head1 METHODS |
|
551
|
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
This class inherits all methods from L and implements the following new ones |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=head2 init |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
$app->init; |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
This is your main hook into the Suffit application, it will be called at application startup |
|
559
|
|
|
|
|
|
|
immediately after calling the Mojolicious startup hook. Meant to be overloaded in a your subclass |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=head2 listeners |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
This method returns server listeners as list of URLs |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
$prefork->listen( $app->listeners ); |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=head2 preforked_run |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
$app->preforked_run( COMMAND ); |
|
570
|
|
|
|
|
|
|
$app->preforked_run( COMMAND, ...OPTIONS... ); |
|
571
|
|
|
|
|
|
|
$app->preforked_run( COMMAND, { ...OPTIONS... } ); |
|
572
|
|
|
|
|
|
|
$app->preforked_run( 'start' ); |
|
573
|
|
|
|
|
|
|
$app->preforked_run( 'start', prerun => sub { ... } ); |
|
574
|
|
|
|
|
|
|
$app->preforked_run( 'stop' ); |
|
575
|
|
|
|
|
|
|
$app->preforked_run( 'restart', prerun => sub { ... } ); |
|
576
|
|
|
|
|
|
|
$app->preforked_run( 'status' ); |
|
577
|
|
|
|
|
|
|
$app->preforked_run( 'reload' ); |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
This method runs your application using a command that is passed as the first argument |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
B |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=over 8 |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=item prerun |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
prerun => sub { |
|
588
|
|
|
|
|
|
|
my ($app, $prefork) = @_; |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
$prefork->on(finish => sub { # Finish |
|
591
|
|
|
|
|
|
|
my $this = shift; # Prefork object |
|
592
|
|
|
|
|
|
|
my $graceful = shift; |
|
593
|
|
|
|
|
|
|
$this->app->log->debug($graceful |
|
594
|
|
|
|
|
|
|
? 'Graceful server shutdown' |
|
595
|
|
|
|
|
|
|
: 'Server shutdown' |
|
596
|
|
|
|
|
|
|
); |
|
597
|
|
|
|
|
|
|
}); |
|
598
|
|
|
|
|
|
|
} |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
This option defines callback function that performs operations with prefork |
|
601
|
|
|
|
|
|
|
instance L befor demonize and server running |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=back |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head2 raise |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
$app->raise("Mask %s", "val"); |
|
608
|
|
|
|
|
|
|
$app->raise("val"); |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Prints error message to STDERR and exit with errorlevel = 1 |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
B For internal use only |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=head2 reload |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
The reload hook |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=head2 startup |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
Main L hook |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head1 HELPERS |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
This class implements the following helpers |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=head2 authdb |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
This is access method to the AuthDB object (state object) |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=head2 clientid |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
my $clientid = $app->clientid; |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
This helper returns client ID that calculates from C |
|
635
|
|
|
|
|
|
|
and C headers: |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
md5(User-Agent . Remote-Address) |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=head2 gen_cachekey |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
my $cachekey = $app->gen_cachekey; |
|
642
|
|
|
|
|
|
|
my $cachekey = $app->gen_cachekey(16); |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
This helper helps generate the new CacheKey for caching user data |
|
645
|
|
|
|
|
|
|
that was got from authorization database |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=head2 gen_rsakeys |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
my %keysdata = $app->gen_rsakeys; |
|
650
|
|
|
|
|
|
|
my %keysdata = $app->gen_rsakeys( 2048 ); |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
This helper generates RSA keys pair and returns structure as hash: |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
private_key => '...', |
|
655
|
|
|
|
|
|
|
public_key => '...', |
|
656
|
|
|
|
|
|
|
key_size => 2048, |
|
657
|
|
|
|
|
|
|
error => '...', |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=head2 jwt |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
This helper makes JWT object with RSA keys and returns it |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=head2 token |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
This helper performs get of current token from HTTP Request headers |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=head1 CONFIGURATION |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
This class supports the following configuration directives |
|
670
|
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=head2 GENERAL DIRECTIVES |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=over 8 |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=item Log |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
Log Syslog |
|
678
|
|
|
|
|
|
|
Log File |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
This directive defines the log provider. Supported providers: C, C |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
Default: File |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=item LogFile |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
LogFile /var/log/myapp.log |
|
687
|
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
This directive sets the path to logfile |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
Default: /var/log/EMONIKERE.log |
|
691
|
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=item LogLevel |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
LogLevel warn |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
This directive defines log level. |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
Available log levels are C, C, C, C, C and C, in that order. |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
Default: warn |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=back |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=head2 SERVER DIRECTIVES |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=over 8 |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=item ListenURL |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
ListenURL http://127.0.0.1:8008 |
|
711
|
|
|
|
|
|
|
ListenURL http://127.0.0.1:8009 |
|
712
|
|
|
|
|
|
|
ListenURL 'https://*:3000?cert=/x/server.crt&key=/y/server.key&ca=/z/ca.crt' |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
Directives that specify additional listening addresses in URL form |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
B This is a multiple directive |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
Default: none |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=item ListenAddr |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
ListenAddr * |
|
723
|
|
|
|
|
|
|
ListenAddr 0.0.0.0 |
|
724
|
|
|
|
|
|
|
ListenAddr 127.0.0.1 |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
This directive sets the master listen address |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Default: * (0.0.0.0) |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=item ListenPort |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
ListenPort 8080 |
|
733
|
|
|
|
|
|
|
ListenPort 80 |
|
734
|
|
|
|
|
|
|
ListenPort 443 |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
This directive sets the master listen port |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
Default: 8080 |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=item Accepts |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
Accepts 0 |
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
Maximum number of connections a worker is allowed to accept, before |
|
745
|
|
|
|
|
|
|
stopping gracefully and then getting replaced with a newly started worker, |
|
746
|
|
|
|
|
|
|
defaults to the value of "accepts" in L. |
|
747
|
|
|
|
|
|
|
Setting the value to 0 will allow workers to accept new connections |
|
748
|
|
|
|
|
|
|
indefinitely |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
Default: 0 |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=item Clients |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
Clients 1000 |
|
755
|
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Maximum number of accepted connections each worker process is allowed to |
|
757
|
|
|
|
|
|
|
handle concurrently, before stopping to accept new incoming connections, |
|
758
|
|
|
|
|
|
|
defaults to 100. Note that high concurrency works best with applications |
|
759
|
|
|
|
|
|
|
that perform mostly non-blocking operations, to optimize for blocking |
|
760
|
|
|
|
|
|
|
operations you can decrease this value and increase "workers" instead |
|
761
|
|
|
|
|
|
|
for better performance |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
Default: 1000 |
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=item Requests |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
Requests 100 |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
Maximum number of keep-alive requests per connection |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
Default: 100 |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=item Spare |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
Spare 2 |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
Temporarily spawn up to this number of additional workers if there |
|
778
|
|
|
|
|
|
|
is a need, defaults to 2. This allows for new workers to be started while |
|
779
|
|
|
|
|
|
|
old ones are still shutting down gracefully, drastically reducing the |
|
780
|
|
|
|
|
|
|
performance cost of worker restarts |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
Default: 2 |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=item Workers |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
Workers 4 |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
Number of worker processes, defaults to 4. A good rule of thumb is two |
|
789
|
|
|
|
|
|
|
worker processes per CPU core for applications that perform mostly |
|
790
|
|
|
|
|
|
|
non-blocking operations, blocking operations often require more and |
|
791
|
|
|
|
|
|
|
benefit from decreasing concurrency with "clients" (often as low as 1) |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
Default: 4 |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=item TrustedProxy |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
TrustedProxy 127.0.0.1 |
|
798
|
|
|
|
|
|
|
TrustedProxy 10.0.0.0/8 |
|
799
|
|
|
|
|
|
|
TrustedProxy 172.16.0.0/12 |
|
800
|
|
|
|
|
|
|
TrustedProxy 192.168.0.0/16 |
|
801
|
|
|
|
|
|
|
TrustedProxy fc00::/7 |
|
802
|
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
Trusted reverse proxies, addresses or networks in C form. |
|
804
|
|
|
|
|
|
|
The real IP address takes from C header |
|
805
|
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
B This is a multiple directive |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
Default: All reverse proxies will be passed |
|
809
|
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=item Reload_Sig |
|
811
|
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
Reload_Sig USR2 |
|
813
|
|
|
|
|
|
|
Reload_Sig HUP |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
This directive sets the dafault signal name that will be used to receive reload commands from the system |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
Default: USR2 |
|
818
|
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
=back |
|
820
|
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=head2 SSL/TLS SERVER DIRECTIVES |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=over 8 |
|
824
|
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
=item TLS |
|
826
|
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
TLS enabled |
|
828
|
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
This directive enables or disables the TLS (https) listening |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Default: disabled |
|
832
|
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=item TLS_CA, TLS_Cert, TLS_Key |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
TLS_CA certs/ca.crt |
|
836
|
|
|
|
|
|
|
TLS_Cert certs/server.crt |
|
837
|
|
|
|
|
|
|
TLS_Key certs/server.key |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
Paths to TLS files. Absolute or relative paths (started from /etc/EMONIKERE) |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
B - Path to TLS certificate authority file used to verify the peer certificate. |
|
842
|
|
|
|
|
|
|
B - Path to the TLS cert file, defaults to a built-in test certificate. |
|
843
|
|
|
|
|
|
|
B - Path to the TLS key file, defaults to a built-in test key |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
Default: none |
|
846
|
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
=item TLS_Ciphers, TLS_Verify, TLS_Version |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
TLS_Version TLSv1_2 |
|
850
|
|
|
|
|
|
|
TLS_Ciphers AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH |
|
851
|
|
|
|
|
|
|
TLS_Verify 0x00 |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
Directives for setting TLS extra data |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
TLS cipher specification string. For more information about the format see |
|
856
|
|
|
|
|
|
|
L. |
|
857
|
|
|
|
|
|
|
B - TLS verification mode. B - TLS protocol version. |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
Default: none |
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
=item TLS_FD, TLS_Reuse, TLS_Single_Accept |
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
B - File descriptor with an already prepared listen socket. |
|
864
|
|
|
|
|
|
|
B - Allow multiple servers to use the same port with the C socket option. |
|
865
|
|
|
|
|
|
|
B - Only accept one connection at a time. |
|
866
|
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
=back |
|
868
|
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=head2 SECURITY DIRECTIVES |
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=over 8 |
|
872
|
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
=item PrivateKeyFile, PublicKeyFile |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
PrivateKeyFile /var/lib/myapp/rsa-private.key |
|
876
|
|
|
|
|
|
|
PublicKeyFile /var/lib/myapp/rsa-public.key |
|
877
|
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
Private and Public RSA key files |
|
879
|
|
|
|
|
|
|
If not possible to read files by the specified paths, they will |
|
880
|
|
|
|
|
|
|
be created automatically |
|
881
|
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
Defaults: |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
PrivateKeyFile /var/lib/EMONIKERE/rsa-private.key |
|
885
|
|
|
|
|
|
|
PublicKeyFile /var/lib/EMONIKERE/rsa-public.key |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
=item RSA_KeySize |
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
RSA_KeySize 2048 |
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
RSA Key size. This is size (length) of the RSA Key. |
|
892
|
|
|
|
|
|
|
Allowed key sizes in bits: C<512>, C<1024>, C<2048>, C<3072>, C<4096> |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
Default: 2048 |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
=item Secret |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
Secret "My$ecretPhr@se!" |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
HMAC secret passphrase |
|
901
|
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
Default: md5(rsa_private_file) |
|
903
|
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=back |
|
905
|
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
=head2 ATHORIZATION DIRECTIVES |
|
907
|
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
=over 8 |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
=item AuthDBURL, AuthDBURI |
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
AuthDBURI "mysql://user:pass@mysql.example.com/authdb \ |
|
913
|
|
|
|
|
|
|
?mysql_auto_reconnect=1&mysql_enable_utf8=1" |
|
914
|
|
|
|
|
|
|
AuthDBURI "sqlite:///var/lib/myapp/auth.db?sqlite_unicode=1" |
|
915
|
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
Authorization database connect string (Data Source URI) |
|
917
|
|
|
|
|
|
|
This directive written in the URI form |
|
918
|
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Default: "sqlite:///var/lib/EMONIKERE/auth.db?sqlite_unicode=1" |
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
=item AuthDBCachedConnection |
|
922
|
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
AuthDBCachedConnection 1 |
|
924
|
|
|
|
|
|
|
AuthDBCachedConnection Yes |
|
925
|
|
|
|
|
|
|
AuthDBCachedConnection On |
|
926
|
|
|
|
|
|
|
AuthDBCachedConnection Enable |
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
This directive defines status of caching while establishing of connection to database |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
See L |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
Default: false (no caching connection) |
|
933
|
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=item AuthDBCacheExpire, AuthDBCacheExpiration |
|
935
|
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
AuthDBCacheExpiration 300 |
|
937
|
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
The expiration time |
|
939
|
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
See L |
|
941
|
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
Default: 300 (5 min) |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
=item AuthDBCacheMaxKeys |
|
945
|
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
AuthDBCacheMaxKeys 1024 |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
The maximum keys number in cache |
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
See L |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
Default: 1024*1024 (1`048`576 keys max) |
|
953
|
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
=item AuthDBSourceFile |
|
955
|
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
AuthDBSourceFile /var/lib/myapp/authdb.json |
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
Authorization database source file path. |
|
959
|
|
|
|
|
|
|
This is simple JSON file that contains three blocks: users, groups and realms. |
|
960
|
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
Default: /var/lib/EMONIKERE/authdb.json |
|
962
|
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
=item Token |
|
964
|
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
Token ed23...3c0a |
|
966
|
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
Development token directive |
|
968
|
|
|
|
|
|
|
This development directive allows authorization without getting real C |
|
969
|
|
|
|
|
|
|
header from the client request |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
Default: none |
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
=back |
|
974
|
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
=head1 EXAMPLE |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
Example of well-structured simplified web application |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# mkdir lib |
|
980
|
|
|
|
|
|
|
# touch lib/MyApp.pm |
|
981
|
|
|
|
|
|
|
# chmod 644 lib/MyApp.pm |
|
982
|
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
We will start by C that contains main application class and controller class |
|
984
|
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
package MyApp; |
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
use Mojo::Base 'WWW::Suffit::Server'; |
|
988
|
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
our $VERSION = '1.00'; |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
sub init { |
|
992
|
|
|
|
|
|
|
my $self = shift; |
|
993
|
|
|
|
|
|
|
my $r = $self->routes; |
|
994
|
|
|
|
|
|
|
$r->any('/' => {text => 'Your test server is working!'})->name('index'); |
|
995
|
|
|
|
|
|
|
$r->get('/test')->to('example#test')->name('test'); |
|
996
|
|
|
|
|
|
|
} |
|
997
|
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
1; |
|
999
|
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
package MyApp::Controller::Example; |
|
1001
|
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
use Mojo::Base 'Mojolicious::Controller'; |
|
1003
|
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
sub test { |
|
1005
|
|
|
|
|
|
|
my $self = shift; |
|
1006
|
|
|
|
|
|
|
$self->render(text => 'Hello World!'); |
|
1007
|
|
|
|
|
|
|
} |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
1; |
|
1010
|
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
The C method gets called right after instantiation and is the place where the whole your application gets set up |
|
1012
|
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
# mkdir bin |
|
1014
|
|
|
|
|
|
|
# touch bin/myapp.pl |
|
1015
|
|
|
|
|
|
|
# chmod 644 bin/myapp.pl |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
C itself can now be created as simplified application script to allow running tests. |
|
1018
|
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
#!/usr/bin/perl -w |
|
1020
|
|
|
|
|
|
|
use strict; |
|
1021
|
|
|
|
|
|
|
use warnings; |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
use Mojo::File qw/ curfile path /; |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
use lib curfile->dirname->sibling('lib')->to_string; |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
use Mojo::Server; |
|
1028
|
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
my $root = curfile->dirname->child('test')->to_string; |
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
Mojo::Server->new->build_app('MyApp', |
|
1032
|
|
|
|
|
|
|
debugmode => 1, |
|
1033
|
|
|
|
|
|
|
loglevel => 'debug', |
|
1034
|
|
|
|
|
|
|
homedir => path($root)->child('www')->make_path->to_string, |
|
1035
|
|
|
|
|
|
|
datadir => path($root)->child('var')->make_path->to_string, |
|
1036
|
|
|
|
|
|
|
tempdir => path($root)->child('tmp')->make_path->to_string, |
|
1037
|
|
|
|
|
|
|
config_opts => { |
|
1038
|
|
|
|
|
|
|
noload => 1, # force disable loading config from file |
|
1039
|
|
|
|
|
|
|
defaults => { |
|
1040
|
|
|
|
|
|
|
foo => 'bar', |
|
1041
|
|
|
|
|
|
|
}, |
|
1042
|
|
|
|
|
|
|
}, |
|
1043
|
|
|
|
|
|
|
)->start(); |
|
1044
|
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
Now try to run it |
|
1046
|
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
# perl bin/myapp.pl daemon -l http://*:8080 |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
Now let's get to simplified testing |
|
1050
|
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
# mkdir t |
|
1052
|
|
|
|
|
|
|
# touch t/myapp.t |
|
1053
|
|
|
|
|
|
|
# chmod 644 t/myapp.t |
|
1054
|
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
Full L applications are easy to test, so C can be containts: |
|
1056
|
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
use strict; |
|
1058
|
|
|
|
|
|
|
use warnings; |
|
1059
|
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
use Test::More; |
|
1061
|
|
|
|
|
|
|
use Test::Mojo; |
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
use Mojo::File qw/ path /; |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
use MyApp; |
|
1066
|
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
my $root = path()->child('test')->to_string; |
|
1068
|
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
my $t = Test::Mojo->new(MyApp->new( |
|
1070
|
|
|
|
|
|
|
homedir => path($root)->child('www')->make_path->to_string, |
|
1071
|
|
|
|
|
|
|
datadir => path($root)->child('var')->make_path->to_string, |
|
1072
|
|
|
|
|
|
|
tempdir => path($root)->child('tmp')->make_path->to_string, |
|
1073
|
|
|
|
|
|
|
config_opts => { |
|
1074
|
|
|
|
|
|
|
noload => 1, # force disable loading config from file |
|
1075
|
|
|
|
|
|
|
defaults => { |
|
1076
|
|
|
|
|
|
|
foo => 'bar', |
|
1077
|
|
|
|
|
|
|
}, |
|
1078
|
|
|
|
|
|
|
}, |
|
1079
|
|
|
|
|
|
|
)); |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
subtest 'Test workflow' => sub { |
|
1082
|
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
$t->get_ok('/') |
|
1084
|
|
|
|
|
|
|
->status_is(200) |
|
1085
|
|
|
|
|
|
|
->content_like(qr/working!/, 'right content by GET /'); |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
$t->post_ok('/' => form => {'_' => time}) |
|
1088
|
|
|
|
|
|
|
->status_is(200) |
|
1089
|
|
|
|
|
|
|
->content_like(qr/working!/, 'right content by POST /'); |
|
1090
|
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
$t->get_ok('/test') |
|
1092
|
|
|
|
|
|
|
->status_is(200) |
|
1093
|
|
|
|
|
|
|
->content_like(qr/World/, 'right content by GET /test'); |
|
1094
|
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
}; |
|
1096
|
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
done_testing(); |
|
1098
|
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
Now try to test |
|
1100
|
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
# prove -lv t/myapp.t |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
And our final directory structure should be looking like this |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
MyApp |
|
1106
|
|
|
|
|
|
|
+- bin |
|
1107
|
|
|
|
|
|
|
| +- myapp.pl |
|
1108
|
|
|
|
|
|
|
+- lib |
|
1109
|
|
|
|
|
|
|
| +- MyApp.pm |
|
1110
|
|
|
|
|
|
|
+- t |
|
1111
|
|
|
|
|
|
|
| +- myapp.t |
|
1112
|
|
|
|
|
|
|
+- test |
|
1113
|
|
|
|
|
|
|
+- tmp |
|
1114
|
|
|
|
|
|
|
+- var |
|
1115
|
|
|
|
|
|
|
+- www |
|
1116
|
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
Test-driven development takes a little getting used to, but can be a very powerful tool |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
=head1 HISTORY |
|
1120
|
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
See C file |
|
1122
|
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
=head1 TO DO |
|
1124
|
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
See C file |
|
1126
|
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
1128
|
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
L, L, L, L, L, L |
|
1130
|
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1132
|
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE |
|
1134
|
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
1136
|
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
Copyright (C) 1998-2026 D&D Corporation |
|
1138
|
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
=head1 LICENSE |
|
1140
|
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
This program is distributed under the terms of the Artistic License Version 2.0 |
|
1142
|
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
See the C file or L for details |
|
1144
|
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
=cut |
|
1146
|
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
our $VERSION = '1.13'; |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
1
|
|
|
1
|
|
1098
|
use Mojo::Base 'Mojolicious'; |
|
|
1
|
|
|
|
|
13850
|
|
|
|
1
|
|
|
|
|
6
|
|
|
1150
|
|
|
|
|
|
|
|
|
1151
|
1
|
|
|
1
|
|
620734
|
use Carp qw/ carp croak /; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
120
|
|
|
1152
|
1
|
|
|
1
|
|
8
|
use POSIX qw//; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
27
|
|
|
1153
|
1
|
|
|
1
|
|
7
|
use File::Spec; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
28
|
|
|
1154
|
|
|
|
|
|
|
|
|
1155
|
1
|
|
|
1
|
|
5
|
use Mojo::URL; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
9
|
|
|
1156
|
1
|
|
|
1
|
|
42
|
use Mojo::File qw/ path /; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
61
|
|
|
1157
|
1
|
|
|
1
|
|
7
|
use Mojo::Home qw//; |
|
|
1
|
|
|
|
|
26
|
|
|
|
1
|
|
|
|
|
33
|
|
|
1158
|
1
|
|
|
1
|
|
6
|
use Mojo::Util qw/ decamelize steady_time md5_sum /; # decamelize(ref($self)) |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
100
|
|
|
1159
|
1
|
|
|
1
|
|
7
|
use Mojo::Loader qw/ load_class /; |
|
|
1
|
|
|
|
|
13
|
|
|
|
1
|
|
|
|
|
59
|
|
|
1160
|
1
|
|
|
1
|
|
917
|
use Mojo::Server::Prefork; |
|
|
1
|
|
|
|
|
4891
|
|
|
|
1
|
|
|
|
|
14
|
|
|
1161
|
|
|
|
|
|
|
|
|
1162
|
1
|
|
|
1
|
|
1051
|
use Acrux::Util qw/ color parse_time_offset randchars /; |
|
|
1
|
|
|
|
|
12167
|
|
|
|
1
|
|
|
|
|
151
|
|
|
1163
|
1
|
|
|
1
|
|
731
|
use Acrux::RefUtil qw/ as_array_ref as_hash_ref isnt_void is_true_flag /; |
|
|
1
|
|
|
|
|
2915
|
|
|
|
1
|
|
|
|
|
141
|
|
|
1164
|
|
|
|
|
|
|
|
|
1165
|
1
|
|
|
|
|
467
|
use WWW::Suffit::Const qw/ |
|
1166
|
|
|
|
|
|
|
:general :security :session :dir :server |
|
1167
|
|
|
|
|
|
|
AUTHDBFILE JWT_REGEXP |
|
1168
|
1
|
|
|
1
|
|
949
|
/; |
|
|
1
|
|
|
|
|
3544
|
|
|
1169
|
1
|
|
|
1
|
|
894
|
use WWW::Suffit::Cache; |
|
|
1
|
|
|
|
|
2042
|
|
|
|
1
|
|
|
|
|
7
|
|
|
1170
|
1
|
|
|
1
|
|
852
|
use WWW::Suffit::RSA; |
|
|
1
|
|
|
|
|
6326
|
|
|
|
1
|
|
|
|
|
11
|
|
|
1171
|
1
|
|
|
1
|
|
1021
|
use WWW::Suffit::JWT; |
|
|
1
|
|
|
|
|
4638
|
|
|
|
1
|
|
|
|
|
10
|
|
|
1172
|
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
use constant { |
|
1174
|
1
|
|
|
|
|
7501
|
MAX_HISTORY_SIZE => 25, |
|
1175
|
|
|
|
|
|
|
DEFAULT_SERVER_URL => 'http://127.0.0.1:8080', |
|
1176
|
|
|
|
|
|
|
DEFAULT_SERVER_ADDR => '*', |
|
1177
|
|
|
|
|
|
|
DEFAULT_SERVER_PORT => 8080, |
|
1178
|
1
|
|
|
1
|
|
79
|
}; |
|
|
1
|
|
|
|
|
3
|
|
|
1179
|
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
# Common attributes |
|
1181
|
|
|
|
|
|
|
has 'project_name'; # Anonymous |
|
1182
|
|
|
|
|
|
|
has 'project_version'; # 1.00 |
|
1183
|
|
|
|
|
|
|
has 'server_url'; # http://127.0.0.1:8080 |
|
1184
|
|
|
|
|
|
|
has 'server_addr'; # * (0.0.0.0) |
|
1185
|
|
|
|
|
|
|
has 'server_port'; # 8080 |
|
1186
|
|
|
|
|
|
|
has 'debugmode'; # 0 |
|
1187
|
|
|
|
|
|
|
has 'configobj'; # Config::General object |
|
1188
|
|
|
|
|
|
|
has 'acruxconfig'; # Acrux::Config object |
|
1189
|
|
|
|
|
|
|
has 'cache' => sub { WWW::Suffit::Cache->new }; |
|
1190
|
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
# Files and directories |
|
1192
|
|
|
|
|
|
|
has 'documentroot'; # /var/www/ |
|
1193
|
|
|
|
|
|
|
has 'homedir'; # /usr/share/ |
|
1194
|
|
|
|
|
|
|
has 'datadir'; # /var/lib/ |
|
1195
|
|
|
|
|
|
|
has 'tempdir'; # /tmp/ |
|
1196
|
|
|
|
|
|
|
has 'logfile'; # /var/log/.log |
|
1197
|
|
|
|
|
|
|
has 'pidfile'; # /run/.pid |
|
1198
|
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
# Logging |
|
1200
|
|
|
|
|
|
|
has 'loglevel' => 'warn'; # warn |
|
1201
|
|
|
|
|
|
|
has 'max_history_size' => MAX_HISTORY_SIZE; |
|
1202
|
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
# Security |
|
1204
|
|
|
|
|
|
|
has 'mysecret' => DEFAULT_SECRET; # Secret |
|
1205
|
|
|
|
|
|
|
has 'private_key' => ''; # Private RSA key |
|
1206
|
|
|
|
|
|
|
has 'public_key' => ''; # Public RSA key |
|
1207
|
|
|
|
|
|
|
has 'rsa_keysize' => sub { shift->conf->latest("/rsa_keysize") }; |
|
1208
|
|
|
|
|
|
|
has 'trustedproxies' => sub { [grep {length} @{(shift->conf->list("/trustedproxy"))}] }; |
|
1209
|
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
# Prefork |
|
1211
|
|
|
|
|
|
|
has 'clients' => sub { shift->conf->latest("/clients") || SERVER_MAX_CLIENTS }; # 10000 |
|
1212
|
|
|
|
|
|
|
has 'requests' => sub { shift->conf->latest("/requests") || SERVER_MAX_REQUESTS}; # 100 |
|
1213
|
|
|
|
|
|
|
has 'accepts' => sub { shift->conf->latest("/accepts") }; # SERVER_ACCEPTS is 0 -- by default not specified |
|
1214
|
|
|
|
|
|
|
has 'spare' => sub { shift->conf->latest("/spare") || SERVER_SPARE }; # 2 |
|
1215
|
|
|
|
|
|
|
has 'workers' => sub { shift->conf->latest("/workers") || SERVER_WORKERS }; # 4 |
|
1216
|
|
|
|
|
|
|
has 'reload_sig' => sub { shift->conf->latest("/reload_sig") // 'USR2' }; |
|
1217
|
|
|
|
|
|
|
has 'no_daemonize'; |
|
1218
|
|
|
|
|
|
|
has 'uid'; |
|
1219
|
|
|
|
|
|
|
has 'gid'; |
|
1220
|
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
# Startup options as attributes |
|
1222
|
|
|
|
|
|
|
has [qw/all_features init_rsa_keys init_authdb init_api_routes init_user_routes init_admin_routes/]; |
|
1223
|
|
|
|
|
|
|
has 'config_opts' => sub { {} }; |
|
1224
|
|
|
|
|
|
|
has 'syslog_opts' => sub { {} }; |
|
1225
|
|
|
|
|
|
|
has 'authdb_opts' => sub { {} }; |
|
1226
|
|
|
|
|
|
|
has 'api_routes_opts' => sub { {} }; |
|
1227
|
|
|
|
|
|
|
has 'user_routes_opts' => sub { {} }; |
|
1228
|
|
|
|
|
|
|
has 'admin_routes_opts' => sub { {} }; |
|
1229
|
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
sub raise { |
|
1231
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1232
|
0
|
|
|
|
|
|
say STDERR color "bright_red" => @_; |
|
1233
|
0
|
0
|
|
|
|
|
$self->log->error((scalar(@_) == 1) ? shift : sprintf(shift, @_)); |
|
1234
|
0
|
|
|
|
|
|
exit 1; |
|
1235
|
|
|
|
|
|
|
} |
|
1236
|
|
|
|
|
|
|
sub startup { |
|
1237
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1238
|
0
|
0
|
|
|
|
|
my $opts = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}; |
|
|
0
|
0
|
|
|
|
|
|
|
1239
|
0
|
0
|
|
|
|
|
$self->project_name(ref($self)) unless defined $self->project_name; |
|
1240
|
0
|
0
|
|
|
|
|
$self->project_version($self->VERSION) unless defined $self->project_version; |
|
1241
|
0
|
0
|
|
|
|
|
$self->raise("Incorrect `project_name`") unless $self->project_name; |
|
1242
|
0
|
0
|
|
|
|
|
$self->raise("Incorrect `project_version`") unless $self->project_version; |
|
1243
|
0
|
|
|
|
|
|
unshift @{$self->plugins->namespaces}, 'WWW::Suffit::Plugin'; # Add another namespace to load plugins from |
|
|
0
|
|
|
|
|
|
|
|
1244
|
0
|
|
|
|
|
|
push @{$self->routes->namespaces}, 'WWW::Suffit::Server'; # Add Server routes namespace |
|
|
0
|
|
|
|
|
|
|
|
1245
|
0
|
|
0
|
|
|
|
my $all_features = is_true_flag($opts->{all_features} // $self->all_features); # on/off |
|
1246
|
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
# Get all ConfigGeneral configuration attributes |
|
1248
|
0
|
|
0
|
|
|
|
my $config_opts = as_hash_ref($opts->{config_opts} || $self->config_opts) || {}; |
|
1249
|
0
|
0
|
|
|
|
|
if (my $configobj = $self->configobj) { |
|
1250
|
0
|
0
|
|
|
|
|
$self->raise("The `configobj` must be Config::General object") |
|
1251
|
|
|
|
|
|
|
unless ref($configobj) eq 'Config::General'; |
|
1252
|
0
|
|
|
|
|
|
$self->config($configobj->getall); # Set config hash |
|
1253
|
0
|
0
|
|
|
|
|
$config_opts->{noload} = 1 unless exists $config_opts->{noload}; |
|
1254
|
|
|
|
|
|
|
} |
|
1255
|
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
# Get all Acrux configuration attributes |
|
1257
|
0
|
0
|
|
|
|
|
if (my $acruxconfig = $self->acruxconfig) { |
|
1258
|
0
|
0
|
|
|
|
|
$self->raise("The `acruxconfig` must be Acrux::Config object") |
|
1259
|
|
|
|
|
|
|
unless ref($acruxconfig) eq 'Acrux::Config'; |
|
1260
|
0
|
|
|
|
|
|
$self->config($acruxconfig->config); # Set config hash |
|
1261
|
0
|
0
|
|
|
|
|
$config_opts->{noload} = 1 unless exists $config_opts->{noload}; |
|
1262
|
|
|
|
|
|
|
} |
|
1263
|
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
# Init ConfigGeneral plugin |
|
1265
|
0
|
0
|
|
|
|
|
unless (exists($config_opts->{noload})) { $config_opts->{noload} = 0 } |
|
|
0
|
|
|
|
|
|
|
|
1266
|
0
|
0
|
|
|
|
|
unless (exists($config_opts->{defaults})) { $config_opts->{defaults} = as_hash_ref($self->config) } |
|
|
0
|
|
|
|
|
|
|
|
1267
|
0
|
|
|
|
|
|
$self->plugin('ConfigGeneral' => $config_opts); |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
# Syslog |
|
1270
|
0
|
|
0
|
|
|
|
my $syslog_opts = as_hash_ref($opts->{syslog_opts} || $self->syslog_opts) || {}; |
|
1271
|
0
|
0
|
0
|
|
|
|
my $syslogen = ($self->conf->latest('/log') && $self->conf->latest('/log') =~ /syslog/i) ? 1 : 0; |
|
1272
|
0
|
0
|
|
|
|
|
unless (exists($syslog_opts->{enable})) { $syslog_opts->{enable} = $syslogen }; |
|
|
0
|
|
|
|
|
|
|
|
1273
|
0
|
|
|
|
|
|
$self->plugin('Syslog' => $syslog_opts); |
|
1274
|
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
# PRE REQUIRED Plugins |
|
1276
|
0
|
|
|
|
|
|
$self->plugin('CommonHelpers'); |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
# Logging |
|
1279
|
0
|
|
0
|
|
|
|
$self->log->level($self->loglevel || ($self->debugmode ? "debug" : "warn")) |
|
|
|
|
0
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
->max_history_size($self->max_history_size || MAX_HISTORY_SIZE); |
|
1281
|
0
|
0
|
|
|
|
|
$self->log->path($self->logfile) if $self->logfile; |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
# Helpers |
|
1284
|
0
|
|
|
|
|
|
$self->helper('token' => \&_getToken); |
|
1285
|
0
|
|
|
|
|
|
$self->helper('jwt' => \&_getJWT); |
|
1286
|
0
|
|
|
|
|
|
$self->helper('clientid' => \&_genClientId); |
|
1287
|
0
|
|
|
|
|
|
$self->helper('gen_cachekey'=> \&_genCacheKey); |
|
1288
|
0
|
|
|
|
|
|
$self->helper('gen_rsakeys' => \&_genRSAKeys); |
|
1289
|
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
# DataDir (variable data, caches, temp files and etc.) -- /var/lib/ |
|
1291
|
0
|
0
|
|
|
|
|
$self->datadir(path(SHAREDSTATEDIR, $self->moniker)->to_string()) unless defined $self->datadir; |
|
1292
|
0
|
0
|
|
|
|
|
$self->raise("Startup error! Data directory %s not exists", $self->datadir) unless -e $self->datadir; |
|
1293
|
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
# HomeDir (shared static files, default templates and etc.) -- /usr/share/ |
|
1295
|
0
|
0
|
|
|
|
|
$self->homedir(path(DATADIR, $self->moniker)->to_string()) unless defined $self->homedir; |
|
1296
|
0
|
|
|
|
|
|
$self->home(Mojo::Home->new($self->homedir)); # Switch to installable home directory |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
# DocumentRoot (user's static data) -- /var/www/ |
|
1299
|
0
|
|
|
|
|
|
my $documentroot = path(WEBDIR, $self->moniker)->to_string(); |
|
1300
|
0
|
0
|
|
|
|
|
$self->documentroot(-e $documentroot ? $documentroot : $self->homedir) unless defined $self->documentroot; |
|
|
|
0
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
# Reset static dirs |
|
1303
|
0
|
|
|
|
|
|
$self->static->paths()->[0] = $self->documentroot; #unshift @{$static->paths}, '/home/sri/themes/blue/public'; |
|
1304
|
0
|
0
|
|
|
|
|
$self->static->paths()->[1] = $self->homedir if $self->documentroot ne $self->homedir; |
|
1305
|
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
# Add renderer path (templates) |
|
1307
|
0
|
|
|
|
|
|
push @{$self->renderer->paths}, $self->documentroot, $self->homedir; |
|
|
0
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
# Remove system favicon file |
|
1310
|
0
|
|
|
|
|
|
delete $self->static->extra->{'favicon.ico'}; |
|
1311
|
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
# Set secret |
|
1313
|
0
|
0
|
|
|
|
|
$self->mysecret($self->conf->latest("/secret")) if $self->conf->latest("/secret"); |
|
1314
|
0
|
|
|
|
|
|
$self->secrets([$self->mysecret]); |
|
1315
|
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
# Init RSA keys (optional) |
|
1317
|
0
|
0
|
0
|
|
|
|
if ($all_features || is_true_flag($opts->{init_rsa_keys} // $self->init_rsa_keys)) { |
|
|
|
|
0
|
|
|
|
|
|
1318
|
0
|
|
0
|
|
|
|
my $private_key_file = $self->conf->latest("/privatekeyfile") || path($self->datadir, PRIVATEKEYFILE)->to_string; |
|
1319
|
0
|
|
0
|
|
|
|
my $public_key_file = $self->conf->latest("/publickeyfile") || path($self->datadir, PUBLICKEYFILE)->to_string; |
|
1320
|
0
|
0
|
0
|
|
|
|
if ((!-r $private_key_file) and (!-r $public_key_file)) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1321
|
0
|
|
|
|
|
|
my $rsa = WWW::Suffit::RSA->new(); |
|
1322
|
0
|
0
|
|
|
|
|
$rsa->key_size($self->rsa_keysize) if $self->rsa_keysize; |
|
1323
|
0
|
|
|
|
|
|
$rsa->keygen; |
|
1324
|
0
|
|
|
|
|
|
path($private_key_file)->spew($rsa->private_key)->chmod(0600); |
|
1325
|
0
|
|
|
|
|
|
$self->private_key($rsa->private_key); |
|
1326
|
0
|
|
|
|
|
|
path($public_key_file)->spew($rsa->public_key)->chmod(0644); |
|
1327
|
0
|
|
|
|
|
|
$self->public_key($rsa->public_key); |
|
1328
|
|
|
|
|
|
|
} elsif (!-r $private_key_file) { |
|
1329
|
0
|
|
|
|
|
|
$self->raise("Can't read RSA private key file: \"%s\"", $private_key_file); |
|
1330
|
|
|
|
|
|
|
} elsif (!-r $public_key_file) { |
|
1331
|
0
|
|
|
|
|
|
$self->raise("Can't read RSA public key file: \"%s\"", $public_key_file); |
|
1332
|
|
|
|
|
|
|
} else { |
|
1333
|
0
|
|
|
|
|
|
$self->private_key(path($private_key_file)->slurp); |
|
1334
|
0
|
|
|
|
|
|
$self->public_key(path($public_key_file)->slurp) |
|
1335
|
|
|
|
|
|
|
} |
|
1336
|
|
|
|
|
|
|
} |
|
1337
|
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
# Init AuthDB plugin (optional) |
|
1339
|
0
|
0
|
0
|
|
|
|
if ($all_features || is_true_flag($opts->{init_authdb} // $self->init_authdb)) { |
|
|
|
|
0
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
#_load_module("WWW::Suffit::AuthDB"); |
|
1341
|
0
|
|
0
|
|
|
|
my $authdb_opts = as_hash_ref($opts->{authdb_opts} || $self->authdb_opts) || {}; |
|
1342
|
0
|
|
|
|
|
|
my $authdb_file = path($self->datadir, AUTHDBFILE)->to_string; |
|
1343
|
|
|
|
|
|
|
my $authdb_uri = $authdb_opts->{uri} || $authdb_opts->{url} |
|
1344
|
0
|
|
0
|
|
|
|
|| $self->conf->latest("/authdburl") || $self->conf->latest("/authdburi") |
|
1345
|
|
|
|
|
|
|
|| qq{sqlite://$authdb_file?sqlite_unicode=1}; |
|
1346
|
0
|
|
0
|
|
|
|
my $cacheexpiration = $self->conf->latest("/authdbcacheexpire") || $self->conf->latest("/authdbcacheexpiration"); |
|
1347
|
|
|
|
|
|
|
$self->plugin('AuthDB' => { |
|
1348
|
|
|
|
|
|
|
ds => $authdb_uri, |
|
1349
|
|
|
|
|
|
|
cached => $authdb_opts->{cachedconnection} // $self->conf->latest("/authdbcachedconnection") // 'on', |
|
1350
|
|
|
|
|
|
|
expiration => $authdb_opts->{cacheexpire} || $authdb_opts->{cacheexpiration} || |
|
1351
|
|
|
|
|
|
|
(defined($cacheexpiration) ? parse_time_offset($cacheexpiration) : undef), |
|
1352
|
|
|
|
|
|
|
max_keys => $authdb_opts->{cachemaxkeys} || $self->conf->latest("/authdbcachemaxkeys"), |
|
1353
|
0
|
|
0
|
|
|
|
sourcefile => $authdb_opts->{sourcefile} || $self->conf->latest("/authdbsourcefile"), |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
}); |
|
1355
|
0
|
|
|
|
|
|
$self->authdb->with_roles(qw/+CRUD +AAA/); |
|
1356
|
|
|
|
|
|
|
#$self->log->info(sprintf("AuthDB URI: \"%s\"", $authdb_uri)); |
|
1357
|
|
|
|
|
|
|
} |
|
1358
|
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
# Set API routes plugin (optional) |
|
1360
|
|
|
|
|
|
|
$self->plugin('API' => as_hash_ref($opts->{api_routes_opts} || $self->api_routes_opts) || {}) |
|
1361
|
0
|
0
|
0
|
|
|
|
if $all_features || is_true_flag($opts->{init_api_routes} // $self->init_api_routes); |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
$self->plugin('API::User' => as_hash_ref($opts->{user_routes_opts} || $self->user_routes_opts) || {}) |
|
1363
|
0
|
0
|
0
|
|
|
|
if $all_features || is_true_flag($opts->{init_user_routes} // $self->init_user_routes); |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
$self->plugin('API::Admin' => as_hash_ref($opts->{admin_routes_opts} || $self->admin_routes_opts) || {}) |
|
1365
|
0
|
0
|
0
|
|
|
|
if $all_features || is_true_flag($opts->{init_admin_routes} // $self->init_admin_routes); |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
# Hooks |
|
1368
|
|
|
|
|
|
|
$self->hook(before_dispatch => sub { |
|
1369
|
0
|
|
|
0
|
|
|
my $c = shift; |
|
1370
|
0
|
|
|
|
|
|
$c->res->headers->server(sprintf("%s/%s", $self->project_name, $self->project_version)); # Set Server header |
|
1371
|
0
|
|
|
|
|
|
}); |
|
1372
|
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
# Init hook |
|
1374
|
0
|
|
|
|
|
|
$self->init; |
|
1375
|
|
|
|
|
|
|
|
|
1376
|
0
|
|
|
|
|
|
return $self; |
|
1377
|
|
|
|
|
|
|
} |
|
1378
|
|
|
|
0
|
1
|
|
sub init { } # Overload it |
|
1379
|
|
|
|
|
|
|
sub reload { # Reload hook |
|
1380
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1381
|
0
|
|
|
|
|
|
$self->log->warn("Request for reload $$"); |
|
1382
|
0
|
|
|
|
|
|
return 1; # 1 - ok; 0 - error :( |
|
1383
|
|
|
|
|
|
|
} |
|
1384
|
|
|
|
|
|
|
sub listeners { |
|
1385
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1386
|
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
# Resilver cert file |
|
1388
|
|
|
|
|
|
|
my $_resolve_cert_file = sub { |
|
1389
|
0
|
|
|
0
|
|
|
my $f = shift; |
|
1390
|
0
|
0
|
|
|
|
|
return $f if File::Spec->file_name_is_absolute($f); |
|
1391
|
0
|
|
|
|
|
|
return File::Spec->catfile(SYSCONFDIR, $self->moniker, $f); |
|
1392
|
0
|
|
|
|
|
|
}; |
|
1393
|
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
# Master URL |
|
1395
|
0
|
|
0
|
|
|
|
my $url = Mojo::URL->new($self->server_url || DEFAULT_SERVER_URL); |
|
1396
|
0
|
0
|
0
|
|
|
|
my $host = $self->server_url ? $url->host : ($self->server_addr || DEFAULT_SERVER_ADDR); |
|
1397
|
0
|
0
|
0
|
|
|
|
my $port = $self->server_url ? $url->port : ($self->server_port || DEFAULT_SERVER_PORT); |
|
1398
|
0
|
|
0
|
|
|
|
$url->host($self->conf->latest("/listenaddr") || $host); |
|
1399
|
0
|
|
0
|
|
|
|
$url->port($self->conf->latest("/listenport") || $port); |
|
1400
|
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
# Added TLS parameters |
|
1402
|
0
|
0
|
|
|
|
|
if (is_true_flag($self->conf->latest("/tls"))) { |
|
1403
|
0
|
|
|
|
|
|
$url->scheme('https'); |
|
1404
|
0
|
|
|
|
|
|
foreach my $k (qw/ciphers version verify fd reuse single_accept/) { |
|
1405
|
0
|
|
0
|
|
|
|
my $v = $self->conf->latest("/tls_$k") // ''; |
|
1406
|
0
|
0
|
|
|
|
|
next unless length $v; |
|
1407
|
0
|
0
|
0
|
|
|
|
$v ||= '0x00' if $k eq 'verify'; |
|
1408
|
0
|
|
|
|
|
|
$url->query->merge($k, $v); |
|
1409
|
|
|
|
|
|
|
} |
|
1410
|
0
|
|
|
|
|
|
foreach my $k (qw/ca cert key/) { |
|
1411
|
0
|
|
0
|
|
|
|
my $v = $self->conf->latest("/tls_$k") // ''; |
|
1412
|
0
|
0
|
|
|
|
|
next unless length $v; |
|
1413
|
0
|
|
|
|
|
|
my $file = $_resolve_cert_file->($v); |
|
1414
|
0
|
0
|
0
|
|
|
|
$self->raise("Can't load file \"%s\"", $file) unless -e $file and -r $file; |
|
1415
|
0
|
|
|
|
|
|
$url->query->merge($k, $file); |
|
1416
|
|
|
|
|
|
|
} |
|
1417
|
|
|
|
|
|
|
} |
|
1418
|
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
# Make master listener |
|
1420
|
0
|
|
|
|
|
|
my $listener = $url->to_unsafe_string; |
|
1421
|
|
|
|
|
|
|
|
|
1422
|
|
|
|
|
|
|
# Make additional (slave) listeners |
|
1423
|
0
|
|
|
|
|
|
my @listeners = (); |
|
1424
|
0
|
|
|
|
|
|
push @listeners, $listener; # Ass master listener |
|
1425
|
0
|
|
0
|
|
|
|
my $slaves = as_array_ref($self->conf->list("/listenurl")) // []; |
|
1426
|
0
|
0
|
|
|
|
|
push @listeners, @$slaves if isnt_void($slaves); |
|
1427
|
|
|
|
|
|
|
|
|
1428
|
|
|
|
|
|
|
# Return listeners |
|
1429
|
0
|
|
|
|
|
|
return [@listeners]; |
|
1430
|
|
|
|
|
|
|
} |
|
1431
|
|
|
|
|
|
|
sub preforked_run { |
|
1432
|
0
|
|
|
0
|
1
|
|
my $self = shift; # app |
|
1433
|
0
|
|
0
|
|
|
|
my $dash_k = shift || ''; |
|
1434
|
0
|
0
|
|
|
|
|
my $opts = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}; |
|
|
0
|
0
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
# Dash k |
|
1437
|
|
|
|
|
|
|
$self->raise("Incorrect LSB command! Please use start, status, stop, restart or reload") |
|
1438
|
0
|
0
|
|
|
|
|
unless (grep {$_ eq $dash_k} qw/start status stop restart reload/); |
|
|
0
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
# Mojolicious Prefork server |
|
1441
|
0
|
|
|
|
|
|
my $prefork = Mojo::Server::Prefork->new( app => $self ); |
|
1442
|
0
|
0
|
|
|
|
|
$prefork->pid_file($self->pidfile) if length $self->pidfile; |
|
1443
|
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
# Hypnotoad Pre-fork settings |
|
1445
|
0
|
0
|
|
|
|
|
$prefork->max_clients($self->clients) if defined $self->clients; |
|
1446
|
0
|
0
|
|
|
|
|
$prefork->max_requests($self->requests) if defined $self->requests; |
|
1447
|
0
|
0
|
|
|
|
|
$prefork->accepts($self->accepts) if defined $self->accepts; |
|
1448
|
0
|
0
|
|
|
|
|
$prefork->spare($self->spare) if defined $self->spare; |
|
1449
|
0
|
0
|
|
|
|
|
$prefork->workers($self->workers) if defined $self->workers; |
|
1450
|
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
# Listener |
|
1452
|
0
|
|
|
|
|
|
$prefork->listen($self->listeners); |
|
1453
|
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
# Working with Dash k |
|
1455
|
0
|
|
|
|
|
|
my $upgrade = 0; |
|
1456
|
0
|
|
|
|
|
|
my $reload = 0; |
|
1457
|
0
|
|
|
|
|
|
my $upgrade_timeout = SERVER_UPGRADE_TIMEOUT; # 30 |
|
1458
|
0
|
0
|
|
|
|
|
if ($dash_k eq 'start') { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1459
|
0
|
0
|
|
|
|
|
if (my $pid = $prefork->check_pid()) { |
|
1460
|
0
|
|
|
|
|
|
print "Already running $pid\n"; |
|
1461
|
0
|
|
|
|
|
|
exit 0; |
|
1462
|
|
|
|
|
|
|
} |
|
1463
|
|
|
|
|
|
|
} elsif ($dash_k eq 'stop') { |
|
1464
|
0
|
0
|
|
|
|
|
if (my $pid = $prefork->check_pid()) { |
|
1465
|
0
|
|
|
|
|
|
kill 'QUIT', $pid; |
|
1466
|
0
|
|
|
|
|
|
print "Stopping $pid\n"; |
|
1467
|
|
|
|
|
|
|
} else { |
|
1468
|
0
|
|
|
|
|
|
print "Not running\n"; |
|
1469
|
|
|
|
|
|
|
} |
|
1470
|
0
|
|
|
|
|
|
exit 0; |
|
1471
|
|
|
|
|
|
|
} elsif ($dash_k eq 'restart') { |
|
1472
|
0
|
0
|
|
|
|
|
if (my $pid = $prefork->check_pid()) { |
|
1473
|
0
|
|
0
|
|
|
|
$upgrade ||= steady_time; |
|
1474
|
0
|
|
|
|
|
|
kill 'QUIT', $pid; |
|
1475
|
0
|
|
|
|
|
|
my $up = $upgrade_timeout; |
|
1476
|
0
|
|
|
|
|
|
while (kill 0, $pid) { |
|
1477
|
0
|
|
|
|
|
|
$up--; |
|
1478
|
0
|
|
|
|
|
|
sleep 1; |
|
1479
|
|
|
|
|
|
|
} |
|
1480
|
0
|
0
|
|
|
|
|
die("Can't stop $pid") if $up <= 0; |
|
1481
|
0
|
|
|
|
|
|
print "Stopping $pid\n"; |
|
1482
|
0
|
|
|
|
|
|
$upgrade = 0; |
|
1483
|
|
|
|
|
|
|
} |
|
1484
|
|
|
|
|
|
|
} elsif ($dash_k eq 'reload') { |
|
1485
|
0
|
|
|
|
|
|
my $pid = $prefork->check_pid(); |
|
1486
|
0
|
0
|
|
|
|
|
if ($pid) { |
|
1487
|
0
|
0
|
|
|
|
|
if (my $s = $self->reload_sig) { |
|
1488
|
|
|
|
|
|
|
# Start hot deployment |
|
1489
|
0
|
|
|
|
|
|
kill $s, $pid; # 'USR2' |
|
1490
|
0
|
|
|
|
|
|
print "Reloading $pid\n"; |
|
1491
|
0
|
|
|
|
|
|
exit 0; |
|
1492
|
|
|
|
|
|
|
} |
|
1493
|
|
|
|
|
|
|
} |
|
1494
|
0
|
|
|
|
|
|
print "Not running\n"; |
|
1495
|
|
|
|
|
|
|
} else { # status |
|
1496
|
0
|
0
|
|
|
|
|
if (my $pid = $prefork->check_pid()) { |
|
1497
|
0
|
|
|
|
|
|
print "Running $pid\n"; |
|
1498
|
|
|
|
|
|
|
} else { |
|
1499
|
0
|
|
|
|
|
|
print "Not running\n"; |
|
1500
|
|
|
|
|
|
|
} |
|
1501
|
0
|
|
|
|
|
|
exit 0; |
|
1502
|
|
|
|
|
|
|
} |
|
1503
|
|
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
# Listen USR2 (reload) |
|
1505
|
0
|
0
|
|
|
|
|
if (my $s = $self->reload_sig) { |
|
1506
|
0
|
|
0
|
0
|
|
|
$SIG{$s} = sub { $upgrade ||= steady_time }; |
|
|
0
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
} |
|
1508
|
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
# Set system hooks |
|
1510
|
|
|
|
|
|
|
$prefork->on(wait => sub { # Manage (every 1 sec) |
|
1511
|
0
|
|
|
0
|
|
|
my $this = shift; # Prefork object |
|
1512
|
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
# Upgrade |
|
1514
|
0
|
0
|
|
|
|
|
if ($upgrade) { |
|
1515
|
|
|
|
|
|
|
#$this->app->log->debug(">>> " . $this->healthy() || '?'); |
|
1516
|
0
|
0
|
|
|
|
|
unless ($reload) { |
|
1517
|
0
|
|
|
|
|
|
$reload = 1; # Off next reloading |
|
1518
|
0
|
0
|
|
|
|
|
if ($this->app->reload()) { |
|
1519
|
0
|
|
|
|
|
|
$reload = 0; |
|
1520
|
0
|
|
|
|
|
|
$upgrade = 0; |
|
1521
|
0
|
|
|
|
|
|
return 1; |
|
1522
|
|
|
|
|
|
|
} |
|
1523
|
|
|
|
|
|
|
} |
|
1524
|
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
# Timeout |
|
1526
|
0
|
0
|
|
|
|
|
if (($upgrade + $upgrade_timeout) <= steady_time()) { |
|
1527
|
0
|
|
|
|
|
|
kill 'KILL', $$; |
|
1528
|
0
|
|
|
|
|
|
$upgrade = 0; |
|
1529
|
|
|
|
|
|
|
} |
|
1530
|
|
|
|
|
|
|
} |
|
1531
|
0
|
|
|
|
|
|
}); |
|
1532
|
|
|
|
|
|
|
$prefork->on(finish => sub { # Finish |
|
1533
|
0
|
|
|
0
|
|
|
my $this = shift; # Prefork object |
|
1534
|
0
|
|
|
|
|
|
my $graceful = shift; |
|
1535
|
0
|
0
|
|
|
|
|
$this->app->log->debug($graceful ? 'Graceful server shutdown' : 'Server shutdown'); |
|
1536
|
0
|
|
|
|
|
|
}); |
|
1537
|
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
# Set GID and UID |
|
1539
|
0
|
|
|
|
|
|
if (IS_ROOT) { |
|
1540
|
0
|
0
|
|
|
|
|
if (my $gid = $self->gid) { |
|
1541
|
0
|
0
|
|
|
|
|
POSIX::setgid($gid) or $self->raise("setgid %s failed - %s", $gid, $!); |
|
1542
|
0
|
|
|
|
|
|
$) = "$gid $gid"; # this calls setgroups |
|
1543
|
0
|
0
|
0
|
|
|
|
$self->raise("detected strange gid") if !($( eq "$gid $gid" && $) eq "$gid $gid"); # just to be sure |
|
1544
|
|
|
|
|
|
|
} |
|
1545
|
0
|
0
|
|
|
|
|
if (my $uid = $self->uid) { |
|
1546
|
0
|
0
|
|
|
|
|
POSIX::setuid($uid) or $self->raise("setuid %s failed - %s", $uid, $!); |
|
1547
|
0
|
0
|
0
|
|
|
|
$self->raise("detected strange uid") if !($< == $uid && $> == $uid); # just to be sure |
|
1548
|
|
|
|
|
|
|
} |
|
1549
|
|
|
|
|
|
|
} |
|
1550
|
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
# PreRun callback |
|
1552
|
0
|
0
|
|
|
|
|
if (my $prerun = $opts->{prerun}) { |
|
1553
|
0
|
0
|
|
|
|
|
$prerun->($self, $prefork) if ref($prerun) eq 'CODE'; |
|
1554
|
|
|
|
|
|
|
} |
|
1555
|
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
# Daemonize |
|
1557
|
0
|
0
|
|
|
|
|
$prefork->daemonize() unless $self->no_daemonize; |
|
1558
|
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
# Running |
|
1560
|
0
|
|
|
|
|
|
print "Running\n"; |
|
1561
|
0
|
|
|
|
|
|
$prefork->run(); |
|
1562
|
|
|
|
|
|
|
} |
|
1563
|
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
sub _load_module { |
|
1565
|
0
|
|
|
0
|
|
|
my $module = shift; |
|
1566
|
0
|
0
|
|
|
|
|
if (my $e = load_class($module)) { |
|
1567
|
0
|
0
|
|
|
|
|
croak ref($e) ? "Exception: $e" : "The module $module not found!"; |
|
1568
|
|
|
|
|
|
|
} |
|
1569
|
0
|
|
|
|
|
|
return 1; |
|
1570
|
|
|
|
|
|
|
} |
|
1571
|
|
|
|
|
|
|
sub _getToken { |
|
1572
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1573
|
|
|
|
|
|
|
|
|
1574
|
|
|
|
|
|
|
# Get authorization string from request header |
|
1575
|
0
|
|
0
|
|
|
|
my $token = $self->req->headers->header(TOKEN_HEADER_NAME) // ''; |
|
1576
|
0
|
0
|
|
|
|
|
if (length($token)) { |
|
1577
|
0
|
0
|
|
|
|
|
return '' unless $token =~ JWT_REGEXP; |
|
1578
|
0
|
|
|
|
|
|
return $token; |
|
1579
|
|
|
|
|
|
|
} |
|
1580
|
|
|
|
|
|
|
|
|
1581
|
|
|
|
|
|
|
# Get authorization string from request authorization header |
|
1582
|
|
|
|
|
|
|
my $auth_string = $self->req->headers->authorization |
|
1583
|
|
|
|
|
|
|
|| $self->req->env->{'X_HTTP_AUTHORIZATION'} |
|
1584
|
0
|
|
0
|
|
|
|
|| $self->req->env->{'HTTP_AUTHORIZATION'} |
|
1585
|
|
|
|
|
|
|
|| ''; |
|
1586
|
0
|
0
|
|
|
|
|
if ($auth_string =~ /(Bearer|Token)\s+(.*)/) { |
|
1587
|
0
|
|
|
|
|
|
$token = $2; |
|
1588
|
0
|
0
|
0
|
|
|
|
return '' unless length($token) && $token =~ JWT_REGEXP; |
|
1589
|
0
|
|
|
|
|
|
return $token; |
|
1590
|
|
|
|
|
|
|
} |
|
1591
|
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
# In debug mode see "Token" config directive |
|
1593
|
0
|
0
|
0
|
|
|
|
if ($self->app->debugmode and $token = $self->conf->latest("/token")) { |
|
1594
|
0
|
0
|
|
|
|
|
return '' unless $token =~ JWT_REGEXP; |
|
1595
|
|
|
|
|
|
|
} |
|
1596
|
|
|
|
|
|
|
|
|
1597
|
0
|
|
0
|
|
|
|
return $token // ''; |
|
1598
|
|
|
|
|
|
|
} |
|
1599
|
|
|
|
|
|
|
sub _getJWT { |
|
1600
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1601
|
0
|
|
|
|
|
|
return WWW::Suffit::JWT->new( |
|
1602
|
|
|
|
|
|
|
secret => $self->app->mysecret, |
|
1603
|
|
|
|
|
|
|
private_key => $self->app->private_key, |
|
1604
|
|
|
|
|
|
|
public_key => $self->app->public_key, |
|
1605
|
|
|
|
|
|
|
); |
|
1606
|
|
|
|
|
|
|
} |
|
1607
|
|
|
|
|
|
|
sub _genCacheKey { |
|
1608
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1609
|
0
|
|
0
|
|
|
|
my $len = shift || 12; |
|
1610
|
0
|
|
|
|
|
|
return randchars($len); |
|
1611
|
|
|
|
|
|
|
} |
|
1612
|
|
|
|
|
|
|
sub _genRSAKeys { |
|
1613
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1614
|
0
|
|
0
|
|
|
|
my $key_size = shift || $self->app->rsa_keysize; |
|
1615
|
0
|
|
|
|
|
|
my $rsa = WWW::Suffit::RSA->new(); |
|
1616
|
0
|
0
|
|
|
|
|
$rsa->key_size($key_size) if $key_size; |
|
1617
|
0
|
|
|
|
|
|
$rsa->keygen; |
|
1618
|
0
|
|
0
|
|
|
|
my ($private_key, $public_key) = ($rsa->private_key // '', $rsa->public_key // ''); |
|
|
|
|
0
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
return ( |
|
1620
|
0
|
0
|
0
|
|
|
|
private_key => $private_key, |
|
1621
|
|
|
|
|
|
|
public_key => $public_key, |
|
1622
|
|
|
|
|
|
|
key_size => $rsa->key_size, |
|
1623
|
|
|
|
|
|
|
error => $rsa->error |
|
1624
|
|
|
|
|
|
|
? sprintf("Error occurred while generation %s bit RSA keys: %s", $rsa->key_size // '?', $rsa->error) |
|
1625
|
|
|
|
|
|
|
: '', |
|
1626
|
|
|
|
|
|
|
); |
|
1627
|
|
|
|
|
|
|
} |
|
1628
|
|
|
|
|
|
|
sub _genClientId { |
|
1629
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1630
|
0
|
|
0
|
|
|
|
my $user_agent = $self->req->headers->header('User-Agent') // 'unknown'; |
|
1631
|
0
|
|
0
|
|
|
|
my $remote_address = $self->remote_ip($self->app->trustedproxies) |
|
1632
|
|
|
|
|
|
|
|| $self->tx->remote_address || '::1'; |
|
1633
|
|
|
|
|
|
|
# md5(User-Agent . Remote-Address) |
|
1634
|
0
|
|
|
|
|
|
return md5_sum(sprintf("%s%s", $user_agent, $remote_address)); |
|
1635
|
|
|
|
|
|
|
} |
|
1636
|
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
1; |
|
1638
|
|
|
|
|
|
|
|
|
1639
|
|
|
|
|
|
|
__END__ |