File Coverage

blib/lib/YATT/Lite/WebMVC0/SiteApp/CGI.pm
Criterion Covered Total %
statement 59 150 39.3
branch 17 72 23.6
condition 8 45 17.7
subroutine 11 18 61.1
pod 0 10 0.0
total 95 295 32.2


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