File Coverage

blib/lib/YATT/Lite/WebMVC0/SiteApp/FCGI.pm
Criterion Covered Total %
statement 12 96 12.5
branch 0 44 0.0
condition 0 28 0.0
subroutine 4 12 33.3
pod 0 2 0.0
total 16 182 8.7


line stmt bran cond sub pod time code
1             package YATT::Lite::WebMVC0::SiteApp::FCGI;
2             # -*- coding: utf-8 -*-
3 1     1   2914 use strict;
  1         2  
  1         34  
4 1     1   4 use warnings qw(FATAL all NONFATAL misc);
  1         2  
  1         37  
5              
6 1     1   6 use YATT::Lite::WebMVC0::SiteApp; # To make lint happy, this is required.
  1         1  
  1         8  
7 1     1   6 use YATT::Lite::WebMVC0::SiteApp::CGI;
  1         1  
  1         1409  
8              
9             package YATT::Lite::WebMVC0::SiteApp;
10              
11              
12             ########################################
13             #
14             # FastCGI support with auto_exit, based on PSGI mode.
15             #
16             # Many parts are stolen from Plack::Handler::FCGI.
17             #
18             ########################################
19              
20             # runas_fcgi() is designed for single process app.
21             # If you want psgi.multiprocess, use Plack's own FCGI instead.
22              
23             sub _runas_fcgi {
24 0     0     (my MY $self, my $fhset, my Env $init_env, my ($args, %opts)) = @_;
25             # $fhset is either stdout or [\*STDIN, \*STDOUT, \*STDERR].
26             # $init_env is just discarded.
27             # $args = \@ARGV
28             # %opts is fcgi specific options.
29              
30 0           local $self->{cf_is_psgi} = 1;
31              
32             # In suexec fcgi, $0 will not be absolute path.
33 0           my $progname = do {
34 0 0 0       if (-r (my $fn = delete $opts{progname} || $0)) {
35 0           $self->rel2abs($fn);
36             } else {
37 0           croak "progname is empty!";
38             }
39             };
40              
41 0 0         if ((my $dn = $progname) =~ s{/html/cgi-bin/[^/]+$}{}) {
42 0   0       $self->{cf_app_root} //= $dn;
43 0   0       $self->{cf_doc_root} //= "$dn/html";
44 0           push @{$self->{tmpldirs}}, $self->{cf_doc_root}
45 0 0 0       unless $self->{tmpldirs} and @{$self->{tmpldirs}};
  0            
46             #print STDERR "Now:", terse_dump($self->{cf_app_root}, $self->{cf_doc_root}
47             # , $self->{tmpldirs}), "\n";
48             }
49              
50 0           $self->prepare_app;
51              
52 0           my $dir = dirname($progname);
53 0           my $age = -M $progname;
54              
55             my ($stdin, $stdout, $stderr) = ref $fhset eq 'ARRAY' ? @$fhset
56             : (\*STDIN, $fhset
57 0 0 0       , ((delete $opts{isolate_stderr}) // 1) ? \*STDERR : $fhset);
    0          
58              
59 0           require FCGI;
60 0           my $sock = do {
61 0 0         if (my $sockfile = delete $opts{listen}) {
62 0 0         unless (-e (my $d = dirname($sockfile))) {
63 0           require File::Path;
64 0 0         File::Path::make_path($d)
65             or die "Can't mkdir $d: $!";
66             }
67 0 0         FCGI::OpenSocket($sockfile, 100)
68             or die "Can't open FCGI socket '$sockfile': $!";
69             } else {
70 0           0;
71             }
72             };
73              
74 0           my %env;
75             my $request = FCGI::Request
76             ($stdin, $stdout, $stderr
77 0 0         , \%env, $sock, $opts{nointr} ? 0 :&FCGI::FAIL_ACCEPT_ON_INTR);
78              
79 0 0         if (keys %opts) {
80 0           croak "Unknown options: ".join(", ", sort keys %opts);
81             }
82              
83 0     0     local $self->{cf_at_done} = sub {die \"DONE"};
  0            
84 0           local $SIG{PIPE} = 'IGNORE';
85 0           while ($request->Accept >= 0) {
86 0           my Env $env = $self->psgi_fcgi_newenv(\%env, $stdin, $stderr);
87              
88 0 0         if (-e "$dir/.htdebug_env") {
89 0           $self->printenv($stdout, $env);
90 0           next;
91             }
92              
93 0           my $res;
94 0 0   0     if (my $err = catch { $res = $self->call($env) }) {
  0            
95             # XXX: Should I do error specific things?
96 0           $res = $err;
97             }
98              
99 0 0 0       unless (defined $res) {
    0          
    0          
    0          
100 0           die "Empty response";
101             }
102             elsif (ref $res eq 'ARRAY') {
103 0           $self->fcgi_handle_response($res);
104             }
105             elsif (ref $res eq 'CODE') {
106             $res->(sub {
107 0     0     $self->fcgi_handle_response($_[0]);
108 0           });
109             }
110             elsif (not ref $res or UNIVERSAL::can($res, 'message')) {
111 0 0         print $stderr $res if $$stderr ne $stdout;
112 0 0         if ($self->is_debug_allowed($env)) {
113 0           $self->cgi_process_error($res, undef, $stdout, $env);
114             } else {
115 0           $self->cgi_process_error("Application error", undef, $stdout, $env);
116             }
117             }
118             else {
119 0           die "Bad response $res";
120             }
121              
122 0           $request->Finish;
123              
124             # Exit if bootscript is modified.
125 0 0 0       last if -e $progname and -M $progname < $age;
126             }
127             }
128              
129             sub psgi_fcgi_newenv {
130 0     0 0   (my MY $self, my Env $init_env, my ($stdin, $stderr)) = @_;
131 0           require Plack::Util;
132 0           require Plack::Request;
133 0           my Env $env = +{ %$init_env };
134 0           $env->{'psgi.version'} = [1,1];
135             $env->{'psgi.url_scheme'}
136 0 0 0       = ($init_env->{HTTPS}||'off') =~ /^(?:on|1)$/i ? 'https' : 'http';
137 0   0       $env->{'psgi.input'} = $stdin || *STDIN;
138 0   0       $env->{'psgi.errors'} = $stderr || *STDERR;
139 0           $env->{'psgi.multithread'} = &Plack::Util::FALSE;
140 0           $env->{'psgi.multiprocess'} = &Plack::Util::FALSE; # XXX:
141 0           $env->{'psgi.run_once'} = &Plack::Util::FALSE;
142 0           $env->{'psgi.streaming'} = &Plack::Util::FALSE; # XXX: Todo.
143 0           $env->{'psgi.nonblocking'} = &Plack::Util::FALSE;
144             # delete $env->{HTTP_CONTENT_TYPE};
145             # delete $env->{HTTP_CONTENT_LENGTH};
146 0           $env;
147             }
148              
149             sub fcgi_handle_response {
150 0     0 0   my ($self, $res) = @_;
151              
152 0           require HTTP::Status;
153              
154 0           *STDOUT->autoflush(1);
155 0           binmode STDOUT;
156              
157 0           my $hdrs;
158 0           my $message = HTTP::Status::status_message($res->[0]);
159 0           $hdrs = "Status: $res->[0] $message\015\012";
160              
161 0           my $headers = $res->[1];
162 0           while (my ($k, $v) = splice @$headers, 0, 2) {
163 0           $hdrs .= "$k: $v\015\012";
164             }
165 0           $hdrs .= "\015\012";
166              
167 0           print STDOUT $hdrs;
168              
169 0     0     my $cb = sub { print STDOUT $_[0] };
  0            
170 0           my $body = $res->[2];
171 0 0         if (defined $body) {
172 0           Plack::Util::foreach($body, $cb);
173             }
174             else {
175             return Plack::Util::inline_object
176             write => $cb,
177 0     0     close => sub { };
178             }
179             }
180              
181             &YATT::Lite::Breakpoint::break_load_dispatcher_fcgi;
182              
183             1;