File Coverage

blib/lib/YATT/Lite/WebMVC0/SiteApp.pm
Criterion Covered Total %
statement 213 285 74.7
branch 59 120 49.1
condition 46 109 42.2
subroutine 47 59 79.6
pod 5 34 14.7
total 370 607 60.9


line stmt bran cond sub pod time code
1             package YATT::Lite::WebMVC0::SiteApp;
2 12     12   37073 use strict;
  12         33  
  12         388  
3 12     12   63 use warnings qw(FATAL all NONFATAL misc);
  12         23  
  12         548  
4 12     12   67 use Carp;
  12         28  
  12         841  
5 12     12   464 use YATT::Lite::Breakpoint;
  12         73  
  12         1242  
6             sub MY () {__PACKAGE__}
7              
8 12     12   211 use 5.010; no if $] >= 5.017011, warnings => "experimental";
  12     12   42  
  12         100  
  12         592  
  12         85  
9              
10             #========================================
11             # Dispatcher Layer: load and run corresponding DirApp for incoming request.
12             #========================================
13              
14 12     12   811 use parent qw(YATT::Lite::Factory);
  12         26  
  12         137  
15 12         98 use YATT::Lite::MFields qw/cf_noheader
16             cf_is_psgi
17             cf_no_nested_query
18             allow_debug_from
19             cf_debug_cgi
20             cf_debug_psgi
21             cf_debug_connection
22             cf_debug_backend
23             cf_psgi_static
24             cf_psgi_fallback
25             cf_per_role_docroot
26             cf_per_role_docroot_key
27             cf_default_role
28             cf_backend
29             cf_site_config
30             cf_logfile
31             cf_debug_allowed_ip
32             cf_overwrite_status_code_for_errors_as
33             re_handled_ext
34              
35             cf_progname
36              
37             cf_no_trim_script_name
38 12     12   1176 /;
  12         30  
39              
40 12         1267 use YATT::Lite::Util qw(cached_in split_path catch
41             lookup_path nonempty try_invoke
42             mk_http_status
43             default ckrequire
44             escape
45             trim_common_suffix_from
46 12     12   108 lexpand rootname extname untaint_any terse_dump);
  12         30  
47 12     12   5670 use YATT::Lite::Util::CmdLine qw(parse_params);
  12         33  
  12         663  
48 12     12   82 use YATT::Lite qw/Entity *SYS *CON/;
  12         31  
  12         212  
49 12     12   4710 use YATT::Lite::WebMVC0::DirApp ();
  12         39  
  12         610  
50             sub DirApp () {'YATT::Lite::WebMVC0::DirApp'}
51             sub default_default_app () {'YATT::Lite::WebMVC0::DirApp'}
52              
53 12     12   86 use File::Basename;
  12         28  
  12         6676  
