Pull deployment info out
Tomas Doran [Sat, 6 Aug 2011 15:26:08 +0000 (16:26 +0100)]
lib/Catalyst/Manual/Deployment.pod [new file with mode: 0644]

diff --git a/lib/Catalyst/Manual/Deployment.pod b/lib/Catalyst/Manual/Deployment.pod
new file mode 100644 (file)
index 0000000..8dfecc8
--- /dev/null
@@ -0,0 +1,1012 @@
+=head1 NAME
+
+Catalyst::Manual::Deployment - Deploying Catalyst
+
+=head1
+
+=head1 mod_perl
+
+L<Catalyst::Manual::Deployment::Apache::mod_perl>
+
+=head1 FastCGI
+
+=head2 Apache
+
+L<Catalyst::Manual::Deployment::Apache::FastCGI>
+
+
+=head2 mod_perl Deployment
+
+mod_perl is not the best solution for many applications, but we'll list some
+pros and cons so you can decide for yourself. The other (recommended)
+deployment option is FastCGI, for which see below.
+
+=head3 Pros
+
+=head4 Speed
+
+mod_perl is fast and your app will be loaded in memory
+within each Apache process.
+
+=head4 Shared memory for multiple apps
+
+If you need to run several Catalyst apps on the same server, mod_perl will
+share the memory for common modules.
+
+=head3 Cons
+
+=head4 Memory usage
+
+Since your application is fully loaded in memory, every Apache process will
+be rather large.  This means a large Apache process will be tied up while
+serving static files, large files, or dealing with slow clients.  For this
+reason, it is best to run a two-tiered web architecture with a lightweight
+frontend server passing dynamic requests to a large backend mod_perl
+server.
+
+=head4 Reloading
+
+Any changes made to the core code of your app require a full Apache restart.
+Catalyst does not support Apache::Reload or StatINC.  This is another good
+reason to run a frontend web server where you can set up an
+C<ErrorDocument 502> page to report that your app is down for maintenance.
+
+=head4 Cannot run multiple versions of the same app
+
+It is not possible to run two different versions of the same application in
+the same Apache instance because the namespaces will collide.
+
+=head4 Cannot run different versions of libraries.
+
+If you have two different applications which run on the same machine,
+which need two different versions of a library then the only way to do
+this is to have per-vhost perl interpreters (with different library paths).
+This is entirely possible, but nullifies all the memory sharing benefits that
+you get from having multiple applications sharing the same interpreter.
+
+=head4 Setup
+
+Now that we have that out of the way, let's talk about setting up mod_perl
+to run a Catalyst app.
+
+=head4 1. Install Catalyst::Engine::Apache
+
+You should install the latest versions of both Catalyst and
+Catalyst::Engine::Apache.  The Apache engines were separated from the
+Catalyst core in version 5.50 to allow for updates to the engine without
+requiring a new Catalyst release.
+
+=head4 2. Install Apache with mod_perl
+
+Both Apache 1.3 and Apache 2 are supported, although Apache 2 is highly
+recommended.  With Apache 2, make sure you are using the prefork MPM and not
+the worker MPM.  The reason for this is that many Perl modules are not
+thread-safe and may have problems running within the threaded worker
+environment.  Catalyst is thread-safe however, so if you know what you're
+doing, you may be able to run using worker.
+
+In Debian, the following commands should get you going.
+
+    apt-get install apache2-mpm-prefork
+    apt-get install libapache2-mod-perl2
+
+=head4 3. Configure your application
+
+Every Catalyst application will automagically become a mod_perl handler
+when run within mod_perl.  This makes the configuration extremely easy.
+Here is a basic Apache 2 configuration.
+
+    PerlSwitches -I/var/www/MyApp/lib
+    PerlModule MyApp
+
+    <Location />
+        SetHandler          modperl
+        PerlResponseHandler MyApp
+    </Location>
+
+The most important line here is C<PerlModule MyApp>.  This causes mod_perl
+to preload your entire application into shared memory, including all of your
+controller, model, and view classes and configuration.  If you have -Debug
+mode enabled, you will see the startup output scroll by when you first
+start Apache.
+
+For an example Apache 1.3 configuration, please see the documentation for
+L<Catalyst::Engine::Apache::MP13>.
+
+=head3 Test It
+
+That's it, your app is now a full-fledged mod_perl application!  Try it out
+by going to http://your.server.com/.
+
+=head3 Other Options
+
+=head4 Non-root location
+
+You may not always want to run your app at the root of your server or virtual
+host.  In this case, it's a simple change to run at any non-root location
+of your choice.
+
+    <Location /myapp>
+        SetHandler          modperl
+        PerlResponseHandler MyApp
+    </Location>
+
+When running this way, it is best to make use of the C<uri_for> method in
+Catalyst for constructing correct links.
+
+=head4 Static file handling
+
+Static files can be served directly by Apache for a performance boost.
+
+    DocumentRoot /var/www/MyApp/root
+    <Location /static>
+        SetHandler default-handler
+    </Location>
+
+This will let all files within root/static be handled directly by Apache.  In
+a two-tiered setup, the frontend server should handle static files.
+The configuration to do this on the frontend will vary.
+
+The same is accomplished in lighttpd with the following snippet:
+
+   $HTTP["url"] !~ "^/(?:img/|static/|css/|favicon.ico$)" {
+         fastcgi.server = (
+             "" => (
+                 "MyApp" => (
+                     "socket"       => "/tmp/myapp.socket",
+                     "check-local"  => "disable",
+                 )
+             )
+         )
+    }
+
+Which serves everything in the img, static, css directories
+statically, as well as the favicon file.
+
+Note the path of the application needs to be stated explicitly in the
+web server configuration for both these recipes.
+
+=head2 Catalyst on shared hosting
+
+So, you want to put your Catalyst app out there for the whole world to
+see, but you don't want to break the bank. There is an answer - if you
+can get shared hosting with FastCGI and a shell, you can install your
+Catalyst app in a local directory on your shared host. First, run
+
+    perl -MCPAN -e shell
+
+and go through the standard CPAN configuration process. Then exit out
+without installing anything. Next, open your .bashrc and add
+
+    export PATH=$HOME/local/bin:$HOME/local/script:$PATH
+    perlversion=`perl -v | grep 'built for' | awk '{print $4}' | sed -e 's/v//;'`
+    export PERL5LIB=$HOME/local/share/perl/$perlversion:$HOME/local/lib/perl/$perlversion:$HOME/local/lib:$PERL5LIB
+
+and log out, then back in again (or run C<". .bashrc"> if you
+prefer). Finally, edit C<.cpan/CPAN/MyConfig.pm> and add
+
+    'make_install_arg' => qq[SITEPREFIX=$ENV{HOME}/local],
+    'makepl_arg' => qq[INSTALLDIRS=site install_base=$ENV{HOME}/local],
+
+Now you can install the modules you need using CPAN as normal; they
+will be installed into your local directory, and perl will pick them
+up. Finally, change directory into the root of your virtual host and
+symlink your application's script directory in:
+
+    cd path/to/mydomain.com
+    ln -s ~/lib/MyApp/script script
+
+And add the following lines to your .htaccess file (assuming the server
+is setup to handle .pl as fcgi - you may need to rename the script to
+myapp_fastcgi.fcgi and/or use a SetHandler directive):
+
+  RewriteEngine On
+  RewriteCond %{REQUEST_URI} !^/?script/myapp_fastcgi.pl
+  RewriteRule ^(.*)$ script/myapp_fastcgi.pl/$1 [PT,L]
+
+Now C<http://mydomain.com/> should now Just Work. Congratulations, now
+you can tell your friends about your new website (or in our case, tell
+the client it's time to pay the invoice :) )
+
+=head2 FastCGI Deployment
+
+FastCGI is a high-performance extension to CGI. It is suitable
+for production environments.
+
+=head3 Pros
+
+=head4 Speed
+
+FastCGI performs equally as well as mod_perl.  Don't let the 'CGI' fool you;
+your app runs as multiple persistent processes ready to receive connections
+from the web server.
+
+=head4 App Server
+
+When using external FastCGI servers, your application runs as a standalone
+application server.  It may be restarted independently from the web server.
+This allows for a more robust environment and faster reload times when
+pushing new app changes.  The frontend server can even be configured to
+display a friendly "down for maintenance" page while the application is
+restarting.
+
+=head4 Load-balancing
+
+You can launch your application on multiple backend servers and allow the
+frontend web server to load-balance between all of them.  And of course, if
+one goes down, your app continues to run fine.
+
+=head4 Multiple versions of the same app
+
+Each FastCGI application is a separate process, so you can run different
+versions of the same app on a single server.
+
+=head4 Can run with threaded Apache
+
+Since your app is not running inside of Apache, the faster mpm_worker module
+can be used without worrying about the thread safety of your application.
+
+=head3 Cons
+
+You may have to disable mod_deflate.  If you experience page hangs with
+mod_fastcgi then remove deflate.load and deflate.conf from mods-enabled/
+
+=head4 More complex environment
+
+With FastCGI, there are more things to monitor and more processes running
+than when using mod_perl.
+
+=head3 Setup
+
+=head4 1. Install Apache with mod_fastcgi
+
+mod_fastcgi for Apache is a third party module, and can be found at
+L<http://www.fastcgi.com/>.  It is also packaged in many distributions,
+for example, libapache2-mod-fastcgi in Debian. You will also need to install
+the L<FCGI> module from cpan.
+
+Important Note! If you experience difficulty properly rendering pages,
+try disabling Apache's mod_deflate (Deflate Module), e.g. 'a2dismod deflate'.
+
+=head4 2. Configure your application
+
+    # Serve static content directly
+    DocumentRoot  /var/www/MyApp/root
+    Alias /static /var/www/MyApp/root/static
+
+    FastCgiServer /var/www/MyApp/script/myapp_fastcgi.pl -processes 3
+    Alias /myapp/ /var/www/MyApp/script/myapp_fastcgi.pl/
+
+    # Or, run at the root
+    Alias / /var/www/MyApp/script/myapp_fastcgi.pl/
+
+The above commands will launch 3 app processes and make the app available at
+/myapp/
+
+=head3 Standalone server mode
+
+While not as easy as the previous method, running your app as an external
+server gives you much more flexibility.
+
+First, launch your app as a standalone server listening on a socket.
+
+    script/myapp_fastcgi.pl -l /tmp/myapp.socket -n 5 -p /tmp/myapp.pid -d
+
+You can also listen on a TCP port if your web server is not on the same
+machine.
+
+    script/myapp_fastcgi.pl -l :8080 -n 5 -p /tmp/myapp.pid -d
+
+You will probably want to write an init script to handle starting/stopping
+of the app using the pid file.
+
+Now, we simply configure Apache to connect to the running server.
+
+    # 502 is a Bad Gateway error, and will occur if the backend server is down
+    # This allows us to display a friendly static page that says "down for
+    # maintenance"
+    Alias /_errors /var/www/MyApp/root/error-pages
+    ErrorDocument 502 /_errors/502.html
+
+    FastCgiExternalServer /tmp/myapp.fcgi -socket /tmp/myapp.socket
+    Alias /myapp/ /tmp/myapp.fcgi/
+
+    # Or, run at the root
+    Alias / /tmp/myapp.fcgi/
+
+=head3 More Info
+
+L<Catalyst::Engine::FastCGI>.
+
+=head2 Development server deployment
+
+The development server is a mini web server written in perl.  If you
+expect a low number of hits or you don't need mod_perl/FastCGI speed,
+you could use the development server as the application server with a
+lightweight proxy web server at the front.  However, consider using
+L<Catalyst::Engine::HTTP::Prefork> for this kind of deployment instead, since
+it can better handle multiple concurrent requests without forking, or can
+prefork a set number of servers for improved performance.
+
+=head3 Pros
+
+As this is an application server setup, the pros are the same as
+FastCGI (with the exception of speed).
+It is also:
+
+=head4 Simple
+
+The development server is what you create your code on, so if it works
+here, it should work in production!
+
+=head3 Cons
+
+=head4 Speed
+
+Not as fast as mod_perl or FastCGI. Needs to fork for each request
+that comes in - make sure static files are served by the web server to
+save forking.
+
+=head3 Setup
+
+=head4 Start up the development server
+
+   script/myapp_server.pl -p 8080 -k  -f -pidfile=/tmp/myapp.pid
+
+You will probably want to write an init script to handle stop/starting
+the app using the pid file.
+
+=head4 Configuring Apache
+
+Make sure mod_proxy is enabled and add:
+
+    # Serve static content directly
+    DocumentRoot /var/www/MyApp/root
+    Alias /static /var/www/MyApp/root/static
+
+    ProxyRequests Off
+    <Proxy *>
+        Order deny,allow
+        Allow from all
+    </Proxy>
+
+    # Need to specifically stop these paths from being passed to proxy
+    ProxyPass /static !
+    ProxyPass /favicon.ico !
+
+    ProxyPass / http://localhost:8080/
+    ProxyPassReverse / http://localhost:8080/
+
+    # This is optional if you'd like to show a custom error page
+    # if the proxy is not available
+    ErrorDocument 502 /static/error_pages/http502.html
+
+You can wrap the above within a VirtualHost container if you want
+different apps served on the same host.
+
+=head2 Quick deployment: Building PAR Packages
+
+You have an application running on your development box, but then you
+have to quickly move it to another one for
+demonstration/deployment/testing...
+
+PAR packages can save you from a lot of trouble here. They are usual Zip
+files that contain a blib tree; you can even include all prereqs and a
+perl interpreter by setting a few flags!
+
+=head3 Follow these few points to try it out!
+
+1. Install Catalyst and PAR 0.89 (or later)
+
+    % perl -MCPAN -e 'install Catalyst'
+    ...
+    % perl -MCPAN -e 'install PAR'
+    ...
+
+2. Create a application
+
+    % catalyst.pl MyApp
+    ...
+    % cd MyApp
+
+Recent versions of Catalyst (5.62 and up) include
+L<Module::Install::Catalyst>, which simplifies the process greatly.  From the shell in your application directory:
+
+    % perl Makefile.PL
+    % make catalyst_par
+
+You can customise the PAR creation process by special "catalyst_par_*" commands
+available from L<Module::Install::Catalyst>. You can add these commands in your
+Makefile.PL just before the line containing "catalyst;"
+
+    #Makefile.PL example with extra PAR options
+    use inc::Module::Install;
+
+    name 'MyApp';
+    all_from 'lib\MyApp.pm';
+
+    requires 'Catalyst::Runtime' => '5.80005';
+    <snip>
+    ...
+    <snip>
+
+    catalyst_par_core(1);      # bundle perl core modules in the resulting PAR
+    catalyst_par_multiarch(1); # build a multi-architecture PAR file
+    catalyst_par_classes(qw/
+      Some::Additional::Module
+      Some::Other::Module
+    /);          # specify additional modules you want to be included into PAR
+    catalyst;
+
+    install_script glob('script/*.pl');
+    auto_install;
+    WriteAll;
+
+Congratulations! Your package "myapp.par" is ready, the following
+steps are just optional.
+
+3. Test your PAR package with "parl" (no typo)
+
+    % parl myapp.par
+    Usage:
+        [parl] myapp[.par] [script] [arguments]
+
+      Examples:
+        parl myapp.par myapp_server.pl -r
+        myapp myapp_cgi.pl
+
+      Available scripts:
+        myapp_cgi.pl
+        myapp_create.pl
+        myapp_fastcgi.pl
+        myapp_server.pl
+        myapp_test.pl
+
+    % parl myapp.par myapp_server.pl
+    You can connect to your server at http://localhost:3000
+
+Yes, this nifty little starter application gets automatically included.
+You can also use "catalyst_par_script('myapp_server.pl')" to set a
+default script to execute.
+
+6. Want to create a binary that includes the Perl interpreter?
+
+    % pp -o myapp myapp.par
+    % ./myapp myapp_server.pl
+    You can connect to your server at http://localhost:3000
+
+=head2 Serving static content
+
+Serving static content in Catalyst used to be somewhat tricky; the use
+of L<Catalyst::Plugin::Static::Simple> makes everything much easier.
+This plugin will automatically serve your static content during development,
+but allows you to easily switch to Apache (or other server) in a
+production environment.
+
+=head3 Introduction to Static::Simple
+
+Static::Simple is a plugin that will help to serve static content for your
+application. By default, it will serve most types of files, excluding some
+standard Template Toolkit extensions, out of your B<root> file directory. All
+files are served by path, so if B<images/me.jpg> is requested, then
+B<root/images/me.jpg> is found and served.
+
+=head3 Usage
+
+Using the plugin is as simple as setting your use line in MyApp.pm to include:
+
+ use Catalyst qw/Static::Simple/;
+
+and already files will be served.
+
+=head3 Configuring
+
+Static content is best served from a single directory within your root
+directory. Having many different directories such as C<root/css> and
+C<root/images> requires more code to manage, because you must separately
+identify each static directory--if you decide to add a C<root/js>
+directory, you'll need to change your code to account for it. In
+contrast, keeping all static directories as subdirectories of a main
+C<root/static> directory makes things much easier to manage. Here's an
+example of a typical root directory structure:
+
+    root/
+    root/content.tt
+    root/controller/stuff.tt
+    root/header.tt
+    root/static/
+    root/static/css/main.css
+    root/static/images/logo.jpg
+    root/static/js/code.js
+
+
+All static content lives under C<root/static>, with everything else being
+Template Toolkit files.
+
+=over 4
+
+=item Include Path
+
+You may of course want to change the default locations, and make
+Static::Simple look somewhere else, this is as easy as:
+
+ MyApp->config->{static}->{include_path} = [
+  MyApp->config->{root},
+  '/path/to/my/files'
+ ];
+
+When you override include_path, it will not automatically append the
+normal root path, so you need to add it yourself if you still want
+it. These will be searched in order given, and the first matching file
+served.
+
+=item Static directories
+
+If you want to force some directories to be only static, you can set
+them using paths relative to the root dir, or regular expressions:
+
+ MyApp->config->{static}->{dirs} = [
+   'static',
+   qr/^(images|css)/,
+ ];
+
+=item File extensions
+
+By default, the following extensions are not served (that is, they will
+be processed by Catalyst): B<tmpl, tt, tt2, html, xhtml>. This list can
+be replaced easily:
+
+ MyApp->config->{static}->{ignore_extensions} = [
+    qw/tmpl tt tt2 html xhtml/
+ ];
+
+=item Ignoring directories
+
+Entire directories can be ignored. If used with include_path,
+directories relative to the include_path dirs will also be ignored:
+
+ MyApp->config->{static}->{ignore_dirs} = [ qw/tmpl css/ ];
+
+=back
+
+=head3 More information
+
+L<http://search.cpan.org/dist/Catalyst-Plugin-Static-Simple/>
+
+=head3 Serving manually with the Static plugin with HTTP::Daemon (myapp_server.pl)
+
+In some situations you might want to control things more directly,
+using L<Catalyst::Plugin::Static>.
+
+In your main application class (MyApp.pm), load the plugin:
+
+    use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
+
+You will also need to make sure your end method does I<not> forward
+static content to the view, perhaps like this:
+
+    sub end : Private {
+        my ( $self, $c ) = @_;
+
+        $c->forward( 'MyApp::View::TT' )
+          unless ( $c->res->body || !$c->stash->{template} );
+    }
+
+This code will only forward to the view if a template has been
+previously defined by a controller and if there is not already data in
+C<$c-E<gt>res-E<gt>body>.
+
+Next, create a controller to handle requests for the /static path. Use
+the Helper to save time. This command will create a stub controller as
+C<lib/MyApp/Controller/Static.pm>.
+
+    $ script/myapp_create.pl controller Static
+
+Edit the file and add the following methods:
+
+    # serve all files under /static as static files
+    sub default : Path('/static') {
+        my ( $self, $c ) = @_;
+
+        # Optional, allow the browser to cache the content
+        $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
+
+        $c->serve_static; # from Catalyst::Plugin::Static
+    }
+
+    # also handle requests for /favicon.ico
+    sub favicon : Path('/favicon.ico') {
+        my ( $self, $c ) = @_;
+
+        $c->serve_static;
+    }
+
+You can also define a different icon for the browser to use instead of
+favicon.ico by using this in your HTML header:
+
+    <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
+
+=head3 Common problems with the Static plugin
+
+The Static plugin makes use of the C<shared-mime-info> package to
+automatically determine MIME types. This package is notoriously
+difficult to install, especially on win32 and OS X. For OS X the easiest
+path might be to install Fink, then use C<apt-get install
+shared-mime-info>. Restart the server, and everything should be fine.
+
+Make sure you are using the latest version (>= 0.16) for best
+results. If you are having errors serving CSS files, or if they get
+served as text/plain instead of text/css, you may have an outdated
+shared-mime-info version. You may also wish to simply use the following
+code in your Static controller:
+
+    if ($c->req->path =~ /css$/i) {
+        $c->serve_static( "text/css" );
+    } else {
+        $c->serve_static;
+    }
+
+=head3 Serving Static Files with Apache
+
+When using Apache, you can bypass Catalyst and any Static
+plugins/controllers controller by intercepting requests for the
+C<root/static> path at the server level. All that is required is to
+define a DocumentRoot and add a separate Location block for your static
+content. Here is a complete config for this application under mod_perl
+1.x:
+
+    <Perl>
+        use lib qw(/var/www/MyApp/lib);
+    </Perl>
+    PerlModule MyApp
+
+    <VirtualHost *>
+        ServerName myapp.example.com
+        DocumentRoot /var/www/MyApp/root
+        <Location />
+            SetHandler perl-script
+            PerlHandler MyApp
+        </Location>
+        <LocationMatch "/(static|favicon.ico)">
+            SetHandler default-handler
+        </LocationMatch>
+    </VirtualHost>
+
+And here's a simpler example that'll get you started:
+
+    Alias /static/ "/my/static/files/"
+    <Location "/static">
+        SetHandler none
+    </Location>
+
+=head2 Caching
+
+Catalyst makes it easy to employ several different types of caching to
+speed up your applications.
+
+=head3 Cache Plugins
+
+There are three wrapper plugins around common CPAN cache modules:
+Cache::FastMmap, Cache::FileCache, and Cache::Memcached.  These can be
+used to cache the result of slow operations.
+
+The Catalyst Advent Calendar uses the FileCache plugin to cache the
+rendered XHTML version of the source POD document.  This is an ideal
+application for a cache because the source document changes
+infrequently but may be viewed many times.
+
+    use Catalyst qw/Cache::FileCache/;
+
+    ...
+
+    use File::stat;
+    sub render_pod : Local {
+        my ( self, $c ) = @_;
+
+        # the cache is keyed on the filename and the modification time
+        # to check for updates to the file.
+        my $file  = $c->path_to( 'root', '2005', '11.pod' );
+        my $mtime = ( stat $file )->mtime;
+
+        my $cached_pod = $c->cache->get("$file $mtime");
+        if ( !$cached_pod ) {
+            $cached_pod = do_slow_pod_rendering();
+            # cache the result for 12 hours
+            $c->cache->set( "$file $mtime", $cached_pod, '12h' );
+        }
+        $c->stash->{pod} = $cached_pod;
+    }
+
+We could actually cache the result forever, but using a value such as 12 hours
+allows old entries to be automatically expired when they are no longer needed.
+
+=head3 Page Caching
+
+Another method of caching is to cache the entire HTML page.  While this is
+traditionally handled by a front-end proxy server like Squid, the Catalyst
+PageCache plugin makes it trivial to cache the entire output from
+frequently-used or slow actions.
+
+Many sites have a busy content-filled front page that might look something
+like this.  It probably takes a while to process, and will do the exact same
+thing for every single user who views the page.
+
+    sub front_page : Path('/') {
+        my ( $self, $c ) = @_;
+
+        $c->forward( 'get_news_articles' );
+        $c->forward( 'build_lots_of_boxes' );
+        $c->forward( 'more_slow_stuff' );
+
+        $c->stash->{template} = 'index.tt';
+    }
+
+We can add the PageCache plugin to speed things up.
+
+    use Catalyst qw/Cache::FileCache PageCache/;
+
+    sub front_page : Path ('/') {
+        my ( $self, $c ) = @_;
+
+        $c->cache_page( 300 );
+
+        # same processing as above
+    }
+
+Now the entire output of the front page, from <html> to </html>, will be
+cached for 5 minutes.  After 5 minutes, the next request will rebuild the
+page and it will be re-cached.
+
+Note that the page cache is keyed on the page URI plus all parameters, so
+requests for / and /?foo=bar will result in different cache items.  Also,
+only GET requests will be cached by the plugin.
+
+You can even get that front-end Squid proxy to help out by enabling HTTP
+headers for the cached page.
+
+    MyApp->config->{page_cache}->{set_http_headers} = 1;
+
+This would now set the following headers so proxies and browsers may cache
+the content themselves.
+
+    Cache-Control: max-age=($expire_time - time)
+    Expires: $expire_time
+    Last-Modified: $cache_created_time
+
+=head3 Template Caching
+
+Template Toolkit provides support for caching compiled versions of your
+templates.  To enable this in Catalyst, use the following configuration.
+TT will cache compiled templates keyed on the file mtime, so changes will
+still be automatically detected.
+
+    package MyApp::View::TT;
+
+    use strict;
+    use warnings;
+    use base 'Catalyst::View::TT';
+
+    __PACKAGE__->config(
+        COMPILE_DIR => '/tmp/template_cache',
+    );
+
+    1;
+
+=head3 More Info
+
+See the documentation for each cache plugin for more details and other
+available configuration options.
+
+L<Catalyst::Plugin::Cache::FastMmap>
+L<Catalyst::Plugin::Cache::FileCache>
+L<Catalyst::Plugin::Cache::Memcached>
+L<Catalyst::Plugin::PageCache>
+L<http://search.cpan.org/dist/Template-Toolkit/lib/Template/Manual/Config.pod#Caching_and_Compiling_Options>
+
+=head1 Testing
+
+Testing is an integral part of the web application development
+process.  Tests make multi developer teams easier to coordinate, and
+they help ensure that there are no nasty surprises after upgrades or
+alterations.
+
+=head2 Testing
+
+Catalyst provides a convenient way of testing your application during
+development and before deployment in a real environment.
+
+C<Catalyst::Test> makes it possible to run the same tests both locally
+(without an external daemon) and against a remote server via HTTP.
+
+=head3 Tests
+
+Let's examine a skeleton application's C<t/> directory:
+
+    mundus:~/MyApp chansen$ ls -l t/
+    total 24
+    -rw-r--r--  1 chansen  chansen   95 18 Dec 20:50 01app.t
+    -rw-r--r--  1 chansen  chansen  190 18 Dec 20:50 02pod.t
+    -rw-r--r--  1 chansen  chansen  213 18 Dec 20:50 03podcoverage.t
+
+=over 4
+
+=item C<01app.t>
+
+Verifies that the application loads, compiles, and returns a successful
+response.
+
+=item C<02pod.t>
+
+Verifies that all POD is free from errors. Only executed if the C<TEST_POD>
+environment variable is true.
+
+=item C<03podcoverage.t>
+
+Verifies that all methods/functions have POD coverage. Only executed if the
+C<TEST_POD> environment variable is true.
+
+=back
+
+=head3 Creating tests
+
+    mundus:~/MyApp chansen$ cat t/01app.t | perl -ne 'printf( "%2d  %s", $., $_ )'
+    1  use Test::More tests => 2;
+    2  BEGIN { use_ok( Catalyst::Test, 'MyApp' ) }
+    3
+    4  ok( request('/')->is_success );
+
+The first line declares how many tests we are going to run, in this case
+two. The second line tests and loads our application in test mode. The
+fourth line verifies that our application returns a successful response.
+
+C<Catalyst::Test> exports two functions, C<request> and C<get>. Each can
+take three different arguments:
+
+=over 4
+
+=item A string which is a relative or absolute URI.
+
+    request('/my/path');
+    request('http://www.host.com/my/path');
+
+=item An instance of C<URI>.
+
+    request( URI->new('http://www.host.com/my/path') );
+
+=item An instance of C<HTTP::Request>.
+
+    request( HTTP::Request->new( GET => 'http://www.host.com/my/path') );
+
+=back
+
+C<request> returns an instance of C<HTTP::Response> and C<get> returns the
+content (body) of the response.
+
+=head3 Running tests locally
+
+    mundus:~/MyApp chansen$ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib/ t/
+    t/01app............ok
+    t/02pod............ok
+    t/03podcoverage....ok
+    All tests successful.
+    Files=3, Tests=4,  2 wallclock secs ( 1.60 cusr +  0.36 csys =  1.96 CPU)
+
+C<CATALYST_DEBUG=0> ensures that debugging is off; if it's enabled you
+will see debug logs between tests.
+
+C<TEST_POD=1> enables POD checking and coverage.
+
+C<prove> A command-line tool that makes it easy to run tests. You can
+find out more about it from the links below.
+
+=head3 Running tests remotely
+
+    mundus:~/MyApp chansen$ CATALYST_SERVER=http://localhost:3000/ prove --lib lib/ t/01app.t
+    t/01app....ok
+    All tests successful.
+    Files=1, Tests=2,  0 wallclock secs ( 0.40 cusr +  0.01 csys =  0.41 CPU)
+
+C<CATALYST_SERVER=http://localhost:3000/> is the absolute deployment URI of
+your application. In C<CGI> or C<FastCGI> it should be the host and path
+to the script.
+
+=head3 C<Test::WWW::Mechanize> and Catalyst
+
+Be sure to check out C<Test::WWW::Mechanize::Catalyst>. It makes it easy to
+test HTML, forms and links. A short example of usage:
+
+    use Test::More tests => 6;
+    BEGIN { use_ok( Test::WWW::Mechanize::Catalyst, 'MyApp' ) }
+
+    my $mech = Test::WWW::Mechanize::Catalyst->new;
+    $mech->get_ok("http://localhost/", 'Got index page');
+    $mech->title_like( qr/^MyApp on Catalyst/, 'Got right index title' );
+    ok( $mech->find_link( text_regex => qr/^Wiki/i ), 'Found link to Wiki' );
+    ok( $mech->find_link( text_regex => qr/^Mailing-List/i ), 'Found link to Mailing-List' );
+    ok( $mech->find_link( text_regex => qr/^IRC channel/i ), 'Found link to IRC channel' );
+
+=head3 Further Reading
+
+=over 4
+
+=item Catalyst::Test
+
+L<Catalyst::Test>
+
+=item Test::WWW::Mechanize::Catalyst
+
+L<http://search.cpan.org/dist/Test-WWW-Mechanize-Catalyst/lib/Test/WWW/Mechanize/Catalyst.pm>
+
+=item Test::WWW::Mechanize
+
+L<http://search.cpan.org/dist/Test-WWW-Mechanize/Mechanize.pm>
+
+=item WWW::Mechanize
+
+L<http://search.cpan.org/dist/WWW-Mechanize/lib/WWW/Mechanize.pm>
+
+=item LWP::UserAgent
+
+L<http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm>
+
+=item HTML::Form
+
+L<http://search.cpan.org/dist/libwww-perl/lib/HTML/Form.pm>
+
+=item HTTP::Message
+
+L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Message.pm>
+
+=item HTTP::Request
+
+L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request.pm>
+
+=item HTTP::Request::Common
+
+L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request/Common.pm>
+
+=item HTTP::Response
+
+L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Response.pm>
+
+=item HTTP::Status
+
+L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Status.pm>
+
+=item URI
+
+L<http://search.cpan.org/dist/URI/URI.pm>
+
+=item Test::More
+
+L<http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm>
+
+=item Test::Pod
+
+L<http://search.cpan.org/dist/Test-Pod/Pod.pm>
+
+=item Test::Pod::Coverage
+
+L<http://search.cpan.org/dist/Test-Pod-Coverage/Coverage.pm>
+
+=item prove (Test::Harness)
+
+L<http://search.cpan.org/dist/Test-Harness/bin/prove>
+
+=back
+
+=head3 More Information
+
+L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::Roles>
+L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::ACL>
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify it under
+the same terms as Perl itself.
+
+=cut