line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojolicious::Plugin::CGI; |
2
|
34
|
|
|
34
|
|
130244
|
use Mojo::Base 'Mojolicious::Plugin'; |
|
34
|
|
|
|
|
72
|
|
|
34
|
|
|
|
|
245
|
|
3
|
|
|
|
|
|
|
|
4
|
34
|
|
|
34
|
|
7173
|
use File::Basename; |
|
34
|
|
|
|
|
73
|
|
|
34
|
|
|
|
|
2409
|
|
5
|
34
|
|
|
34
|
|
207
|
use File::Spec; |
|
34
|
|
|
|
|
66
|
|
|
34
|
|
|
|
|
837
|
|
6
|
34
|
|
|
34
|
|
17922
|
use IO::Pipely 'pipely'; |
|
34
|
|
|
|
|
84740
|
|
|
34
|
|
|
|
|
2357
|
|
7
|
34
|
|
|
34
|
|
267
|
use Mojo::Util qw(b64_decode encode); |
|
34
|
|
|
|
|
83
|
|
|
34
|
|
|
|
|
1789
|
|
8
|
34
|
|
|
34
|
|
208
|
use POSIX 'WNOHANG'; |
|
34
|
|
|
|
|
74
|
|
|
34
|
|
|
|
|
279
|
|
9
|
34
|
|
|
34
|
|
17641
|
use Perl::OSType 'is_os_type'; |
|
34
|
|
|
|
|
14314
|
|
|
34
|
|
|
|
|
2210
|
|
10
|
34
|
|
|
34
|
|
255
|
use Socket qw(AF_INET inet_aton); |
|
34
|
|
|
|
|
76
|
|
|
34
|
|
|
|
|
1963
|
|
11
|
34
|
|
|
34
|
|
16705
|
use Sys::Hostname; |
|
34
|
|
|
|
|
37548
|
|
|
34
|
|
|
|
|
2918
|
|
12
|
|
|
|
|
|
|
|
13
|
34
|
|
50
|
34
|
|
274
|
use constant CHECK_CHILD_INTERVAL => $ENV{CHECK_CHILD_INTERVAL} || 0.01; |
|
34
|
|
|
|
|
76
|
|
|
34
|
|
|
|
|
3518
|
|
14
|
34
|
|
|
34
|
|
237
|
use constant DEBUG => $ENV{MOJO_PLUGIN_CGI_DEBUG}; |
|
34
|
|
|
|
|
86
|
|
|
34
|
|
|
|
|
2057
|
|
15
|
34
|
|
|
34
|
|
204
|
use constant IS_WINDOWS => is_os_type('Windows'); |
|
34
|
|
|
|
|
71
|
|
|
34
|
|
|
|
|
139
|
|
16
|
34
|
|
|
34
|
|
2262
|
use constant READ => 0; |
|
34
|
|
|
|
|
318
|
|
|
34
|
|
|
|
|
1623
|
|
17
|
34
|
|
|
34
|
|
199
|
use constant WRITE => 1; |
|
34
|
|
|
|
|
64
|
|
|
34
|
|
|
|
|
103259
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our $VERSION = '0.40'; |
20
|
|
|
|
|
|
|
our %ORIGINAL_ENV = %ENV; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
has env => sub { +{%ORIGINAL_ENV} }; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub register { |
25
|
43
|
|
|
43
|
1
|
50759
|
my ($self, $app, $args) = @_; |
26
|
43
|
|
100
|
|
|
328
|
my $pids = $app->{'mojolicious_plugin_cgi.pids'} ||= {}; |
27
|
|
|
|
|
|
|
|
28
|
43
|
100
|
|
|
|
281
|
$args = {route => shift @$args, script => shift @$args} if ref $args eq 'ARRAY'; |
29
|
43
|
|
66
|
|
|
259
|
$args->{env} ||= $self->env; |
30
|
43
|
50
|
|
|
|
157
|
$args->{run} = delete $args->{script} if ref $args->{script} eq 'CODE'; |
31
|
43
|
|
|
|
|
107
|
$args->{pids} = $pids; |
32
|
|
|
|
|
|
|
|
33
|
43
|
100
|
|
9
|
|
278
|
$app->helper('cgi.run' => sub { _run($args, @_) }) unless $app->renderer->helpers->{'cgi.run'}; |
|
9
|
|
|
|
|
101274
|
|
34
|
|
|
|
|
|
|
$app->{'mojolicious_plugin_cgi.tid'} |
35
|
43
|
|
66
|
1684
|
|
16677
|
||= Mojo::IOLoop->recurring(CHECK_CHILD_INTERVAL, sub { local ($?, $!); _waitpids($pids); }); |
|
1684
|
|
|
|
|
15349676
|
|
|
1684
|
|
|
|
|
6754
|
|
36
|
|
|
|
|
|
|
|
37
|
43
|
100
|
66
|
|
|
4632
|
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
|
|
4578
|
$_[0]->stash('cgi.query_string' => $_[0]->req->url->query->to_string); |
43
|
|
|
|
|
|
|
} |
44
|
2
|
|
|
|
|
18
|
); |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
43
|
100
|
|
|
|
197
|
return unless $args->{route}; # just register the helper |
48
|
37
|
50
|
66
|
|
|
207
|
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
|
|
|
|
240
|
unless ref $args->{route}; |
51
|
37
|
100
|
33
|
|
|
21916
|
$args->{script} = File::Spec->rel2abs($args->{script}) || $args->{script} if $args->{script}; |
52
|
37
|
|
|
47
|
|
363
|
$args->{route}->to(cb => sub { _run($args, @_) }); |
|
47
|
|
|
|
|
273282
|
|
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub _child { |
56
|
20
|
|
|
20
|
|
911
|
my ($c, $args, $stdin, $stdout, $stderr) = @_; |
57
|
20
|
100
|
|
|
|
1515
|
my @STDERR = @$stderr ? ('>&', fileno $stderr->[WRITE]) : ('>>', $args->{errlog}); |
58
|
|
|
|
|
|
|
|
59
|
20
|
|
|
|
|
2161
|
Mojo::IOLoop->reset; |
60
|
20
|
|
|
|
|
68650
|
warn "[CGI:$args->{name}:$$] <<< (@{[$stdin->slurp]})\n" if DEBUG; |
61
|
20
|
100
|
50
|
|
|
965
|
open STDIN, '<', $stdin->path or die "STDIN @{[$stdin->path]}: $!" if -s $stdin->path; |
|
0
|
|
|
|
|
0
|
|
62
|
20
|
50
|
|
|
|
4367
|
open STDERR, $STDERR[0], $STDERR[1] or die "STDERR: @$stderr: $!"; |
63
|
20
|
50
|
|
|
|
799
|
open STDOUT, '>&', fileno $stdout->[WRITE] or die "STDOUT: $!"; |
64
|
20
|
|
|
|
|
411
|
select STDERR; |
65
|
20
|
|
|
|
|
458
|
$| = 1; |
66
|
20
|
|
|
|
|
329
|
select STDOUT; |
67
|
20
|
|
|
|
|
164
|
$| = 1; |
68
|
|
|
|
|
|
|
|
69
|
20
|
|
|
|
|
425
|
%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
|
|
178
|
my ($c, $args) = @_; |
80
|
20
|
|
|
|
|
609
|
my $tx = $c->tx; |
81
|
20
|
|
|
|
|
503
|
my $req = $tx->req; |
82
|
20
|
|
|
|
|
473
|
my $headers = $req->headers; |
83
|
20
|
100
|
|
|
|
647
|
my $content_length = $req->content->is_multipart ? $req->body_size : $headers->content_length; |
84
|
20
|
|
|
|
|
1724
|
my %env_headers = (HTTP_COOKIE => '', HTTP_REFERER => ''); |
85
|
20
|
|
|
|
|
154
|
my ($remote_user, $script_name); |
86
|
|
|
|
|
|
|
|
87
|
20
|
|
|
|
|
96
|
for my $name (@{$headers->names}) { |
|
20
|
|
|
|
|
716
|
|
88
|
86
|
|
|
|
|
2829
|
my $key = uc "http_$name"; |
89
|
86
|
|
|
|
|
804
|
$key =~ s!\W!_!g; |
90
|
86
|
|
|
|
|
523
|
$env_headers{$key} = $headers->header($name); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
20
|
100
|
|
|
|
947
|
if (my $userinfo = $c->req->url->to_abs->userinfo) { |
|
|
50
|
|
|
|
|
|
94
|
2
|
50
|
|
|
|
1686
|
$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
|
|
|
|
14852
|
if ($args->{route}) { |
|
|
50
|
|
|
|
|
|
102
|
17
|
|
|
|
|
771
|
$script_name = $c->url_for($args->{route}->name, {path_info => ''})->path->to_string; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
elsif (my $name = $c->stash('script_name')) { |
105
|
3
|
|
|
|
|
181
|
my $name = quotemeta $name; |
106
|
3
|
50
|
|
|
|
42
|
$script_name = $c->req->url->path =~ m!^(.*?/$name)! ? $1 : $c->stash('script_name'); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
return ( |
110
|
20
|
|
|
|
|
1904
|
%{$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
|
|
|
30647
|
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
|
|
243
|
my ($defaults, $c) = (shift, shift); |
135
|
56
|
50
|
|
|
|
305
|
my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}; |
|
0
|
100
|
|
|
|
0
|
|
136
|
56
|
|
66
|
|
|
527
|
my $before = $args->{before} || $defaults->{before}; |
137
|
56
|
|
|
|
|
274
|
my $stdin = _stdin($c); |
138
|
56
|
|
|
|
|
61619
|
my @stdout = pipely; |
139
|
56
|
|
|
|
|
5565
|
my ($pid, $log_key, @stderr); |
140
|
|
|
|
|
|
|
|
141
|
56
|
|
100
|
|
|
1123
|
$args->{$_} ||= $defaults->{$_} for qw(env errlog route run script); |
142
|
56
|
100
|
|
|
|
5164
|
$args->{name} = $args->{run} ? "$args->{run}" : basename $args->{script}; |
143
|
56
|
100
|
|
|
|
270
|
$c->$before($args) if $before; |
144
|
56
|
100
|
|
|
|
576
|
@stderr = (pipely) unless $args->{errlog}; |
145
|
56
|
50
|
|
|
|
102798
|
defined($pid = fork) or die "[CGI:$args->{name}] fork failed: $!"; |
146
|
56
|
100
|
|
|
|
3506
|
_child($c, $args, $stdin, \@stdout, \@stderr) unless $pid; |
147
|
36
|
|
|
|
|
2461
|
$args->{pids}{$pid} = $args->{name}; |
148
|
36
|
|
|
|
|
879
|
$log_key = "CGI:$args->{name}:$pid"; |
149
|
36
|
|
66
|
|
|
2720
|
$c->app->log->debug("[$log_key] START @{[$args->{script} || $args->{run}]}"); |
|
36
|
|
|
|
|
4915
|
|
150
|
|
|
|
|
|
|
|
151
|
36
|
|
|
|
|
1663
|
for my $p (\@stdout, \@stderr) { |
152
|
72
|
100
|
|
|
|
14425
|
next unless $p->[READ]; |
153
|
69
|
|
|
|
|
1368
|
close $p->[WRITE]; |
154
|
69
|
|
|
|
|
2508
|
$p->[READ] = Mojo::IOLoop::Stream->new($p->[READ])->timeout(0); |
155
|
69
|
|
|
|
|
12977
|
Mojo::IOLoop->stream($p->[READ]); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
36
|
|
|
|
|
5011
|
$c->stash('cgi.pid' => $pid, 'cgi.stdin' => $stdin); |
159
|
36
|
|
|
|
|
3249
|
$c->render_later; |
160
|
|
|
|
|
|
|
|
161
|
36
|
100
|
|
|
|
1905
|
$stderr[READ]->on(read => _stderr_cb($c, $log_key)) if $stderr[READ]; |
162
|
36
|
|
|
|
|
939
|
$stdout[READ]->on(read => _stdout_cb($c, $log_key)); |
163
|
|
|
|
|
|
|
$stdout[READ]->on(close => sub { |
164
|
36
|
|
|
36
|
|
69434
|
my $GUARD = 50; |
165
|
36
|
|
|
|
|
182
|
warn "[CGI:$args->{name}:$pid] Child closed STDOUT\n" if DEBUG; |
166
|
36
|
50
|
50
|
|
|
784
|
unlink $stdin->path or die "Could not remove STDIN @{[$stdin->path]}" if -e $stdin->path; |
|
0
|
|
|
|
|
0
|
|
167
|
36
|
|
|
|
|
4706
|
local ($?, $!); |
168
|
|
|
|
|
|
|
_waitpids({$pid => $args->{pids}{$pid}}) |
169
|
36
|
|
66
|
|
|
2403
|
while $args->{pids}{$pid} |
|
|
|
66
|
|
|
|
|
170
|
|
|
|
|
|
|
and kill 0, $pid |
171
|
|
|
|
|
|
|
and $GUARD--; |
172
|
36
|
50
|
|
|
|
473
|
$defaults->{pids}{$pid} = $args->{pids}{$pid} if kill 0, $pid; |
173
|
36
|
100
|
|
|
|
317
|
return $c->finish if $c->res->code; |
174
|
3
|
|
|
|
|
234
|
return $c->render(text => "Could not run CGI script ($?, $!).\n", status => 500); |
175
|
|
|
|
|
|
|
} |
176
|
36
|
|
|
|
|
1315
|
); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _stderr_cb { |
180
|
33
|
|
|
33
|
|
274
|
my ($c, $log_key) = @_; |
181
|
33
|
|
|
|
|
704
|
my $log = $c->app->log; |
182
|
33
|
|
|
|
|
663
|
my $buf = ''; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
return sub { |
185
|
3
|
|
|
3
|
|
11499
|
my ($stream, $chunk) = @_; |
186
|
3
|
|
|
|
|
24
|
warn "[$log_key] !!! ($chunk)\n" if DEBUG; |
187
|
3
|
|
|
|
|
21
|
$buf .= $chunk; |
188
|
3
|
|
|
|
|
189
|
$log->warn("[$log_key] $1") while $buf =~ s!^(.+)[\r\n]+$!!m; |
189
|
33
|
|
|
|
|
1552
|
}; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub _stdout_cb { |
193
|
36
|
|
|
36
|
|
281
|
my ($c, $log_key) = @_; |
194
|
36
|
|
|
|
|
277
|
my $buf = ''; |
195
|
36
|
|
|
|
|
237
|
my $headers; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
return sub { |
198
|
33
|
|
|
33
|
|
177563
|
my ($stream, $chunk) = @_; |
199
|
33
|
|
|
|
|
573
|
warn "[$log_key] >>> ($chunk)\n" if DEBUG; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# true if HTTP header has been written to client |
202
|
33
|
50
|
|
|
|
376
|
return $c->write($chunk) if $headers; |
203
|
|
|
|
|
|
|
|
204
|
33
|
|
|
|
|
351
|
$buf .= $chunk; |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# false until all headers has been read from the CGI script |
207
|
33
|
50
|
|
|
|
1016
|
$buf =~ s/^(.*?\x0a\x0d?\x0a\x0d?)//s or return; |
208
|
33
|
|
|
|
|
610
|
$headers = $1; |
209
|
|
|
|
|
|
|
|
210
|
33
|
100
|
|
|
|
503
|
if ($headers =~ /^HTTP/) { |
211
|
6
|
100
|
|
|
|
96
|
$c->res->code($headers =~ m!^HTTP (\d\d\d)! ? $1 : 200); |
212
|
6
|
|
|
|
|
428
|
$c->res->parse($headers); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
else { |
215
|
27
|
100
|
|
|
|
365
|
$c->res->code($1) if $headers =~ /^Status: (\d\d\d)/m; |
216
|
27
|
100
|
|
|
|
654
|
$c->res->code($headers =~ /Location:/ ? 302 : 200) unless $c->res->code; |
|
|
100
|
|
|
|
|
|
217
|
27
|
|
|
|
|
2961
|
$c->res->parse($c->res->get_start_line_chunk(0) . $headers); |
218
|
|
|
|
|
|
|
} |
219
|
33
|
100
|
|
|
|
47129
|
$c->write($buf) if length $buf; |
220
|
36
|
|
|
|
|
1197
|
}; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub _stdin { |
224
|
56
|
|
|
56
|
|
166
|
my $c = shift; |
225
|
56
|
|
|
|
|
132
|
my $stdin; |
226
|
|
|
|
|
|
|
|
227
|
56
|
100
|
|
|
|
242
|
if ($c->req->content->is_multipart) { |
228
|
2
|
|
|
|
|
60
|
$stdin = Mojo::Asset::File->new; |
229
|
2
|
|
|
|
|
34
|
$stdin->add_chunk($c->req->build_body); |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
else { |
232
|
54
|
|
|
|
|
1029
|
$stdin = $c->req->content->asset; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
56
|
100
|
|
|
|
5797
|
return $stdin if $stdin->isa('Mojo::Asset::File'); |
236
|
54
|
|
|
|
|
435
|
return Mojo::Asset::File->new->add_chunk($stdin->slurp); |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub _waitpids { |
240
|
1720
|
|
|
1720
|
|
4030
|
my $pids = shift; |
241
|
|
|
|
|
|
|
|
242
|
1720
|
|
|
|
|
10972
|
for my $pid (keys %$pids) { |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# no idea why i need to do this, but it seems like waitpid() below return -1 if not |
245
|
36
|
|
|
|
|
1841
|
local $SIG{CHLD} = 'DEFAULT'; |
246
|
36
|
50
|
|
|
|
1493
|
next unless waitpid $pid, WNOHANG; |
247
|
36
|
|
50
|
|
|
325
|
my $name = delete $pids->{$pid} || 'unknown'; |
248
|
36
|
|
|
|
|
259
|
my ($exit_value, $signal) = ($? >> 8, $? & 127); |
249
|
36
|
|
|
|
|
1012
|
warn "[CGI:$name:$pid] Child exit_value=$exit_value ($signal)\n" if DEBUG; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
1; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=encoding utf8 |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=head1 NAME |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
Mojolicious::Plugin::CGI - Run CGI script from Mojolicious |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=head1 VERSION |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
0.40 |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=head1 DESCRIPTION |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
This plugin enables L to run Perl CGI scripts. It does so by forking |
268
|
|
|
|
|
|
|
a new process with a modified environment and reads the STDOUT in a non-blocking |
269
|
|
|
|
|
|
|
manner. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=head1 SYNOPSIS |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=head2 Standard usage |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
use Mojolicious::Lite; |
276
|
|
|
|
|
|
|
plugin CGI => [ "/cgi-bin/script" => "/path/to/cgi/script.pl" ]; |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
Using the code above is enough to run C when accessing |
279
|
|
|
|
|
|
|
L. |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head2 Complex usage |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
plugin CGI => { |
284
|
|
|
|
|
|
|
# Specify the script and mount point |
285
|
|
|
|
|
|
|
script => "/path/to/cgi/script.pl", |
286
|
|
|
|
|
|
|
route => "/some/route", |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# %ENV variables visible from inside the CGI script |
289
|
|
|
|
|
|
|
env => {}, # default is \%ENV |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# Path to where STDERR from cgi script goes |
292
|
|
|
|
|
|
|
errlog => "/path/to/file.log", |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# The "before" hook is called before script start |
295
|
|
|
|
|
|
|
# It receives a Mojolicious::Controller which can be modified |
296
|
|
|
|
|
|
|
before => sub { |
297
|
|
|
|
|
|
|
my $c = shift; |
298
|
|
|
|
|
|
|
$c->req->url->query->param(a => 123); |
299
|
|
|
|
|
|
|
}, |
300
|
|
|
|
|
|
|
}; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
The above contains all the options you can pass on to the plugin. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head2 Helper |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
plugin "CGI"; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# GET /cgi-bin/some-script.cgi/path/info?x=123 |
309
|
|
|
|
|
|
|
get "/cgi-bin/#script_name/*path_info" => {path_info => ''}, sub { |
310
|
|
|
|
|
|
|
my $c = shift; |
311
|
|
|
|
|
|
|
my $name = $c->stash("script_name"); |
312
|
|
|
|
|
|
|
$c->cgi->run(script => File::Spec->rel2abs("/path/to/cgi/$name")); |
313
|
|
|
|
|
|
|
}; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
The helper can take most of the arguments that L takes, with the |
316
|
|
|
|
|
|
|
exception of C. |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
It is critical that "script_name" and "path_info" is present in |
319
|
|
|
|
|
|
|
L. Whether the values are extracted directly |
320
|
|
|
|
|
|
|
from the path or set manually does not matter. |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Note that the helper is registered in all of the examples. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=head2 Running code refs |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
plugin CGI => { |
327
|
|
|
|
|
|
|
route => "/some/path", |
328
|
|
|
|
|
|
|
run => sub { |
329
|
|
|
|
|
|
|
my $cgi = CGI->new; |
330
|
|
|
|
|
|
|
# ... |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
}; |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
Instead of calling a script, you can run a code block when accessing the route. |
335
|
|
|
|
|
|
|
This is (pretty much) safe, even if the code block modifies global state, |
336
|
|
|
|
|
|
|
since it runs in a separate fork/process. |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head2 Support for semicolon in query string |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
plugin CGI => { |
341
|
|
|
|
|
|
|
support_semicolon_in_query_string => 1, |
342
|
|
|
|
|
|
|
... |
343
|
|
|
|
|
|
|
}; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
The code above needs to be added before other plugins or handlers which use |
346
|
|
|
|
|
|
|
L. It will inject a C |
347
|
|
|
|
|
|
|
hook which saves the original QUERY_STRING, before it is split on |
348
|
|
|
|
|
|
|
"&" in L. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=head2 env |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Holds a hash ref containing the environment variables that should be |
355
|
|
|
|
|
|
|
used when starting the CGI script. Defaults to C<%ENV> when this module |
356
|
|
|
|
|
|
|
was loaded. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
This plugin will create a set of environment variables depenendent on the |
359
|
|
|
|
|
|
|
request passed in which is according to the CGI spec. In addition to L, |
360
|
|
|
|
|
|
|
these dynamic variables are set: |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
CONTENT_LENGTH, CONTENT_TYPE, HTTPS, PATH, PATH_INFO, QUERY_STRING, |
363
|
|
|
|
|
|
|
REMOTE_ADDR, REMOTE_HOST, REMOTE_PORT, REMOTE_USER, REQUEST_METHOD, |
364
|
|
|
|
|
|
|
SCRIPT_NAME, SERVER_PORT, SERVER_PROTOCOL. |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Additional static variables: |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
GATEWAY_INTERFACE = "CGI/1.1" |
369
|
|
|
|
|
|
|
SERVER_ADMIN = $ENV{USER} |
370
|
|
|
|
|
|
|
SCRIPT_FILENAME = Script name given as argument to register. |
371
|
|
|
|
|
|
|
SERVER_NAME = Sys::Hostname::hostname() |
372
|
|
|
|
|
|
|
SERVER_SOFTWARE = "Mojolicious::Plugin::CGI" |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
Plus all headers are exposed. Examples: |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
.----------------------------------------. |
377
|
|
|
|
|
|
|
| Header | Variable | |
378
|
|
|
|
|
|
|
|-----------------|----------------------| |
379
|
|
|
|
|
|
|
| Referer | HTTP_REFERER | |
380
|
|
|
|
|
|
|
| User-Agent | HTTP_USER_AGENT | |
381
|
|
|
|
|
|
|
| X-Forwarded-For | HTTP_X_FORWARDED_FOR | |
382
|
|
|
|
|
|
|
'----------------------------------------' |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=head2 register |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
$self->register($app, [ $route => $script ]); |
387
|
|
|
|
|
|
|
$self->register($app, %args); |
388
|
|
|
|
|
|
|
$self->register($app, \%args); |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
C and L need to exist as keys in C<%args> unless given as plain |
391
|
|
|
|
|
|
|
arguments. |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
C<$route> can be either a plain path or a route object. |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
Copyright (C) 2014, Jan Henning Thorsen |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
This program is free software, you can redistribute it and/or modify it under |
400
|
|
|
|
|
|
|
the terms of the Artistic License version 2.0. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=head1 AUTHOR |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Jan Henning Thorsen - C |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=cut |