54              
55             sub after_new {
56 24     24 1 75 (my MY $self) = @_;
57 24         164 $self->SUPER::after_new();
58 24         476 $self->{re_handled_ext} = qr{\.($self->{cf_ext_public}|ydo)$};
59 24   33     196 $self->{cf_per_role_docroot_key} ||= $self->default_per_role_docroot_key;
60 24   33     160 $self->{cf_default_role} ||= $self->default_default_role;
61             }
62              
63 24     24 0 101 sub default_per_role_docroot_key { 'yatt.role' }
64 24     24 0 107 sub default_default_role { 'nobody' }
65              
66             sub _cf_delegates {
67             (shift->SUPER::_cf_delegates
68 32     32   161 , qw(overwrite_status_code_for_errors_as));
69             }
70              
71             #========================================
72             # runas($type, $fh, \%ENV, \@ARGV) ... for CGI/FCGI support.
73             #========================================
74              
75             sub runas {
76 16     16 0 13471 (my $this, my $type) = splice @_, 0, 2;
77 16 50       84 my MY $self = ref $this ? $this : $this->new;
78 16 50       134 my $sub = $self->can("runas_$type")
79             or die "\n\nUnknown runas type: $type";
80 16         66 $sub->($self, @_);
81             }
82              
83             sub runas_cgi {
84 16     16 0 155 require YATT::Lite::WebMVC0::SiteApp::CGI;
85 16         106 shift->_runas_cgi(@_);
86             }
87             sub runas_fcgi {
88 0     0 0 0 require YATT::Lite::WebMVC0::SiteApp::FCGI;
89 0         0 shift->_runas_fcgi(@_);
90             }
91              
92             # callas($type, $app, $fh, \%ENV, \@ARGV, %opts)
93              
94             sub callas {
95 0     0 0 0 (my $this, my $type) = splice @_, 0, 2;
96 0 0       0 my MY $self = ref $this ? $this : $this->new;
97 0 0       0 my $sub = $self->can("callas_$type")
98             or die "\n\nUnknown callas type: $type";
99 0         0 $sub->($self, @_);
100             }
101              
102             sub callas_cgi {
103 0     0 0 0 require YATT::Lite::WebMVC0::SiteApp::CGI;
104 0         0 shift->_callas_cgi(@_);
105             }
106             sub callas_fcgi {
107 0     0 0 0 require YATT::Lite::WebMVC0::SiteApp::FCGI;
108 0         0 shift->_callas_fcgi(@_);
109             }
110              
111              
112             #========================================
113              
114             # Dispatcher::get_dirhandler
115             # -> Util::cached_in
116             # -> Factory::load
117             # -> Factory::buildspec
118              
119             sub get_lochandler {
120 201     201 0 14237 (my MY $self, my ($location, $tmpldir)) = @_;
121 201 50       624 if ($self->{cf_per_role_docroot}) {
122             # When per_role_docroot is on, $tmpldir already points
123             # $per_role_docroot/$role. So just append $location.
124 0         0 $self->get_dirhandler($tmpldir.$location);
125             } else {
126 201         1047 $self->SUPER::get_lochandler($location, $tmpldir);
127             }
128             }
129              
130             #----------------------------------------
131             # preload_handlers
132              
133             sub preload_apps {
134 1     1 0 3 (my MY $self, my (@dir)) = @_;
135 1 50       5 push @dir, $self->{cf_doc_root} unless @dir;
136              
137 1         2 my @apps;
138 1         4 foreach my $dir ($self->find_apps(@dir)) {
139 3         16 push @apps, my $app = $self->get_dirhandler($dir);
140             }
141 1         6 @apps;
142             }
143              
144             sub find_apps {
145 1     1 0 3 (my MY $self, my @dir) = @_;
146 1         4 require File::Find;
147 1         2 my @apps;
148             my $handler = sub {
149 12 100   12   845 push @apps, $_ if -d;
150 1         3 };
151 1         105 File::Find::find({wanted => $handler
152             , no_chdir => 1
153             , follow_skip => 2}
154             , @dir
155             );
156 1         8 @apps;
157             }
158              
159             #========================================
160             # PSGI Adaptor
161             #========================================
162 12     12   96 use YATT::Lite::PSGIEnv;
  12         29  
  12         108  
