| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Mojolicious::Plugin::CGI; |
|
2
|
34
|
|
|
34
|
|
75480
|
use Mojo::Base 'Mojolicious::Plugin'; |
|
|
34
|
|
|
|
|
90
|
|
|
|
34
|
|
|
|
|
234
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
34
|
|
|
34
|
|
6154
|
use File::Basename; |
|
|
34
|
|
|
|
|
78
|
|
|
|
34
|
|
|
|
|
2105
|
|
|
5
|
34
|
|
|
34
|
|
185
|
use File::Spec; |
|
|
34
|
|
|
|
|
78
|
|
|
|
34
|
|
|
|
|
921
|
|
|
6
|
34
|
|
|
34
|
|
14471
|
use IO::Pipely 'pipely'; |
|
|
34
|
|
|
|
|
61172
|
|
|
|
34
|
|
|
|
|
1991
|
|
|
7
|
34
|
|
|
34
|
|
247
|
use Mojo::Util qw(b64_decode encode); |
|
|
34
|
|
|
|
|
76
|
|
|
|
34
|
|
|
|
|
1728
|
|
|
8
|
34
|
|
|
34
|
|
198
|
use POSIX 'WNOHANG'; |
|
|
34
|
|
|
|
|
84
|
|
|
|
34
|
|
|
|
|
243
|
|
|
9
|
34
|
|
|
34
|
|
15127
|
use Perl::OSType 'is_os_type'; |
|
|
34
|
|
|
|
|
11045
|
|
|
|
34
|
|
|
|
|
2068
|
|
|
10
|
34
|
|
|
34
|
|
228
|
use Socket qw(AF_INET inet_aton); |
|
|
34
|
|
|
|
|
83
|
|
|
|
34
|
|
|
|
|
1464
|
|
|
11
|
34
|
|
|
34
|
|
12710
|
use Sys::Hostname; |
|
|
34
|
|
|
|
|
27702
|
|
|
|
34
|
|
|
|
|
1951
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
34
|
|
50
|
34
|
|
233
|
use constant CHECK_CHILD_INTERVAL => $ENV{CHECK_CHILD_INTERVAL} || 0.01; |
|
|
34
|
|
|
|
|
69
|
|
|
|
34
|
|
|
|
|
2798
|
|
|
14
|
34
|
|
|
34
|
|
207
|
use constant DEBUG => $ENV{MOJO_PLUGIN_CGI_DEBUG}; |
|
|
34
|
|
|
|
|
66
|
|
|
|
34
|
|
|
|
|
1802
|
|
|
15
|
34
|
|
|
34
|
|
191
|
use constant IS_WINDOWS => is_os_type('Windows'); |
|
|
34
|
|
|
|
|
70
|
|
|
|
34
|
|
|
|
|
124
|
|
|
16
|
34
|
|
|
34
|
|
2225
|
use constant READ => 0; |
|
|
34
|
|
|
|
|
73
|
|
|
|
34
|
|
|
|
|
1337
|
|
|
17
|
34
|
|
|
34
|
|
177
|
use constant WRITE => 1; |
|
|
34
|
|
|
|
|
65
|
|
|
|
34
|
|
|
|
|
77290
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our $VERSION = '0.38'; |
|
20
|
|
|
|
|
|
|
our %ORIGINAL_ENV = %ENV; |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
has env => sub { +{%ORIGINAL_ENV} }; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub register { |
|
25
|
43
|
|
|
43
|
1
|
24022
|
my ($self, $app, $args) = @_; |
|
26
|
43
|
|
100
|
|
|
269
|
my $pids = $app->{'mojolicious_plugin_cgi.pids'} ||= {}; |
|
27
|
|
|
|
|
|
|
|
|
28
|
43
|
100
|
|
|
|
210
|
$args = {route => shift @$args, script => shift @$args} if ref $args eq 'ARRAY'; |
|
29
|
43
|
|
66
|
|
|
250
|
$args->{env} ||= $self->env; |
|
30
|
43
|
50
|
|
|
|
193
|
$args->{run} = delete $args->{script} if ref $args->{script} eq 'CODE'; |
|
31
|
43
|
|
|
|
|
94
|
$args->{pids} = $pids; |
|
32
|
|
|
|
|
|
|
|
|
33
|
43
|
100
|
|
9
|
|
243
|
$app->helper('cgi.run' => sub { _run($args, @_) }) unless $app->renderer->helpers->{'cgi.run'}; |
|
|
9
|
|
|
|
|
62497
|
|
|
34
|
|
|
|
|
|
|
$app->{'mojolicious_plugin_cgi.tid'} |
|
35
|
43
|
|
66
|
1139
|
|
1964
|
||= Mojo::IOLoop->recurring(CHECK_CHILD_INTERVAL, sub { local ($?, $!); _waitpids($pids); }); |
|
|
1139
|
|
|
|
|
10182947
|
|
|
|
1139
|
|
|
|
|
5013
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
43
|
100
|
66
|
|
|
3936
|
if ($args->{support_semicolon_in_query_string} |
|
38
|
|
|
|
|
|
|
and !$app->{'mojolicious_plugin_cgi.before_dispatch'}++) |
|
39
|
|
|
|
|
|
|
{ |
|
40
|
|
|
|
|
|
|
$app->hook( |
|
41
|
|
|
|
|
|
|
before_dispatch => sub { |
|
42
|
2
|
|
|
2
|
|
17810
|
$_[0]->stash('cgi.query_string' => $_[0]->req->url->query->to_string); |
|
43
|
|
|
|
|
|
|
} |
|
44
|
2
|
|
|
|
|
12
|
); |
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
43
|
100
|
|
|
|
183
|
return unless $args->{route}; # just register the helper |
|
48
|
37
|
50
|
66
|
|
|
231
|
die "Neither 'run', nor 'script' is specified." unless $args->{run} or $args->{script}; |
|
49
|
|
|
|
|
|
|
$args->{route} = $app->routes->any("$args->{route}/*path_info", {path_info => ''}) |
|
50
|
37
|
50
|
|
|
|
230
|
unless ref $args->{route}; |
|
51
|
37
|
100
|
33
|
|
|
16959
|
$args->{script} = File::Spec->rel2abs($args->{script}) || $args->{script} if $args->{script}; |
|
52
|
37
|
|
|
47
|
|
270
|
$args->{route}->to(cb => sub { _run($args, @_) }); |
|
|
47
|
|
|
|
|
394163
|
|
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub _child { |
|
56
|
20
|
|
|
20
|
|
483
|
my ($c, $args, $stdin, $stdout, $stderr) = @_; |
|
57
|
20
|
100
|
|
|
|
941
|
my @STDERR = @$stderr ? ('>&', fileno $stderr->[WRITE]) : ('>>', $args->{errlog}); |
|
58
|
|
|
|
|
|
|
|
|
59
|
20
|
|
|
|
|
1389
|
Mojo::IOLoop->reset; |
|
60
|
20
|
|
|
|
|
44472
|
warn "[CGI:$args->{name}:$$] <<< (@{[$stdin->slurp]})\n" if DEBUG; |
|
61
|
20
|
100
|
50
|
|
|
449
|
open STDIN, '<', $stdin->path or die "STDIN @{[$stdin->path]}: $!" if -s $stdin->path; |
|
|
0
|
|
|
|
|
0
|
|
|
62
|
20
|
50
|
|
|
|
2556
|
open STDERR, $STDERR[0], $STDERR[1] or die "STDERR: @$stderr: $!"; |
|
63
|
20
|
50
|
|
|
|
302
|
open STDOUT, '>&', fileno $stdout->[WRITE] or die "STDOUT: $!"; |
|
64
|
20
|
|
|
|
|
223
|
select STDERR; |
|
65
|
20
|
|
|
|
|
271
|
$| = 1; |
|
66
|
20
|
|
|
|
|
204
|
select STDOUT; |
|
67
|
20
|
|
|
|
|
98
|
$| = 1; |
|
68
|
|
|
|
|
|
|
|
|
69
|
20
|
|
|
|
|
333
|
%ENV = _emulate_environment($c, $args); |
|
70
|
|
|
|
|
|
|
$args->{run} ? $args->{run}->($c) : exec $args->{script} |
|
71
|
20
|
50
|
50
|
|
|
0
|
|| die "Could not execute $args->{script}: $!"; |
|
72
|
|
|
|
|
|
|
|
|
73
|
0
|
|
|
|
|
0
|
eval { POSIX::_exit($!) } unless IS_WINDOWS; |
|
|
0
|
|
|
|
|
0
|
|
|
74
|
0
|
|
|
|
|
0
|
eval { CORE::kill KILL => $$ }; |
|
|
0
|
|
|
|
|
0
|
|
|
75
|
0
|
|
|
|
|
0
|
exit $!; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub _emulate_environment { |
|
79
|
20
|
|
|
20
|
|
124
|
my ($c, $args) = @_; |
|
80
|
20
|
|
|
|
|
292
|
my $tx = $c->tx; |
|
81
|
20
|
|
|
|
|
416
|
my $req = $tx->req; |
|
82
|
20
|
|
|
|
|
395
|
my $headers = $req->headers; |
|
83
|
20
|
100
|
|
|
|
319
|
my $content_length = $req->content->is_multipart ? $req->body_size : $headers->content_length; |
|
84
|
20
|
|
|
|
|
1219
|
my %env_headers = (HTTP_COOKIE => '', HTTP_REFERER => ''); |
|
85
|
20
|
|
|
|
|
95
|
my ($remote_user, $script_name); |
|
86
|
|
|
|
|
|
|
|
|
87
|
20
|
|
|
|
|
76
|
for my $name (@{$headers->names}) { |
|
|
20
|
|
|
|
|
357
|
|
|
88
|
86
|
|
|
|
|
2083
|
my $key = uc "http_$name"; |
|
89
|
86
|
|
|
|
|
546
|
$key =~ s!\W!_!g; |
|
90
|
86
|
|
|
|
|
721
|
$env_headers{$key} = $headers->header($name); |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
20
|
100
|
|
|
|
473
|
if (my $userinfo = $c->req->url->to_abs->userinfo) { |
|
|
|
50
|
|
|
|
|
|
|
94
|
2
|
50
|
|
|
|
1032
|
$remote_user = $userinfo =~ /([^:]+)/ ? $1 : ''; |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
elsif (my $authenticate = $headers->authorization) { |
|
97
|
0
|
0
|
|
|
|
0
|
$remote_user = $authenticate =~ /Basic\s+(.*)/ ? b64_decode $1 : ''; |
|
98
|
0
|
0
|
|
|
|
0
|
$remote_user = $remote_user =~ /([^:]+)/ ? $1 : ''; |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
|
|
101
|
20
|
100
|
|
|
|
9809
|
if ($args->{route}) { |
|
|
|
50
|
|
|
|
|
|
|
102
|
17
|
|
|
|
|
426
|
$script_name = $c->url_for($args->{route}->name, {path_info => ''})->path->to_string; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
elsif (my $name = $c->stash('script_name')) { |
|
105
|
3
|
|
|
|
|
70
|
my $name = quotemeta $name; |
|
106
|
3
|
50
|
|
|
|
17
|
$script_name = $c->req->url->path =~ m!^(.*?/$name)! ? $1 : $c->stash('script_name'); |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
return ( |
|
110
|
20
|
|
|
|
|
1013
|
%{$args->{env}}, |
|
111
|
|
|
|
|
|
|
CONTENT_LENGTH => $content_length || 0, |
|
112
|
|
|
|
|
|
|
CONTENT_TYPE => $headers->content_type || '', |
|
113
|
|
|
|
|
|
|
GATEWAY_INTERFACE => 'CGI/1.1', |
|
114
|
|
|
|
|
|
|
HTTPS => $req->is_secure ? 'YES' : 'NO', |
|
115
|
|
|
|
|
|
|
%env_headers, |
|
116
|
|
|
|
|
|
|
PATH_INFO => '/' . encode('UTF-8', $c->stash('path_info') // ''), |
|
117
|
|
|
|
|
|
|
QUERY_STRING => $c->stash('cgi.query_string') || $req->url->query->to_string, |
|
118
|
|
|
|
|
|
|
REMOTE_ADDR => $tx->remote_address, |
|
119
|
|
|
|
|
|
|
REMOTE_HOST => gethostbyaddr(inet_aton($tx->remote_address || '127.0.0.1'), AF_INET) || '', |
|
120
|
|
|
|
|
|
|
REMOTE_PORT => $tx->remote_port, |
|
121
|
|
|
|
|
|
|
REMOTE_USER => $remote_user || '', |
|
122
|
|
|
|
|
|
|
REQUEST_METHOD => $req->method, |
|
123
|
|
|
|
|
|
|
SCRIPT_FILENAME => $args->{script} || '', |
|
124
|
|
|
|
|
|
|
SCRIPT_NAME => $script_name || $args->{name}, |
|
125
|
20
|
50
|
100
|
|
|
16321
|
SERVER_ADMIN => $ENV{USER} || '', |
|
|
|
50
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
126
|
|
|
|
|
|
|
SERVER_NAME => hostname, |
|
127
|
|
|
|
|
|
|
SERVER_PORT => $tx->local_port, |
|
128
|
|
|
|
|
|
|
SERVER_PROTOCOL => $req->is_secure ? 'HTTPS' : 'HTTP', # TODO: Version is missing |
|
129
|
|
|
|
|
|
|
SERVER_SOFTWARE => __PACKAGE__, |
|
130
|
|
|
|
|
|
|
); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub _run { |
|
134
|
56
|
|
|
56
|
|
459
|
my ($defaults, $c) = (shift, shift); |
|
135
|
56
|
50
|
|
|
|
310
|
my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}; |
|
|
0
|
100
|
|
|
|
0
|
|
|
136
|
56
|
|
66
|
|
|
477
|
my $before = $args->{before} || $defaults->{before}; |
|
137
|
56
|
|
|
|
|
228
|
my $stdin = _stdin($c); |
|
138
|
56
|
|
|
|
|
50096
|
my @stdout = pipely; |
|
139
|
56
|
|
|
|
|
4018
|
my ($pid, $log_key, @stderr); |
|
140
|
|
|
|
|
|
|
|
|
141
|
56
|
|
100
|
|
|
1172
|
$args->{$_} ||= $defaults->{$_} for qw(env errlog route run script); |
|
142
|
56
|
100
|
|
|
|
3285
|
$args->{name} = $args->{run} ? "$args->{run}" : basename $args->{script}; |
|
143
|
56
|
100
|
|
|
|
263
|
$c->$before($args) if $before; |
|
144
|
56
|
100
|
|
|
|
537
|
@stderr = (pipely) unless $args->{errlog}; |
|
145
|
56
|
50
|
|
|
|
74620
|
defined($pid = fork) or die "[CGI:$args->{name}] fork failed: $!"; |
|
146
|
56
|
100
|
|
|
|
2020
|
_child($c, $args, $stdin, \@stdout, \@stderr) unless $pid; |
|
147
|
36
|
|
|
|
|
1376
|
$args->{pids}{$pid} = $args->{name}; |
|
148
|
36
|
|
|
|
|
533
|
$log_key = "CGI:$args->{name}:$pid"; |
|
149
|
36
|
|
66
|
|
|
1641
|
$c->app->log->debug("[$log_key] START @{[$args->{script} || $args->{run}]}"); |
|
|
36
|
|
|
|
|
2714
|
|
|
150
|
|
|
|
|
|
|
|
|
151
|
36
|
|
|
|
|
2144
|
for my $p (\@stdout, \@stderr) { |
|
152
|
72
|
100
|
|
|
|
9615
|
next unless $p->[READ]; |
|
153
|
69
|
|
|
|
|
721
|
close $p->[WRITE]; |
|
154
|
69
|
|
|
|
|
1414
|
$p->[READ] = Mojo::IOLoop::Stream->new($p->[READ])->timeout(0); |
|
155
|
69
|
|
|
|
|
5295
|
Mojo::IOLoop->stream($p->[READ]); |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
$c->delay( |
|
159
|
|
|
|
|
|
|
sub { |
|
160
|
36
|
|
|
36
|
|
20151
|
my ($delay) = @_; |
|
161
|
36
|
|
|
|
|
273
|
$c->stash('cgi.pid' => $pid, 'cgi.stdin' => $stdin); |
|
162
|
36
|
100
|
|
|
|
1084
|
$stderr[READ]->on(read => _stderr_cb($c, $log_key)) if $stderr[READ]; |
|
163
|
36
|
|
|
|
|
392
|
$stdout[READ]->on(read => _stdout_cb($c, $log_key)); |
|
164
|
36
|
|
|
|
|
297
|
$stdout[READ]->on(close => $delay->begin); |
|
165
|
|
|
|
|
|
|
}, |
|
166
|
|
|
|
|
|
|
sub { |
|
167
|
36
|
|
|
36
|
|
59504
|
my ($delay) = @_; |
|
168
|
36
|
|
|
|
|
110
|
my $GUARD = 50; |
|
169
|
36
|
|
|
|
|
87
|
warn "[CGI:$args->{name}:$pid] Child closed STDOUT\n" if DEBUG; |
|
170
|
36
|
50
|
50
|
|
|
382
|
unlink $stdin->path or die "Could not remove STDIN @{[$stdin->path]}" if -e $stdin->path; |
|
|
0
|
|
|
|
|
0
|
|
|
171
|
36
|
|
|
|
|
3638
|
local ($?, $!); |
|
172
|
|
|
|
|
|
|
_waitpids({$pid => $args->{pids}{$pid}}) |
|
173
|
36
|
|
66
|
|
|
1101
|
while $args->{pids}{$pid} |
|
|
|
|
66
|
|
|
|
|
|
174
|
|
|
|
|
|
|
and kill 0, $pid |
|
175
|
|
|
|
|
|
|
and $GUARD--; |
|
176
|
36
|
50
|
|
|
|
185
|
$defaults->{pids}{$pid} = $args->{pids}{$pid} if kill 0, $pid; |
|
177
|
36
|
100
|
|
|
|
216
|
return $c->finish if $c->res->code; |
|
178
|
3
|
|
|
|
|
132
|
return $c->render(text => "Could not run CGI script ($?, $!).\n", status => 500); |
|
179
|
|
|
|
|
|
|
}, |
|
180
|
36
|
|
|
|
|
6666
|
); |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub _stderr_cb { |
|
184
|
33
|
|
|
33
|
|
147
|
my ($c, $log_key) = @_; |
|
185
|
33
|
|
|
|
|
161
|
my $log = $c->app->log; |
|
186
|
33
|
|
|
|
|
419
|
my $buf = ''; |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
return sub { |
|
189
|
3
|
|
|
3
|
|
19074
|
my ($stream, $chunk) = @_; |
|
190
|
3
|
|
|
|
|
9
|
warn "[$log_key] !!! ($chunk)\n" if DEBUG; |
|
191
|
3
|
|
|
|
|
27
|
$buf .= $chunk; |
|
192
|
3
|
|
|
|
|
99
|
$log->warn("[$log_key] $1") while $buf =~ s!^(.+)[\r\n]+$!!m; |
|
193
|
33
|
|
|
|
|
598
|
}; |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _stdout_cb { |
|
197
|
36
|
|
|
36
|
|
159
|
my ($c, $log_key) = @_; |
|
198
|
36
|
|
|
|
|
181
|
my $buf = ''; |
|
199
|
36
|
|
|
|
|
97
|
my $headers; |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
return sub { |
|
202
|
33
|
|
|
33
|
|
157837
|
my ($stream, $chunk) = @_; |
|
203
|
33
|
|
|
|
|
148
|
warn "[$log_key] >>> ($chunk)\n" if DEBUG; |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# true if HTTP header has been written to client |
|
206
|
33
|
50
|
|
|
|
237
|
return $c->write($chunk) if $headers; |
|
207
|
|
|
|
|
|
|
|
|
208
|
33
|
|
|
|
|
204
|
$buf .= $chunk; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# false until all headers has been read from the CGI script |
|
211
|
33
|
50
|
|
|
|
529
|
$buf =~ s/^(.*?\x0a\x0d?\x0a\x0d?)//s or return; |
|
212
|
33
|
|
|
|
|
309
|
$headers = $1; |
|
213
|
|
|
|
|
|
|
|
|
214
|
33
|
100
|
|
|
|
278
|
if ($headers =~ /^HTTP/) { |
|
215
|
6
|
100
|
|
|
|
42
|
$c->res->code($headers =~ m!^HTTP (\d\d\d)! ? $1 : 200); |
|
216
|
6
|
|
|
|
|
231
|
$c->res->parse($headers); |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
else { |
|
219
|
27
|
100
|
|
|
|
203
|
$c->res->code($1) if $headers =~ /^Status: (\d\d\d)/m; |
|
220
|
27
|
100
|
|
|
|
350
|
$c->res->code($headers =~ /Location:/ ? 302 : 200) unless $c->res->code; |
|
|
|
100
|
|
|
|
|
|
|
221
|
27
|
|
|
|
|
1518
|
$c->res->parse($c->res->get_start_line_chunk(0) . $headers); |
|
222
|
|
|
|
|
|
|
} |
|
223
|
33
|
100
|
|
|
|
25748
|
$c->write($buf) if length $buf; |
|
224
|
36
|
|
|
|
|
470
|
}; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub _stdin { |
|
228
|
56
|
|
|
56
|
|
166
|
my $c = shift; |
|
229
|
56
|
|
|
|
|
124
|
my $stdin; |
|
230
|
|
|
|
|
|
|
|
|
231
|
56
|
100
|
|
|
|
232
|
if ($c->req->content->is_multipart) { |
|
232
|
2
|
|
|
|
|
108
|
$stdin = Mojo::Asset::File->new; |
|
233
|
2
|
|
|
|
|
38
|
$stdin->add_chunk($c->req->build_body); |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
else { |
|
236
|
54
|
|
|
|
|
1046
|
$stdin = $c->req->content->asset; |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
56
|
100
|
|
|
|
6220
|
return $stdin if $stdin->isa('Mojo::Asset::File'); |
|
240
|
54
|
|
|
|
|
300
|
return Mojo::Asset::File->new->add_chunk($stdin->slurp); |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub _waitpids { |
|
244
|
1175
|
|
|
1175
|
|
3052
|
my $pids = shift; |
|
245
|
|
|
|
|
|
|
|
|
246
|
1175
|
|
|
|
|
6876
|
for my $pid (keys %$pids) { |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# no idea why i need to do this, but it seems like waitpid() below return -1 if not |
|
249
|
36
|
|
|
|
|
603
|
local $SIG{CHLD} = 'DEFAULT'; |
|
250
|
36
|
50
|
|
|
|
1243
|
next unless waitpid $pid, WNOHANG; |
|
251
|
36
|
|
50
|
|
|
230
|
my $name = delete $pids->{$pid} || 'unknown'; |
|
252
|
36
|
|
|
|
|
186
|
my ($exit_value, $signal) = ($? >> 8, $? & 127); |
|
253
|
36
|
|
|
|
|
485
|
warn "[CGI:$name:$pid] Child exit_value=$exit_value ($signal)\n" if DEBUG; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
1; |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=encoding utf8 |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=head1 NAME |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
Mojolicious::Plugin::CGI - Run CGI script from Mojolicious |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=head1 VERSION |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
0.38 |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
This plugin enable L to run Perl CGI scripts. It does so by forking |
|
272
|
|
|
|
|
|
|
a new process with a modified environment and reads the STDOUT in a non-blocking |
|
273
|
|
|
|
|
|
|
manner. |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=head2 Standard usage |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
use Mojolicious::Lite; |
|
280
|
|
|
|
|
|
|
plugin CGI => [ "/cgi-bin/script" => "/path/to/cgi/script.pl" ]; |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
Using the code above is enough to run C when accessing |
|
283
|
|
|
|
|
|
|
L. |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=head2 Complex usage |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
plugin CGI => { |
|
288
|
|
|
|
|
|
|
# Specify the script and mount point |
|
289
|
|
|
|
|
|
|
script => "/path/to/cgi/script.pl", |
|
290
|
|
|
|
|
|
|
route => "/some/route", |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# %ENV variables visible from inside the CGI script |
|
293
|
|
|
|
|
|
|
env => {}, # default is \%ENV |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# Path to where STDERR from cgi script goes |
|
296
|
|
|
|
|
|
|
errlog => "/path/to/file.log", |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# The "before" hook is called before script start |
|
299
|
|
|
|
|
|
|
# It receives a Mojolicious::Controller which can be modified |
|
300
|
|
|
|
|
|
|
before => sub { |
|
301
|
|
|
|
|
|
|
my $c = shift; |
|
302
|
|
|
|
|
|
|
$c->req->url->query->param(a => 123); |
|
303
|
|
|
|
|
|
|
}, |
|
304
|
|
|
|
|
|
|
}; |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
The above contains all the options you can pass on to the plugin. |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=head2 Helper |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
plugin "CGI"; |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# GET /cgi-bin/some-script.cgi/path/info?x=123 |
|
313
|
|
|
|
|
|
|
get "/cgi-bin/#script_name/*path_info" => {path_info => ''}, sub { |
|
314
|
|
|
|
|
|
|
my $c = shift; |
|
315
|
|
|
|
|
|
|
my $name = $c->stash("script_name"); |
|
316
|
|
|
|
|
|
|
$c->cgi->run(script => File::Spec->rel2abs("/path/to/cgi/$name")); |
|
317
|
|
|
|
|
|
|
}; |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
The helper can take most the arguments that L takes, with the |
|
320
|
|
|
|
|
|
|
exception of C. |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
It is critical that "script_name" and "path_info" is present in |
|
323
|
|
|
|
|
|
|
L. If the values are extracted directly |
|
324
|
|
|
|
|
|
|
from the path or set manually does not matter. |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Note that the helper is registered in all of the examples. |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=head2 Running code refs |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
plugin CGI => { |
|
331
|
|
|
|
|
|
|
route => "/some/path", |
|
332
|
|
|
|
|
|
|
run => sub { |
|
333
|
|
|
|
|
|
|
my $cgi = CGI->new; |
|
334
|
|
|
|
|
|
|
# ... |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
}; |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Instead of calling a script, you can run a code block when accessing the route. |
|
339
|
|
|
|
|
|
|
This is (pretty much) safe, even if the code block modifies global state, |
|
340
|
|
|
|
|
|
|
since it runs in a separate fork/process. |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head2 Support for semicolon in query string |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
plugin CGI => { |
|
345
|
|
|
|
|
|
|
support_semicolon_in_query_string => 1, |
|
346
|
|
|
|
|
|
|
... |
|
347
|
|
|
|
|
|
|
}; |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
The code above need to be added before other plugins or handler which use |
|
350
|
|
|
|
|
|
|
L. It will inject a C |
|
351
|
|
|
|
|
|
|
hook which saves the original QUERY_STRING, before it is split on |
|
352
|
|
|
|
|
|
|
"&" in L. |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=head2 env |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
Holds a hash ref containing the environment variables that should be |
|
359
|
|
|
|
|
|
|
used when starting the CGI script. Defaults to C<%ENV> when this module |
|
360
|
|
|
|
|
|
|
was loaded. |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
This plugin will create a set of environment variables depenendent on the |
|
363
|
|
|
|
|
|
|
request passed in which is according to the CGI spec. In addition to L, |
|
364
|
|
|
|
|
|
|
these dynamic variables are set: |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
CONTENT_LENGTH, CONTENT_TYPE, HTTPS, PATH, PATH_INFO, QUERY_STRING, |
|
367
|
|
|
|
|
|
|
REMOTE_ADDR, REMOTE_HOST, REMOTE_PORT, REMOTE_USER, REQUEST_METHOD, |
|
368
|
|
|
|
|
|
|
SCRIPT_NAME, SERVER_PORT, SERVER_PROTOCOL. |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Additional static variables: |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
GATEWAY_INTERFACE = "CGI/1.1" |
|
373
|
|
|
|
|
|
|
SERVER_ADMIN = $ENV{USER} |
|
374
|
|
|
|
|
|
|
SCRIPT_FILENAME = Script name given as argument to register. |
|
375
|
|
|
|
|
|
|
SERVER_NAME = Sys::Hostname::hostname() |
|
376
|
|
|
|
|
|
|
SERVER_SOFTWARE = "Mojolicious::Plugin::CGI" |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
Plus all headers are exposed. Examples: |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
.----------------------------------------. |
|
381
|
|
|
|
|
|
|
| Header | Variable | |
|
382
|
|
|
|
|
|
|
|-----------------|----------------------| |
|
383
|
|
|
|
|
|
|
| Referer | HTTP_REFERER | |
|
384
|
|
|
|
|
|
|
| User-Agent | HTTP_USER_AGENT | |
|
385
|
|
|
|
|
|
|
| X-Forwarded-For | HTTP_X_FORWARDED_FOR | |
|
386
|
|
|
|
|
|
|
'----------------------------------------' |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=head2 register |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
$self->register($app, [ $route => $script ]); |
|
391
|
|
|
|
|
|
|
$self->register($app, %args); |
|
392
|
|
|
|
|
|
|
$self->register($app, \%args); |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
C and L need to exist as keys in C<%args> unless given as plain |
|
395
|
|
|
|
|
|
|
arguments. |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
C<$route> can be either a plain path or a route object. |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
Copyright (C) 2014, Jan Henning Thorsen |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
This program is free software, you can redistribute it and/or modify it under |
|
404
|
|
|
|
|
|
|
the terms of the Artistic License version 2.0. |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=head1 AUTHOR |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
Jan Henning Thorsen - C |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=cut |