clean up my changelog entries
[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
0d83c5de 8use HTTP::Request::AsCGI ();
9use HTTP::Request ();
10use URI ();
21a20b7e 11use Catalyst::Exception ();
32b32c62 12
13=head1 NAME
14
15Catalyst::Controller::WrapCGI - Run CGIs in Catalyst
16
17=head1 VERSION
18
0d83c5de 19Version 0.0030
32b32c62 20
21=cut
22
0d83c5de 23our $VERSION = '0.0030';
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) = @_;
0d83c5de 105
b2a17df2 106 my $res = $self->wrap_cgi($c, $script);
107
108 # if the CGI doesn't set the response code but sets location they were
109 # probably trying to redirect so set 302 for them
110
32b32c62 111 my $location = $res->headers->header('Location');
112
113 if (defined $location && length $location && $res->code == 200) {
b2a17df2 114 $c->res->status(302);
115 } else {
116 $c->res->status($res->code);
117 }
118 $c->res->body($res->content);
119 $c->res->headers($res->headers);
120}
121
32b32c62 122=head2 $self->wrap_cgi($c, $coderef)
123
124Runs $coderef in a CGI environment using L<HTTP::Request::AsCGI>, returns an
125L<HTTP::Response>.
126
127The CGI environment is set up based on $c.
128
457c1d76 129The environment variables to pass on are taken from the configuration for your
130Controller, see L</SYNOPSIS> for an example. If you don't supply a list of
131environment variables to pass, the whole of %ENV is used.
132
21a20b7e 133Used by cgi_to_response (above), which is probably what you want to use as well.
32b32c62 134
135=cut
136
b2a17df2 137sub wrap_cgi {
138 my ($self, $c, $call) = @_;
139 my $req = HTTP::Request->new(
140 map { $c->req->$_ } qw/method uri headers/
141 );
142 my $body = $c->req->body;
143 my $body_content = '';
144
145 $req->content_type($c->req->content_type); # set this now so we can override
146
147 if ($body) { # Slurp from body filehandle
148 local $/; $body_content = <$body>;
149 } else {
150 my $body_params = $c->req->body_parameters;
32b32c62 151 if (%$body_params) {
152 my $encoder = URI->new;
153 $encoder->query_form(%$body_params);
154 $body_content = $encoder->query;
b2a17df2 155 $req->content_type('application/x-www-form-urlencoded');
156 }
157 }
158
c212b57b 159 my $filtered_env = $self->_filtered_env(\%ENV);
457c1d76 160
b2a17df2 161 $req->content($body_content);
162 $req->content_length(length($body_content));
21a20b7e 163
164 my $username_field = $self->{CGI}{username_field} || 'username';
165
166 my $username = (($c->can('user_exists') && $c->user_exists)
167 ? eval { $c->user->obj->$username_field }
b2a17df2 168 : '');
0d83c5de 169
170 my $path_info = '/'.join '/' => @{ $c->req->args };
171
b2a17df2 172 my $env = HTTP::Request::AsCGI->new(
173 $req,
21a20b7e 174 ($username ? (REMOTE_USER => $username) : ()),
c212b57b 175 %$filtered_env,
0d83c5de 176 PATH_INFO => $path_info
b2a17df2 177 );
178
179 {
32b32c62 180 local *STDIN = $REAL_STDIN; # restore the real ones so the filenos
181 local *STDOUT = $REAL_STDOUT; # are 0 and 1 for the env setup
b2a17df2 182
32b32c62 183 my $old = select($REAL_STDOUT); # in case somebody just calls 'print'
b2a17df2 184
185 my $saved_error;
186
187 $env->setup;
188 eval { $call->() };
189 $saved_error = $@;
190 $env->restore;
191
192 select($old);
193
21a20b7e 194 Catalyst::Exception->throw(
195 message => "CGI invocation failed: $saved_error"
196 ) if $saved_error;
b2a17df2 197 }
198
199 return $env->response;
200}
201
0d83c5de 202sub _filtered_env {
203 my ($self, $env) = @_;
204 my @ok;
205
206 my $pass_env = $self->{CGI}{pass_env};
207 $pass_env = [] if not defined $pass_env;
208 $pass_env = [ $pass_env ] unless ref $pass_env;
209
210 my $kill_env = $self->{CGI}{kill_env};
211 $kill_env = [ 'MOD_PERL' ] unless defined $kill_env;
212 $kill_env = [ $kill_env ] unless ref $kill_env;
213
214 if (@$pass_env) {
215 for (@$pass_env) {
216 if (m!^/(.*)/\z!) {
217 my $re = qr/$1/;
218 push @ok, grep /$re/, keys %$env;
219 } else {
220 push @ok, $_;
221 }
222 }
223 } else {
224 @ok = keys %$env;
225 }
226
227 for my $k (@$kill_env) {
228 if ($k =~ m!^/(.*)/\z!) {
229 my $re = qr/$1/;
230 @ok = grep { ! /$re/ } @ok;
231 } else {
232 @ok = grep { $_ ne $k } @ok;
233 }
234 }
235 return { map {; $_ => $env->{$_} } @ok };
236}
237
238
32b32c62 239=head1 ACKNOWLEDGEMENTS
240
241Original development sponsored by L<http://www.altinity.com/>
242
457c1d76 243=head1 SEE ALSO
244
21a20b7e 245L<Catalyst::Controller::CGIBin>, L<CatalystX::GlobalContext>,
457c1d76 246L<Catalyst::Controller>, L<CGI>, L<Catalyst>
247
0d83c5de 248=head1 AUTHORS
249
250Originally written by:
32b32c62 251
252Matt S. Trout, C<< <mst at shadowcat.co.uk> >>
253
0d83c5de 254Contributors:
255
256Rafael Kitover C<< <rkitover at cpan.org> >>
257
258Hans Dieter Pearcey C<< <hdp at cpan.org> >>
259
32b32c62 260=head1 BUGS
261
262Please report any bugs or feature requests to C<bug-catalyst-controller-wrapcgi
263at rt.cpan.org>, or through the web interface at
264L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-Controller-WrapCGI>.
265I will be notified, and then you'll automatically be notified of progress on
266your bug as I make changes.
267
268=head1 SUPPORT
269
270More information at:
271
272=over 4
273
274=item * RT: CPAN's request tracker
275
276L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Catalyst-Controller-WrapCGI>
277
278=item * AnnoCPAN: Annotated CPAN documentation
279
280L<http://annocpan.org/dist/Catalyst-Controller-WrapCGI>
281
282=item * CPAN Ratings
283
284L<http://cpanratings.perl.org/d/Catalyst-Controller-WrapCGI>
285
286=item * Search CPAN
287
288L<http://search.cpan.org/dist/Catalyst-Controller-WrapCGI>
289
290=back
291
292=head1 COPYRIGHT & LICENSE
293
294Copyright (c) 2008 Matt S. Trout
295
296This program is free software; you can redistribute it and/or modify it
297under the same terms as Perl itself.
298
299=cut
300
3011; # End of Catalyst::Controller::WrapCGI
302
21a20b7e 303# vim: expandtab shiftwidth=2 ts=2 tw=80: