| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
8
|
|
|
8
|
|
348439
|
use v5.36; |
|
|
8
|
|
|
|
|
31
|
|
|
2
|
|
|
|
|
|
|
package PlackX::Framework::Handler { |
|
3
|
8
|
|
|
8
|
|
515
|
use PXF::Util (); |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
214
|
|
|
4
|
8
|
|
|
8
|
|
38
|
use Scalar::Util qw(blessed); |
|
|
8
|
|
|
|
|
15
|
|
|
|
8
|
|
|
|
|
521
|
|
|
5
|
8
|
|
|
8
|
|
4445
|
use HTTP::Status qw(status_message); |
|
|
8
|
|
|
|
|
47703
|
|
|
|
8
|
|
|
|
|
17220
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
my %globals; |
|
8
|
|
|
|
|
|
|
our $psgix_streaming; # memoized, but in an "our" var so tests can change it |
|
9
|
|
|
|
41
|
0
|
|
sub use_global_request_response { } # Override in subclass to turn on |
|
10
|
0
|
|
|
0
|
0
|
0
|
sub global_request ($class) { $globals{$class->app_namespace}->[0] } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
11
|
0
|
|
|
0
|
0
|
0
|
sub global_response ($class) { $globals{$class->app_namespace}->[1] } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
12
|
10
|
|
|
10
|
0
|
19
|
sub error_response ($class, $code) { [$code, [], [status_message($code)." ($code)"]] } # Override for nicer message |
|
|
10
|
|
|
|
|
38
|
|
|
|
10
|
|
|
|
|
19
|
|
|
|
10
|
|
|
|
|
14
|
|
|
|
10
|
|
|
|
|
39
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# |
|
15
|
|
|
|
|
|
|
# App assembly section |
|
16
|
|
|
|
|
|
|
# |
|
17
|
11
|
|
|
11
|
0
|
1268
|
sub build_app ($class, %options) { |
|
|
11
|
|
|
|
|
22
|
|
|
|
11
|
|
|
|
|
19
|
|
|
|
11
|
|
|
|
|
14
|
|
|
18
|
|
|
|
|
|
|
# Freeze the router |
|
19
|
11
|
|
|
|
|
43
|
my $rt_engine = ($class->app_namespace . '::Router::Engine')->instance; |
|
20
|
11
|
|
|
|
|
72
|
$rt_engine->freeze; |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Honestly, it is probably better for the user to use Plack::Builder |
|
23
|
|
|
|
|
|
|
# or URLMap or Cascade instead of doing this, but we do it here for |
|
24
|
|
|
|
|
|
|
# convenience in development environments, at least for now. Think about |
|
25
|
|
|
|
|
|
|
# removing this feature at a later date. |
|
26
|
11
|
|
|
|
|
26
|
my $serve_static_files = delete $options{'serve_static_files'}; |
|
27
|
11
|
|
|
|
|
22
|
my $static_docroot = delete $options{'static_docroot'}; |
|
28
|
11
|
50
|
|
|
|
32
|
die "Unknown options: " . join(', ', keys %options) if %options; |
|
29
|
|
|
|
|
|
|
|
|
30
|
11
|
|
|
35
|
|
90
|
my $main_app = sub ($env) { psgi_response($class->handle_request($env, undef, $rt_engine)) }; |
|
|
35
|
|
|
|
|
266
|
|
|
|
35
|
|
|
|
|
104582
|
|
|
|
35
|
|
|
|
|
89
|
|
|
|
35
|
|
|
|
|
51
|
|
|
31
|
11
|
|
33
|
|
|
36
|
my $file_app = ($serve_static_files and do { |
|
32
|
|
|
|
|
|
|
require Plack::App::File; |
|
33
|
|
|
|
|
|
|
Plack::App::File->new(root => $static_docroot)->to_app; |
|
34
|
|
|
|
|
|
|
}); |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# if app_base is specified, use URLMap |
|
37
|
11
|
50
|
|
|
|
61
|
if (my $app_base = $class->app_base) { |
|
38
|
0
|
|
|
|
|
0
|
require Plack::App::URLMap; |
|
39
|
0
|
|
|
|
|
0
|
my $mapper = Plack::App::URLMap->new; |
|
40
|
0
|
|
|
|
|
0
|
$mapper->map($app_base => $main_app); |
|
41
|
0
|
0
|
|
|
|
0
|
$mapper->map('/' => $file_app) if $file_app; |
|
42
|
0
|
|
|
|
|
0
|
return $mapper->to_app; |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Static file app with no app_base, so try one, try the other if it's 404 |
|
46
|
|
|
|
|
|
|
# (basically our own cascade whereas we could use Plack::App::Cascade). |
|
47
|
|
|
|
|
|
|
# We prefer to serve the app's 404 page if the file app also returns 404 |
|
48
|
|
|
|
|
|
|
# because it is easier to customize the 404 page with PXF. |
|
49
|
|
|
|
|
|
|
# Add a later date we might add a feature to intercept all 4xx and 5xx |
|
50
|
|
|
|
|
|
|
# error codes at the last possible moment and render a user-defined page. |
|
51
|
0
|
|
|
0
|
|
0
|
return sub ($env) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
52
|
0
|
|
|
|
|
0
|
my $main_resp = $main_app->($env); |
|
53
|
0
|
0
|
0
|
|
|
0
|
return $main_resp if ref $main_resp and $main_resp->[0] != 404; |
|
54
|
0
|
|
|
|
|
0
|
my $file_resp = $file_app->($env); |
|
55
|
0
|
0
|
0
|
|
|
0
|
return $file_resp if ref $file_resp and $file_resp->[0] != 404; |
|
56
|
0
|
|
|
|
|
0
|
return $main_resp; |
|
57
|
11
|
50
|
|
|
|
33
|
} if $file_app; |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# no app_base, no static file app, just return the main app |
|
60
|
11
|
|
|
|
|
106
|
return $main_app; |
|
61
|
|
|
|
|
|
|
} |
|
62
|
|
|
|
|
|
|
|
|
63
|
11
|
|
|
11
|
0
|
23
|
sub app_base ($class) { |
|
|
11
|
|
|
|
|
19
|
|
|
|
11
|
|
|
|
|
21
|
|
|
64
|
11
|
|
50
|
|
|
23
|
my $base = eval { $class->app_namespace->app_base } || eval { $class->app_namespace->uri_prefix } || ''; |
|
65
|
11
|
0
|
33
|
|
|
42
|
$base = '/'.$base if $base and length $base and substr($base,0,1) ne '/'; |
|
|
|
|
33
|
|
|
|
|
|
66
|
11
|
|
|
|
|
36
|
return $base; |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# |
|
70
|
|
|
|
|
|
|
# Request handling section |
|
71
|
|
|
|
|
|
|
# |
|
72
|
42
|
|
|
42
|
0
|
1355
|
sub handle_request ($class, $env_or_req, $maybe_resp = undef, $maybe_rt_engine = undef) { |
|
|
41
|
|
|
|
|
81
|
|
|
|
41
|
|
|
|
|
77
|
|
|
|
41
|
|
|
|
|
80
|
|
|
|
41
|
|
|
|
|
99
|
|
|
|
41
|
|
|
|
|
103
|
|
|
73
|
41
|
|
|
|
|
245
|
my $app_namespace = $class->app_namespace; |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# Get or create default request and response objects |
|
76
|
41
|
|
|
|
|
161
|
my $env = $class->env_or_req_to_env($env_or_req); |
|
77
|
41
|
|
|
|
|
178
|
my $request = $class->env_or_req_to_req($env_or_req); |
|
78
|
41
|
|
33
|
|
|
689
|
my $response = $maybe_resp || ($app_namespace . '::Response')->new->set_defaults; |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# Memoize server info and maybe set request/response globals |
|
81
|
41
|
50
|
|
|
|
145
|
$psgix_streaming = $env->{'psgi.streaming'} ? !!1 : !!0 |
|
|
|
100
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
if !defined $psgix_streaming; |
|
83
|
41
|
50
|
|
|
|
157
|
$globals{$app_namespace} = [$request, $response] |
|
84
|
|
|
|
|
|
|
if $class->use_global_request_response; |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Set up stash |
|
87
|
41
|
|
100
|
|
|
166
|
my $stash = ($request->stash or $response->stash or {}); |
|
88
|
41
|
|
|
|
|
575
|
$request->stash($stash); |
|
89
|
41
|
|
|
|
|
264
|
$response->stash($stash); |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Maybe set up Templating, if loaded |
|
92
|
41
|
50
|
|
|
|
460
|
if (PXF::Util::is_module_loaded($app_namespace . '::Template')) { |
|
93
|
|
|
|
|
|
|
eval { |
|
94
|
0
|
|
|
|
|
0
|
my $template = ($app_namespace . '::Template')->new($response); |
|
95
|
0
|
|
|
|
|
0
|
$template->set(STASH => $stash, REQUEST => $request, RESPONSE => $response); |
|
96
|
0
|
|
|
|
|
0
|
$response->template($template); |
|
97
|
0
|
0
|
|
|
|
0
|
} or do { |
|
98
|
0
|
|
|
|
|
0
|
warn "$app_namespace\::Template module loaded, but unable to set up template: $@" |
|
99
|
|
|
|
|
|
|
. " (Hint: Did you use/import from it or set up templating manually?)\n"; |
|
100
|
|
|
|
|
|
|
}; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# Clear flash if set, set response defaults, and route request |
|
104
|
41
|
100
|
|
|
|
228
|
$response->flash(undef) if $request->flash; |
|
105
|
41
|
|
|
|
|
174
|
return $class->route_request($request, $response, $maybe_rt_engine); |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
41
|
|
|
41
|
0
|
71
|
sub route_request ($class, $request, $response, $rt_engine = undef) { |
|
|
41
|
|
|
|
|
72
|
|
|
|
41
|
|
|
|
|
67
|
|
|
|
41
|
|
|
|
|
63
|
|
|
|
41
|
|
|
|
|
71
|
|
|
|
41
|
|
|
|
|
70
|
|
|
109
|
41
|
|
66
|
|
|
134
|
$rt_engine //= ($class->app_namespace . '::Router::Engine')->instance; |
|
110
|
41
|
100
|
|
|
|
192
|
if (my $match = $rt_engine->match($request)) { |
|
111
|
31
|
50
|
|
|
|
163
|
$request->route_base($match->{base}) if defined $match->{base}; |
|
112
|
31
|
|
|
|
|
169
|
$request->route_parameters($match->{route_parameters}); |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Execute global and route-specific prefilters |
|
115
|
31
|
100
|
|
|
|
261
|
if (my $filterset = $match->{prefilters}) { |
|
116
|
11
|
|
|
|
|
37
|
my $ret = execute_filters($filterset, $request, $response); |
|
117
|
11
|
50
|
33
|
|
|
38
|
return $ret if $ret and is_valid_response($ret); |
|
118
|
|
|
|
|
|
|
} |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Execute main action |
|
121
|
31
|
|
|
|
|
303
|
my $result = $match->{action}->($request, $response); |
|
122
|
31
|
50
|
33
|
|
|
172
|
unless ($result and ref $result) { |
|
123
|
0
|
|
|
|
|
0
|
warn "PlackX::Framework - Invalid result '$result'\n"; |
|
124
|
0
|
|
|
|
|
0
|
return $class->error_response(500); |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# Check if the result is actually another request object |
|
128
|
31
|
100
|
|
|
|
261
|
return $class->handle_request($result) if $result->isa('Plack::Request'); |
|
129
|
25
|
50
|
|
|
|
92
|
return $class->error_response unless $result->isa('Plack::Response'); |
|
130
|
25
|
|
|
|
|
51
|
$response = $result; |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# Execute postfilters |
|
133
|
25
|
100
|
|
|
|
89
|
if (my $filterset = $match->{postfilters}) { |
|
134
|
11
|
|
|
|
|
30
|
my $ret = execute_filters($filterset, $request, $response); |
|
135
|
11
|
50
|
33
|
|
|
40
|
return $ret if $ret and is_valid_response($ret); |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Clean up (does server support cleanup handlers? Add to list or else execute now) |
|
139
|
25
|
100
|
66
|
|
|
98
|
if ($response->cleanup_callbacks and scalar $response->cleanup_callbacks->@* > 0) { |
|
140
|
3
|
50
|
|
|
|
77
|
if ($request->env->{'psgix.cleanup'}) { |
|
141
|
0
|
|
|
|
|
0
|
push $request->env->{'psgix.cleanup.handlers'}->@*, $response->cleanup_callbacks->@*; |
|
142
|
|
|
|
|
|
|
} else { |
|
143
|
3
|
|
|
|
|
46
|
$_->($request->env) for $response->cleanup_callbacks->@*; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
25
|
50
|
|
|
|
293
|
return $response if is_valid_response($response); |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
10
|
|
|
|
|
357
|
return $class->error_response(404); |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# |
|
154
|
|
|
|
|
|
|
# Helper function and method section |
|
155
|
|
|
|
|
|
|
# |
|
156
|
22
|
|
|
22
|
0
|
37
|
sub execute_filters ($filters, $request, $response) { |
|
|
22
|
|
|
|
|
37
|
|
|
|
22
|
|
|
|
|
37
|
|
|
|
22
|
|
|
|
|
31
|
|
|
|
22
|
|
|
|
|
36
|
|
|
157
|
22
|
50
|
33
|
|
|
132
|
return unless $filters and ref $filters eq 'ARRAY'; |
|
158
|
22
|
|
|
|
|
52
|
foreach my $filter (@$filters) { |
|
159
|
37
|
100
|
|
|
|
183
|
$filter = { action => $filter, params => [] } if ref $filter eq 'CODE'; |
|
160
|
37
|
|
|
|
|
70
|
my $response = $filter->{action}->($request, $response, @{$filter->{params}}); |
|
|
37
|
|
|
|
|
155
|
|
|
161
|
37
|
50
|
33
|
|
|
135
|
return $response if $response and is_valid_response($response); |
|
162
|
|
|
|
|
|
|
} |
|
163
|
22
|
|
|
|
|
52
|
return; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub is_valid_response { |
|
167
|
35
|
|
|
35
|
0
|
86648
|
my $response = pop; |
|
168
|
35
|
100
|
100
|
|
|
214
|
return !!0 unless defined $response and ref $response; |
|
169
|
33
|
100
|
100
|
|
|
141
|
return !!1 if ref $response eq 'ARRAY' and (@$response == 3 or @$response == 2); |
|
|
|
|
100
|
|
|
|
|
|
170
|
31
|
100
|
100
|
|
|
626
|
return !!1 if blessed $response and $response->can('finalize'); |
|
171
|
5
|
|
|
|
|
29
|
return !!0; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
|
|
174
|
37
|
|
|
37
|
0
|
1142
|
sub psgi_response ($resp) { |
|
|
37
|
|
|
|
|
168
|
|
|
|
37
|
|
|
|
|
65
|
|
|
175
|
37
|
100
|
|
|
|
163
|
return $resp |
|
176
|
|
|
|
|
|
|
if !blessed $resp; |
|
177
|
|
|
|
|
|
|
|
|
178
|
26
|
100
|
100
|
|
|
213
|
return $resp->finalize |
|
179
|
|
|
|
|
|
|
if not $resp->can('stream') or not $resp->stream; |
|
180
|
|
|
|
|
|
|
|
|
181
|
1
|
|
|
1
|
|
1075
|
return sub ($PSGI_responder) { |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
3
|
|
|
182
|
1
|
|
|
|
|
11
|
my $PSGI_writer = $PSGI_responder->($resp->finalize_sb); |
|
183
|
1
|
|
|
|
|
3454
|
$resp->stream_writer($PSGI_writer); |
|
184
|
1
|
|
|
|
|
11
|
$resp->stream->(); |
|
185
|
1
|
|
|
|
|
17
|
$PSGI_writer->close; |
|
186
|
2
|
100
|
|
|
|
25
|
} if $psgix_streaming; |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Simulate streaming, use "do" to make it look consistent with the above |
|
189
|
1
|
|
|
|
|
2
|
return do { |
|
190
|
1
|
|
|
|
|
3
|
$resp->stream->(); # execute coderef |
|
191
|
1
|
|
|
|
|
8
|
$resp->stream(undef); # unset stream property |
|
192
|
1
|
|
|
|
|
19
|
$resp->finalize; # finalize |
|
193
|
|
|
|
|
|
|
}; |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
|
|
196
|
41
|
|
|
41
|
0
|
72
|
sub env_or_req_to_req ($class, $env_or_req) { |
|
|
41
|
|
|
|
|
100
|
|
|
|
41
|
|
|
|
|
69
|
|
|
|
41
|
|
|
|
|
76
|
|
|
197
|
41
|
100
|
66
|
|
|
209
|
if (ref $env_or_req and ref $env_or_req eq 'HASH') { |
|
|
|
50
|
33
|
|
|
|
|
|
198
|
35
|
|
|
|
|
102
|
return ($class->app_namespace . '::Request')->new($env_or_req); |
|
199
|
|
|
|
|
|
|
} elsif (blessed $env_or_req and $env_or_req->isa('PlackX::Framework::Request')) { |
|
200
|
6
|
|
|
|
|
14
|
return $env_or_req; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
0
|
|
|
|
|
0
|
die 'Neither a PSGI-type HASH reference nor a PlackX::Framework::Request object.'; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
41
|
|
|
41
|
0
|
89
|
sub env_or_req_to_env ($class, $env_or_req) { |
|
|
41
|
|
|
|
|
74
|
|
|
|
41
|
|
|
|
|
76
|
|
|
|
41
|
|
|
|
|
66
|
|
|
206
|
41
|
100
|
66
|
|
|
279
|
if (ref $env_or_req and ref $env_or_req eq 'HASH') { |
|
|
|
50
|
33
|
|
|
|
|
|
207
|
35
|
|
|
|
|
95
|
return $env_or_req; |
|
208
|
|
|
|
|
|
|
} elsif (blessed $env_or_req and $env_or_req->isa('PlackX::Framework::Request')) { |
|
209
|
6
|
|
|
|
|
31
|
return $env_or_req->env; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
0
|
|
|
|
|
|
die 'Neither a PSGI-type HASH reference nor a PlackX::Framework::Request object.'; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
1; |