Added detection of older engine versions
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
CommitLineData
fc7ec1d9 1package Catalyst;
2
3use strict;
fbcc39ad 4use base 'Catalyst::Base';
5use bytes;
fc7ec1d9 6use UNIVERSAL::require;
a2f2cde9 7use Catalyst::Exception;
fc7ec1d9 8use Catalyst::Log;
fbcc39ad 9use Catalyst::Request;
10use Catalyst::Request::Upload;
11use Catalyst::Response;
812a28c9 12use Catalyst::Utils;
5d9a6d47 13use NEXT;
fbcc39ad 14use Text::ASCIITable;
4f6748f1 15use Path::Class;
fbcc39ad 16use Time::HiRes qw/gettimeofday tv_interval/;
17use URI;
18use Scalar::Util qw/weaken/;
fc7ec1d9 19
66e28e3f 20__PACKAGE__->mk_accessors(
21 qw/counter depth request response state action namespace/
22);
10dd6896 23
fbcc39ad 24# Laziness++
25*comp = \&component;
26*req = \&request;
27*res = \&response;
28
29# For backwards compatibility
30*finalize_output = \&finalize_body;
31
32# For statistics
33our $COUNT = 1;
34our $START = time;
35our $RECURSION = 1000;
36our $DETACH = "catalyst_detach\n";
37
38require Module::Pluggable::Fast;
39
40# Helper script generation
b41ee097 41our $CATALYST_SCRIPT_GEN = 9;
fbcc39ad 42
43__PACKAGE__->mk_classdata($_)
44 for qw/components arguments dispatcher engine log/;
45
bf88a181 46our $VERSION = '5.49_02';
189e2a51 47
fbcc39ad 48sub import {
49 my ( $class, @arguments ) = @_;
50
51 # We have to limit $class to Catalyst to avoid pushing Catalyst upon every
52 # callers @ISA.
53 return unless $class eq 'Catalyst';
54
55 my $caller = caller(0);
56
57 unless ( $caller->isa('Catalyst') ) {
58 no strict 'refs';
59 push @{"$caller\::ISA"}, $class;
60 }
61
62 $caller->arguments( [@arguments] );
63 $caller->setup_home;
64}
fc7ec1d9 65
66=head1 NAME
67
68Catalyst - The Elegant MVC Web Application Framework
69
70=head1 SYNOPSIS
71
72 # use the helper to start a new application
91864987 73 catalyst.pl MyApp
fc7ec1d9 74 cd MyApp
75
76 # add models, views, controllers
ae4e40a7 77 script/myapp_create.pl model Something
78 script/myapp_create.pl view Stuff
79 script/myapp_create.pl controller Yada
fc7ec1d9 80
81 # built in testserver
ae4e40a7 82 script/myapp_server.pl
fc7ec1d9 83
84 # command line interface
ae4e40a7 85 script/myapp_test.pl /yada
fc7ec1d9 86
87
fc7ec1d9 88 use Catalyst;
89
90 use Catalyst qw/My::Module My::OtherModule/;
91
92 use Catalyst '-Debug';
93
94 use Catalyst qw/-Debug -Engine=CGI/;
95
5a8ed4fe 96 sub default : Private { $_[1]->res->output('Hello') } );
97
e3dc9d78 98 sub index : Path('/index.html') {
5a8ed4fe 99 my ( $self, $c ) = @_;
100 $c->res->output('Hello');
064834ea 101 $c->forward('foo');
5a8ed4fe 102 }
103
064834ea 104 sub product : Regex('^product[_]*(\d*).html$') {
5a8ed4fe 105 my ( $self, $c ) = @_;
106 $c->stash->{template} = 'product.tt';
107 $c->stash->{product} = $c->req->snippets->[0];
108 }
fc7ec1d9 109
3803e98f 110See also L<Catalyst::Manual::Intro>
111
fc7ec1d9 112=head1 DESCRIPTION
113
fc7ec1d9 114The key concept of Catalyst is DRY (Don't Repeat Yourself).
115
116See L<Catalyst::Manual> for more documentation.
117
23f9d934 118Catalyst plugins can be loaded by naming them as arguments to the "use Catalyst" statement.
1985c30b 119Omit the C<Catalyst::Plugin::> prefix from the plugin name,
23f9d934 120so C<Catalyst::Plugin::My::Module> becomes C<My::Module>.
fc7ec1d9 121
122 use Catalyst 'My::Module';
123
26e73131 124Special flags like -Debug and -Engine can also be specified as arguments when
23f9d934 125Catalyst is loaded:
fc7ec1d9 126
127 use Catalyst qw/-Debug My::Module/;
128
23f9d934 129The position of plugins and flags in the chain is important, because they are
130loaded in exactly the order that they appear.
fc7ec1d9 131
23f9d934 132The following flags are supported:
133
134=over 4
135
136=item -Debug
137
138enables debug output, i.e.:
fc7ec1d9 139
140 use Catalyst '-Debug';
141
23f9d934 142this is equivalent to:
fc7ec1d9 143
144 use Catalyst;
145 sub debug { 1 }
146
fbcc39ad 147=item -Dispatcher
148
149Force Catalyst to use a specific dispatcher.
150
23f9d934 151=item -Engine
fc7ec1d9 152
153Force Catalyst to use a specific engine.
23f9d934 154Omit the C<Catalyst::Engine::> prefix of the engine name, i.e.:
fc7ec1d9 155
156 use Catalyst '-Engine=CGI';
157
fbcc39ad 158=item -Home
159
160Force Catalyst to use a specific home directory.
161
162=item -Log
163
164Specify log level.
165
23f9d934 166=back
fc7ec1d9 167
23f9d934 168=head1 METHODS
169
170=over 4
171
66e28e3f 172=item $c->action
173
174Accessor for the current action
175
fbcc39ad 176=item $c->comp($name)
177
178=item $c->component($name)
179
180Get a component object by name.
181
182 $c->comp('MyApp::Model::MyModel')->do_stuff;
183
184=cut
185
186sub component {
187 my $c = shift;
188
189 if (@_) {
190
191 my $name = shift;
192
193 my $appclass = ref $c || $c;
194
195 my @names = (
196 $name, "${appclass}::${name}",
197 map { "${appclass}::${_}::${name}" } qw/M V C/
198 );
199
200 foreach my $try (@names) {
201
202 if ( exists $c->components->{$try} ) {
203
204 return $c->components->{$try};
205 }
206 }
207
208 foreach my $component ( keys %{ $c->components } ) {
209
210 return $c->components->{$component} if $component =~ /$name/i;
211 }
212
213 }
214
215 return sort keys %{ $c->components };
216}
217
218=item config
219
220Returns a hashref containing your applications settings.
221
23f9d934 222=item debug
fc7ec1d9 223
224Overload to enable debug messages.
225
226=cut
227
228sub debug { 0 }
229
fbcc39ad 230=item $c->detach( $command [, \@arguments ] )
fc7ec1d9 231
fbcc39ad 232Like C<forward> but doesn't return.
fc7ec1d9 233
234=cut
235
fbcc39ad 236sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
237
238=item $c->dispatcher
239
240Contains the dispatcher instance.
241Stringifies to class.
242
243=item $c->forward( $command [, \@arguments ] )
244
245Forward processing to a private action or a method from a class.
246If you define a class without method it will default to process().
247also takes an optional arrayref containing arguments to be passed
248to the new function. $c->req->args will be reset upon returning
249from the function.
250
251 $c->forward('/foo');
252 $c->forward('index');
253 $c->forward(qw/MyApp::Model::CDBI::Foo do_stuff/);
254 $c->forward('MyApp::View::TT');
255
256=cut
257
258sub forward { my $c = shift; $c->dispatcher->forward( $c, @_ ) }
259
66e28e3f 260=item $c->namespace
261
262Accessor to the namespace of the current action
263
01033d73 264=item $c->path_to(@path)
265
266Merges C<@path> with $c->config->{home} and returns a L<Path::Class> object.
267
268For example:
269
270 $c->path_to( 'db', 'sqlite.db' );
271
272=cut
273
274sub path_to {
275 my ( $c, @path ) = @_;
276 my $path = dir( $c->config->{home}, @path );
277 if ( -d $path ) { return $path }
278 else { return file( $c->config->{home}, @path ) }
279}
280
fbcc39ad 281=item $c->setup
282
283Setup.
284
285 $c->setup;
286
287=cut
288
289sub setup {
0319a12c 290 my ( $class, @arguments ) = @_;
599b5295 291
fbcc39ad 292 unless ( $class->isa('Catalyst') ) {
953b0e15 293
fbcc39ad 294 Catalyst::Exception->throw(
295 message => qq/'$class' does not inherit from Catalyst/ );
1c99e125 296 }
0319a12c 297
fbcc39ad 298 if ( $class->arguments ) {
299 @arguments = ( @arguments, @{ $class->arguments } );
300 }
301
302 # Process options
303 my $flags = {};
304
305 foreach (@arguments) {
306
307 if (/^-Debug$/) {
308 $flags->{log} =
309 ( $flags->{log} ) ? 'debug,' . $flags->{log} : 'debug';
310 }
311 elsif (/^-(\w+)=?(.*)$/) {
312 $flags->{ lc $1 } = $2;
313 }
314 else {
315 push @{ $flags->{plugins} }, $_;
316 }
317 }
318
319 $class->setup_log( delete $flags->{log} );
320 $class->setup_plugins( delete $flags->{plugins} );
321 $class->setup_dispatcher( delete $flags->{dispatcher} );
322 $class->setup_engine( delete $flags->{engine} );
323 $class->setup_home( delete $flags->{home} );
324
325 for my $flag ( sort keys %{$flags} ) {
326
327 if ( my $code = $class->can( 'setup_' . $flag ) ) {
328 &$code( $class, delete $flags->{$flag} );
329 }
330 else {
331 $class->log->warn(qq/Unknown flag "$flag"/);
332 }
333 }
334
335 $class->log->warn( "You are running an old helper script! "
336 . "Please update your scripts by regenerating the "
337 . "application and copying over the new scripts." )
338 if ( $ENV{CATALYST_SCRIPT_GEN}
339 && ( $ENV{CATALYST_SCRIPT_GEN} < $Catalyst::CATALYST_SCRIPT_GEN ) );
340
341 if ( $class->debug ) {
342
343 my @plugins = ();
344
345 {
346 no strict 'refs';
347 @plugins = grep { /^Catalyst::Plugin/ } @{"$class\::ISA"};
348 }
349
350 if (@plugins) {
351 my $t = Text::ASCIITable->new;
352 $t->setOptions( 'hide_HeadRow', 1 );
353 $t->setOptions( 'hide_HeadLine', 1 );
354 $t->setCols('Class');
355 $t->setColWidth( 'Class', 75, 1 );
356 $t->addRow($_) for @plugins;
357 $class->log->debug( "Loaded plugins:\n" . $t->draw );
358 }
359
360 my $dispatcher = $class->dispatcher;
361 my $engine = $class->engine;
362 my $home = $class->config->{home};
363
364 $class->log->debug(qq/Loaded dispatcher "$dispatcher"/);
365 $class->log->debug(qq/Loaded engine "$engine"/);
366
367 $home
368 ? ( -d $home )
369 ? $class->log->debug(qq/Found home "$home"/)
370 : $class->log->debug(qq/Home "$home" doesn't exist/)
371 : $class->log->debug(q/Couldn't find home/);
372 }
373
374 # Call plugins setup
375 {
376 no warnings qw/redefine/;
377 local *setup = sub { };
378 $class->setup;
379 }
380
381 # Initialize our data structure
382 $class->components( {} );
383
384 $class->setup_components;
385
386 if ( $class->debug ) {
387 my $t = Text::ASCIITable->new;
388 $t->setOptions( 'hide_HeadRow', 1 );
389 $t->setOptions( 'hide_HeadLine', 1 );
390 $t->setCols('Class');
391 $t->setColWidth( 'Class', 75, 1 );
392 $t->addRow($_) for sort keys %{ $class->components };
393 $class->log->debug( "Loaded components:\n" . $t->draw )
394 if ( @{ $t->{tbl_rows} } );
395 }
396
397 # Add our self to components, since we are also a component
398 $class->components->{$class} = $class;
399
400 $class->setup_actions;
401
402 if ( $class->debug ) {
403 my $name = $class->config->{name} || 'Application';
404 $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
405 }
406 $class->log->_flush() if $class->log->can('_flush');
407}
408
189e2a51 409=item $c->uri_for($path,[@args])
fbcc39ad 410
411Merges path with $c->request->base for absolute uri's and with
412$c->request->match for relative uri's, then returns a normalized
189e2a51 413L<URI> object. If any args are passed, they are added at the end
414of the path.
fbcc39ad 415
416=cut
417
418sub uri_for {
00e6a2b7 419 my ( $c, $path, @args ) = @_;
fbcc39ad 420 my $base = $c->request->base->clone;
421 my $basepath = $base->path;
422 $basepath =~ s/\/$//;
fdba7a9d 423 $basepath .= '/';
fbcc39ad 424 my $match = $c->request->match;
00e6a2b7 425
189e2a51 426 # massage match, empty if absolute path
fbcc39ad 427 $match =~ s/^\///;
428 $match .= '/' if $match;
429 $match = '' if $path =~ /^\//;
430 $path =~ s/^\///;
00e6a2b7 431
189e2a51 432 # join args with '/', or a blank string
00e6a2b7 433 my $args = ( scalar @args ? '/' . join( '/', @args ) : '' );
434 return URI->new_abs( URI->new_abs( "$path$args", "$basepath$match" ),
435 $base )->canonical;
fbcc39ad 436}
437
438=item $c->error
439
440=item $c->error($error, ...)
441
442=item $c->error($arrayref)
443
444Returns an arrayref containing error messages.
445
446 my @error = @{ $c->error };
447
448Add a new error.
449
450 $c->error('Something bad happened');
451
00e6a2b7 452Clean errors.
453
454 $c->error(0);
455
fbcc39ad 456=cut
457
458sub error {
459 my $c = shift;
00e6a2b7 460 if ( $_[0] ) {
461 my $error = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
462 push @{ $c->{error} }, @$error;
463 }
464 elsif ( defined $_[0] ) { $c->{error} = undef }
465 return $c->{error} || [];
0319a12c 466}
467
468=item $c->engine
469
fbcc39ad 470Contains the engine instance.
471Stringifies to the class.
fc7ec1d9 472
0319a12c 473=item $c->log
474
475Contains the logging object. Unless it is already set Catalyst sets this up with a
476C<Catalyst::Log> object. To use your own log class:
477
478 $c->log( MyLogger->new );
479 $c->log->info("now logging with my own logger!");
480
481Your log class should implement the methods described in the C<Catalyst::Log>
482man page.
483
484=item $c->plugin( $name, $class, @args )
485
486Instant plugins for Catalyst.
487Classdata accessor/mutator will be created, class loaded and instantiated.
488
489 MyApp->plugin( 'prototype', 'HTML::Prototype' );
490
491 $c->prototype->define_javascript_functions;
492
493=cut
494
495sub plugin {
496 my ( $class, $name, $plugin, @args ) = @_;
497 $plugin->require;
498
499 if ( my $error = $UNIVERSAL::require::ERROR ) {
500 Catalyst::Exception->throw(
fbcc39ad 501 message => qq/Couldn't load instant plugin "$plugin", "$error"/ );
0319a12c 502 }
503
504 eval { $plugin->import };
505 $class->mk_classdata($name);
506 my $obj;
507 eval { $obj = $plugin->new(@args) };
508
fbcc39ad 509 if ($@) {
510 Catalyst::Exception->throw( message =>
511 qq/Couldn't instantiate instant plugin "$plugin", "$@"/ );
0319a12c 512 }
513
514 $class->$name($obj);
515 $class->log->debug(qq/Initialized instant plugin "$plugin" as "$name"/)
516 if $class->debug;
517}
518
fbcc39ad 519=item $c->request
520
521=item $c->req
522
523Returns a C<Catalyst::Request> object.
524
525 my $req = $c->req;
526
527=item $c->response
528
529=item $c->res
530
531Returns a C<Catalyst::Response> object.
532
533 my $res = $c->res;
534
535=item $c->state
536
537Contains the return value of the last executed action.
538
539=item $c->stash
540
541Returns a hashref containing all your data.
542
fbcc39ad 543 print $c->stash->{foo};
544
23eb3f51 545Keys may be set in the stash by assigning to the hash reference, or by passing
546either a single hash reference or a list of key/value pairs as arguments.
547
548For example:
549
550 $c->stash->{foo} ||= 'yada';
551 $c->stash( { moose => 'majestic', qux => 0 } );
552 $c->stash( bar => 1, gorch => 2 );
553
fbcc39ad 554=cut
555
556sub stash {
557 my $c = shift;
558 if (@_) {
559 my $stash = @_ > 1 ? {@_} : $_[0];
560 while ( my ( $key, $val ) = each %$stash ) {
561 $c->{stash}->{$key} = $val;
562 }
563 }
564 return $c->{stash};
565}
566
2c63fc07 567=item $c->welcome_message
ab2374d3 568
569Returns the Catalyst welcome HTML page.
570
571=cut
572
573sub welcome_message {
bf1f2c60 574 my $c = shift;
575 my $name = $c->config->{name};
576 my $logo = $c->uri_for('/static/images/catalyst_logo.png');
577 my $prefix = Catalyst::Utils::appprefix( ref $c );
ab2374d3 578 return <<"EOF";
579<html>
580 <head>
581 <title>$name on Catalyst $VERSION</title>
582 <style type="text/css">
583 body {
584 text-align: center;
585 padding-left: 50%;
586 color: #000;
587 background-color: #eee;
588 }
589 div#content {
590 width: 640px;
591 margin-left: -320px;
592 margin-top: 10px;
593 margin-bottom: 10px;
594 text-align: left;
595 background-color: #ccc;
596 border: 1px solid #aaa;
597 -moz-border-radius: 10px;
598 }
d84c4dab 599 p, h1, h2 {
ab2374d3 600 margin-left: 20px;
601 margin-right: 20px;
16215972 602 font-family: verdana, tahoma, sans-serif;
ab2374d3 603 }
d84c4dab 604 a {
605 font-family: verdana, tahoma, sans-serif;
606 }
d114e033 607 :link, :visited {
608 text-decoration: none;
609 color: #b00;
610 border-bottom: 1px dotted #bbb;
611 }
612 :link:hover, :visited:hover {
d114e033 613 color: #555;
614 }
ab2374d3 615 div#topbar {
616 margin: 0px;
617 }
3e82a295 618 pre {
3e82a295 619 margin: 10px;
620 padding: 8px;
621 }
ab2374d3 622 div#answers {
623 padding: 8px;
624 margin: 10px;
d114e033 625 background-color: #fff;
ab2374d3 626 border: 1px solid #aaa;
627 -moz-border-radius: 10px;
628 }
629 h1 {
33108eaf 630 font-size: 0.9em;
631 font-weight: normal;
ab2374d3 632 text-align: center;
633 }
634 h2 {
635 font-size: 1.0em;
636 }
637 p {
638 font-size: 0.9em;
639 }
ae7c5252 640 p img {
641 float: right;
642 margin-left: 10px;
643 }
33108eaf 644 b#appname {
645 font-size: 1.6em;
ab2374d3 646 }
647 </style>
648 </head>
649 <body>
650 <div id="content">
651 <div id="topbar">
33108eaf 652 <h1><b id="appname">$name</b> on <a href="http://catalyst.perl.org">Catalyst</a>
d84c4dab 653 $VERSION</h1>
ab2374d3 654 </div>
655 <div id="answers">
ae7c5252 656 <p>
f68d720e 657 <img src="$logo"/>
ae7c5252 658 </p>
4b8cb778 659 <p>Welcome to the wonderful world of Catalyst.
f92fd545 660 This <a href="http://en.wikipedia.org/wiki/MVC">MVC</a>
661 framework will make web development something you had
662 never expected it to be: Fun, rewarding and quick.</p>
ab2374d3 663 <h2>What to do now?</h2>
4b8cb778 664 <p>That really depends on what <b>you</b> want to do.
ab2374d3 665 We do, however, provide you with a few starting points.</p>
666 <p>If you want to jump right into web development with Catalyst
5db7f9a1 667 you might want to check out the documentation.</p>
bf1f2c60 668 <pre><code>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a>
669perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual.pod">Catalyst::Manual</a></code></pre>
ab2374d3 670 <h2>What to do next?</h2>
f5681c92 671 <p>Next it's time to write an actual application. Use the
bf1f2c60 672 helper scripts to generate <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AController%3A%3A&mode=all">controllers</a>,
673 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AModel%3A%3A&mode=all">models</a> and
674 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AView%3A%3A&mode=all">views</a>,
675 they can save you a lot of work.</p>
676 <pre><code>script/${prefix}_create.pl -help</code></pre>
677 <p>Also, be sure to check out the vast and growing
678 collection of <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3APlugin%3A%3A&mode=all">plugins for Catalyst on CPAN</a>,
679 you are likely to find what you need there.
f5681c92 680 </p>
681
82245cc4 682 <h2>Need help?</h2>
f5681c92 683 <p>Catalyst has a very active community. Here are the main places to
684 get in touch with us.</p>
16215972 685 <ul>
686 <li>
2b9a7d76 687 <a href="http://dev.catalyst.perl.org">Wiki</a>
16215972 688 </li>
689 <li>
690 <a href="http://lists.rawmode.org/mailman/listinfo/catalyst">Mailing-List</a>
691 </li>
692 <li>
ea7cd80d 693 <a href="irc://irc.perl.org/catalyst">IRC channel #catalyst on irc.perl.org</a>
16215972 694 </li>
695 </ul>
ab2374d3 696 <h2>In conclusion</h2>
4e7aa2ea 697 <p>The Catalyst team hopes you will enjoy using Catalyst as much
f5681c92 698 as we enjoyed making it. Please contact us if you have ideas
699 for improvement or other feedback.</p>
ab2374d3 700 </div>
701 </div>
702 </body>
703</html>
704EOF
705}
706
fbcc39ad 707=back
708
709=head1 INTERNAL METHODS
710
711=over 4
712
713=item $c->benchmark($coderef)
714
715Takes a coderef with arguments and returns elapsed time as float.
716
717 my ( $elapsed, $status ) = $c->benchmark( sub { return 1 } );
718 $c->log->info( sprintf "Processing took %f seconds", $elapsed );
719
720=cut
721
722sub benchmark {
723 my $c = shift;
724 my $code = shift;
725 my $time = [gettimeofday];
726 my @return = &$code(@_);
727 my $elapsed = tv_interval $time;
728 return wantarray ? ( $elapsed, @return ) : $elapsed;
729}
730
731=item $c->components
732
733Contains the components.
734
735=item $c->counter
736
737Returns a hashref containing coderefs and execution counts.
738(Needed for deep recursion detection)
739
740=item $c->depth
741
742Returns the actual forward depth.
743
744=item $c->dispatch
745
746Dispatch request to actions.
747
748=cut
749
750sub dispatch { my $c = shift; $c->dispatcher->dispatch( $c, @_ ) }
751
752=item $c->execute($class, $coderef)
753
754Execute a coderef in given class and catch exceptions.
755Errors are available via $c->error.
756
757=cut
758
759sub execute {
760 my ( $c, $class, $code ) = @_;
761 $class = $c->components->{$class} || $class;
762 $c->state(0);
763 my $callsub = ( caller(1) )[3];
764
765 my $action = '';
766 if ( $c->debug ) {
767 $action = "$code";
768 $action = "/$action" unless $action =~ /\-\>/;
769 $c->counter->{"$code"}++;
770
771 if ( $c->counter->{"$code"} > $RECURSION ) {
772 my $error = qq/Deep recursion detected in "$action"/;
773 $c->log->error($error);
774 $c->error($error);
775 $c->state(0);
776 return $c->state;
777 }
778
779 $action = "-> $action" if $callsub =~ /forward$/;
780 }
781 $c->{depth}++;
782 eval {
00e6a2b7 783 if ( $c->debug )
784 {
fbcc39ad 785 my ( $elapsed, @state ) =
786 $c->benchmark( $code, $class, $c, @{ $c->req->args } );
787 push @{ $c->{stats} }, [ $action, sprintf( '%fs', $elapsed ) ];
788 $c->state(@state);
789 }
7cfcfd27 790 else {
00e6a2b7 791 $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 );
7cfcfd27 792 }
fbcc39ad 793 };
794 $c->{depth}--;
795
796 if ( my $error = $@ ) {
797
798 if ( $error eq $DETACH ) { die $DETACH if $c->{depth} > 1 }
799 else {
800 unless ( ref $error ) {
801 chomp $error;
802 $error = qq/Caught exception "$error"/;
803 }
804
805 $c->log->error($error);
806 $c->error($error);
807 $c->state(0);
808 }
809 }
810 return $c->state;
811}
812
813=item $c->finalize
814
815Finalize request.
816
817=cut
818
819sub finalize {
820 my $c = shift;
821
822 $c->finalize_uploads;
823
824 # Error
825 if ( $#{ $c->error } >= 0 ) {
826 $c->finalize_error;
827 }
828
829 $c->finalize_headers;
830
831 # HEAD request
832 if ( $c->request->method eq 'HEAD' ) {
833 $c->response->body('');
834 }
835
836 $c->finalize_body;
837
838 return $c->response->status;
839}
840
841=item $c->finalize_body
842
843Finalize body.
844
845=cut
846
847sub finalize_body { my $c = shift; $c->engine->finalize_body( $c, @_ ) }
848
849=item $c->finalize_cookies
850
851Finalize cookies.
852
853=cut
854
855sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
856
857=item $c->finalize_error
858
859Finalize error.
860
861=cut
862
863sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
864
865=item $c->finalize_headers
866
867Finalize headers.
868
869=cut
870
871sub finalize_headers {
872 my $c = shift;
873
874 # Check if we already finalized headers
875 return if $c->response->{_finalized_headers};
876
877 # Handle redirects
878 if ( my $location = $c->response->redirect ) {
879 $c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
880 $c->response->header( Location => $location );
881 }
882
883 # Content-Length
884 if ( $c->response->body && !$c->response->content_length ) {
885 $c->response->content_length( bytes::length( $c->response->body ) );
886 }
887
888 # Errors
889 if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) {
890 $c->response->headers->remove_header("Content-Length");
891 $c->response->body('');
892 }
893
894 $c->finalize_cookies;
895
896 $c->engine->finalize_headers( $c, @_ );
897
898 # Done
899 $c->response->{_finalized_headers} = 1;
900}
901
902=item $c->finalize_output
903
904An alias for finalize_body.
905
906=item $c->finalize_read
907
908Finalize the input after reading is complete.
909
910=cut
911
912sub finalize_read { my $c = shift; $c->engine->finalize_read( $c, @_ ) }
913
914=item $c->finalize_uploads
915
916Finalize uploads. Cleans up any temporary files.
917
918=cut
919
920sub finalize_uploads { my $c = shift; $c->engine->finalize_uploads( $c, @_ ) }
921
a9dc674c 922=item $c->get_action( $action, $namespace )
fbcc39ad 923
924Get an action in a given namespace.
925
926=cut
927
928sub get_action { my $c = shift; $c->dispatcher->get_action( $c, @_ ) }
929
a9dc674c 930=item $c->get_actions( $action, $namespace )
931
932Get all actions of a given name in a namespace and all base namespaces.
933
934=cut
935
936sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
937
fbcc39ad 938=item handle_request( $class, @arguments )
939
940Handles the request.
941
942=cut
943
944sub handle_request {
945 my ( $class, @arguments ) = @_;
946
947 # Always expect worst case!
948 my $status = -1;
949 eval {
950 my @stats = ();
951
952 my $handler = sub {
953 my $c = $class->prepare(@arguments);
954 $c->{stats} = \@stats;
955 $c->dispatch;
956 return $c->finalize;
957 };
958
959 if ( $class->debug ) {
960 my $elapsed;
961 ( $elapsed, $status ) = $class->benchmark($handler);
962 $elapsed = sprintf '%f', $elapsed;
963 my $av = sprintf '%.3f',
964 ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
965 my $t = Text::ASCIITable->new;
966 $t->setCols( 'Action', 'Time' );
967 $t->setColWidth( 'Action', 64, 1 );
968 $t->setColWidth( 'Time', 9, 1 );
969
970 for my $stat (@stats) { $t->addRow( $stat->[0], $stat->[1] ) }
971 $class->log->info(
972 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
973 }
974 else { $status = &$handler }
975
976 };
977
978 if ( my $error = $@ ) {
979 chomp $error;
980 $class->log->error(qq/Caught exception in engine "$error"/);
981 }
982
983 $COUNT++;
984 $class->log->_flush() if $class->log->can('_flush');
985 return $status;
986}
987
988=item $c->prepare(@arguments)
989
990Turns the engine-specific request( Apache, CGI ... )
991into a Catalyst context .
992
993=cut
994
995sub prepare {
996 my ( $class, @arguments ) = @_;
997
998 my $c = bless {
999 counter => {},
1000 depth => 0,
1001 request => Catalyst::Request->new(
1002 {
1003 arguments => [],
1004 body_parameters => {},
1005 cookies => {},
fbcc39ad 1006 headers => HTTP::Headers->new,
1007 parameters => {},
1008 query_parameters => {},
1009 secure => 0,
1010 snippets => [],
1011 uploads => {}
1012 }
1013 ),
1014 response => Catalyst::Response->new(
1015 {
1016 body => '',
1017 cookies => {},
fbcc39ad 1018 headers => HTTP::Headers->new(),
1019 status => 200
1020 }
1021 ),
1022 stash => {},
1023 state => 0
1024 }, $class;
1025
1026 # For on-demand data
1027 $c->request->{_context} = $c;
1028 $c->response->{_context} = $c;
1029 weaken( $c->request->{_context} );
1030 weaken( $c->response->{_context} );
1031
1032 if ( $c->debug ) {
1033 my $secs = time - $START || 1;
1034 my $av = sprintf '%.3f', $COUNT / $secs;
1035 $c->log->debug('**********************************');
1036 $c->log->debug("* Request $COUNT ($av/s) [$$]");
1037 $c->log->debug('**********************************');
1038 $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
1039 }
1040
1041 $c->prepare_request(@arguments);
1042 $c->prepare_connection;
1043 $c->prepare_query_parameters;
1044 $c->prepare_headers;
1045 $c->prepare_cookies;
1046 $c->prepare_path;
1047
1048 # On-demand parsing
1049 $c->prepare_body unless $c->config->{parse_on_demand};
1050
1051 $c->prepare_action;
1052 my $method = $c->req->method || '';
1053 my $path = $c->req->path || '';
1054 my $address = $c->req->address || '';
1055
1056 $c->log->debug(qq/"$method" request for "$path" from $address/)
1057 if $c->debug;
1058
1059 return $c;
1060}
1061
1062=item $c->prepare_action
1063
1064Prepare action.
1065
1066=cut
1067
1068sub prepare_action { my $c = shift; $c->dispatcher->prepare_action( $c, @_ ) }
1069
1070=item $c->prepare_body
1071
1072Prepare message body.
1073
1074=cut
1075
1076sub prepare_body {
1077 my $c = shift;
1078
1079 # Do we run for the first time?
1080 return if defined $c->request->{_body};
1081
1082 # Initialize on-demand data
1083 $c->engine->prepare_body( $c, @_ );
1084 $c->prepare_parameters;
1085 $c->prepare_uploads;
1086
1087 if ( $c->debug && keys %{ $c->req->body_parameters } ) {
1088 my $t = Text::ASCIITable->new;
1089 $t->setCols( 'Key', 'Value' );
1090 $t->setColWidth( 'Key', 37, 1 );
1091 $t->setColWidth( 'Value', 36, 1 );
1092 $t->alignCol( 'Value', 'right' );
1093 for my $key ( sort keys %{ $c->req->body_parameters } ) {
1094 my $param = $c->req->body_parameters->{$key};
1095 my $value = defined($param) ? $param : '';
1096 $t->addRow( $key,
1097 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1098 }
1099 $c->log->debug( "Body Parameters are:\n" . $t->draw );
1100 }
1101}
1102
4bd82c41 1103=item $c->prepare_body_chunk( $chunk )
1104
1105Prepare a chunk of data before sending it to HTTP::Body.
1106
1107=cut
1108
4f5ebacd 1109sub prepare_body_chunk {
1110 my $c = shift;
4bd82c41 1111 $c->engine->prepare_body_chunk( $c, @_ );
1112}
1113
fbcc39ad 1114=item $c->prepare_body_parameters
1115
1116Prepare body parameters.
1117
1118=cut
1119
1120sub prepare_body_parameters {
1121 my $c = shift;
1122 $c->engine->prepare_body_parameters( $c, @_ );
1123}
1124
1125=item $c->prepare_connection
1126
1127Prepare connection.
1128
1129=cut
1130
1131sub prepare_connection {
1132 my $c = shift;
1133 $c->engine->prepare_connection( $c, @_ );
1134}
1135
1136=item $c->prepare_cookies
1137
1138Prepare cookies.
1139
1140=cut
1141
1142sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
1143
1144=item $c->prepare_headers
1145
1146Prepare headers.
1147
1148=cut
1149
1150sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
1151
1152=item $c->prepare_parameters
1153
1154Prepare parameters.
1155
1156=cut
1157
1158sub prepare_parameters {
1159 my $c = shift;
1160 $c->prepare_body_parameters;
1161 $c->engine->prepare_parameters( $c, @_ );
1162}
1163
1164=item $c->prepare_path
1165
1166Prepare path and base.
1167
1168=cut
1169
1170sub prepare_path { my $c = shift; $c->engine->prepare_path( $c, @_ ) }
1171
1172=item $c->prepare_query_parameters
1173
1174Prepare query parameters.
1175
1176=cut
1177
1178sub prepare_query_parameters {
1179 my $c = shift;
1180
1181 $c->engine->prepare_query_parameters( $c, @_ );
1182
1183 if ( $c->debug && keys %{ $c->request->query_parameters } ) {
1184 my $t = Text::ASCIITable->new;
1185 $t->setCols( 'Key', 'Value' );
1186 $t->setColWidth( 'Key', 37, 1 );
1187 $t->setColWidth( 'Value', 36, 1 );
1188 $t->alignCol( 'Value', 'right' );
1189 for my $key ( sort keys %{ $c->req->query_parameters } ) {
1190 my $param = $c->req->query_parameters->{$key};
1191 my $value = defined($param) ? $param : '';
1192 $t->addRow( $key,
1193 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1194 }
1195 $c->log->debug( "Query Parameters are:\n" . $t->draw );
1196 }
1197}
1198
1199=item $c->prepare_read
1200
1201Prepare the input for reading.
1202
1203=cut
1204
1205sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
1206
1207=item $c->prepare_request
1208
1209Prepare the engine request.
1210
1211=cut
1212
1213sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
1214
1215=item $c->prepare_uploads
1216
1217Prepare uploads.
1218
1219=cut
1220
1221sub prepare_uploads {
1222 my $c = shift;
1223
1224 $c->engine->prepare_uploads( $c, @_ );
1225
1226 if ( $c->debug && keys %{ $c->request->uploads } ) {
1227 my $t = Text::ASCIITable->new;
bc2beef5 1228 $t->setCols( 'Key', 'Filename', 'Type', 'Size' );
1229 $t->setColWidth( 'Key', 12, 1 );
1230 $t->setColWidth( 'Filename', 28, 1 );
1231 $t->setColWidth( 'Type', 18, 1 );
fbcc39ad 1232 $t->setColWidth( 'Size', 9, 1 );
1233 $t->alignCol( 'Size', 'left' );
1234 for my $key ( sort keys %{ $c->request->uploads } ) {
1235 my $upload = $c->request->uploads->{$key};
1236 for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
bc2beef5 1237 $t->addRow( $key, $u->filename, $u->type, $u->size );
fbcc39ad 1238 }
1239 }
1240 $c->log->debug( "File Uploads are:\n" . $t->draw );
1241 }
1242}
1243
1244=item $c->prepare_write
1245
1246Prepare the output for writing.
1247
1248=cut
1249
1250sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
1251
1252=item $c->read( [$maxlength] )
1253
1254Read a chunk of data from the request body. This method is designed to be
1255used in a while loop, reading $maxlength bytes on every call. $maxlength
1256defaults to the size of the request if not specified.
1257
1258You have to set MyApp->config->{parse_on_demand} to use this directly.
1259
1260=cut
1261
1262sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
1263
1264=item $c->run
1265
1266Starts the engine.
1267
1268=cut
1269
1270sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
1271
1272=item $c->set_action( $action, $code, $namespace, $attrs )
1273
1274Set an action in a given namespace.
1275
1276=cut
1277
1278sub set_action { my $c = shift; $c->dispatcher->set_action( $c, @_ ) }
1279
1280=item $c->setup_actions($component)
1281
1282Setup actions for a component.
1283
1284=cut
1285
1286sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
1287
1288=item $c->setup_components
1289
1290Setup components.
1291
1292=cut
1293
1294sub setup_components {
1295 my $class = shift;
1296
1297 my $callback = sub {
1298 my ( $component, $context ) = @_;
1299
1300 unless ( $component->isa('Catalyst::Base') ) {
1301 return $component;
1302 }
1303
1304 my $suffix = Catalyst::Utils::class2classsuffix($component);
1305 my $config = $class->config->{$suffix} || {};
1306
1307 my $instance;
1308
1309 eval { $instance = $component->new( $context, $config ); };
1310
1311 if ( my $error = $@ ) {
1312
1313 chomp $error;
1314
1315 Catalyst::Exception->throw( message =>
1316 qq/Couldn't instantiate component "$component", "$error"/ );
1317 }
1318
1319 Catalyst::Exception->throw( message =>
1320qq/Couldn't instantiate component "$component", "new() didn't return a object"/
1321 )
1322 unless ref $instance;
1323 return $instance;
1324 };
1325
1326 eval {
1327 Module::Pluggable::Fast->import(
1328 name => '_catalyst_components',
1329 search => [
1330 "$class\::Controller", "$class\::C",
1331 "$class\::Model", "$class\::M",
1332 "$class\::View", "$class\::V"
1333 ],
1334 callback => $callback
1335 );
1336 };
1337
1338 if ( my $error = $@ ) {
1339
1340 chomp $error;
1341
1342 Catalyst::Exception->throw(
1343 message => qq/Couldn't load components "$error"/ );
1344 }
1345
1346 for my $component ( $class->_catalyst_components($class) ) {
1347 $class->components->{ ref $component || $component } = $component;
1348 }
1349}
1350
1351=item $c->setup_dispatcher
1352
1353=cut
1354
1355sub setup_dispatcher {
1356 my ( $class, $dispatcher ) = @_;
1357
1358 if ($dispatcher) {
1359 $dispatcher = 'Catalyst::Dispatcher::' . $dispatcher;
1360 }
1361
1362 if ( $ENV{CATALYST_DISPATCHER} ) {
1363 $dispatcher = 'Catalyst::Dispatcher::' . $ENV{CATALYST_DISPATCHER};
1364 }
1365
1366 if ( $ENV{ uc($class) . '_DISPATCHER' } ) {
1367 $dispatcher =
1368 'Catalyst::Dispatcher::' . $ENV{ uc($class) . '_DISPATCHER' };
1369 }
1370
1371 unless ($dispatcher) {
1372 $dispatcher = 'Catalyst::Dispatcher';
1373 }
1374
1375 $dispatcher->require;
1376
1377 if ($@) {
1378 Catalyst::Exception->throw(
1379 message => qq/Couldn't load dispatcher "$dispatcher", "$@"/ );
1380 }
1381
1382 # dispatcher instance
1383 $class->dispatcher( $dispatcher->new );
1384}
1385
1386=item $c->setup_engine
1387
1388=cut
1389
1390sub setup_engine {
1391 my ( $class, $engine ) = @_;
1392
1393 if ($engine) {
1394 $engine = 'Catalyst::Engine::' . $engine;
1395 }
1396
1397 if ( $ENV{CATALYST_ENGINE} ) {
1398 $engine = 'Catalyst::Engine::' . $ENV{CATALYST_ENGINE};
1399 }
1400
1401 if ( $ENV{ uc($class) . '_ENGINE' } ) {
1402 $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' };
1403 }
1404
1405 if ( !$engine && $ENV{MOD_PERL} ) {
1406
1407 # create the apache method
1408 {
1409 no strict 'refs';
1410 *{"$class\::apache"} = sub { shift->engine->apache };
1411 }
1412
1413 my ( $software, $version ) =
1414 $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
1415
1416 $version =~ s/_//g;
1417 $version =~ s/(\.[^.]+)\./$1/g;
1418
1419 if ( $software eq 'mod_perl' ) {
1420
1421 if ( $version >= 1.99922 ) {
1422 $engine = 'Catalyst::Engine::Apache2::MP20';
1423 }
1424
1425 elsif ( $version >= 1.9901 ) {
1426 $engine = 'Catalyst::Engine::Apache2::MP19';
1427 }
1428
1429 elsif ( $version >= 1.24 ) {
1430 $engine = 'Catalyst::Engine::Apache::MP13';
1431 }
1432
1433 else {
1434 Catalyst::Exception->throw( message =>
1435 qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
1436 }
1437
1438 # install the correct mod_perl handler
1439 if ( $version >= 1.9901 ) {
1440 *handler = sub : method {
1441 shift->handle_request(@_);
1442 };
1443 }
1444 else {
1445 *handler = sub ($$) { shift->handle_request(@_) };
1446 }
1447
1448 }
1449
1450 elsif ( $software eq 'Zeus-Perl' ) {
1451 $engine = 'Catalyst::Engine::Zeus';
1452 }
1453
1454 else {
1455 Catalyst::Exception->throw(
1456 message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
1457 }
1458 }
1459
1460 unless ($engine) {
1461 $engine = 'Catalyst::Engine::CGI';
1462 }
1463
1464 $engine->require;
1465
1466 if ($@) {
1467 Catalyst::Exception->throw( message =>
1468qq/Couldn't load engine "$engine" (maybe you forgot to install it?), "$@"/
1469 );
1470 }
d54484bf 1471
1472 # check for old engines that are no longer compatible
1473 my $old_engine;
1474 if ( $engine->isa('Catalyst::Engine::Apache')
1475 && ! Catalyst::Engine::Apache->VERSION )
1476 {
1477 $old_engine = 1;
1478 }
1479
1480 elsif ( $engine->isa('Catalyst::Engine::Server::Base')
1481 && Catalyst::Engine::Server->VERSION le '0.02' )
1482 {
1483 $old_engine = 1;
1484 }
1485
1486 elsif ( $engine->isa('Catalyst::Engine::HTTP::POE')
1487 && $engine->VERSION eq '0.01' )
1488 {
1489 $old_engine = 1;
1490 }
1491
1492 elsif ( $engine->isa('Catalyst::Engine::Zeus')
1493 && $engine->VERSION eq '0.01' )
1494 {
1495 $old_engine = 1;
1496 }
fbcc39ad 1497
d54484bf 1498 if ($old_engine) {
1499 Catalyst::Exception->throw( message =>
1500 qq/Engine "$engine" is not supported by this version of Catalyst/
1501 );
1502 }
1503
fbcc39ad 1504 # engine instance
1505 $class->engine( $engine->new );
1506}
1507
1508=item $c->setup_home
1509
1510=cut
1511
1512sub setup_home {
1513 my ( $class, $home ) = @_;
1514
1515 if ( $ENV{CATALYST_HOME} ) {
1516 $home = $ENV{CATALYST_HOME};
1517 }
1518
1519 if ( $ENV{ uc($class) . '_HOME' } ) {
1520 $home = $ENV{ uc($class) . '_HOME' };
1521 }
1522
1523 unless ($home) {
1524 $home = Catalyst::Utils::home($class);
1525 }
1526
1527 if ($home) {
1528 $class->config->{home} ||= $home;
1529 $class->config->{root} ||= dir($home)->subdir('root');
1530 }
1531}
1532
1533=item $c->setup_log
1534
1535=cut
1536
1537sub setup_log {
1538 my ( $class, $debug ) = @_;
1539
1540 unless ( $class->log ) {
1541 $class->log( Catalyst::Log->new );
1542 }
1543
1544 if ( $ENV{CATALYST_DEBUG} || $ENV{ uc($class) . '_DEBUG' } || $debug ) {
1545 no strict 'refs';
1546 *{"$class\::debug"} = sub { 1 };
1547 $class->log->debug('Debug messages enabled');
1548 }
1549}
1550
1551=item $c->setup_plugins
1552
1553=cut
1554
1555sub setup_plugins {
1556 my ( $class, $plugins ) = @_;
1557
1558 $plugins ||= [];
1559 for my $plugin ( reverse @$plugins ) {
1560
1561 $plugin = "Catalyst::Plugin::$plugin";
1562
1563 $plugin->require;
1564
1565 if ($@) {
1566 Catalyst::Exception->throw(
1567 message => qq/Couldn't load plugin "$plugin", "$@"/ );
1568 }
1569
1570 {
1571 no strict 'refs';
1572 unshift @{"$class\::ISA"}, $plugin;
1573 }
1574 }
1575}
1576
1577=item $c->write( $data )
1578
1579Writes $data to the output stream. When using this method directly, you will
1580need to manually set the Content-Length header to the length of your output
1581data, if known.
1582
1583=cut
1584
4f5ebacd 1585sub write {
1586 my $c = shift;
1587
1588 # Finalize headers if someone manually writes output
1589 $c->finalize_headers;
1590
1591 return $c->engine->write( $c, @_ );
1592}
fbcc39ad 1593
bf88a181 1594=item version
1595
1596Returns the Catalyst version number. mostly useful for powered by messages
1597in template systems.
1598
1599=cut
1600
1601sub version { return $Catalyst::VERSION }
1602
23f9d934 1603=back
1604
b0bb11ec 1605=head1 INTERNAL ACTIONS
1606
1607Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>
1608C<_ACTION> and C<_END>, these are by default not shown in the private
1609action table.
1610
1611But you can deactivate this with a config parameter.
1612
1613 MyApp->config->{show_internal_actions} = 1;
1614
d2ee9760 1615=head1 CASE SENSITIVITY
1616
1617By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> becomes
1618C</foo/bar>.
1619
1620But you can activate case sensitivity with a config parameter.
1621
1622 MyApp->config->{case_sensitive} = 1;
1623
fbcc39ad 1624So C<MyApp::C::Foo::Bar> becomes C</Foo/Bar>.
1625
1626=head1 ON-DEMAND PARSER
1627
1628The request body is usually parsed at the beginning of a request,
1629but if you want to handle input yourself or speed things up a bit
1630you can enable on-demand parsing with a config parameter.
1631
1632 MyApp->config->{parse_on_demand} = 1;
1633
1634=head1 PROXY SUPPORT
1635
1636Many production servers operate using the common double-server approach, with
1637a lightweight frontend web server passing requests to a larger backend
1638server. An application running on the backend server must deal with two
1639problems: the remote user always appears to be '127.0.0.1' and the server's
1640hostname will appear to be 'localhost' regardless of the virtual host the
1641user connected through.
1642
1643Catalyst will automatically detect this situation when you are running both
1644the frontend and backend servers on the same machine. The following changes
1645are made to the request.
1646
1647 $c->req->address is set to the user's real IP address, as read from the
1648 HTTP_X_FORWARDED_FOR header.
1649
1650 The host value for $c->req->base and $c->req->uri is set to the real host,
1651 as read from the HTTP_X_FORWARDED_HOST header.
1652
1653Obviously, your web server must support these 2 headers for this to work.
1654
1655In a more complex server farm environment where you may have your frontend
1656proxy server(s) on different machines, you will need to set a configuration
1657option to tell Catalyst to read the proxied data from the headers.
1658
1659 MyApp->config->{using_frontend_proxy} = 1;
1660
1661If you do not wish to use the proxy support at all, you may set:
d1a31ac6 1662
fbcc39ad 1663 MyApp->config->{ignore_frontend_proxy} = 1;
1664
1665=head1 THREAD SAFETY
1666
1667Catalyst has been tested under Apache 2's threading mpm_worker, mpm_winnt,
1668and the standalone forking HTTP server on Windows. We believe the Catalyst
1669core to be thread-safe.
1670
1671If you plan to operate in a threaded environment, remember that all other
1672modules you are using must also be thread-safe. Some modules, most notably
1673DBD::SQLite, are not thread-safe.
d1a31ac6 1674
3cb1db8c 1675=head1 SUPPORT
1676
1677IRC:
1678
1679 Join #catalyst on irc.perl.org.
1680
1681Mailing-Lists:
1682
1683 http://lists.rawmode.org/mailman/listinfo/catalyst
1684 http://lists.rawmode.org/mailman/listinfo/catalyst-dev
1985c30b 1685
432d507d 1686Web:
1687
1688 http://catalyst.perl.org
1689
fc7ec1d9 1690=head1 SEE ALSO
1691
61b1e958 1692=over 4
1693
1694=item L<Catalyst::Manual> - The Catalyst Manual
1695
1696=item L<Catalyst::Engine> - Core Engine
1697
1698=item L<Catalyst::Log> - The Log Class.
1699
1700=item L<Catalyst::Request> - The Request Object
1701
1702=item L<Catalyst::Response> - The Response Object
1703
1704=item L<Catalyst::Test> - The test suite.
1705
1706=back
fc7ec1d9 1707
15f0b5b7 1708=head1 CREDITS
fc7ec1d9 1709
15f0b5b7 1710Andy Grundman
1711
fbcc39ad 1712Andy Wardley
1713
33108eaf 1714Andreas Marienborg
1715
f4a57de4 1716Andrew Bramble
1717
15f0b5b7 1718Andrew Ford
1719
1720Andrew Ruthven
1721
fbcc39ad 1722Arthur Bergman
1723
15f0b5b7 1724Autrijus Tang
1725
1726Christian Hansen
1727
1728Christopher Hicks
1729
1730Dan Sully
1731
1732Danijel Milicevic
1733
1734David Naughton
1735
1736Gary Ashton Jones
1737
1738Geoff Richards
1739
1740Jesse Sheidlower
1741
fbcc39ad 1742Jesse Vincent
1743
15f0b5b7 1744Jody Belka
1745
1746Johan Lindstrom
1747
1748Juan Camacho
1749
1750Leon Brocard
1751
1752Marcus Ramberg
1753
1754Matt S Trout
1755
71c3bcc3 1756Robert Sedlacek
1757
a727119f 1758Sam Vilain
1759
15f0b5b7 1760Tatsuhiko Miyagawa
fc7ec1d9 1761
51f0308d 1762Ulf Edvinsson
1763
bdcb95ef 1764Yuval Kogman
1765
51f0308d 1766=head1 AUTHOR
1767
1768Sebastian Riedel, C<sri@oook.de>
1769
fc7ec1d9 1770=head1 LICENSE
1771
9ce5ab63 1772This library is free software, you can redistribute it and/or modify it under
41ca9ba7 1773the same terms as Perl itself.
fc7ec1d9 1774
1775=cut
1776
17771;