C::C::CGIBin - new release
[catagits/Catalyst-Controller-WrapCGI.git] / lib / Catalyst / Controller / WrapCGI.pm
CommitLineData
71e6daf6 1package Catalyst::Controller::WrapCGI;
b2a17df2 2
9cd47364 3use Moose;
4use mro 'c3';
5
6extends 'Catalyst::Controller';
b2a17df2 7
8use HTTP::Request::AsCGI;
9use HTTP::Request;
32b32c62 10use URI;
21a20b7e 11use Catalyst::Exception ();
32b32c62 12
13=head1 NAME
14
15Catalyst::Controller::WrapCGI - Run CGIs in Catalyst
16
17=head1 VERSION
18
fbaba9dd 19Version 0.0029
32b32c62 20
21=cut
22
fbaba9dd 23our $VERSION = '0.0029';
32b32c62 24
25=head1 SYNOPSIS
26
27 package MyApp::Controller::Foo;
28
29 use parent qw/Catalyst::Controller::WrapCGI/;
12d29ebf 30 use CGI ();
32b32c62 31
32 sub hello : Path('cgi-bin/hello.cgi') {
33 my ($self, $c) = @_;
34
35 $self->cgi_to_response($c, sub {
12d29ebf 36 my $q = CGI->new;
37 print $q->header, $q->start_html('Hello'),
38 $q->h1('Catalyst Rocks!'),
39 $q->end_html;
32b32c62 40 });
41 }
42
457c1d76 43In your .conf, configure which environment variables to pass:
44
45 <Controller::Foo>
46 <CGI>
21a20b7e 47 username_field username # used for REMOTE_USER env var
457c1d76 48 pass_env PERL5LIB
49 pass_env PATH
21a20b7e 50 pass_env /^MYAPP_/
c212b57b 51 kill_env MOD_PERL
457c1d76 52 </CGI>
53 </Controller::Foo>
54
55=head1 DESCRIPTION
56
57Allows you to run Perl code in a CGI environment derived from your L<Catalyst>
58context.
59
12d29ebf 60B<*WARNING*>: do not export L<CGI> functions into a Controller, it will break
61with L<Catalyst> 5.8 onward.
62
21a20b7e 63If you just want to run CGIs from files, see L<Catalyst::Controller::CGIBin>.
64
65=head1 CONFIGURATION
66
c212b57b 67C<< $your_controller->{CGI}{pass_env} >> should be an array of environment variables
21a20b7e 68or regular expressions to pass through to your CGIs. Entries surrounded by C</>
69characters are considered regular expressions.
70
c212b57b 71C<< $your_controller->{CGI}{kill_env} >> should be an array of environment
72variables or regular expressions to remove from the environment before passing
73it to your CGIs. Entries surrounded by C</> characters are considered regular
74expressions.
21a20b7e 75
c212b57b 76Default is to pass the whole of C<%ENV>, except for C<MOD_PERL> (that is, the
77default C<kill_env> is C<[ 'MOD_PERL' ]>.
78
79C<< $your_controller->{CGI}{username_field} >> should be the field for your user's name, which will be
80read from C<< $c->user->obj >>. Defaults to 'username'.
21a20b7e 81
82See L</SYNOPSIS> for an example.
83
32b32c62 84=cut
b2a17df2 85
86# Hack-around because Catalyst::Engine::HTTP goes and changes
87# them to be the remote socket, and FCGI.pm does even dumber things.
88
32b32c62 89open my $REAL_STDIN, "<&=".fileno(*STDIN);
90open my $REAL_STDOUT, ">>&=".fileno(*STDOUT);
91
92=head1 METHODS
93
94=head2 $self->cgi_to_response($c, $coderef)
95
96Does the magic of running $coderef in a CGI environment, and populating the
97appropriate parts of your Catalyst context with the results.
98
457c1d76 99Calls wrap_cgi (below.)
100
32b32c62 101=cut
b2a17df2 102
103sub cgi_to_response {
104 my ($self, $c, $script) = @_;
105 my $res = $self->wrap_cgi($c, $script);
106
107 # if the CGI doesn't set the response code but sets location they were
108 # probably trying to redirect so set 302 for them
109
32b32c62 110 my $location = $res->headers->header('Location');
111
112 if (defined $location && length $location && $res->code == 200) {
b2a17df2 113 $c->res->status(302);
114 } else {
115 $c->res->status($res->code);
116 }
117 $c->res->body($res->content);
118 $c->res->headers($res->headers);
119}
120
32b32c62 121=head2 $self->wrap_cgi($c, $coderef)
122
123Runs $coderef in a CGI environment using L<HTTP::Request::AsCGI>, returns an
124L<HTTP::Response>.
125
126The CGI environment is set up based on $c.
127
457c1d76 128The environment variables to pass on are taken from the configuration for your
129Controller, see L</SYNOPSIS> for an example. If you don't supply a list of
130environment variables to pass, the whole of %ENV is used.
131
21a20b7e 132Used by cgi_to_response (above), which is probably what you want to use as well.
32b32c62 133
134=cut
135
c212b57b 136sub _filtered_env {
137 my ($self, $env) = @_;
138 my @ok;
139
140 my $pass_env = $self->{CGI}{pass_env};
141 $pass_env = [] if not defined $pass_env;
142 $pass_env = [ $pass_env ] unless ref $pass_env;
143
144 my $kill_env = $self->{CGI}{kill_env};
145 $kill_env = [ 'MOD_PERL' ] unless defined $kill_env;
146 $kill_env = [ $kill_env ] unless ref $kill_env;
147
148 if (@$pass_env) {
149 for (@$pass_env) {
150 if (m!^/(.*)/\z!) {
151 my $re = qr/$1/;
152 push @ok, grep /$re/, keys %$env;
153 } else {
154 push @ok, $_;
155 }
156 }
157 } else {
158 @ok = keys %$env;
159 }
160
161 for my $k (@$kill_env) {
162 if ($k =~ m!^/(.*)/\z!) {
163 my $re = qr/$1/;
164 @ok = grep { ! /$re/ } @ok;
165 } else {
166 @ok = grep { $_ ne $k } @ok;
167 }
168 }
169 return { map {; $_ => $env->{$_} } @ok };
170}
171
b2a17df2 172sub wrap_cgi {
173 my ($self, $c, $call) = @_;
174 my $req = HTTP::Request->new(
175 map { $c->req->$_ } qw/method uri headers/
176 );
177 my $body = $c->req->body;
178 my $body_content = '';
179
180 $req->content_type($c->req->content_type); # set this now so we can override
181
182 if ($body) { # Slurp from body filehandle
183 local $/; $body_content = <$body>;
184 } else {
185 my $body_params = $c->req->body_parameters;
32b32c62 186 if (%$body_params) {
187 my $encoder = URI->new;
188 $encoder->query_form(%$body_params);
189 $body_content = $encoder->query;
b2a17df2 190 $req->content_type('application/x-www-form-urlencoded');
191 }
192 }
193
c212b57b 194 my $filtered_env = $self->_filtered_env(\%ENV);
457c1d76 195
b2a17df2 196 $req->content($body_content);
197 $req->content_length(length($body_content));
21a20b7e 198
199 my $username_field = $self->{CGI}{username_field} || 'username';
200
201 my $username = (($c->can('user_exists') && $c->user_exists)
202 ? eval { $c->user->obj->$username_field }
b2a17df2 203 : '');
204 my $env = HTTP::Request::AsCGI->new(
205 $req,
21a20b7e 206 ($username ? (REMOTE_USER => $username) : ()),
c212b57b 207 %$filtered_env,
b2a17df2 208 );
209
210 {
32b32c62 211 local *STDIN = $REAL_STDIN; # restore the real ones so the filenos
212 local *STDOUT = $REAL_STDOUT; # are 0 and 1 for the env setup
b2a17df2 213
32b32c62 214 my $old = select($REAL_STDOUT); # in case somebody just calls 'print'
b2a17df2 215
216 my $saved_error;
217
218 $env->setup;
219 eval { $call->() };
220 $saved_error = $@;
221 $env->restore;
222
223 select($old);
224
21a20b7e 225 Catalyst::Exception->throw(
226 message => "CGI invocation failed: $saved_error"
227 ) if $saved_error;
b2a17df2 228 }
229
230 return $env->response;
231}
232
32b32c62 233=head1 ACKNOWLEDGEMENTS
234
235Original development sponsored by L<http://www.altinity.com/>
236
457c1d76 237=head1 SEE ALSO
238
21a20b7e 239L<Catalyst::Controller::CGIBin>, L<CatalystX::GlobalContext>,
457c1d76 240L<Catalyst::Controller>, L<CGI>, L<Catalyst>
241
32b32c62 242=head1 AUTHOR
243
244Matt S. Trout, C<< <mst at shadowcat.co.uk> >>
245
246=head1 BUGS
247
248Please report any bugs or feature requests to C<bug-catalyst-controller-wrapcgi
249at rt.cpan.org>, or through the web interface at
250L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-Controller-WrapCGI>.
251I will be notified, and then you'll automatically be notified of progress on
252your bug as I make changes.
253
254=head1 SUPPORT
255
256More information at:
257
258=over 4
259
260=item * RT: CPAN's request tracker
261
262L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Catalyst-Controller-WrapCGI>
263
264=item * AnnoCPAN: Annotated CPAN documentation
265
266L<http://annocpan.org/dist/Catalyst-Controller-WrapCGI>
267
268=item * CPAN Ratings
269
270L<http://cpanratings.perl.org/d/Catalyst-Controller-WrapCGI>
271
272=item * Search CPAN
273
274L<http://search.cpan.org/dist/Catalyst-Controller-WrapCGI>
275
276=back
277
278=head1 COPYRIGHT & LICENSE
279
280Copyright (c) 2008 Matt S. Trout
281
282This program is free software; you can redistribute it and/or modify it
283under the same terms as Perl itself.
284
285=cut
286
2871; # End of Catalyst::Controller::WrapCGI
288
21a20b7e 289# vim: expandtab shiftwidth=2 ts=2 tw=80: