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 |