163              
164             sub to_app {
165 8     8 0 2480 my MY $self = shift;
166             # XXX: Should check it.
167             # unless (defined $self->{cf_app_root}) {
168             # croak "app_root is undef!";
169             # }
170 8 50 33     57 unless (defined $self->{cf_doc_root}
171             or defined $self->{cf_per_role_docroot}) {
172 0         0 croak "document_root is undef!";
173             }
174 8         590 return $self->SUPER::to_app(@_);
175             }
176              
177             sub prepare_app {
178 8     8 1 24 (my MY $self) = @_;
179 8         21 $self->{cf_is_psgi} = 1;
180 8         215 require Plack::Request;
181 8         173017 require Plack::Response;
182 8         28 my $backend;
183 8 100 66     79 if ($backend = $self->{cf_backend}
184             and my $sub = $backend->can('startup')) {
185 1         4 $sub->($backend, $self, $self->preload_apps);
186             }
187             }
188              
189             sub call {
190 170     170 1 330505 (my MY $self, my Env $env) = @_;
191              
192 170         966 YATT::Lite::Breakpoint::break_psgi_call();
193              
194 170 50       724 if ($self->has_htdebug("env")) {
195 0   0     0 return $self->psgi_dump(map {"$_\t".($env->{$_}//"(undef)")."\n"}
  0         0  
196             sort keys %$env);
197             }
198              
199 170 100 66     870 if (my $deny = $self->has_forbidden_path($env->{PATH_INFO})
200             // $self->has_forbidden_path($env->{PATH_TRANSLATED})) {
201 3         20 return $self->psgi_error(403, "Forbidden $deny");
202             }
203              
204 167 50 33     1164 if (not $self->{cf_no_unicode_params}
205             and $self->{cf_output_encoding}) {
206             $env->{PATH_INFO} = Encode::decode($self->{cf_output_encoding}
207 167         926 , $env->{PATH_INFO});
208             }
209              
210 167 100 100     12375 if ($self->{loc2psgi_dict}
211             and my $psgi_app = $self->lookup_psgi_mount($env->{PATH_INFO})) {
212 3         17 require Plack::Util;
213 3         12 return Plack::Util::run_app($psgi_app, $env);
214             }
215              
216             # XXX: user_dir?
217 164         652 my ($tmpldir, $loc, $file, $trailer, $is_index)
218             = my @pi = $self->split_path_info($env);
219              
220             # Set $env->{yatt.script_name}
221 164         915 $self->set_yatt_script_name($env);
222              
223 164         375 my ($realdir, $virtdir);
224 164 100       516 if (@pi) {
225 163         381 $realdir = "$tmpldir$loc";
226             $virtdir = defined $self->{cf_doc_root}
227 163 50       665 ? "$self->{cf_doc_root}$loc" : $realdir;
228             }
229              
230 164 50       514 if ($self->has_htdebug("path_info")) {
231 0         0 return $self->psgi_dump([tmpldir => $tmpldir]
232             , [loc => $loc]
233             , [file => $file]
234             , [trailer => $trailer]
235             , [virtdir => $virtdir, realdir => $realdir]
236             );
237             }
238              
239 164 50       642 if ($self->{cf_debug_psgi}) {
240             # XXX: should be configurable.
241 0 0       0 if (my $errfh = fileno(STDERR) ? \*STDERR : $env->{'psgi.errors'}) {
    0          
242             print $errfh join("\t"
243             , "# REQ: "
244             , terse_dump([tmpldir => $tmpldir]
245             , [loc => $loc]
246             , [file => $file]
247             , [trailer => $trailer]
248             , ['all templdirs', $self->{tmpldirs}]
249 0         0 , map {[$_ => $env->{$_}]} sort keys %$env)
  0         0  
250             ), "\n";
251             }
252             }
253              
254 164 100       495 unless (@pi) {
255 1         8 return $self->psgi_handle_fallback($env);
256             }
257              
258 163 50       1835 unless (-d $realdir) {
259 0         0 return $self->psgi_error(404, "Not found: $loc");
260             }
261              
262             # Default index file.
263             # Note: Files may placed under (one of) tmpldirs instead of docroot.
264 163 50       878 if ($file eq '') {
    50          
265 0         0 $file = "$self->{cf_index_name}.$self->{cf_ext_public}";
266             } elsif ($file eq $self->{cf_index_name}) { #XXX: $is_index
267 0         0 $file .= ".$self->{cf_ext_public}";
268             }
269              
270 163 100       1885 if ($file !~ $self->{re_handled_ext}) {
271 1 50 33     8 if ($self->{cf_debug_psgi} and $self->has_htdebug("static")) {
272             return $self->psgi_dump("Not handled since extension doesn't match"
273 0         0 , $file, $self->{re_handled_ext});
274             }
275 1         6 return $self->psgi_handle_static($env);
276             }
277              
278 162 50       452 my $dh = $self->get_lochandler(map {untaint_any($_)} $loc, $tmpldir) or do {
  324         1015  
279 0         0 return $self->psgi_error(404, "No such directory: $loc");
280             };
281              
282             # To support $con->param and other cgi compat methods.
283 162         1499 my $req = Plack::Request->new($env);
284              
285             my @params = (env => $env
286             , path_info => $env->{PATH_INFO}
287 162 100       2617 , $self->connection_quad([$virtdir, $loc, $file, $trailer])
288             , $is_index ? (is_index => 1) : ()
289             , is_psgi => 1, cgi => $req);
290              
291 162         823 my $con = $self->make_connection(undef, @params, yatt => $dh, noheader => 1);
292              
293             my $error = catch {
294 162     162   772 $self->run_dirhandler($dh, $con, $file);
295 162         1178 };
296              
297 162         964 try_invoke($con, 'flush_headers');
298              
299 162 100 66     625 if (not $error or is_done($error)) {
    100 100        
300 142         1225 my $res = Plack::Response->new(200);
301             $res->content_type("text/html"
302             . ($self->{cf_header_charset}
303 142 50       4140 ? qq{; charset="$self->{cf_header_charset}"}
304             : ""));
305 142 50       5331 if (my @h = $con->list_header) {
306 0         0 $res->headers->header(@h);
307             }
308 142         661 $res->body($con->buffer);
309 142         1235 return $res->finalize;
310             } elsif (ref $error eq 'ARRAY' or ref $error eq 'CODE') {
311             # redirect
312 19 50       73 if ($self->{cf_debug_psgi}) {
313 0 0       0 if (my $errfh = fileno(STDERR) ? \*STDERR : $env->{'psgi.errors'}) {
    0          
314 0         0 print $errfh "PSGI Response: ", terse_dump($error), "\n";
315             }
316             }
317 19         99 return $error;
318             } else {
319             # system_error. Should be treated by PSGI Server.
320 1         6 die $error;
321             }
322             }
323              
324             sub make_debug_params {
325 37     37 0 82 (my MY $self, my ($reqrec, $args)) = @_;
326              
327 37 50       97 my ($path_info, @rest) = ref $reqrec ? @$reqrec : $reqrec;
328              
329 37         291 my Env $env = Env->psgi_simple_env;
330 37         76 $env->{PATH_INFO} = $path_info;
331 37         72 $env->{REQUEST_URI} = $path_info;
332              
333 37         120 my @params = ($self->SUPER::make_debug_params($reqrec, $args)
334             , env => $env);
335              
336             #
337             # Only for debugging aid. See YATT/samples/db_backed/1/t/t_signup.pm
338             #
339 37 0 33     116 if (@rest == 2 and defined $rest[-1] and ref $args eq 'HASH') {
      33        
340 0         0 require Hash::MultiValue;
341 0         0 push @params, hmv => Hash::MultiValue->from_mixed($args);
342             }
343              
344 37         135 @params;
345             }
346              
347             #========================================
348              
349             sub psgi_handle_static {
350 1     1 0 3 (my MY $self, my Env $env) = @_;
351             my $app = $self->{cf_psgi_static}
352 1   33     9 || $self->psgi_file_app($self->{cf_doc_root});
353              
354             # When PATH_INFO contains virtual path prefix (like /~$user/),
355             # we need to strip them (for Plack::App::File).
356 1         33 local $env->{PATH_INFO} = $self->trim_site_prefix($env->{PATH_INFO});
357              
358 1         4 $app->($env);
359             }
360              
361             sub psgi_handle_fallback {
362 1     1 0 5 (my MY $self, my Env $env) = @_;
363             (my $app = $self->{cf_psgi_fallback}
364             ||= $self->psgi_file_app($self->{cf_doc_root}))
365 1 50 33     11 or return [404, [], ["Cannot understand: ", $env->{PATH_INFO}]];
366              
367 1         7 local $env->{PATH_INFO} = $self->trim_site_prefix($env->{PATH_INFO});
368              
369 1         9 $app->($env);
370             }
371              
372             sub trim_site_prefix {
373 2     2 0 7 (my MY $self, my $path) = @_;
374 2 50       9 if (my $pfx = $self->{cf_site_prefix}) {
375 0         0 substr($path, length($pfx));
376             } else {
377 2         8 $path;
378             }
379             }
380              
381             # XXX: Do we need to care about following headers too?:
382             # * X-Content-Security-Policy
383             # * X-Request-With
384             # * X-Frame-Options
385             # * Strict-Transport-Security
386              
387             sub is_done {
388 0         0 defined $_[0] and ref $_[0] eq 'SCALAR' and not ref ${$_[0]}
389 22 50 33 22 0 264 and ${$_[0]} eq 'DONE';
  0   33     0  
390             }
391              
392             #========================================
393              
394             sub set_yatt_script_name {
395 164     164 0 455 (my MY $self, my Env $env) = @_;
396              
397 164         327 $env->{'yatt.script_name'} = do {
398 164 100 66     1359 if (not $self->{cf_no_trim_script_name}
      50        
      66        
      66        
399             and $env->{REDIRECT_HANDLER}
400             and ($env->{REDIRECT_STATUS} // 0) == 200
401             and $env->{SCRIPT_FILENAME}
402             ) {
403             #
404             # For Apache Action+AddHandler mapping.
405             #
406             trim_common_suffix_from($env->{SCRIPT_NAME}
407 8         45 , $env->{SCRIPT_FILENAME});
408             } else {
409             #
410             # Normal case.
411             #
412 156         614 $env->{SCRIPT_NAME};
413             }
414             };
415             }
416              
417             sub split_path_info {
418 180     180 0 488 (my MY $self, my Env $env) = @_;
419              
420 180 100 66     1354 if (! $self->{cf_per_role_docroot}
    50 66        
421             && nonempty($env->{PATH_TRANSLATED})
422             && $self->is_path_translated_mode($env)) {
423             #
424             # [1] PATH_TRANSLATED mode.
425             #
426             # If REDIRECT_STATUS == 200 and PATH_TRANSLATED is not empty,
427             # use it as a template path. It must be located under app_root.
428             #
429             # In this case, PATH_TRANSLATED should be valid physical path
430             # + optionally trailing sub path_info.
431             #
432             # XXX: What should be done when app_root is empty?
433             # XXX: Is userdir ok? like /~$USER/dir?
434             # XXX: should have cut_depth option.
435             #
436             my ($tmpldir, $loc, $file, $trailer, $is_index)
437             = split_path($env->{PATH_TRANSLATED}, $self->{cf_app_root}
438             , $self->{cf_use_subpath}
439             , $self->{cf_ext_public}
440 16         101 );
441              
442             # This is a workaround for $is_index. Determining $is_index only from
443             # PATH_TRANSLATED was just wrong.
444             #
445             # So instead turn on it when "$env->{REQUEST_URI}$file" eq $env->{REDIRECT_URL};
446             #
447             $is_index ||= ($env->{REQUEST_URI}
448             and $env->{REDIRECT_URL}
449 16   100     182 and "$env->{REQUEST_URI}$file" eq $env->{REDIRECT_URL});
      66        
450              
451 16         117 ($tmpldir, $loc, $file, $trailer, $is_index);
452              
453             } elsif (nonempty($env->{PATH_INFO})) {
454             #
455             # [2] Template lookup mode.
456             #
457              
458 164         320 my $tmpldirs = do {
459 164 50       481 if ($self->{cf_per_role_docroot}) {
460 0         0 my $user = $env->{$self->{cf_per_role_docroot_key}};
461 0   0     0 $user ||= $self->{cf_default_role};
462 0         0 ["$self->{cf_per_role_docroot}/$user"]
463             } else {
464             $self->{tmpldirs}
465 164         428 }
466             };
467              
468             lookup_path($env->{PATH_INFO}
469             , $tmpldirs
470             , $self->{cf_index_name}, ".$self->{cf_ext_public}"
471 164         1061 , $self->{cf_use_subpath});
472             } else {
473             # or die
474 0         0 return;
475             }
476             }
477              
478             sub has_forbidden_path {
479 337     337 0 1084 (my MY $self, my $path) = @_;
480 337         672 given ($path) {
481 337         768 when (undef) {
482 159         785 return undef;
483             }
484 178         570 when (m{\.lib(?:/|$)}) {
485 1         6 return ".lib: $path";
486             }
487 177         1730 when (m{(?:^|/)\.ht|\.ytmpl$}) {
488             # XXX: basename() is just to ease testing.
489 2         73 return "filetype: " . basename($path);
490             }
491             }
492             }
493              
494             sub is_path_translated_mode {
495 16     16 0 50 (my MY $self, my Env $env) = @_;
496 16   50     108 ($env->{REDIRECT_STATUS} // 0) == 200
497             }
498              
499             # XXX: kludge! redundant!
500             sub split_path_url {
501 2     2 0 15 (my MY $self, my ($path_translated, $path_info, $document_root)) = @_;
502              
503 2         3 my @info = do {
504 2 100       17 if ($path_info =~ s{^(/~[^/]+)(?=/)}{}) {
505 1         5 my $user = $1;
506 1         6 my ($root, $loc, $file, $trailer)
507             = split_path($path_translated
508             , substr($path_translated, 0
509             , length($path_translated) - length($path_info))
510             , 0
511             );
512 1         9 (dir => "$root$loc", file => $file, subpath => $trailer
513             , root => $root, location => "$user$loc");
514             } else {
515 1         5 my ($root, $loc, $file, $trailer)
516             = split_path($path_translated, $document_root, 0);
517 1         7 (dir => "$root$loc", file => $file, subpath => $trailer
518             , root => $root, location => $loc);
519             }
520             };
521              
522 2 50       7 if (wantarray) {
523             @info
524 0         0 } else {
525 2         8 my %info = @info;
526 2         18 \%info;
527             }
528             }
529              
530             # どこを起点に split_path するか。UserDir の場合は '' を返す。
531             sub document_dir {
532 0     0 0 0 (my MY $self, my $cgi) = @_;
533 0         0 my $path_info = $cgi->path_info;
534 0 0       0 if (my ($user) = $path_info =~ m{^/~([^/]+)/}) {
535 0         0 '';
536             } else {
537 0   0     0 $self->{cf_doc_root} // '';
538             }
539             }
540              
541             #========================================
542             sub is_debug_allowed {
543 0     0 0 0 (my MY $self, my Env $env) = @_;
544 0 0       0 return unless $self->{allow_debug_from};
545 0 0       0 return unless defined(my $ip = $self->guess_client_ip($env));
546 0         0 $ip =~ $self->{allow_debug_from};
547             }
548              
549             sub guess_client_ip {
550 0     0 0 0 (my MY $self, my Env $env) = @_;
551 0   0     0 $env->{HTTP_X_REAL_IP} // $env->{HTTP_X_CLIENT_IP} // do {
      0        
552 0 0       0 if (defined(my $forward = $env->{HTTP_X_FORWARDED_FOR})) {
553 0         0 [split /(?:\s*,\s*|\s+)/, $forward]->[0];
554             } else {
555             $env->{REMOTE_ADDR}
556 0         0 }
557             }
558             }
559              
560             sub configure_allow_debug_from {
561 0     0 0 0 (my MY $self, my $data) = @_;
562 0         0 my $pat = join "|", map { quotemeta($_) } lexpand($data);
  0         0  
563 0         0 $self->{allow_debug_from} = qr{^(?:$pat)};
564             }
565              
566             sub has_htdebug {
567 334     334 0 768 (my MY $self, my $name) = @_;
568             defined $self->{cf_app_root}
569 334 50       6446 and -e "$self->{cf_app_root}/.htdebug_$name"
570             }
571              
572             sub psgi_dump {
573 0     0 0 0 my MY $self = shift;
574             [200
575             , [$self->secure_text_plain]
576 0         0 , [map {escape(terse_dump($_))} @_]];
  0         0  
577             }
578              
579             #========================================
580              
581             #========================================
582              
583 12     12   131 use YATT::Lite::WebMVC0::Connection;
  12         37  
  12         3243  
584             sub Connection () {'YATT::Lite::WebMVC0::Connection'}
585             sub ConnProp () {Connection}
586              
587             sub make_connection {
588 230     230 1 3909 (my MY $self, my ($fh, @args)) = @_;
589 230         505 my @opts = do {
590 230 50       726 if ($self->{cf_noheader}) {
591             # direct mode.
592 0         0 ($fh, noheader => 1);
593             } else {
594             # buffered mode.
595 230         758 (undef, parent_fh => $fh);
596             }
597             };
598              
599 230         673 push @opts, site_prefix => $self->{cf_site_prefix};
600              
601 230 50       766 if (my $fn = $self->{cf_logfile}) {
602 0         0 my $dir = $self->app_path_ensure_existing(dirname($fn));
603 0         0 my $real = "$dir/" . basename($fn);
604 0 0       0 open my $fh, '>>', $real or die "Can't open logfile: fn=$real: $!";
605 0         0 push @opts, logfh => $fh;
606             }
607              
608             push @opts, debug => $self->{cf_debug_connection}
609 230 50       713 if $self->{cf_debug_connection};
610              
611 230 50       684 if (my $back = $self->{cf_backend}) {
612 0   0     0 push @opts, (backend => try_invoke($back, 'clone') // $back);
613             }
614              
615 230 50       744 if (my $enc = $$self{cf_output_encoding}) {
616 230         505 push @opts, encoding => $enc;
617             }
618             $self->SUPER::make_connection
619             (@opts
620 230         1232 , $self->cf_delegate_defined(qw(is_psgi
621             no_unicode_params
622             no_nested_query))
623             , @args);
624             }
625              
626             sub finalize_connection {
627 230     230 0 448 my MY $self = shift;
628 230         644 my ConnProp $prop = (my $glob = shift)->prop;
629 230 50       953 $self->session_flush($glob) if $prop->{session};
630             }
631              
632 12     12   6624 use YATT::Lite::WebMVC0::Partial::Session;
  12         37  
  12         590  
633              
634             #========================================
635             # misc.
636             #========================================
637              
638             sub header_charset {
639 0     0 1 0 (my MY $self) = @_;
640 0 0       0 $self->{cf_header_charset} || $self->{cf_output_encoding};
641             }
642              
643             #========================================
644              
645             Entity site_config => sub {
646 0     0   0 my ($this, $name, $default) = @_;
647 0         0 my MY $self = $SYS;
648 0 0       0 return $self->{cf_site_config} unless defined $name;
649 0   0     0 $self->{cf_site_config}{$name} // $default;
650             };
651              
652             Entity is_debug_allowed_ip => sub {
653 2     2   7 my ($this, $remote_addr) = @_;
654 2         7 my MY $self = $SYS;
655              
656 2   33     13 $remote_addr //= do {
657 2         22 my Env $env = $CON->env;
658             $env->{HTTP_X_REAL_IP}
659             // $env->{HTTP_X_CLIENT_IP}
660             // $env->{HTTP_X_FORWARDED_FOR}
661 2   33     34 // $env->{REMOTE_ADDR};
      33        
      33        
662             };
663              
664 2 50 33     15 unless (defined $remote_addr and $remote_addr ne '') {
665 0         0 return 0;
666             }
667              
668 2         16 grep {$remote_addr ~~ $_} lexpand($self->{cf_debug_allowed_ip}
669 2   50     21 // ['127.0.0.1']);
670             };
671              
672             foreach my $name (qw/
673             file_location
674             dir_location
675             is_current_file
676             is_current_page
677             /
678             ) {
679             my $method = $name;
680             Entity $method => sub {
681 584     584   1033 shift;
        576      
682 584         2018 $CON->$method(@_);
683             };
684             }
685              
686             foreach my $item (map([$_ => uc($_)]
687             , qw/path_info
688             request_uri
689              
690             script_uri
691             script_url
692             script_filename
693              
694             SCRIPT_NAME
695             /)) {
696             my ($method, $env_name) = @$item;
697             Entity $method => sub {
698 0     0   0 my Env $env = $CON->env;
699 0         0 $env->{$env_name};
700             };
701             }
702              
703             # &yatt:SCRIPT_NAME(); is original $env->{SCRIPT_NAME}
704             # &yatt:script_name(); is $env->{'yatt.script_name'}
705              
706             Entity script_name => sub {
707 8     8   22 my ($this) = @_;
708 8         44 my Env $env = $CON->env;
709 8         46 $env->{'yatt.script_name'};
710             };
711              
712             #========================================
713              
714             YATT::Lite::Breakpoint::break_load_dispatcher();
715              
716             1;