File Coverage

blib/lib/YATT/Lite/WebMVC0/SiteApp/CGI.pm
Criterion Covered Total %
statement 59 113 52.2
branch 17 54 31.4
condition 8 32 25.0
subroutine 11 14 78.5
pod 0 9 0.0
total 95 222 42.7


line stmt bran cond sub pod time code
1             package YATT::Lite::WebMVC0::SiteApp::CGI;
2             # -*- coding: utf-8 -*-
3 2     2   3913 use strict;
  2         3  
  2         63  
4 2     2   10 use warnings qw(FATAL all NONFATAL misc);
  2         5  
  2         93  
5              
6 2     2   11 use YATT::Lite::WebMVC0::SiteApp; # To make lint happy, this is required.
  2         9  
  2         28  
7              
8             package YATT::Lite::WebMVC0::SiteApp;
9              
10             sub _runas_cgi {
11 16     16   121 (my MY $self, my $fh, my Env $env, my ($args, %opts)) = @_;
12 16 50       180 if (-e ".htdebug_env") {
13 0         0 $self->printenv($fh, $env);
14 0         0 return;
15             }
16              
17 16         126 $self->init_by_env($env);
18              
19 16 50       79 if ($self->{cf_noheader}) {
    50          
20             # コマンド行起動時
21 0         0 require Cwd;
22 0         0 local $env->{GATEWAY_INTERFACE} = 'CGI/YATT';
23 0   0     0 local $env->{REQUEST_METHOD} //= 'GET';
24 0 0       0 local @{$env}{qw(PATH_TRANSLATED REDIRECT_STATUS)}
  0         0  
25             = (Cwd::abs_path(shift @$args), 200)
26             if @_;
27              
28 0         0 my @params = $self->make_cgi($env, $args, \%opts);
29 0         0 my $con = $self->make_connection($fh, @params);
30 0         0 $self->cgi_dirhandler($con, @params);
31              
32             } elsif ($env->{GATEWAY_INTERFACE}) {
33             # Normal CGI
34 16 50 33     170 if (defined $fh and fileno($fh) >= 0) {
35 0 0       0 open STDERR, '>&', $fh or die "can't redirect STDERR: $!";
36             }
37              
38 16         22 my $con;
39             my $error = catch {
40 16     16   59 my @params = $self->make_cgi($env, $args, \%opts);
41 16         113 $con = $self->make_connection($fh, @params);
42 16         78 $self->cgi_dirhandler($con, @params);
43 16         565 };
44              
45 16         107 $self->cgi_process_error($error, $con, $fh, $env);
46              
47             } else {
48             # dispatch without catch.
49 0         0 my @params = $self->make_cgi($env, $args, \%opts);
50 0         0 my $con = $self->make_connection($fh, @params);
51 0         0 $self->cgi_dirhandler($con, @params);
52             }
53             }
54              
55             #========================================
56              
57             sub cgi_dirhandler {
58 16     16 0 171 (my MY $self, my ($con, %params)) = @_;
59             # dirhandler は必ず load することにする。 *.yatt だけでなくて *.ydo でも。
60             # 結局は機能集約モジュールが欲しくなるから。
61             # そのために、 dirhandler は死重を減らすよう、部分毎に delayed load する
62              
63 16 50       110 my $dh = $self->get_dirhandler(untaint_any($params{dir}))
64             or die "Unknown directory: $params{dir}";
65             # XXX: cache のキーは相対パスか、絶対パスか?
66              
67 16         87 $con->configure(yatt => $dh);
68              
69 16         94 $self->run_dirhandler($dh, $con, $params{file});
70              
71 14         288 try_invoke($con, 'flush_headers');
72              
73 14 50       115 wantarray ? ($dh, $con) : $con;
74             }
75              
76             #========================================
77             # XXX: $env 渡し中心に変更する. 現状では...
78             # [1] $fh, $cgi を外から渡すケース... そもそも、これを止めるべき. $env でええやん、と。
79             # [2] $fh, $file, []/{}
80             # [3] $fh, $file, k=v, k=v... のケース
81              
82             sub make_cgi {
83 16     16 0 40 (my MY $self, my Env $env, my ($args, $opts)) = @_;
84 16         31 my ($cgi, $root, $loc, $file, $trailer);
85 16 50       51 unless ($self->{cf_noheader}) {
86 16         30 $cgi = do {
87 16 50 33     61 if (ref $args and UNIVERSAL::can($args, 'param')) {
88 0         0 $args;
89             } else {
90 16         52 $self->new_cgi(@$args);
91             }
92             };
93              
94 16         5466 ($root, $loc, $file, $trailer) = my @pi = $self->split_path_info($env);
95              
96 16 50       92 unless (@pi) {
97             # XXX: This is too early for fatal to browser. mmm
98 0         0 $self->error("Can't parse request. env='%s'", terse_dump($env));
99             }
100              
101             # XXX: /~user_dir の場合は $dir ne $root$loc じゃんか orz...
102              
103             } else {
104 0         0 my $path = shift @$args;
105 0 0       0 unless (defined $path) {
106 0         0 die "Usage: $0 tmplfile args...\n";
107             }
108 0 0       0 unless ($path =~ m{^/}) {
109 0 0       0 unless (-e $path) {
110 0         0 die "No such file: $path\n";
111             }
112             # XXX: $path が相対パスだったら?この時点で abs 変換よね?
113 0         0 require Cwd; # でも、これで 10ms 遅くなるのよね。
114 0   0     0 $path = Cwd::abs_path($path) // die "No such file: $path\n";
115             }
116             # XXX: widget 直接呼び出しは? cgi じゃなしに、直接パラメータ渡しは? =>
117 0         0 ($root, $loc, $file, $trailer) = split_path($path, $self->{cf_app_root});
118 0         0 $cgi = $self->new_cgi(@$args);
119             }
120              
121 16         128 (env => $env, $self->connection_quad(["$root$loc", $loc, $file, $trailer])
122             , cgi => $cgi, root => $root, is_psgi => 0);
123             }
124              
125             sub init_by_env {
126 16     16 0 30 (my MY $self, my Env $env) = @_;
127 16 50 100     92 $self->{cf_noheader} //= 0 if $env->{GATEWAY_INTERFACE};
128 16 100 33     66 $self->{cf_doc_root} //= $env->{DOCUMENT_ROOT} if $env->{DOCUMENT_ROOT};
129 16         28 $self;
130             }
131              
132             sub new_cgi {
133 16     16 0 27 my MY $self = shift;
134 16         30 my (@params) = do {
135 16 50 0     42 unless (@_) {
    0 0        
    0          
    0          
136             ()
137 16         38 } elsif (@_ > 1 or defined $_[0] and not ref $_[0]) {
138 0         0 $self->parse_params(\@_, {})
139             } elsif (not defined $_[0]) {
140 0         0 ();
141             } elsif (ref $_[0] eq 'ARRAY') {
142 0         0 my %hash = @{$_[0]};
  0         0  
143 0         0 \%hash;
144             } else {
145 0         0 $_[0];
146             }
147             };
148 16         106 require CGI; CGI->new(@params);
  16         137  
149             # shift; require CGI::Simple; CGI::Simple->new(@_);
150             }
151              
152             sub cgi_process_error {
153 16     16 0 49 (my MY $self, my ($error, $con, $fh, $env)) = @_;
154 16 100 66     72 if (not $error or is_done($error)) {
    50 0        
    0          
155 14 50 33     117 if (not tell($fh) and (my $len = length($con->buffer))) {
156             # We have buffered content but not yet delivered!
157             # Possible misuse of Connection API.
158 0         0 $self->cgi_response($fh, $env
159             , 200
160             , ["Content-type", $con->_mk_content_type]
161             # XXX: We should add API usage suggestion here.
162             , [$con->buffer]);
163             } else {
164             # Already delivered.
165             }
166             } elsif (ref $error eq 'ARRAY') {
167             # Non local exit with PSGI response triplet.
168 2         4 $self->cgi_response($fh, $env, @{$error});
  2         13  
169              
170             } elsif (defined $con and UNIVERSAL::isa($error, $self->Error)) {
171             # To erase premature output.
172 0         0 $con->rewind;
173              
174             # Known error. Header (may be) already printed.
175 0         0 (undef, my $ct, my @rest) = $self->secure_text_plain;
176 0         0 $con->set_content_type($ct);
177 0 0       0 $con->set_header_list(@rest) if @rest;
178 0         0 $con->flush_headers;
179 0         0 print $fh $error->message;
180              
181             } else {
182             # Unknown error.
183 0 0       0 $con->header_was_sent if $con;
184 0         0 $self->show_error($fh, $error, $env);
185             }
186             }
187              
188             sub cgi_response {
189 2     2 0 7 (my MY $self, my ($fh, $env, $code, $headers, $body)) = @_;
190 2         10 my $header = mk_http_status($code);
191 2         10 while (my ($k, $v) = splice @$headers, 0, 2) {
192 2         11 $header .= "$k: $v\015\012";
193             }
194 2         4 $header .= "\015\012";
195              
196 2         3 print {*$fh} $header;
  2         11  
197 2         5 print {*$fh} @$body;
  2         12  
198             }
199              
200             sub printenv {
201 0     0 0   (my MY $self, my ($fh, $env)) = @_;
202 0           $self->dump_pairs($fh, map {$_ => $env->{$_}} sort keys %$env);
  0            
203             }
204              
205             sub dump_pairs {
206 0     0 0   (my MY $self, my ($fh)) = splice @_, 0, 2;
207 0           $self->show_error($fh);
208 0           while (my ($name, $value) = splice @_, 0, 2) {
209 0           print $fh $name, "\t", terse_dump($value), "\n";
210             }
211             }
212              
213             sub show_error {
214 0     0 0   (my MY $self, my ($fh, $error, $env)) = @_;
215 0 0         if (my @kv = $self->secure_text_plain) {
216 0           while (my ($k, $v) = splice @kv, 0, 2) {
217 0           print $fh "$k: $v\n";
218             }
219             } else {
220 0           print $fh "\n";
221             }
222 0   0       print $fh "\n". ($error // "");
223             }
224              
225             &YATT::Lite::Breakpoint::break_load_dispatcher_cgi;
226              
227             1;