Commit | Line | Data |
01b62b15 |
1 | =head1 NAME |
2 | |
3 | Catalyst::Manual::Deployment - Deploying Catalyst |
4 | |
5 | =head1 |
6 | |
7 | =head1 mod_perl |
8 | |
9 | L<Catalyst::Manual::Deployment::Apache::mod_perl> |
10 | |
11 | =head1 FastCGI |
12 | |
13 | =head2 Apache |
14 | |
15 | L<Catalyst::Manual::Deployment::Apache::FastCGI> |
16 | |
17 | |
18 | =head2 mod_perl Deployment |
19 | |
20 | mod_perl is not the best solution for many applications, but we'll list some |
21 | pros and cons so you can decide for yourself. The other (recommended) |
22 | deployment option is FastCGI, for which see below. |
23 | |
24 | =head3 Pros |
25 | |
26 | =head4 Speed |
27 | |
28 | mod_perl is fast and your app will be loaded in memory |
29 | within each Apache process. |
30 | |
31 | =head4 Shared memory for multiple apps |
32 | |
33 | If you need to run several Catalyst apps on the same server, mod_perl will |
34 | share the memory for common modules. |
35 | |
36 | =head3 Cons |
37 | |
38 | =head4 Memory usage |
39 | |
40 | Since your application is fully loaded in memory, every Apache process will |
41 | be rather large. This means a large Apache process will be tied up while |
42 | serving static files, large files, or dealing with slow clients. For this |
43 | reason, it is best to run a two-tiered web architecture with a lightweight |
44 | frontend server passing dynamic requests to a large backend mod_perl |
45 | server. |
46 | |
47 | =head4 Reloading |
48 | |
49 | Any changes made to the core code of your app require a full Apache restart. |
50 | Catalyst does not support Apache::Reload or StatINC. This is another good |
51 | reason to run a frontend web server where you can set up an |
52 | C<ErrorDocument 502> page to report that your app is down for maintenance. |
53 | |
54 | =head4 Cannot run multiple versions of the same app |
55 | |
56 | It is not possible to run two different versions of the same application in |
57 | the same Apache instance because the namespaces will collide. |
58 | |
59 | =head4 Cannot run different versions of libraries. |
60 | |
61 | If you have two different applications which run on the same machine, |
62 | which need two different versions of a library then the only way to do |
63 | this is to have per-vhost perl interpreters (with different library paths). |
64 | This is entirely possible, but nullifies all the memory sharing benefits that |
65 | you get from having multiple applications sharing the same interpreter. |
66 | |
67 | =head4 Setup |
68 | |
69 | Now that we have that out of the way, let's talk about setting up mod_perl |
70 | to run a Catalyst app. |
71 | |
72 | =head4 1. Install Catalyst::Engine::Apache |
73 | |
74 | You should install the latest versions of both Catalyst and |
75 | Catalyst::Engine::Apache. The Apache engines were separated from the |
76 | Catalyst core in version 5.50 to allow for updates to the engine without |
77 | requiring a new Catalyst release. |
78 | |
79 | =head4 2. Install Apache with mod_perl |
80 | |
81 | Both Apache 1.3 and Apache 2 are supported, although Apache 2 is highly |
82 | recommended. With Apache 2, make sure you are using the prefork MPM and not |
83 | the worker MPM. The reason for this is that many Perl modules are not |
84 | thread-safe and may have problems running within the threaded worker |
85 | environment. Catalyst is thread-safe however, so if you know what you're |
86 | doing, you may be able to run using worker. |
87 | |
88 | In Debian, the following commands should get you going. |
89 | |
90 | apt-get install apache2-mpm-prefork |
91 | apt-get install libapache2-mod-perl2 |
92 | |
93 | =head4 3. Configure your application |
94 | |
95 | Every Catalyst application will automagically become a mod_perl handler |
96 | when run within mod_perl. This makes the configuration extremely easy. |
97 | Here is a basic Apache 2 configuration. |
98 | |
99 | PerlSwitches -I/var/www/MyApp/lib |
100 | PerlModule MyApp |
101 | |
102 | <Location /> |
103 | SetHandler modperl |
104 | PerlResponseHandler MyApp |
105 | </Location> |
106 | |
107 | The most important line here is C<PerlModule MyApp>. This causes mod_perl |
108 | to preload your entire application into shared memory, including all of your |
109 | controller, model, and view classes and configuration. If you have -Debug |
110 | mode enabled, you will see the startup output scroll by when you first |
111 | start Apache. |
112 | |
113 | For an example Apache 1.3 configuration, please see the documentation for |
114 | L<Catalyst::Engine::Apache::MP13>. |
115 | |
116 | =head3 Test It |
117 | |
118 | That's it, your app is now a full-fledged mod_perl application! Try it out |
119 | by going to http://your.server.com/. |
120 | |
121 | =head3 Other Options |
122 | |
123 | =head4 Non-root location |
124 | |
125 | You may not always want to run your app at the root of your server or virtual |
126 | host. In this case, it's a simple change to run at any non-root location |
127 | of your choice. |
128 | |
129 | <Location /myapp> |
130 | SetHandler modperl |
131 | PerlResponseHandler MyApp |
132 | </Location> |
133 | |
134 | When running this way, it is best to make use of the C<uri_for> method in |
135 | Catalyst for constructing correct links. |
136 | |
137 | =head4 Static file handling |
138 | |
139 | Static files can be served directly by Apache for a performance boost. |
140 | |
141 | DocumentRoot /var/www/MyApp/root |
142 | <Location /static> |
143 | SetHandler default-handler |
144 | </Location> |
145 | |
146 | This will let all files within root/static be handled directly by Apache. In |
147 | a two-tiered setup, the frontend server should handle static files. |
148 | The configuration to do this on the frontend will vary. |
149 | |
150 | The same is accomplished in lighttpd with the following snippet: |
151 | |
152 | $HTTP["url"] !~ "^/(?:img/|static/|css/|favicon.ico$)" { |
153 | fastcgi.server = ( |
154 | "" => ( |
155 | "MyApp" => ( |
156 | "socket" => "/tmp/myapp.socket", |
157 | "check-local" => "disable", |
158 | ) |
159 | ) |
160 | ) |
161 | } |
162 | |
163 | Which serves everything in the img, static, css directories |
164 | statically, as well as the favicon file. |
165 | |
166 | Note the path of the application needs to be stated explicitly in the |
167 | web server configuration for both these recipes. |
168 | |
169 | =head2 Catalyst on shared hosting |
170 | |
171 | So, you want to put your Catalyst app out there for the whole world to |
172 | see, but you don't want to break the bank. There is an answer - if you |
173 | can get shared hosting with FastCGI and a shell, you can install your |
174 | Catalyst app in a local directory on your shared host. First, run |
175 | |
176 | perl -MCPAN -e shell |
177 | |
178 | and go through the standard CPAN configuration process. Then exit out |
179 | without installing anything. Next, open your .bashrc and add |
180 | |
181 | export PATH=$HOME/local/bin:$HOME/local/script:$PATH |
182 | perlversion=`perl -v | grep 'built for' | awk '{print $4}' | sed -e 's/v//;'` |
183 | export PERL5LIB=$HOME/local/share/perl/$perlversion:$HOME/local/lib/perl/$perlversion:$HOME/local/lib:$PERL5LIB |
184 | |
185 | and log out, then back in again (or run C<". .bashrc"> if you |
186 | prefer). Finally, edit C<.cpan/CPAN/MyConfig.pm> and add |
187 | |
188 | 'make_install_arg' => qq[SITEPREFIX=$ENV{HOME}/local], |
189 | 'makepl_arg' => qq[INSTALLDIRS=site install_base=$ENV{HOME}/local], |
190 | |
191 | Now you can install the modules you need using CPAN as normal; they |
192 | will be installed into your local directory, and perl will pick them |
193 | up. Finally, change directory into the root of your virtual host and |
194 | symlink your application's script directory in: |
195 | |
196 | cd path/to/mydomain.com |
197 | ln -s ~/lib/MyApp/script script |
198 | |
199 | And add the following lines to your .htaccess file (assuming the server |
200 | is setup to handle .pl as fcgi - you may need to rename the script to |
201 | myapp_fastcgi.fcgi and/or use a SetHandler directive): |
202 | |
203 | RewriteEngine On |
204 | RewriteCond %{REQUEST_URI} !^/?script/myapp_fastcgi.pl |
205 | RewriteRule ^(.*)$ script/myapp_fastcgi.pl/$1 [PT,L] |
206 | |
207 | Now C<http://mydomain.com/> should now Just Work. Congratulations, now |
208 | you can tell your friends about your new website (or in our case, tell |
209 | the client it's time to pay the invoice :) ) |
210 | |
211 | =head2 FastCGI Deployment |
212 | |
213 | FastCGI is a high-performance extension to CGI. It is suitable |
214 | for production environments. |
215 | |
216 | =head3 Pros |
217 | |
218 | =head4 Speed |
219 | |
220 | FastCGI performs equally as well as mod_perl. Don't let the 'CGI' fool you; |
221 | your app runs as multiple persistent processes ready to receive connections |
222 | from the web server. |
223 | |
224 | =head4 App Server |
225 | |
226 | When using external FastCGI servers, your application runs as a standalone |
227 | application server. It may be restarted independently from the web server. |
228 | This allows for a more robust environment and faster reload times when |
229 | pushing new app changes. The frontend server can even be configured to |
230 | display a friendly "down for maintenance" page while the application is |
231 | restarting. |
232 | |
233 | =head4 Load-balancing |
234 | |
235 | You can launch your application on multiple backend servers and allow the |
236 | frontend web server to load-balance between all of them. And of course, if |
237 | one goes down, your app continues to run fine. |
238 | |
239 | =head4 Multiple versions of the same app |
240 | |
241 | Each FastCGI application is a separate process, so you can run different |
242 | versions of the same app on a single server. |
243 | |
244 | =head4 Can run with threaded Apache |
245 | |
246 | Since your app is not running inside of Apache, the faster mpm_worker module |
247 | can be used without worrying about the thread safety of your application. |
248 | |
249 | =head3 Cons |
250 | |
251 | You may have to disable mod_deflate. If you experience page hangs with |
252 | mod_fastcgi then remove deflate.load and deflate.conf from mods-enabled/ |
253 | |
254 | =head4 More complex environment |
255 | |
256 | With FastCGI, there are more things to monitor and more processes running |
257 | than when using mod_perl. |
258 | |
259 | =head3 Setup |
260 | |
261 | =head4 1. Install Apache with mod_fastcgi |
262 | |
263 | mod_fastcgi for Apache is a third party module, and can be found at |
264 | L<http://www.fastcgi.com/>. It is also packaged in many distributions, |
265 | for example, libapache2-mod-fastcgi in Debian. You will also need to install |
266 | the L<FCGI> module from cpan. |
267 | |
268 | Important Note! If you experience difficulty properly rendering pages, |
269 | try disabling Apache's mod_deflate (Deflate Module), e.g. 'a2dismod deflate'. |
270 | |
271 | =head4 2. Configure your application |
272 | |
273 | # Serve static content directly |
274 | DocumentRoot /var/www/MyApp/root |
275 | Alias /static /var/www/MyApp/root/static |
276 | |
277 | FastCgiServer /var/www/MyApp/script/myapp_fastcgi.pl -processes 3 |
278 | Alias /myapp/ /var/www/MyApp/script/myapp_fastcgi.pl/ |
279 | |
280 | # Or, run at the root |
281 | Alias / /var/www/MyApp/script/myapp_fastcgi.pl/ |
282 | |
283 | The above commands will launch 3 app processes and make the app available at |
284 | /myapp/ |
285 | |
286 | =head3 Standalone server mode |
287 | |
288 | While not as easy as the previous method, running your app as an external |
289 | server gives you much more flexibility. |
290 | |
291 | First, launch your app as a standalone server listening on a socket. |
292 | |
293 | script/myapp_fastcgi.pl -l /tmp/myapp.socket -n 5 -p /tmp/myapp.pid -d |
294 | |
295 | You can also listen on a TCP port if your web server is not on the same |
296 | machine. |
297 | |
298 | script/myapp_fastcgi.pl -l :8080 -n 5 -p /tmp/myapp.pid -d |
299 | |
300 | You will probably want to write an init script to handle starting/stopping |
301 | of the app using the pid file. |
302 | |
303 | Now, we simply configure Apache to connect to the running server. |
304 | |
305 | # 502 is a Bad Gateway error, and will occur if the backend server is down |
306 | # This allows us to display a friendly static page that says "down for |
307 | # maintenance" |
308 | Alias /_errors /var/www/MyApp/root/error-pages |
309 | ErrorDocument 502 /_errors/502.html |
310 | |
311 | FastCgiExternalServer /tmp/myapp.fcgi -socket /tmp/myapp.socket |
312 | Alias /myapp/ /tmp/myapp.fcgi/ |
313 | |
314 | # Or, run at the root |
315 | Alias / /tmp/myapp.fcgi/ |
316 | |
317 | =head3 More Info |
318 | |
319 | L<Catalyst::Engine::FastCGI>. |
320 | |
321 | =head2 Development server deployment |
322 | |
323 | The development server is a mini web server written in perl. If you |
324 | expect a low number of hits or you don't need mod_perl/FastCGI speed, |
325 | you could use the development server as the application server with a |
326 | lightweight proxy web server at the front. However, consider using |
327 | L<Catalyst::Engine::HTTP::Prefork> for this kind of deployment instead, since |
328 | it can better handle multiple concurrent requests without forking, or can |
329 | prefork a set number of servers for improved performance. |
330 | |
331 | =head3 Pros |
332 | |
333 | As this is an application server setup, the pros are the same as |
334 | FastCGI (with the exception of speed). |
335 | It is also: |
336 | |
337 | =head4 Simple |
338 | |
339 | The development server is what you create your code on, so if it works |
340 | here, it should work in production! |
341 | |
342 | =head3 Cons |
343 | |
344 | =head4 Speed |
345 | |
346 | Not as fast as mod_perl or FastCGI. Needs to fork for each request |
347 | that comes in - make sure static files are served by the web server to |
348 | save forking. |
349 | |
350 | =head3 Setup |
351 | |
352 | =head4 Start up the development server |
353 | |
354 | script/myapp_server.pl -p 8080 -k -f -pidfile=/tmp/myapp.pid |
355 | |
356 | You will probably want to write an init script to handle stop/starting |
357 | the app using the pid file. |
358 | |
359 | =head4 Configuring Apache |
360 | |
361 | Make sure mod_proxy is enabled and add: |
362 | |
363 | # Serve static content directly |
364 | DocumentRoot /var/www/MyApp/root |
365 | Alias /static /var/www/MyApp/root/static |
366 | |
367 | ProxyRequests Off |
368 | <Proxy *> |
369 | Order deny,allow |
370 | Allow from all |
371 | </Proxy> |
372 | |
373 | # Need to specifically stop these paths from being passed to proxy |
374 | ProxyPass /static ! |
375 | ProxyPass /favicon.ico ! |
376 | |
377 | ProxyPass / http://localhost:8080/ |
378 | ProxyPassReverse / http://localhost:8080/ |
379 | |
380 | # This is optional if you'd like to show a custom error page |
381 | # if the proxy is not available |
382 | ErrorDocument 502 /static/error_pages/http502.html |
383 | |
384 | You can wrap the above within a VirtualHost container if you want |
385 | different apps served on the same host. |
386 | |
387 | =head2 Quick deployment: Building PAR Packages |
388 | |
389 | You have an application running on your development box, but then you |
390 | have to quickly move it to another one for |
391 | demonstration/deployment/testing... |
392 | |
393 | PAR packages can save you from a lot of trouble here. They are usual Zip |
394 | files that contain a blib tree; you can even include all prereqs and a |
395 | perl interpreter by setting a few flags! |
396 | |
397 | =head3 Follow these few points to try it out! |
398 | |
399 | 1. Install Catalyst and PAR 0.89 (or later) |
400 | |
401 | % perl -MCPAN -e 'install Catalyst' |
402 | ... |
403 | % perl -MCPAN -e 'install PAR' |
404 | ... |
405 | |
406 | 2. Create a application |
407 | |
408 | % catalyst.pl MyApp |
409 | ... |
410 | % cd MyApp |
411 | |
412 | Recent versions of Catalyst (5.62 and up) include |
413 | L<Module::Install::Catalyst>, which simplifies the process greatly. From the shell in your application directory: |
414 | |
415 | % perl Makefile.PL |
416 | % make catalyst_par |
417 | |
418 | You can customise the PAR creation process by special "catalyst_par_*" commands |
419 | available from L<Module::Install::Catalyst>. You can add these commands in your |
420 | Makefile.PL just before the line containing "catalyst;" |
421 | |
422 | #Makefile.PL example with extra PAR options |
423 | use inc::Module::Install; |
424 | |
425 | name 'MyApp'; |
426 | all_from 'lib\MyApp.pm'; |
427 | |
428 | requires 'Catalyst::Runtime' => '5.80005'; |
429 | <snip> |
430 | ... |
431 | <snip> |
432 | |
433 | catalyst_par_core(1); # bundle perl core modules in the resulting PAR |
434 | catalyst_par_multiarch(1); # build a multi-architecture PAR file |
435 | catalyst_par_classes(qw/ |
436 | Some::Additional::Module |
437 | Some::Other::Module |
438 | /); # specify additional modules you want to be included into PAR |
439 | catalyst; |
440 | |
441 | install_script glob('script/*.pl'); |
442 | auto_install; |
443 | WriteAll; |
444 | |
445 | Congratulations! Your package "myapp.par" is ready, the following |
446 | steps are just optional. |
447 | |
448 | 3. Test your PAR package with "parl" (no typo) |
449 | |
450 | % parl myapp.par |
451 | Usage: |
452 | [parl] myapp[.par] [script] [arguments] |
453 | |
454 | Examples: |
455 | parl myapp.par myapp_server.pl -r |
456 | myapp myapp_cgi.pl |
457 | |
458 | Available scripts: |
459 | myapp_cgi.pl |
460 | myapp_create.pl |
461 | myapp_fastcgi.pl |
462 | myapp_server.pl |
463 | myapp_test.pl |
464 | |
465 | % parl myapp.par myapp_server.pl |
466 | You can connect to your server at http://localhost:3000 |
467 | |
468 | Yes, this nifty little starter application gets automatically included. |
469 | You can also use "catalyst_par_script('myapp_server.pl')" to set a |
470 | default script to execute. |
471 | |
472 | 6. Want to create a binary that includes the Perl interpreter? |
473 | |
474 | % pp -o myapp myapp.par |
475 | % ./myapp myapp_server.pl |
476 | You can connect to your server at http://localhost:3000 |
477 | |
478 | =head2 Serving static content |
479 | |
480 | Serving static content in Catalyst used to be somewhat tricky; the use |
481 | of L<Catalyst::Plugin::Static::Simple> makes everything much easier. |
482 | This plugin will automatically serve your static content during development, |
483 | but allows you to easily switch to Apache (or other server) in a |
484 | production environment. |
485 | |
486 | =head3 Introduction to Static::Simple |
487 | |
488 | Static::Simple is a plugin that will help to serve static content for your |
489 | application. By default, it will serve most types of files, excluding some |
490 | standard Template Toolkit extensions, out of your B<root> file directory. All |
491 | files are served by path, so if B<images/me.jpg> is requested, then |
492 | B<root/images/me.jpg> is found and served. |
493 | |
494 | =head3 Usage |
495 | |
496 | Using the plugin is as simple as setting your use line in MyApp.pm to include: |
497 | |
498 | use Catalyst qw/Static::Simple/; |
499 | |
500 | and already files will be served. |
501 | |
502 | =head3 Configuring |
503 | |
504 | Static content is best served from a single directory within your root |
505 | directory. Having many different directories such as C<root/css> and |
506 | C<root/images> requires more code to manage, because you must separately |
507 | identify each static directory--if you decide to add a C<root/js> |
508 | directory, you'll need to change your code to account for it. In |
509 | contrast, keeping all static directories as subdirectories of a main |
510 | C<root/static> directory makes things much easier to manage. Here's an |
511 | example of a typical root directory structure: |
512 | |
513 | root/ |
514 | root/content.tt |
515 | root/controller/stuff.tt |
516 | root/header.tt |
517 | root/static/ |
518 | root/static/css/main.css |
519 | root/static/images/logo.jpg |
520 | root/static/js/code.js |
521 | |
522 | |
523 | All static content lives under C<root/static>, with everything else being |
524 | Template Toolkit files. |
525 | |
526 | =over 4 |
527 | |
528 | =item Include Path |
529 | |
530 | You may of course want to change the default locations, and make |
531 | Static::Simple look somewhere else, this is as easy as: |
532 | |
533 | MyApp->config->{static}->{include_path} = [ |
534 | MyApp->config->{root}, |
535 | '/path/to/my/files' |
536 | ]; |
537 | |
538 | When you override include_path, it will not automatically append the |
539 | normal root path, so you need to add it yourself if you still want |
540 | it. These will be searched in order given, and the first matching file |
541 | served. |
542 | |
543 | =item Static directories |
544 | |
545 | If you want to force some directories to be only static, you can set |
546 | them using paths relative to the root dir, or regular expressions: |
547 | |
548 | MyApp->config->{static}->{dirs} = [ |
549 | 'static', |
550 | qr/^(images|css)/, |
551 | ]; |
552 | |
553 | =item File extensions |
554 | |
555 | By default, the following extensions are not served (that is, they will |
556 | be processed by Catalyst): B<tmpl, tt, tt2, html, xhtml>. This list can |
557 | be replaced easily: |
558 | |
559 | MyApp->config->{static}->{ignore_extensions} = [ |
560 | qw/tmpl tt tt2 html xhtml/ |
561 | ]; |
562 | |
563 | =item Ignoring directories |
564 | |
565 | Entire directories can be ignored. If used with include_path, |
566 | directories relative to the include_path dirs will also be ignored: |
567 | |
568 | MyApp->config->{static}->{ignore_dirs} = [ qw/tmpl css/ ]; |
569 | |
570 | =back |
571 | |
572 | =head3 More information |
573 | |
574 | L<http://search.cpan.org/dist/Catalyst-Plugin-Static-Simple/> |
575 | |
576 | =head3 Serving manually with the Static plugin with HTTP::Daemon (myapp_server.pl) |
577 | |
578 | In some situations you might want to control things more directly, |
579 | using L<Catalyst::Plugin::Static>. |
580 | |
581 | In your main application class (MyApp.pm), load the plugin: |
582 | |
583 | use Catalyst qw/-Debug FormValidator Static OtherPlugin/; |
584 | |
585 | You will also need to make sure your end method does I<not> forward |
586 | static content to the view, perhaps like this: |
587 | |
588 | sub end : Private { |
589 | my ( $self, $c ) = @_; |
590 | |
591 | $c->forward( 'MyApp::View::TT' ) |
592 | unless ( $c->res->body || !$c->stash->{template} ); |
593 | } |
594 | |
595 | This code will only forward to the view if a template has been |
596 | previously defined by a controller and if there is not already data in |
597 | C<$c-E<gt>res-E<gt>body>. |
598 | |
599 | Next, create a controller to handle requests for the /static path. Use |
600 | the Helper to save time. This command will create a stub controller as |
601 | C<lib/MyApp/Controller/Static.pm>. |
602 | |
603 | $ script/myapp_create.pl controller Static |
604 | |
605 | Edit the file and add the following methods: |
606 | |
607 | # serve all files under /static as static files |
608 | sub default : Path('/static') { |
609 | my ( $self, $c ) = @_; |
610 | |
611 | # Optional, allow the browser to cache the content |
612 | $c->res->headers->header( 'Cache-Control' => 'max-age=86400' ); |
613 | |
614 | $c->serve_static; # from Catalyst::Plugin::Static |
615 | } |
616 | |
617 | # also handle requests for /favicon.ico |
618 | sub favicon : Path('/favicon.ico') { |
619 | my ( $self, $c ) = @_; |
620 | |
621 | $c->serve_static; |
622 | } |
623 | |
624 | You can also define a different icon for the browser to use instead of |
625 | favicon.ico by using this in your HTML header: |
626 | |
627 | <link rel="icon" href="/static/myapp.ico" type="image/x-icon" /> |
628 | |
629 | =head3 Common problems with the Static plugin |
630 | |
631 | The Static plugin makes use of the C<shared-mime-info> package to |
632 | automatically determine MIME types. This package is notoriously |
633 | difficult to install, especially on win32 and OS X. For OS X the easiest |
634 | path might be to install Fink, then use C<apt-get install |
635 | shared-mime-info>. Restart the server, and everything should be fine. |
636 | |
637 | Make sure you are using the latest version (>= 0.16) for best |
638 | results. If you are having errors serving CSS files, or if they get |
639 | served as text/plain instead of text/css, you may have an outdated |
640 | shared-mime-info version. You may also wish to simply use the following |
641 | code in your Static controller: |
642 | |
643 | if ($c->req->path =~ /css$/i) { |
644 | $c->serve_static( "text/css" ); |
645 | } else { |
646 | $c->serve_static; |
647 | } |
648 | |
649 | =head3 Serving Static Files with Apache |
650 | |
651 | When using Apache, you can bypass Catalyst and any Static |
652 | plugins/controllers controller by intercepting requests for the |
653 | C<root/static> path at the server level. All that is required is to |
654 | define a DocumentRoot and add a separate Location block for your static |
655 | content. Here is a complete config for this application under mod_perl |
656 | 1.x: |
657 | |
658 | <Perl> |
659 | use lib qw(/var/www/MyApp/lib); |
660 | </Perl> |
661 | PerlModule MyApp |
662 | |
663 | <VirtualHost *> |
664 | ServerName myapp.example.com |
665 | DocumentRoot /var/www/MyApp/root |
666 | <Location /> |
667 | SetHandler perl-script |
668 | PerlHandler MyApp |
669 | </Location> |
670 | <LocationMatch "/(static|favicon.ico)"> |
671 | SetHandler default-handler |
672 | </LocationMatch> |
673 | </VirtualHost> |
674 | |
675 | And here's a simpler example that'll get you started: |
676 | |
677 | Alias /static/ "/my/static/files/" |
678 | <Location "/static"> |
679 | SetHandler none |
680 | </Location> |
681 | |
682 | =head2 Caching |
683 | |
684 | Catalyst makes it easy to employ several different types of caching to |
685 | speed up your applications. |
686 | |
687 | =head3 Cache Plugins |
688 | |
689 | There are three wrapper plugins around common CPAN cache modules: |
690 | Cache::FastMmap, Cache::FileCache, and Cache::Memcached. These can be |
691 | used to cache the result of slow operations. |
692 | |
693 | The Catalyst Advent Calendar uses the FileCache plugin to cache the |
694 | rendered XHTML version of the source POD document. This is an ideal |
695 | application for a cache because the source document changes |
696 | infrequently but may be viewed many times. |
697 | |
698 | use Catalyst qw/Cache::FileCache/; |
699 | |
700 | ... |
701 | |
702 | use File::stat; |
703 | sub render_pod : Local { |
704 | my ( self, $c ) = @_; |
705 | |
706 | # the cache is keyed on the filename and the modification time |
707 | # to check for updates to the file. |
708 | my $file = $c->path_to( 'root', '2005', '11.pod' ); |
709 | my $mtime = ( stat $file )->mtime; |
710 | |
711 | my $cached_pod = $c->cache->get("$file $mtime"); |
712 | if ( !$cached_pod ) { |
713 | $cached_pod = do_slow_pod_rendering(); |
714 | # cache the result for 12 hours |
715 | $c->cache->set( "$file $mtime", $cached_pod, '12h' ); |
716 | } |
717 | $c->stash->{pod} = $cached_pod; |
718 | } |
719 | |
720 | We could actually cache the result forever, but using a value such as 12 hours |
721 | allows old entries to be automatically expired when they are no longer needed. |
722 | |
723 | =head3 Page Caching |
724 | |
725 | Another method of caching is to cache the entire HTML page. While this is |
726 | traditionally handled by a front-end proxy server like Squid, the Catalyst |
727 | PageCache plugin makes it trivial to cache the entire output from |
728 | frequently-used or slow actions. |
729 | |
730 | Many sites have a busy content-filled front page that might look something |
731 | like this. It probably takes a while to process, and will do the exact same |
732 | thing for every single user who views the page. |
733 | |
734 | sub front_page : Path('/') { |
735 | my ( $self, $c ) = @_; |
736 | |
737 | $c->forward( 'get_news_articles' ); |
738 | $c->forward( 'build_lots_of_boxes' ); |
739 | $c->forward( 'more_slow_stuff' ); |
740 | |
741 | $c->stash->{template} = 'index.tt'; |
742 | } |
743 | |
744 | We can add the PageCache plugin to speed things up. |
745 | |
746 | use Catalyst qw/Cache::FileCache PageCache/; |
747 | |
748 | sub front_page : Path ('/') { |
749 | my ( $self, $c ) = @_; |
750 | |
751 | $c->cache_page( 300 ); |
752 | |
753 | # same processing as above |
754 | } |
755 | |
756 | Now the entire output of the front page, from <html> to </html>, will be |
757 | cached for 5 minutes. After 5 minutes, the next request will rebuild the |
758 | page and it will be re-cached. |
759 | |
760 | Note that the page cache is keyed on the page URI plus all parameters, so |
761 | requests for / and /?foo=bar will result in different cache items. Also, |
762 | only GET requests will be cached by the plugin. |
763 | |
764 | You can even get that front-end Squid proxy to help out by enabling HTTP |
765 | headers for the cached page. |
766 | |
767 | MyApp->config->{page_cache}->{set_http_headers} = 1; |
768 | |
769 | This would now set the following headers so proxies and browsers may cache |
770 | the content themselves. |
771 | |
772 | Cache-Control: max-age=($expire_time - time) |
773 | Expires: $expire_time |
774 | Last-Modified: $cache_created_time |
775 | |
776 | =head3 Template Caching |
777 | |
778 | Template Toolkit provides support for caching compiled versions of your |
779 | templates. To enable this in Catalyst, use the following configuration. |
780 | TT will cache compiled templates keyed on the file mtime, so changes will |
781 | still be automatically detected. |
782 | |
783 | package MyApp::View::TT; |
784 | |
785 | use strict; |
786 | use warnings; |
787 | use base 'Catalyst::View::TT'; |
788 | |
789 | __PACKAGE__->config( |
790 | COMPILE_DIR => '/tmp/template_cache', |
791 | ); |
792 | |
793 | 1; |
794 | |
795 | =head3 More Info |
796 | |
797 | See the documentation for each cache plugin for more details and other |
798 | available configuration options. |
799 | |
800 | L<Catalyst::Plugin::Cache::FastMmap> |
801 | L<Catalyst::Plugin::Cache::FileCache> |
802 | L<Catalyst::Plugin::Cache::Memcached> |
803 | L<Catalyst::Plugin::PageCache> |
804 | L<http://search.cpan.org/dist/Template-Toolkit/lib/Template/Manual/Config.pod#Caching_and_Compiling_Options> |
805 | |
806 | =head1 Testing |
807 | |
808 | Testing is an integral part of the web application development |
809 | process. Tests make multi developer teams easier to coordinate, and |
810 | they help ensure that there are no nasty surprises after upgrades or |
811 | alterations. |
812 | |
813 | =head2 Testing |
814 | |
815 | Catalyst provides a convenient way of testing your application during |
816 | development and before deployment in a real environment. |
817 | |
818 | C<Catalyst::Test> makes it possible to run the same tests both locally |
819 | (without an external daemon) and against a remote server via HTTP. |
820 | |
821 | =head3 Tests |
822 | |
823 | Let's examine a skeleton application's C<t/> directory: |
824 | |
825 | mundus:~/MyApp chansen$ ls -l t/ |
826 | total 24 |
827 | -rw-r--r-- 1 chansen chansen 95 18 Dec 20:50 01app.t |
828 | -rw-r--r-- 1 chansen chansen 190 18 Dec 20:50 02pod.t |
829 | -rw-r--r-- 1 chansen chansen 213 18 Dec 20:50 03podcoverage.t |
830 | |
831 | =over 4 |
832 | |
833 | =item C<01app.t> |
834 | |
835 | Verifies that the application loads, compiles, and returns a successful |
836 | response. |
837 | |
838 | =item C<02pod.t> |
839 | |
840 | Verifies that all POD is free from errors. Only executed if the C<TEST_POD> |
841 | environment variable is true. |
842 | |
843 | =item C<03podcoverage.t> |
844 | |
845 | Verifies that all methods/functions have POD coverage. Only executed if the |
846 | C<TEST_POD> environment variable is true. |
847 | |
848 | =back |
849 | |
850 | =head3 Creating tests |
851 | |
852 | mundus:~/MyApp chansen$ cat t/01app.t | perl -ne 'printf( "%2d %s", $., $_ )' |
853 | 1 use Test::More tests => 2; |
854 | 2 BEGIN { use_ok( Catalyst::Test, 'MyApp' ) } |
855 | 3 |
856 | 4 ok( request('/')->is_success ); |
857 | |
858 | The first line declares how many tests we are going to run, in this case |
859 | two. The second line tests and loads our application in test mode. The |
860 | fourth line verifies that our application returns a successful response. |
861 | |
862 | C<Catalyst::Test> exports two functions, C<request> and C<get>. Each can |
863 | take three different arguments: |
864 | |
865 | =over 4 |
866 | |
867 | =item A string which is a relative or absolute URI. |
868 | |
869 | request('/my/path'); |
870 | request('http://www.host.com/my/path'); |
871 | |
872 | =item An instance of C<URI>. |
873 | |
874 | request( URI->new('http://www.host.com/my/path') ); |
875 | |
876 | =item An instance of C<HTTP::Request>. |
877 | |
878 | request( HTTP::Request->new( GET => 'http://www.host.com/my/path') ); |
879 | |
880 | =back |
881 | |
882 | C<request> returns an instance of C<HTTP::Response> and C<get> returns the |
883 | content (body) of the response. |
884 | |
885 | =head3 Running tests locally |
886 | |
887 | mundus:~/MyApp chansen$ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib/ t/ |
888 | t/01app............ok |
889 | t/02pod............ok |
890 | t/03podcoverage....ok |
891 | All tests successful. |
892 | Files=3, Tests=4, 2 wallclock secs ( 1.60 cusr + 0.36 csys = 1.96 CPU) |
893 | |
894 | C<CATALYST_DEBUG=0> ensures that debugging is off; if it's enabled you |
895 | will see debug logs between tests. |
896 | |
897 | C<TEST_POD=1> enables POD checking and coverage. |
898 | |
899 | C<prove> A command-line tool that makes it easy to run tests. You can |
900 | find out more about it from the links below. |
901 | |
902 | =head3 Running tests remotely |
903 | |
904 | mundus:~/MyApp chansen$ CATALYST_SERVER=http://localhost:3000/ prove --lib lib/ t/01app.t |
905 | t/01app....ok |
906 | All tests successful. |
907 | Files=1, Tests=2, 0 wallclock secs ( 0.40 cusr + 0.01 csys = 0.41 CPU) |
908 | |
909 | C<CATALYST_SERVER=http://localhost:3000/> is the absolute deployment URI of |
910 | your application. In C<CGI> or C<FastCGI> it should be the host and path |
911 | to the script. |
912 | |
913 | =head3 C<Test::WWW::Mechanize> and Catalyst |
914 | |
915 | Be sure to check out C<Test::WWW::Mechanize::Catalyst>. It makes it easy to |
916 | test HTML, forms and links. A short example of usage: |
917 | |
918 | use Test::More tests => 6; |
919 | BEGIN { use_ok( Test::WWW::Mechanize::Catalyst, 'MyApp' ) } |
920 | |
921 | my $mech = Test::WWW::Mechanize::Catalyst->new; |
922 | $mech->get_ok("http://localhost/", 'Got index page'); |
923 | $mech->title_like( qr/^MyApp on Catalyst/, 'Got right index title' ); |
924 | ok( $mech->find_link( text_regex => qr/^Wiki/i ), 'Found link to Wiki' ); |
925 | ok( $mech->find_link( text_regex => qr/^Mailing-List/i ), 'Found link to Mailing-List' ); |
926 | ok( $mech->find_link( text_regex => qr/^IRC channel/i ), 'Found link to IRC channel' ); |
927 | |
928 | =head3 Further Reading |
929 | |
930 | =over 4 |
931 | |
932 | =item Catalyst::Test |
933 | |
934 | L<Catalyst::Test> |
935 | |
936 | =item Test::WWW::Mechanize::Catalyst |
937 | |
938 | L<http://search.cpan.org/dist/Test-WWW-Mechanize-Catalyst/lib/Test/WWW/Mechanize/Catalyst.pm> |
939 | |
940 | =item Test::WWW::Mechanize |
941 | |
942 | L<http://search.cpan.org/dist/Test-WWW-Mechanize/Mechanize.pm> |
943 | |
944 | =item WWW::Mechanize |
945 | |
946 | L<http://search.cpan.org/dist/WWW-Mechanize/lib/WWW/Mechanize.pm> |
947 | |
948 | =item LWP::UserAgent |
949 | |
950 | L<http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm> |
951 | |
952 | =item HTML::Form |
953 | |
954 | L<http://search.cpan.org/dist/libwww-perl/lib/HTML/Form.pm> |
955 | |
956 | =item HTTP::Message |
957 | |
958 | L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Message.pm> |
959 | |
960 | =item HTTP::Request |
961 | |
962 | L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request.pm> |
963 | |
964 | =item HTTP::Request::Common |
965 | |
966 | L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request/Common.pm> |
967 | |
968 | =item HTTP::Response |
969 | |
970 | L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Response.pm> |
971 | |
972 | =item HTTP::Status |
973 | |
974 | L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Status.pm> |
975 | |
976 | =item URI |
977 | |
978 | L<http://search.cpan.org/dist/URI/URI.pm> |
979 | |
980 | =item Test::More |
981 | |
982 | L<http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm> |
983 | |
984 | =item Test::Pod |
985 | |
986 | L<http://search.cpan.org/dist/Test-Pod/Pod.pm> |
987 | |
988 | =item Test::Pod::Coverage |
989 | |
990 | L<http://search.cpan.org/dist/Test-Pod-Coverage/Coverage.pm> |
991 | |
992 | =item prove (Test::Harness) |
993 | |
994 | L<http://search.cpan.org/dist/Test-Harness/bin/prove> |
995 | |
996 | =back |
997 | |
998 | =head3 More Information |
999 | |
1000 | L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::Roles> |
1001 | L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::ACL> |
1002 | |
1003 | =head1 AUTHORS |
1004 | |
1005 | Catalyst Contributors, see Catalyst.pm |
1006 | |
1007 | =head1 COPYRIGHT |
1008 | |
1009 | This library is free software. You can redistribute it and/or modify it under |
1010 | the same terms as Perl itself. |
1011 | |
1012 | =cut |