├── .gitignore ├── LICENSE ├── README.md ├── en ├── 00_intro.md ├── 01_getting_plack.md ├── 02_hello_world.md ├── 03_using_plackup.md ├── 04_reloading_applications.md ├── 05_run_a_static_web_server_with_plack.md ├── 06_convert_cgi_apps_to_psgi.md ├── 07_use_web_application_framework_in_psgi.md ├── 08_adapting_web_frameworks_to_psgi.md ├── 09_running_cgi_scripts_on_plack.md ├── 10_using_plack_middleware.md ├── 11_using_plack_builder.md ├── 12_maps_multiple_apps_with_mount_and_urlmap.md ├── 13_use_plack_test_to_test_your_application.md ├── 14_use_plack_request.md ├── 15_authenticate_your_app_with_middleware.md ├── 16_adding_jsonp_support_to_your_app.md ├── 17_serving_static_files_from_your_application.md ├── 18_load_middleware_conditionally.md ├── 19_cascade_multiple_applications.md ├── 20_access_your_local_app_from_the_internet.md ├── 21_lint_your_application_and_middleware.md ├── 22_discover_more_middleware.md ├── 23_write_your_own_middleware.md └── 24_wrap_up.md ├── images └── pylons_as_onion.png └── ja ├── 00_intro.md ├── 01_getting_plack.md ├── 02_hello_world.md ├── 03_using_plackup.md ├── 04_reloading_applications.md ├── 05_run_a_static_web_server_with_plack.md ├── 06_convert_cgi_apps_to_psgi.md ├── 07_use_web_application_framework_in_psgi.md ├── 08_adapting_web_frameworks_to_psgi.md ├── 09_running_cgi_scripts_on_plack.md ├── 10_using_plack_middleware.md ├── 11_using_plack_builder.md ├── 12_maps_multiple_apps_with_mount_and_urlmap.md ├── 13_use_plack_test_to_test_your_application.md ├── 14_use_plack_request.md ├── 15_authenticate_your_app_with_middleware.md ├── 16_adding_jsonp_support_to_your_app.md ├── 17_serving_static_files_from_your_application.md ├── 18_load_middleware_conditionally.md ├── 19_cascade_multiple_applications.md ├── 20_access_your_local_app_from_the_internet.md ├── 21_lint_your_application_and_middleware.md ├── 22_discover_more_middleware.md ├── 23_write_your_own_middleware.md ├── 24_wrap_up.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | book* 2 | plack-handbook* 3 | _site 4 | bin/export_html.rb 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2009-2012 Tatsuhiko Miyagawa 2 | 3 | The content of this book is licensed under CC BY-NC 3.0. 4 | http://creativecommons.org/licenses/by-nc/3.0/ 5 | 6 | If you want it re-licensed for commercial purposes, email me at miyagawa[at]bulknews.net. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | See [the Plack Handbook website](http://handbook.plackperl.org/) for more details about this book. -------------------------------------------------------------------------------- /en/00_intro.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | This little handbook is based on the content of the [Plack Advent Calendar](http://advent.plackperl.org/) website. The calendar had 24 useful short posts explaining the concept of PSGI and how to adapt existing web applications to Plack. 4 | 5 | The calendar was so successful it's been considered a canonical reference for many beginners trying to learn Plack and for web framework authors trying to adapt their code to work with PSGI. 6 | 7 | But the content was written 3 years ago and some of it is a little dated. In this book I tried to get it correct and up-to-date, and it will continue to be updated as we find errata and get feedback from the reader community. 8 | 9 | Raw source code of the book, in Markdown format, is available on [GitHub repository](https://github.com/miyagawa/plack-handbook) under the Creative Commons license. See the LICENSE file for details. 10 | 11 | 2012 September, Tatsuhiko Miyagawa 12 | -------------------------------------------------------------------------------- /en/01_getting_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 1: Getting Plack 2 | 3 | The most important step to get started is to install [Plack](http://search.cpan.org/dist/Plack) and other utilities. Because PSGI and Plack are just normal Perl module distributions the installation is easy: just launch your CPAN shell and type: 4 | 5 | ``` 6 | cpan> install PSGI Plack 7 | ``` 8 | 9 | [PSGI](http://search.cpan.org/dist/PSGI) is a specification document for the PSGI interface. By installing the distribution you can read the documents in your shell with the `perldoc PSGI` or `perldoc PSGI::FAQ` commands. Plack gives you the standard server implementations, core middleware components, and utilities like plackup and Plack::Test. 10 | 11 | Plack doesn't depend on any non-core XS modules so with any Perl distribution later than 5.8.1 (which was released more than 6 years ago!) it can be installed very easily, even on platforms like Win32 or Mac OS X without developer tools (e.g., C compilers). 12 | 13 | If you're a developer of web applications or frameworks (I suppose you are!), it's highly recommended you install the optional module bundle [Task::Plack](http://search.cpan.org/dist/Task-Plack) as well. The installation is as easy as typing: 14 | 15 | ``` 16 | cpan> install Task::Plack 17 | ``` 18 | 19 | You will be prompted with a couple of questions depending on your environment. If you're unsure whether you should or should not install, just type return to select the default. You'll get optional XS speedups by default, while other options like non-blocking environments are disabled by default. 20 | 21 | Start reading the documentation with `perldoc Plack` to get prepared. 22 | -------------------------------------------------------------------------------- /en/02_hello_world.md: -------------------------------------------------------------------------------- 1 | ## Day 2: Hello World 2 | 3 | The first program you write in any programming language is the one that prints "Hello World". Let's follow that tradition for PSGI as well. 4 | 5 | **Note:** Today's code is written to a raw PSGI interface to help you understand what's going on. In reality you should never have to do this unless you're a web application framework developer. Instead you should use one of the [existing frameworks that supports PSGI](http://plackperl.org/#frameworks). 6 | 7 | ### Hello, World 8 | 9 | Here's the minimal code that prints "Hello World" to the client: 10 | 11 | my $app = sub { 12 | return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ]; 13 | }; 14 | 15 | A PSGI application is a Perl subroutine reference (a coderef) and is usually referenced as `$app` (it could be named anything obviously). It takes exactly one argument `$env` (which is not used in this code) and returns an array ref containing status, headers, and body. That's it. 16 | 17 | Save this code in a file named `hello.psgi` and then use the plackup command to run it: 18 | 19 | > plackup hello.psgi 20 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ 21 | 22 | plackup runs your application with the default HTTP server HTTP::Server::PSGI on localhost port 5000. Open the URL http://127.0.0.1:5000/ and you should see the "Hello World" page. 23 | 24 | ### Give me something different 25 | 26 | Hello World is the simplest code imaginable. We can do more here. Let's read and display the client information using the PSGI environment hash. 27 | 28 | my $app = sub { 29 | my $env = shift; 30 | return [ 31 | 200, 32 | ['Content-Type' => 'text/plain'], 33 | [ "Hello stranger from $env->{REMOTE_ADDR}!"], 34 | ]; 35 | }; 36 | 37 | This code will display the remote address using the PSGI environment hash. It will normally be 127.0.0.1 if you're running the server on your localhost. The PSGI environment hash contains lots of information about an HTTP connection like incoming HTTP headers and request paths, much like the CGI environment variables. 38 | 39 | Want to display something that isn't just text? We can do this by reading a file: 40 | 41 | my $app = sub { 42 | my $env = shift; 43 | if ($env->{PATH_INFO} eq '/favicon.ico') { 44 | open my $fh, "<:raw", "/path/to/favicon.ico" or die $!; 45 | return [ 200, ['Content-Type' => 'image/x-icon'], $fh ]; 46 | } elsif ($env->{PATH_INFO} eq '/') { 47 | return [ 200, ['Content-Type' => 'text/plain'], [ "Hello again" ] ]; 48 | } else { 49 | return [ 404, ['Content-Type' => 'text/html'], [ '404 Not Found' ] ]; 50 | } 51 | }; 52 | 53 | This app would serve favicon.ico if the request path looks like /favicon.ico, the "Hello World" page for requests to the root (/) and otherwise a 404. You can see that a Perl filehandle (`$fh`) is a valid PSGI response, and you can use any valid HTTP status code for a response. 54 | -------------------------------------------------------------------------------- /en/03_using_plackup.md: -------------------------------------------------------------------------------- 1 | ## Day 3: Using plackup 2 | 3 | In the [Day 2](http://advent.plackperl.org/2009/12/day-2-hello-world.html) article I used the plackup command to run the Hello World PSGI application. 4 | 5 | plackup is a command line launcher of PSGI applications inspired by Rack's rackup command. It can be used to run any PSGI application saved in a .psgi file with one of the PSGI web server backends using Plack handlers. The usage is simple: just pass a .psgi file path to the command: 6 | 7 | > plackup hello.psgi 8 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ 9 | 10 | You can actually omit the filename if you're trying to run the file named `app.psgi` in the current directory. 11 | 12 | The default backend is chosen using one of the following methods: 13 | 14 | * If the environment variable `PLACK_SERVER` is set it is used. 15 | * If some environment specific variable like `GATEWAY_INTERFACE` or `FCGI_ROLE` is set the backend for CGI or FCGI is used accordingly. 16 | * If the loaded `.psgi` file uses a specific event module like AnyEvent, Coro or POE the equivalent and most appropriate backend is chosen automatically. 17 | * Otherwise, fallback to the default "Standalone" backend implemented in the HTTP::Server::PSGI module. 18 | 19 | You can also specify the backend yourself from the command line using the `-s` or `--server` switch: 20 | 21 | > plackup -s Starman hello.psgi 22 | 23 | By default the plackup command enables three middleware components to aid development: Lint, AccessLog, and StackTrace. You can disable them with the `-E` (or `--environment`) switch: 24 | 25 | > plackup -E production -s Starman hello.psgi 26 | 27 | In the case that you really want to use the `development` Plack environment but want to disable the default middleware there is the `--no-default-middleware` option. 28 | 29 | Other command line switches can be passed to the server. You can specify the server listen port with: 30 | 31 | > plackup -s Starlet --host 127.0.0.1 --port 8080 hello.psgi 32 | Plack::Handler::Starlet: Accepting connections at http://127.0.0.1:8080/ 33 | 34 | or specify the unix domain socket the FCGI backend should listen on with: 35 | 36 | > plackup -s FCGI --listen /tmp/fcgi.sock app.psgi 37 | 38 | For more options for plackup, run `perldoc plackup` from the command line. You'll see more plackup options and hacks tomorrow as well. 39 | -------------------------------------------------------------------------------- /en/04_reloading_applications.md: -------------------------------------------------------------------------------- 1 | ## Day 4: Reloading applications 2 | 3 | [Yesterday](http://advent.plackperl.org/2009/12/day-3-using-plackup.html) I introduced the basics of plackup and its command line options. Today I'll show you more! 4 | 5 | ### Reload the application as necessary 6 | 7 | During development you often change your Perl code, saved in `.psgi` or `.pm` files. Because the Plack server launched by the plackup command is a persistent process you need to restart your server whenever your code changes. This is a little painful. 8 | 9 | So there's an option to watch for changes to files under your working directory and reload the application as needed: `-r` (or `--reload`). 10 | 11 | plackup -r hello.psgi 12 | 13 | It will watch files under the current directory by default, but you can change it to watch additional locations by using the `-R` option (note the uppercase). 14 | 15 | plackup -R lib,/path/to/scripts hello.psgi 16 | 17 | As you can see, multiple paths can be monitored by combining them with `,` (comma). 18 | 19 | By default plackup uses a dumb timer to scan the whole directory, but if you're on Linux and have Linux::Inotify2 installed or on Mac OS and have Mac::FSEvents installed filesystem notification is used so it's more efficient. 20 | 21 | ### -r vs Server auto-detection 22 | 23 | In Day 3 I told you that plackup's automatic server detection is smart enough to tell if PSGI application uses an event module such as AnyEvent or Coro and choose the correct backend. Be aware that this automatic selection doesn't work if you use the `-r` option because plackup uses a delayed loading technique to reload apps in forked processes. It's recommended that you explicitly set the server with the `-s` option when using the `-r` option. 24 | 25 | ### Reloading sucks? Shotgun! 26 | 27 | Reloading a module or application in a persistent Perl process can cause problems. For instance, module package variables could be redefined or overwritten and then get stuck in a bad state. 28 | 29 | Plack now has the Shotgun loader, inspired by [Rack's Shotgun](http://github.com/rtomayko/shotgun), which solves the reloading problem by loading the app on *every request* in a forked child environment. 30 | 31 | Using the Shotgun loader is easy: 32 | 33 | > plackup -L Shotgun myapp.psgi 34 | 35 | This will delay the compilation of your application until runtime. When a request is received it will fork off a new child process to compile your app and return the PSGI response over the pipe. You can also preload modules in the parent process that are not likely to be updated to reduce the time needed to compile your application. 36 | 37 | For instance, if your application uses Moose and DBIx::Class then use the following options: 38 | 39 | > plackup -MMoose -MDBIx::Class -L Shotgun myapp.psgi 40 | 41 | This speeds up the time required to compile your application at runtime. 42 | -------------------------------------------------------------------------------- /en/05_run_a_static_web_server_with_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 5: Run a static web server with Plack 2 | 3 | The Plack distribution comes with some ready made PSGI applications in the Plack::App namespace. Some of them are pretty handy, for instance [Plack::App::File](http://search.cpan.org/perldoc?Plack::App::File) and [Plack::App::Directory](http://search.cpan.org/perldoc?Plack::App::Directory). 4 | 5 | Plack::App::File translates a request path like `/foo/bar.html` into a local file like `/path/to/htdocs/foo/bar.html`, opens the file handle, and passes it back as a PSGI response. It basically does what a static web server like lighttpd, nginx or Apache does. 6 | 7 | Plack::App::Directory is a wrapper around Plack::App::File that gives a directory index, just like [Apache's mod_autoindex](http://httpd.apache.org/docs/2.0/mod/mod_autoindex.html) does. 8 | 9 | Using these applications is easy. Just write a .psgi file like this: 10 | 11 | use Plack::App::File; 12 | my $app = Plack::App::File->new(root => "$ENV{HOME}/public_html"); 13 | 14 | and run it with plackup: 15 | 16 | > plackup file.psgi 17 | 18 | Now you can access any file under your `~/public_html` with the URL http://localhost:5000/somefile.html 19 | 20 | You can also use Plack::App::Directory. This time let's run it with just the plackup command without a .psgi file: 21 | 22 | > plackup -MPlack::App::Directory \ 23 | -e 'Plack::App::Directory->new(root => "$ENV{HOME}/Sites")' 24 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ 25 | 26 | The plackup command, like the perl command, accepts flags like `-I` (include path), `-M` (modules to load), and `-e` (the code to eval), so it's easy to load these Plack::App::* applications without ever touching a .psgi file! 27 | 28 | There are a couple of other Plack::App applications in the Plack distribution. 29 | -------------------------------------------------------------------------------- /en/06_convert_cgi_apps_to_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 6: Convert CGI apps to PSGI 2 | 3 | The most popular web server environments to run web applications for Perl have been CGI, FastCGI, and mod_perl. CGI.pm is one of the Perl core modules that happens to run fine in any of these environments (with some tweaks). This means many web applications and frameworks use CGI.pm to deal with environment differences because it's the easiest. 4 | 5 | [CGI::PSGI](http://search.cpan.org/perldoc?CGI::PSGI) is a CGI module subclass that makes it easy to migrate existing CGI.pm based applications to PSGI. Imagine you have the following CGI application: 6 | 7 | use CGI; 8 | 9 | my $q = CGI->new; 10 | print $q->header('text/plain'), 11 | "Hello ", $q->param('name'); 12 | 13 | This is a very simple CGI application and converting this to PSGI is easy using the CGI::PSGI module: 14 | 15 | use CGI::PSGI; 16 | 17 | my $app = sub { 18 | my $env = shift; 19 | my $q = CGI::PSGI->new($env); 20 | return [ 21 | $q->psgi_header('text/plain'), 22 | [ "Hello ", $q->param('name') ], 23 | ]; 24 | }; 25 | 26 | `CGI::PSGI->new($env)` takes the PSGI environment hash and creates an instance of CGI::PSGI, which is a subclass of CGI.pm. All methods including `param()`, `query_string`, etc. do the right thing to get the values from the PSGI environment rather than CGI's ENV values. 27 | 28 | `psgi_header` is a utility method that works just like CGI's `header` method and returns the status code and an array reference containing the list of HTTP headers. 29 | 30 | Tomorrow I'll talk about how to convert existing web frameworks that use CGI.pm to PSGI using CGI::PSGI. 31 | -------------------------------------------------------------------------------- /en/07_use_web_application_framework_in_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 7: Use web application framework in PSGI 2 | 3 | Since we started the Plack and PSGI project in September 2009 there has been a lot of feedback from the authors of popular frameworks such as Catalyst, Jifty, and CGI::Application. 4 | 5 | [CGI::Application](http://cgi-app.org/) is one of the most "traditional" CGI-based web application framework and it uses CGI.pm exclusively to handle web server environments just like we [discussed yesterday](http://advent.plackperl.org/2009/12/day-6-convert-cgi-apps-to-psgi.html). 6 | 7 | Mark Stosberg, the current maintainer of CGI::Application, and I have been collaborating on adding PSGI support to CGI::Application. We thought of multiple approaches including adding native PSGI support to CGI.pm, but we ended up implementing [CGI::PSGI](http://search.cpan.org/perldoc?CGI::PSGI) as a CGI.pm wrapper and then using [CGI::Application::PSGI](http://search.cpan.org/perldoc?CGI::Application::PSGI) to run existing CGI::Application code *unmodified* in a PSGI compatible mode. 8 | 9 | All you have to do is install CGI::Application::PSGI from CPAN and write a .psgi file that looks like this: 10 | 11 | use CGI::Application::PSGI; 12 | use WebApp; 13 | 14 | my $app = sub { 15 | my $env = shift; 16 | my $app = WebApp->new({ QUERY => CGI::PSGI->new($env) }); 17 | CGI::Application::PSGI->run($app); 18 | }; 19 | 20 | Then use [plackup](http://advent.plackperl.org/2009/12/day-3-using-plackup.html) to run the application with a standalone server or any of the other backends. 21 | 22 | Similarly, most web frameworks that support PSGI provide a plugin, engine, or adapter to make the framework run in PSGI mode. For instance, [Catalyst](http://www.catalystframework.org/) has a Catalyst::Engine::* web server abstraction and [Catalyst::Engine::PSGI](http://search.cpan.org/perldoc?Catalyst::Engine::PSGI) is the engine to adapt Catalyst to run on PSGI. (**EDIT**: As of Catalyst 5.8 released in 2011, Catalyst supports PSGI by default and there's no need to install a separate engine.) 23 | 24 | The point is that with support from web frameworks you often won't need to modify a single line of code in your application to use PSGI. And by switching to PSGI there are lots of benefits like being able to use the toolchain of plackup, Plack::Test, and middleware which we'll discuss in future advent entries. 25 | -------------------------------------------------------------------------------- /en/08_adapting_web_frameworks_to_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 8: Adapting web frameworks to PSGI 2 | 3 | The biggest benefit of PSGI for web application framework developers is that once you adapt your framework to run on PSGI you can forget and throw away everything else that you needed to deal with to, say, handle the differences between a bunch of FastCGI servers. 4 | 5 | Similarly, if you have a large scale web application, open source or proprietary, you probably have your own web application framework (or a base class or the like). 6 | 7 | Today's entry discusses how to convert existing web application frameworks to the PSGI interface. 8 | 9 | ### CGI.pm based framework 10 | 11 | In Day 7 we saw how to run a CGI::Application based application in PSGI using CGI::Application::PSGI. CGI::Application, as the name suggests, uses CGI.pm, so using CGI::PSGI instead and defining a new runner class is the easiest way to go. 12 | 13 | package CGI::Application::PSGI; 14 | use strict; 15 | use CGI::PSGI; 16 | 17 | sub run { 18 | my($class, $app) = @_; 19 | 20 | # HACK: deprecate HTTP header generation 21 | # -- CGI::Application should support some flag to turn this off cleanly 22 | my $body = do { 23 | no warnings 'redefine'; 24 | local *CGI::Application::_send_headers = sub { '' }; 25 | local $ENV{CGI_APP_RETURN_ONLY} = 1; 26 | $app->run; 27 | }; 28 | 29 | my $q = $app->query; 30 | my $type = $app->header_type; 31 | 32 | my @headers = $q->psgi_header($app->header_props); 33 | return [ @headers, [ $body ] ]; 34 | } 35 | 36 | That's quite simple, isn't it? CGI::Application's `run()` method usually returns the whole output, including HTTP headers and content body. As you can see, the module has a gross hack to disable the header generation since you can use the `psgi_header` method of CGI::PSGI to generate the status code and HTTP headers as an array ref. 37 | 38 | I've implemented PSGI adapters for [Mason](http://search.cpan.org/perldoc?HTML::Mason) and [Maypole](http://search.cpan.org/perldoc?Maypole) and the code pretty much all looked alike: 39 | 40 | * Create CGI::PSGI out of `$env` and set that instead of the default CGI.pm instance. 41 | * Disable HTTP header generation if needed. 42 | * Run the app main dispatcher. 43 | * Extract the HTTP headers to be sent, use `psgi_header` to generate the status and headers. 44 | * Extract the response body (content). 45 | 46 | ### Adapter based framework 47 | 48 | If the framework in question already uses adapter based approaches to abstract server environments it should be much easier to adapt to PSGI by reusing most of the CGI adapter code. Here's the code to adapt [Squatting](http://search.cpan.org/perldoc?Squatting) to PSGI. Squatting uses the Squatting::On::* namespace to adapt to environments like mod_perl, FastCGI, or even other frameworks like Catalyst or HTTP::Engine. It was extremely easy to write [Squatting::On::PSGI](http://search.cpan.org/perldoc?Squatting::On::PSGI): 49 | 50 | package Squatting::On::PSGI; 51 | use strict; 52 | use CGI::Cookie; 53 | use Plack::Request; 54 | use Squatting::H; 55 | 56 | my %p; 57 | $p{init_cc} = sub { 58 | my ($c, $env) = @_; 59 | my $cc = $c->clone; 60 | $cc->env = $env; 61 | $cc->cookies = $p{c}->($env->{HTTP_COOKIE} || ''); 62 | $cc->input = $p{i}->($env); 63 | $cc->headers = { 'Content-Type' => 'text/html' }; 64 | $cc->v = { }; 65 | $cc->status = 200; 66 | $cc; 67 | }; 68 | 69 | # \%input = i($env) # Extract CGI parameters from an env object 70 | $p{i} = sub { 71 | my $r = Plack::Request->new($_[0]); 72 | my $p = $r->params; 73 | +{%$p}; 74 | }; 75 | 76 | # \%cookies = $p{c}->($cookie_header) # Parse Cookie header(s). 77 | $p{c} = sub { 78 | +{ map { ref($_) ? $_->value : $_ } CGI::Cookie->parse($_[0]) }; 79 | }; 80 | 81 | sub psgi { 82 | my ($app, $env) = @_; 83 | 84 | $env->{PATH_INFO} ||= "/"; 85 | $env->{REQUEST_PATH} ||= do { 86 | my $script_name = $env->{SCRIPT_NAME}; 87 | $script_name =~ s{/$}{}; 88 | $script_name . $env->{PATH_INFO}; 89 | }; 90 | $env->{REQUEST_URI} ||= do { 91 | ($env->{QUERY_STRING}) 92 | ? "$env->{REQUEST_PATH}?$env->{QUERY_STRING}" 93 | : $env->{REQUEST_PATH}; 94 | }; 95 | 96 | my $res; 97 | eval { 98 | no strict 'refs'; 99 | my ($c, $args) = &{ $app . "::D" }($env->{REQUEST_PATH}); 100 | my $cc = $p{init_cc}->($c, $env); 101 | my $content = $app->service($cc, @$args); 102 | 103 | $res = [ 104 | $cc->status, 105 | [ %{ $cc->{headers} } ], 106 | [ $content ], 107 | ]; 108 | }; 109 | 110 | if ($@) { 111 | $res = [ 500, [ 'Content-Type' => 'text/plain' ], [ "
$@
" ] ]; 112 | } 113 | 114 | return $res; 115 | } 116 | 117 | That's very straightforward, especially when compared with [Squatting::On::CGI](http://cpansearch.perl.org/src/BEPPU/Squatting-0.70/lib/Squatting/On/CGI.pm). It's almost a line-by-line copy (with some adjustment) using Plack::Request to parse parameters instead of CGI.pm. 118 | 119 | Similarly, Catalyst uses the Catalyst::Engine abstraction and [Catalyst::Engine::PSGI](http://search.cpan.org/perldoc?Catalyst::Engine::PSGI) is the adapter to run Catalyst on PSGI, where most of the code is copied from CGI. 120 | 121 | ### mod_perl centric frameworks 122 | 123 | Some frameworks are centered around mod_perl's API, in which case we can't use the approaches we've seen here. Instead, you should probably start by mocking Apache::Request APIs using a fake/mock object. Patrick Donelan, a WebGUI developer, explains his approach to make a mod_perl-like API in [his blog post](http://blog.patspam.com/2009/plack-roundup-at-sf-pm). The [mock request class linked](http://github.com/pdonelan/webgui/blob/plebgui/lib/WebGUI/Session/Plack.pm) is a good place to start. 124 | -------------------------------------------------------------------------------- /en/09_running_cgi_scripts_on_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 9: Running CGI scripts on Plack 2 | 3 | For the last couple of days we've been talking about how to convert existing CGI based applications to PSGI, and then run them as a PSGI application. Today we will show you the ultimate way to run *any* CGI scripts as a PSGI application, most of the time unmodified. 4 | 5 | [CGI::PSGI](http://search.cpan.org/perldoc?CGI::PSGI) is a subclass of CGI.pm to allow you a very easy migration from CGI.pm with only *a few lines of code changes* to run it on PSGI environment. But what about a messy or legacy CGI script that just prints to STDOUT a lot and is not easy to fix? 6 | 7 | [CGI::Emulate::PSGI](http://search.cpan.org/perldoc?CGI::Emulate::PSGI) is a module to run any CGI based perl program in a PSGI environment. Whatever messy/old script that prints stuff to STDOUT or directly reads HTTP headers from `%ENV` would just work because that's what CGI::Emulate::PSGI tries to emulate. The original POD of CGI::Emulate::PSGI was illustrating it like: 8 | 9 | use CGI::Emulate::PSGI; 10 | CGI::Emulate::PSGI->handler(sub { 11 | do "/path/to/foo.cgi"; 12 | CGI::initialize_globals() if &CGI::initialize_globals; 13 | }); 14 | 15 | to run existing CGI application that may or may not use CGI.pm (CGI.pm caches lots of environment variables so it needs `initialize_globals()` call to clear out the previous request variables). 16 | 17 | A few days ago on my flight from San Francisco to London to attend London Perl Workshop I was hacking on something more intelligent, that is to take any CGI scripts and compiles it into a subroutine. The module is named [CGI::Compile](http://search.cpan.org/perldoc?CGI::Compile) and should be best used combined with CGI::Emulate::PSGI. 18 | 19 | my $sub = CGI::Compile->compile("/path/to/script.cgi"); 20 | my $app = CGI::Emulate::PSGI->handler($sub); 21 | 22 | There's also [Plack::App::CGIBin](http://search.cpan.org/perldoc?Plack::App::CGIBin) Plack application to run existing CGI scripts written in Perl as PSGI applications, suppose you have bunch of CGI scripts in `/path/to/cgi-bin`, you'll run the server with: 23 | 24 | > plackup -MPlack::App::CGIBin -e 'Plack::App::CGIBin->new(root => "/path/to/cgi-bin"))' 25 | 26 | And that will mount the path `/path/to/cgi-bin`, so suppose you have `foo.pl` in that directory, you can access http://localhost:5000/foo.pl to run the CGI application as a PSGI over the plackup, just like the scripts running on Apache mod_perl Registry mechanism. 27 | -------------------------------------------------------------------------------- /en/10_using_plack_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 10: Using Plack middleware 2 | 3 | ### Middleware 4 | 5 | Middleware is a concept in PSGI (as always, stolen from Python's WSGI and Ruby's Rack) where we define components that plays the both side of a server and an application. 6 | 7 | ![WSGI middleware onion](../images/pylons_as_onion.png) 8 | 9 | (Image courtesy of Pylons project for Python WSGI) 10 | 11 | This picture illustrates the middleware concept very well. The PSGI application is in the core of the Onion layers, and middleware components wrap the original application in return, and they preprocess as a request comes in (outer to inner) and then postprocess the response as a response goes out (inner to outer). 12 | 13 | Lots of functionalities can be added to the PSGI application by wrapping it with a middleware component, from HTTP authentication, capturing errors to logging output or wrapping JSON output with JSONP. 14 | 15 | ### Plack::Middleware 16 | 17 | [Plack::Middleware](http://search.cpan.org/perldoc?Plack::Middleware) is a base class for middleware components and it allows you to write middleware really simply but in a reusable fashion. 18 | 19 | Using Middleware components written with Plack::Middleware is easy, just wrap the original application with `wrap` method: 20 | 21 | my $app = sub { [ 200, ... ] }; 22 | 23 | use Plack::Middleware::StackTrace; 24 | $app = Plack::Middleware::StackTrace->wrap($app); 25 | 26 | This example wraps the original application with StackTrace middleware (which is actually enabled [by default using plackup](http://advent.plackperl.org/2009/12/day-3-using-plackup.html)) with the `wrap` method. So when the wrapped application throws an error, the middleware component catches the error to [display a beautiful HTML page](http://bulknews.typepad.com/blog/2009/10/develstacktraceashtml.html) using Devel::StackTrace::AsHTML. 27 | 28 | Some other middleware components take parameters, in which case you can pass the parameters as a hash after `$app`, like: 29 | 30 | my $app = sub { ... }; 31 | 32 | use Plack::Middleware::MethodOverride; 33 | $app = Plack::Middleware::MethodOverride->wrap($app, header => 'X-Method'); 34 | 35 | Installing multiple middleware components is tedious especially since you need to `use` those modules first, and we have a quick solution for that using a DSL style syntax. 36 | 37 | use Plack::Builder; 38 | my $app = sub { ... }; 39 | 40 | builder { 41 | enable "StackTrace"; 42 | enable "MethodOverride", header => 'X-Method'; 43 | enable "Deflater"; 44 | $app; 45 | }; 46 | 47 | We'll see more about Plack::Builder tomorrow. 48 | 49 | ### Middleware and Frameworks 50 | 51 | The beauty of Middleware is that it can wrap *any* PSGI application. It might not be obvious from the code examples, but the wrapped application can be anything, which means you can [run your existing web application in the PSGI mode](http://advent.plackperl.org/2009/12/day-7-use-web-application-framework-in-psgi.html) and apply middleware components to it. For instance, with CGI::Application: 52 | 53 | use CGI::Application::PSGI; 54 | use WebApp; 55 | 56 | my $app = sub { 57 | my $env = shift; 58 | my $app = WebApp->new({ QUERY => CGI::PSGI->new($env) }); 59 | CGI::Application::PSGI->run($app); 60 | }; 61 | 62 | use Plack::Builder; 63 | builder { 64 | enable "Auth::Basic", authenticator => sub { $_[1] eq 'foobar' }; 65 | $app; 66 | }; 67 | 68 | This will enable the Basic authentication middleware to CGI::Application based application. You can do the same with [any other frameworks that supports PSGI](http://plackperl.org/#frameworks). 69 | -------------------------------------------------------------------------------- /en/11_using_plack_builder.md: -------------------------------------------------------------------------------- 1 | ## Day 11: Using Plack::Builder 2 | 3 | [Yesterday](http://advent.plackperl.org/2009/12/day-10-using-plack-middleware.html) we saw how to enable Plack middleware components in .psgi file, using its `wrap` class method. The way you `use` the middleware and then wrap the `$app` with `wrap` is tedious and not intuitive, so we have a DSL (Domain Specific Language) to make it much easier, and that is Plack::Builder. 4 | 5 | ### Using Plack::Builder 6 | 7 | The way you use Plack::Builder is so easy. Just use the keywords `builder` and `enable`: 8 | 9 | my $app = sub { 10 | return [ 200, [], [ "Hello World" ] ]; 11 | }; 12 | 13 | use Plack::Builder; 14 | builder { 15 | enable "JSONP"; 16 | enable "Auth::Basic", authenticator => sub { ... }; 17 | enable "Deflater"; 18 | $app; 19 | }; 20 | 21 | This takes the original application (`$app`) and wraps it with Deflater, Auth::Basic and JSONP middleware components (inner to outer). So it's equivalent to: 22 | 23 | $app = Plack::Middleware::Deflater->wrap($app); 24 | $app = Plack::Middleware::Auth::Basic->wrap($app, authenticator => sub { }); 25 | $app = Plack::Middleware::JSONP->wrap($app); 26 | 27 | but without lots of `use`ing the module which is anti DRY. 28 | 29 | ### Outer to Inner, Top to the bottom 30 | 31 | Notice that the order of middleware wrapping is in reverse? The builder/enable DSL allows you to *wrap* application so the line close to the original `$app` is *inner*, while the first one in the top is *outer*. You can compare that with [the onion picture](http://pylonshq.com/docs/en/0.9.7/_images/pylons_as_onion.png) and see that it's more obvious: something closer to the application is inner. 32 | 33 | `enable` takes the middleware name without the Plack::Middleware:: prefix but in case you want to enable some other namespace, like MyFramework::PSGI::MW::Foo, you can say: 34 | 35 | enable "+MyFramework::PSGI::MW::Foo"; 36 | 37 | The key here is to use the plus (+) sign to indicate that it is a fully qualified class name. 38 | 39 | ### What's happening behind 40 | 41 | If you're curious what Plack::Builder is doing, take a look at the code and see what's happening. The `builder` takes the code block and executes the code, and take the result (return value of the last statement) as an original application (`$app`), and then returns the wrapped application by applying Middleware in the reverse order. So it is important to have `$app` in the last line inside the `builder` block, and have the `builder` statement as the final statement in .psgi file as well. 42 | 43 | ### Thanks, Rack 44 | 45 | This idea of Plack::Builder is totally an inspiration by [Rack::Builder](http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder). You can see that they use the `use` keyword but obviously we can't *use* that in Perl :) so we chose `enable` instead. You can see that they have `map` which is to map applications to a different path, and we'll talk about its equivalent in Plack tomorrow ;) 46 | -------------------------------------------------------------------------------- /en/12_maps_multiple_apps_with_mount_and_urlmap.md: -------------------------------------------------------------------------------- 1 | ## Day 12: Maps multiple apps with mount and URLMap 2 | 3 | ### Hello World! but anyone else? 4 | 5 | Throughout the advent calendar we most of the time use the simplest web application using the "Hello World" example, like 6 | 7 | my $app = sub { 8 | return [ 200, [], [ "Hello World" ] ]; 9 | }; 10 | 11 | what about more complex examples, like you have multiple applications, each of which inherit from one of the web application frameworks, and use one of apache magic like mod_alias etc. 12 | 13 | ### Plack::App::URLMap 14 | 15 | Plack::App::URLMap allows you to *composite* multiple PSGI applications into one application, to dispatch requests to multiple applications using the URL path, or even with virtual host based dispatch. 16 | 17 | my $app1 = sub { 18 | return [ 200, [], [ "Hello John" ] ]; 19 | }; 20 | 21 | my $app2 = sub { 22 | return [ 200, [], [ "Hello Bob" ] ]; 23 | }; 24 | 25 | So you have two apps, one is to say hi to John and another to Bob, and say if you want to run this two applications on the same server. With Plack::App::URLMap, you can do this: 26 | 27 | use Plack::App::URLMap; 28 | my $app = Plack::App::URLMap->new; 29 | $app->mount("/john" => $app1); 30 | $app->mount("/bob" => $app2); 31 | 32 | There you go. Your app now dispatches all requests beginning with `/john` to `$app1` which says "Hello John" and `/bob` to `$app2`, which is to say "Hello Bob". As a result, all requests to unmapped paths, like the root ("/") gives you 404. 33 | 34 | The environment variables such as `PATH_INFO` and `SCRIPT_NAME` are automatically adjusted so it just works like when your application is mounted using Apache's mod_alias or CGI scripts. Your application framework should always use `PATH_INFO` to dispatch requests, and concatenate with `SCRIPT_NAME` to build links. 35 | 36 | ### mount in DSL 37 | 38 | This `mount` interface with Plack::App::URLMap is quite useful, so we decided to add to Plack::Builder DSL itself, which is again an inspiration by Rack::Builder, using the syntax `mount`: 39 | 40 | use Plack::Builder; 41 | builder { 42 | mount "/john" => $app1; 43 | mount "/bob" => builder { 44 | enable "Auth::Basic", authenticator => ...; 45 | $app2; 46 | }; 47 | }; 48 | 49 | Requests to '/john' is handled exactly the same way with the normal URLMap. But this example uses `builder` for "/bob", so it enables the basic authentication to display the "Hello Bob" page. This should be syntactically equivalent to: 50 | 51 | $app = Plack::App::URLMap->new; 52 | $app->mount("/john", $app1); 53 | 54 | $app2 = Plack::Middleware::Auth::Basic->wrap($app2, authenticator => ...); 55 | $app->mount("/bob", $app2); 56 | 57 | but obviously, with less code to write and more obvious to understand what's going on. 58 | 59 | ### Multi tenant frameworks 60 | 61 | Of course you can use this URLMap and mount API to run multiple framework applications on one server. Imagine you have three applications, "Foo" which is based on Catalyst, "Bar" which is based on CGI::Application and "Baz" which is based on Squatting. Do this: 62 | 63 | # Catalyst 64 | use Foo; 65 | my $app1 = Foo->psgi_app; 66 | 67 | # CGI::Application 68 | use Bar; 69 | use CGI::Application::PSGI; 70 | my $app2 = sub { 71 | my $app = Bar->new({ QUERY => CGI::PSGI->new(shift) }); 72 | CGI::Application::PSGI->run($app); 73 | }; 74 | 75 | # Squatting 76 | use Baz 'On::PSGI'; 77 | Baz->init; 78 | my $app3 = sub { Baz->psgi(shift) }; 79 | 80 | builder { 81 | mount "/foo" => $app1; 82 | mount "/bar" => $app2; 83 | mount "/baz" => $app3; 84 | }; 85 | 86 | And now you have three applications, each of which inherit from different web framework, running on the same server (via plackup or other Plack::Handler::* implementations) mapped on different paths. 87 | -------------------------------------------------------------------------------- /en/13_use_plack_test_to_test_your_application.md: -------------------------------------------------------------------------------- 1 | ## Day 13: use Plack::Test to test your application 2 | 3 | ### Testing 4 | 5 | There are many ways to test web applications, either with a live server or using a mock request technique. Some web application frameworks allow you to write an unit test using one of those methods, but the way you write tests differs per framework of your choice. 6 | 7 | Plack::Test gives you *a unified interface* to test *any* web applications and frameworks that is compatible to PSGI using *both* mock request and live HTTP server. 8 | 9 | ### Using Plack::Test 10 | 11 | Using Plack::Test is pretty simple and it's of course compatible to the Perl's standard testing protocol [TAP](http://testanything.org/wiki/) and [Test::More](http://search.cpan.org/perldoc?Test::More). 12 | 13 | use Plack::Test; 14 | use Test::More; 15 | use HTTP::Request; 16 | 17 | my $app = sub { 18 | return [ 200, [ 'Content-Type', 'text/plain' ], [ "Hello" ] ]; 19 | }; 20 | 21 | test_psgi $app, sub { 22 | my $cb = shift; 23 | 24 | my $req = HTTP::Request->new(GET => 'http://localhost/'); 25 | my $res = $cb->($req); 26 | 27 | is $res->code, 200; 28 | is $res->content, "Hello"; 29 | }; 30 | 31 | done_testing; 32 | 33 | Create or load PSGI application like usual (you can use [Plack::Util](http://search.cpan.org/perldoc?Plack::Util)'s `load_psgi` function if you want to load an app from a `.psgi` file), and call `test_psgi` function to test the application. The second argument is a callback that acts as a testing client. 34 | 35 | You can use the named parameters as well, like the following. 36 | 37 | test_psgi app => $app, client => sub { ... } 38 | 39 | The client code takes a callback (`$cb`), which you can pass an HTTP::Request object that would return HTTP::Response object, like normal LWP::UserAgent would do, and you can make as many requests as you want, and test various attributes and response details. 40 | 41 | Save that code as `.t` file and use the tool such as `prove` to run the tests. 42 | 43 | ### use HTTP::Request::Common 44 | 45 | This is not required, but recommended to use [HTTP::Request::Common](http://search.cpan.org/perldoc?HTTP::Request::Common) when you want to make an HTTP request, since it's more obvious and less code to write: 46 | 47 | use HTTP::Request::Common; 48 | 49 | test_psgi $app, sub { 50 | my $cb = shift; 51 | my $res = $cb->(GET "/"); 52 | # ... 53 | }; 54 | 55 | Notice that you can even omit the scheme and hostname, which would default to http://localhost/ anyway. 56 | 57 | ### Run in a server/mock mode 58 | 59 | By default the `test_psgi` function's callback runs as a *Mock HTTP* request mode, turning a HTTP::Request object into a PSGI env hash and then run the PSGI application, and returns the response as a HTTP::Response object. 60 | 61 | You can change this to live HTTP mode, by setting either a) the package variable `$Plack::Test::Impl` or b) the environment variable `PLACK_TEST_IMPL` to the string `Server`. 62 | 63 | use Plack::Test; 64 | $Plack::Test::Impl = "Server"; 65 | 66 | test_psgi ... # the same code 67 | 68 | By using the environment variable, you don't really need to change the .t code: 69 | 70 | env PLACK_TEST_IMPL=Server prove -l t/test.t 71 | 72 | This will run the PSGI application using the Standalone server backend and uses LWP::UserAgent to send the live HTTP request. You don't need to modify your testing client code, and the callback would automatically adjust host names and port numbers depending on the test configuration. 73 | 74 | ### Test your web application framework with Plack::Test 75 | 76 | Once again, the beauty of PSGI and Plack is that everything written to run for the PSGI interface can be used for *any* web application frameworks that speaks PSGI. By [running your web application framework in PSGI mode](http://advent.plackperl.org/2009/12/day-7-use-web-application-framework-in-psgi.html), you can also use Plack::Test: 77 | 78 | use Plack::Test; 79 | use MyCatalystApp; 80 | 81 | my $app = MyCatalystApp->psgi_app; 82 | 83 | test_psgi $app, sub { 84 | my $cb = shift; 85 | # ... 86 | }; 87 | done_testing; 88 | 89 | You can of course do the same thing against any frameworks that supports PSGI. 90 | -------------------------------------------------------------------------------- /en/14_use_plack_request.md: -------------------------------------------------------------------------------- 1 | ## Day 14: Use Plack::Request 2 | 3 | Plack is not a framework per se, but is more of a toolkit that contains PSGI server implementations as well as utilities like [plackup](http://advent.plackperl.org/2009/12/day-3-using-plackup.html), [Plack::Test](http://advent.plackperl.org/2009/12/day-13-use-placktest-to-test-your-application.html) and [Middleware components](http://advent.plackperl.org/2009/12/day-10-using-plack-middleware.html). 4 | 5 | Since Plack project is a revolution from [HTTP::Engine](http://search.cpan.org/perldoc?HTTP::Engine), there seems a demand to write a quick web application in Request/Response style handler API. Plack::Request gives you a nice Object Oriented API around PSGI environment hash and response array, just like Rack's Rack::Request and Response objects. It could also be used as a library when writing a new middleware component, and a base class for requests/responses when you write a new web application framework based on Plack. 6 | 7 | ### Use Plack::Request and Response 8 | 9 | Plack::Request is a wrapper around PSGI environment, and the code goes like this: 10 | 11 | use Plack::Request; 12 | 13 | my $app = sub { 14 | my $req = Plack::Request->new(shift); 15 | 16 | my $name = $req->param('name'); 17 | my $res = $req->new_response(200); 18 | $res->content_type('text/html'); 19 | $res->content("Hello World"); 20 | 21 | return $res->finalize; 22 | }; 23 | 24 | The only thing you need to change, if you're migrating from HTTP::Engine, is the first line of the application to create a Plack::Request out of PSGI env (`shift`) and then call `finalize` to get an array reference out of Response object. 25 | 26 | Many other methods like `path_info`, `uri`, `param`, `redirect` etc. work like HTTP::Engine::Request and Response object which is very similar to [Catalyst](http://search.cpan.org/dist/Catalyst-Runtime) 's Request and Response object. 27 | 28 | ### Plack::Request and Plack 29 | 30 | Plack::Request is available as part of Plack on CPAN. Your framework can use Plack::Request to handle parameters and can also make it run on other PSGI server implementations such as mod_psgi. 31 | 32 | ### Use Plack::Request or not? 33 | 34 | Directly using Plack::Request in the `.psgi` code is quite handy to quickly write and test your code but not really recommended for a large scale application. It's exactly like writing a 1000 lines of `.cgi` script where you could factor out the application code into a module (`.pm` files). The same thing applies to `.psgi` file: it's best to create an application class by using and possibly extending Plack::Request, and then have just a few lines of code in `.psgi` file with [Plack::Builder to configure middleware components](http://advent.plackperl.org/2009/12/day-11-using-plackbuilder.html). 35 | 36 | Plack::Request is also supposed to be used from a web application framework to [adapt to PSGI interface](http://advent.plackperl.org/2009/12/day-8-adapting-web-frameworks-to-psgi.html). 37 | -------------------------------------------------------------------------------- /en/15_authenticate_your_app_with_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 15: Authenticate your app with Middleware 2 | 3 | There're lots of Plack middleware components out there, whether in Plack core distribution as well as separate distributions on CPAN. While I've been writing this Plack advent calendar lots of people have shown their interest and taken ideas out of my wishlist. 4 | 5 | From today we'll introduce some of the nice middleware components that you can use quickly to enhance any of your PSGI ready applications. 6 | 7 | ### Basic authentication 8 | 9 | Since Plack middleware wraps an application, the best thing it can do is pre-process or post-process to do things around HTTP layers. Today let's talk about the Basic Authentication. 10 | 11 | Adding a basic authentication can be done in multiple ways: you can do that in the web application framework layer if it's supported in your framework. In case of Catalyst it's [Catalyst::Authentication::Credential::HTTP](http://search.cpan.org/perldoc?Catalyst::Authentication::Credential::HTTP). Just like other Catalyst tools, it allows you to configure the authentication from simple to very complex, by using credential (how to authenticate: basic and/or digest) and store (how to authorize username and passwords). 12 | 13 | Otherwise you can do the authentication in the web server layer. For instance if you run your application with Apache and mod_perl, using Apache's default mod_auth module to authenticate is pretty easy and handy for development, while it limits the ability to share "how to authenticate users" since usually you need to write your custom module to do things like database backed authentication. 14 | 15 | Plack middleware allows web application frameworks to share such a functionality, mostly with a pretty simple Perl callback system, and Plack::Middleware::Auth::Basic is to do this for Basic authentication. And this is why most Plack standalone servers do not have an authentication system: it's best implemented as a middleware component. 16 | 17 | ### Using Plack::Middleware::Auth::Basic 18 | 19 | Just like [other middleware](http://advent.plackperl.org/2009/12/day-10-using-plack-middleware.html), using Auth::Basic middleware is quite simple: 20 | 21 | use Plack::Builder; 22 | 23 | my $app = sub { ... }; 24 | 25 | builder { 26 | enable "Auth::Basic", authenticator => sub { 27 | my($username, $password) = @_; 28 | return $username eq 'admin' && $password eq 'foobar'; 29 | }; 30 | $app; 31 | }; 32 | 33 | This adds a basic authentication to your application `$app`, and the user *admin* can sign in with the password *foobar* and nobody else. The successful signed-in user gets `REMOTE_USER` set in PSGI `$env` hash so it can be used in the applications and is logged using the standard AccessLog middleware. 34 | 35 | Since it's a callback based, adding another authentication system such as Kerberos would be pretty trivial and easy with modules such as Authen::Simple: 36 | 37 | use Plack::Builder; 38 | use Authen::Simple; 39 | use Authen::Simple::Kerberos; 40 | 41 | my $auth = Authen::Simple->new( 42 | Authen::Simple::Kerberos->new(realm => ...), 43 | ); 44 | 45 | builder { 46 | enable "Auth::Basic", authenticator => sub { 47 | $auth->authenticate(@_): 48 | }; 49 | $app; 50 | }; 51 | 52 | The same way you can use lots of [Authen::Simple backends](http://search.cpan.org/search?query=authen+simple&mode=all) with small changes. 53 | 54 | ### With URLMap 55 | 56 | URLMap allows you to compound multiple apps into one app, so combined with Auth middleware, you can run the same application in a auth vs. non-auth mode, using different paths: 57 | 58 | use Plack::Builder; 59 | my $app = sub { 60 | my $env = shift; 61 | if ($env->{REMOTE_USER}) { 62 | # Authenticated 63 | } else { 64 | # Unauthenticated 65 | } 66 | }; 67 | 68 | builder { 69 | mount "/private" => builder { 70 | enable "Auth::Basic", authenticator => ...; 71 | $app; 72 | }; 73 | mount "/public" => $app; 74 | }; 75 | 76 | This way you run the same `$app` in "/public" and "/private" paths, while "/private" requires a basic authentication and "/public" doesn't. (Inlining `$env->{REMOTE_USER}`, or whatever application logic in .psgi is not really recommended -- I just used it to explain it in an obvious way) 77 | -------------------------------------------------------------------------------- /en/16_adding_jsonp_support_to_your_app.md: -------------------------------------------------------------------------------- 1 | ## Day 16: Adding JSONP support to your app 2 | 3 | Today we'll see another very simple but useful example of a middleware component, this time to add functionality beyond just basic HTTP functions. 4 | 5 | ### JSONP 6 | 7 | [JSONP](http://ajaxian.com/archives/jsonp-json-with-padding) (JSON-Padding) is a technology to wrap JSON in a JavaScript callback function. This is normally useful when you want to allow your JSON-based content included programatically in the third party websites using HTML `script` tags. 8 | 9 | ### Middleware::JSONP 10 | 11 | Assume your web application returns a JSON encoded data with the Content-Type `application/json`, again with a simple inline PSGI application: 12 | 13 | use JSON; 14 | my $app = sub { 15 | my $env = shift; 16 | if ($env->{PATH_INFO} eq '/whatever.json') { 17 | my $body = JSON::encode_json({ 18 | hello => 'world', 19 | }); 20 | return [ 200, ['Content-Type', 'application/json'], [ $body ] ]; 21 | } 22 | return [ 404, ['Content-Type', 'text/html'], ['Not Found']]; 23 | }; 24 | 25 | Adding a JSONP support is easy using Middleware::JSONP: 26 | 27 | use Plack::Builder; 28 | builder { 29 | enable "JSONP"; 30 | $app; 31 | }; 32 | 33 | So it's just one line! The middleware checks if the response content type is `application/json` and if so, checks if there is a `callback` parameter in the URL. So a request to "/whatever.json" continues to return the JSON but requests to "/whatever.json?callback=myCallback" would return: 34 | 35 | myCallback({"hello":"world"}); 36 | 37 | with the Content-Type `text/javascript`. Content-Length is automatically adjusted if there's any. 38 | 39 | ### Works with frameworks 40 | 41 | Supporting JSONP in addition to JSON would be fairly trivial for most frameworks to do, but Middleware::JSONP should be an example of the things that could be done in Plack middleware layer with no complexity. 42 | 43 | And of course, this JSONP middleware should work with any existing web frameworks that emits JSON output. So with Catalyst: 44 | 45 | package MyApp::View::JSON; 46 | use base qw( Catalyst::View::JSON ); 47 | 48 | package MyApp::Controller::Foo; 49 | sub hello : Local { 50 | my($self, $c) = @_; 51 | $c->stash->{message} = 'Hello World!'; 52 | $c->forward('MyApp::View::JSON'); 53 | } 54 | 55 | And then using Catalyst::Engine::PSGI and Plack::Builder, you can add a JSONP support to this controller. 56 | 57 | use MyApp; 58 | my $app = MyApp->psgi_app; 59 | 60 | use Plack::Builder; 61 | builder { 62 | enable "JSONP"; 63 | $app; 64 | }; 65 | 66 | Accidentally this [Catalyst::View::JSON](http://search.cpan.org/perldoc?Catalyst::View::JSON) is my module :) and supports JSONP callback configuration by default, but there is more than one way to do it! 67 | -------------------------------------------------------------------------------- /en/17_serving_static_files_from_your_application.md: -------------------------------------------------------------------------------- 1 | ## Day 17: Serving static files from your application 2 | 3 | On [day 5](http://advent.plackperl.org/2009/12/day-5-run-a-static-file-web-server-with-plack.html) we talked about serving files from the current directory using plackup. Now that we've learned how to [use middleware](http://advent.plackperl.org/2009/12/day-10-using-plack-middleware.html) and [compound multiple applications with URLMap](http://advent.plackperl.org/2009/12/day-12-maps-multiple-apps-with-mount-and-urlmap.html) it's extremely trivial to add a functionality you definitely need when developing an application: serving static files. 4 | 5 | ### Serving files from a certain path 6 | 7 | Most frameworks come with this feature but with PSGI and Plack, frameworks don't need to implement this feature anymore. Just use the Static middleware. 8 | 9 | use Plack::Builder; 10 | 11 | my $app = sub { ... }; 12 | 13 | builder { 14 | enable "Static", path => qr!^/static!, root => './htdocs'; 15 | $app; 16 | } 17 | 18 | This will intercept all requests beginning with "/static" and map that to the root directory "htdocs". So requests to "/static/images/foo.jpg" will result in serving a file "./htdocs/static/images/foo.jpg". 19 | 20 | Often you want to overlap or cofigure the directory names, so a request to the URL "/static/index.css" mapped to "./static-files/index.css", here's how to do that: 21 | 22 | builder { 23 | enable "Static", path => sub { s!^/static/!! }, root => './static-files'; 24 | $app; 25 | } 26 | 27 | The important thing here is to use a callback and a pattern match `sub { s/// }` instead of a plain regular expression (`qr`). The callback is tested against a request path and if it matches, the value of `$_` is being used as a request path. So in this example we tested to see if the request begins with "/static/" and in that case, strip off that part, and map the files under "./static-files/". 28 | 29 | As a result, "/static/foo.jpg" would become "./static-files/foo.jpg". All requests not matching the pattern match just pass through to the original `$app`. 30 | 31 | ### Do it your own with URLMap and App::File 32 | 33 | Just like Perl there's more than one way to do it. When you grok how to use [mount and URLMap](http://advent.plackperl.org/2009/12/day-12-maps-multiple-apps-with-mount-and-urlmap.html) then using App::File with mount should be more intuitive. The previous example can be written like this: 34 | 35 | use Plack::Builder; 36 | 37 | builder { 38 | mount "/static" => Plack::App::File->new(root => "./static-files"); 39 | mount "/" => $app; 40 | }; 41 | 42 | Your mileage may vary, but I think this one is more obvious. Static's callback based configuration allows you to write more complex regular expression, which you can't do with URLMap and mount, so choose whichever fits your need. 43 | -------------------------------------------------------------------------------- /en/18_load_middleware_conditionally.md: -------------------------------------------------------------------------------- 1 | ## Day 18: Load middleware conditionally 2 | 3 | I've introduced a couple of middleware components. Some of them are useful and could be enabled globally, while others might be better enabled on certain conditions. Today we'll talk about a solution to this. 4 | 5 | ### Load middleware conditionally 6 | 7 | Conditional middleware is a super (or meta) middleware that takes one middleware and enable that middleware based on a runtime condition. Let's take some examples: 8 | 9 | * You want to enable [JSONP middleware](http://advent.plackperl.org/2009/12/day-16-adding-jsonp-support-to-your-app.html) only if the path begins with /public 10 | * You don't want to enable [Basic Auth](http://advent.plackperl.org/2009/12/day-15-authenticate-your-app-with-middleware.html) if the request comes from local IP 11 | 12 | We investigated how they deal with situations like this in WSGI and Rack, but couldn't find a generic solution, and they mostly just implement options to individual component, which did not look cool for me. 13 | 14 | ### Middleware::Conditional 15 | 16 | The Conditional middleware is an ultimate flexible solution to this: 17 | 18 | use Plack::Builder; 19 | 20 | builder { 21 | enable_if { $_[0]->{REMOTE_ADDR} !~ /^192\.168\.0\./ } 22 | "Auth::Basic", authenticator => ...; 23 | $app; 24 | }; 25 | 26 | We added a new keyword to Plack::Builder `enable_if`, which takes a block that gets evaluated in the request time (`$_[0]` there is the `$env` hash) and if the block returns true, run the wrapped application but otherwise pass through. 27 | 28 | This example code examines if the request comes from a local network and runs a basic authentication otherwise. 29 | 30 | Conditional is implemented as a normal piece of middleware, and internally this is equivalent to: 31 | 32 | use Plack::Middleware::Conditional; 33 | use Plack::Middleware::Auth::Basic; 34 | 35 | my $app = sub { ... }; 36 | 37 | $app = Plack::Middleware::Conditional->wrap($app, 38 | builder => sub { 39 | Plack::Middleware::Auth::Basic->wrap( 40 | $_[0], authenticator => ..., 41 | ); 42 | }, 43 | condition => sub { 44 | my $env = shift; 45 | $env->{REMOTE_ADDR} !~ /^192\.168\.0\./; 46 | }, 47 | ); 48 | 49 | But it's a little boring to write, so we added a DSL version, which I recommend to use :) 50 | -------------------------------------------------------------------------------- /en/19_cascade_multiple_applications.md: -------------------------------------------------------------------------------- 1 | ## Day 19: Cascade multiple applications 2 | 3 | [Conditional middleware](http://advent.plackperl.org/2009/12/day-18-load-middleware-conditionally.html) and [URLMap app](http://advent.plackperl.org/2009/12/day-12-maps-multiple-apps-with-mount-and-urlmap.html) have something in common: they're PSGI applications but both takes PSGI application or middleware and dispatch them. This is the beauty of PSGI application and middleware architecture and today's application is another example of this. 4 | 5 | ### Cascading multiple applications 6 | 7 | Cascading can be useful if you have a couple of applications and runs in order, then try until it returns a successful response. This is sometimes called [Chain of responsibility](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) design pattern and often used in web applications such as [mod_perl handlers](http://perl.apache.org/docs/2.0/user/handlers/intro.html). 8 | 9 | ### Cascade Application 10 | 11 | Plack::App::Cascade allows you to compound multiple applications in order and runs until it returns non-404 responses. 12 | 13 | use Plack::App::Cascade; 14 | use Plack::App::File; 15 | use Plack::App::URLMap; 16 | 17 | my @paths = qw( 18 | /home/www/static 19 | /virtualhost/example.com/htdocs/static 20 | /users/miyagawa/public_html/images 21 | ); 22 | 23 | my $app = Plack::App::Cascade->new; 24 | for my $path (@paths) { 25 | my $file = Plack::App::File->new(root => $path); 26 | $app->add($file); 27 | } 28 | 29 | my $map = Plack::App::URLMap->new; 30 | $map->mount("/static" => $app); 31 | $map->to_app; 32 | 33 | This application is mapped to `/static` using URLMap, and all requests will try the three directories specified in `@paths` using App::File application and returns the first found file. It might be useful if you want to serve static files but want to cascade from multiple directories like this. 34 | 35 | ### Cascade different apps 36 | 37 | use CatalystApp; 38 | CatalystApp->setup_engine('PSGI'); 39 | my $app1 = sub { CatalystApp->run(@_) }; 40 | 41 | use CGI::Application::PSGI; 42 | use CGIApp; 43 | my $app2 = sub { 44 | my $app = CGIApp->new({ 45 | QUERY => CGI::PSGI->new($_[0]), 46 | }); 47 | CGI::Application::PSGI->run($app); 48 | }; 49 | 50 | use Plack::App::Cascade; 51 | Plack::App::Cascade->new(apps => [ $app1, $app2 ])->to_app; 52 | 53 | This will create two applications, one with Catalyst and the other with CGI::Application and runs two applications in order. Suppose you have an overlapping URL structure and `/what/ever.cat` served with the Catalyst application and `/what/ever.cgiapp` served with the CGI::Application app. 54 | 55 | Well that might sound crazy and I guess it's better to use URLMap to map two applications in different paths, but if you *really want* to cascade them, this is the way to go :) 56 | -------------------------------------------------------------------------------- /en/20_access_your_local_app_from_the_internet.md: -------------------------------------------------------------------------------- 1 | ## Day 20: Access your local app from the internet 2 | 3 | (**EDIT**: the reverseHTTP service we mention here is not available as of 2012) 4 | 5 | These days laptops with modern operation systems allows you to quickly develop a web application and test it locally with its local IP address. Often you want to test your application with a global access, to show off your work to friends who don't have an access to your local network, or you're writing a web application that works as a [webhooks](http://www.webhooks.org/) callback. 6 | 7 | ### Reverse HTTP to the rescue 8 | 9 | There are many solutions to this problem, but one notable solution is [ReverseHTTP](http://www.reversehttp.net/). It is a very simple specification of client-server-gateway protocol that uses pure HTTP/1.1 payloads, and what's nice about it is that there's a demo gateway service running on reversehttp.net, so you can actually use it for demo or testing purpose pretty quickly without setting up servers etc. 10 | 11 | If you're curious how this really works, take a look at [the spec](http://www.reversehttp.net/specs.html). The reason why it's called *Reverse* HTTP is that your application (server) acts as a long-poll HTTP client and the gateway server sends back an HTTP request as a response. This might sound complex but well, it's really simple :) 12 | 13 | ### Plack::Server::ReverseHTTP 14 | 15 | [Plack::Server::ReverseHTTP](http://search.cpan.org/~miyagawa/Plack-Server-ReverseHTTP-0.01/) is a Plack server backend that implements this ReverseHTTP protocol, so your PSGI based application can be accessed from the outside world via this reversehttp.net gateway service. 16 | 17 | To use ReverseHTTP, install the required modules and run this: 18 | 19 | > plackup -s ReverseHTTP -o yourhostname --token password \ 20 | -e 'sub { [200, ["Content-Type","text/plain"], ["Hello"]] }' 21 | Public Application URL: http://yourhostname.www.reversehttp.net/ 22 | 23 | `-o` is an alias for `--host` for plackup (because `-h` is taken for `--help` :)), and you should specify the subdomain (label) you're going to use. You should also supply `--token` which is like a generic password so nobody else can use your label once registered. You can omit this option if you *really* want anyone else to take that subdomain over. 24 | 25 | The console will display the address (URL) like seen, and open the URL from the browser and viola! You see the "Hello" page, right? 26 | 27 | ### Use with frameworks 28 | 29 | Of course because this is a PSGI server backend, you can use with *any* frameworks. Want to use it with Catalyst application? 30 | 31 | > catalyst.pl MyApp 32 | > cd MyApp 33 | > ./scripts/myapp_create.pl PSGI 34 | > plackup -o yourhost --token password ./scripts/myapp.psgi 35 | 36 | That's it! The default Catalyst application will now be accessible with the URL http://yourhost.reversehttp.net/ from anywhere in the world. 37 | 38 | ### Notes 39 | 40 | ReverseHTTP.net gateway service is an experimental service and there's no SLA or whatever, so I don't really think it's usable for production environment and such. But it's really handy and useful to quickly test your application that needs a global access, or show off your work to friends that don't have an internal access. Much easier than other solutions that require other software like SSH or VPN tunneling. 41 | -------------------------------------------------------------------------------- /en/21_lint_your_application_and_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 21: Lint your application and middleware 2 | 3 | We've been talking about [adapting existing web frameworks to PSGI](http://advent.plackperl.org/2009/12/day-8-adapting-web-frameworks-to-psgi.html) and writing a new application using PSGI as an interface, but we haven't talked about error handling. 4 | 5 | ### Handling errors 6 | 7 | We have [an awesome stack trace](http://advent.plackperl.org/2009/12/day-3-using-plackup.html) middleware enabled by default, so if an end user application throws an error, we can catch them and display a nice error page. But what if there is an error or a bug in one of middleware components, or web application framework adapters themselves? 8 | 9 | Try this code: 10 | 11 | > plackup -e 'sub { return [ 0, {"Content-Type","text/html"}, "Hello" ] }' 12 | 13 | Again, writing a raw PSGI interface is not something end users would do every day, but this could be a good emulation of what would happen if there's a bug in one of middleware components or framework adapters itself. 14 | 15 | When you access this application with the browser, the server dies with: 16 | 17 | Not an ARRAY reference at lib/Plack/Util.pm line 145. 18 | 19 | or something similar. This is because the response format is invalid per the PSGI interface: the status code is not valid, HTTP headers are not an array ref but a hash reference and the response body is a string instead of an array ref. 20 | 21 | ### Lint middleware 22 | 23 | Checking them in the individual server for every request at runtime is *possible* but not *ideal*: that will be a duplicate of codes, and doing so in every request is not efficient from the performance standpoint. We should better validate if an application, middleware or server backend conforms to the PSGI interface using the test suite during the development and disable that when running on production for the best performance. 24 | 25 | Middleware::Lint is the middleware to validate request and response interface. Run the application above with the middleware: 26 | 27 | > plackup -e 'enable "Lint"; sub { return [ 0, { "Content-Type"=>"text/html" }, ["Hello"] ] }' 28 | 29 | and now requests for the application would give a nice stack trace saying: 30 | 31 | status code needs to be an integer greater than or equal to 100 at ... 32 | 33 | since now the Lint middleware checks if the response in the valid PSGI format. 34 | 35 | When you develop a new framework adapter or a middleware component, be sure to check with Middleware::Lint during the development. 36 | 37 | ### Writing a new PSGI server 38 | 39 | Middleware::Lint validates both request and response interface, so this can be used when you develop a new PSGI web server as well. However if you are a server developer there's a more comprehensive testing tool to make sure your server behaves correctly, and that is Plack::Test::Suite. 40 | 41 | You can look at the existing tests in the `t/Plack-Handler` directory for how to use this utility, but it defines lots of expected requests and responses pairs to test a new PSGI server backend. Existing Plack::Handler backends included in Plack core distribution as well as other CPAN distributions all pass this test suite. 42 | -------------------------------------------------------------------------------- /en/22_discover_more_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 22: Discover more middleware 2 | 3 | Christmas is coming near and there aren't enough days to explore more middleware components. Today I'll show you a quick intro of great middleware components that I haven't had time to show. 4 | 5 | ### ErrorDocument 6 | 7 | When you die out from an application or display some "Forbidden" error message when an auth wasn't successful you'll probably want to display a custom error page based on the response status code. ErrorDocument is exactly the middleware that does this, like Apache's ErrorDocument directive. 8 | 9 | builder { 10 | enable "ErrorDocument", 500 => "/path/to/error.html"; 11 | $app; 12 | }; 13 | 14 | You can just map arbitrary error code to a static file path to be served. You can enable StackTrace middleware during the development and then this ErrorDocument middleware on the production so as to display nicer error pages. 15 | 16 | This middleware is included in the Plack core distribution. 17 | 18 | ### Session 19 | 20 | Actually this is (again) a steal from [Rack](http://rack.rubyforge.org/). Rack defines `rack.session` as a standard Rack environment hash and defines the interface as Ruby's built-in Hash object. We didn't define it as part of the standard interface but stole the idea and actual implementation a lot. 21 | 22 | builder { 23 | enable "Session", store => "File"; 24 | $app; 25 | }; 26 | 27 | By default Session will save the session in on-memory hash, which wouldn't work with the prefork (or multi process) servers. It's shipped with a couple of default store engines such as [CHI](http://search.cpan.org/perldoc?CHI), so it's so easy to adapt to other storage engines, exactly like we see with other middleware components such as Auth. 28 | 29 | Session data is stored as a plain hash reference in `psgix.session` key in the PSGI env hash. Application and frameworks with access to PSGI env hash can use this Session freely in the app by wrapping it with Plack::Session module, like in Tatsumaki: 30 | 31 | # Tatsumaki app 32 | sub get { 33 | my $self = shift; 34 | my $uid = $self->request->session->get('uid'); 35 | $self->request->session->set(last_access => time); 36 | ... 37 | } 38 | 39 | And the nice thing is that *any* PSGI apps can share this session data as long as they use the same storage etc. Some existing framework adapters don't have an access to this environment hash from end users application yet, so it should be updated gradually in the near future. 40 | 41 | Session middleware is developed by Stevan Little on [github](http://github.com/stevan/plack-middleware-session) and is available on CPAN as well. 42 | 43 | ### Debug 44 | 45 | This is a steal from [Rack-bug](http://github.com/brynary/rack-bug) and [django debug toolbar](http://github.com/robhudson/django-debug-toolbar). By enabling this middleware you'll see the handy debug "panels" in the right side where you can click and see the detailed data and analysis about the request. 46 | 47 | The panels include Timer (the request time), Memory (how is memory increased if there's any leaks), Request (Detailed request headers) and Responses (Response headers etc.) and so on. 48 | 49 | builder { 50 | enable "Debug"; 51 | $app; 52 | }; 53 | 54 | Using it is so easy as this, and you an also pass the list of `panels` to enable only certain panels or additional non default panels. 55 | 56 | More extensions for the panels, such as DBI query profiler or Catalyst log dumper are being developed on [github](http://github.com/miyagawa/plack-middleware-debug/). 57 | 58 | ### Proxy 59 | 60 | It's often useful to proxy HTTP requests to another application, either running on the internet or inside the same network. The former would be necessary if you want to proxy long poll or some JSON API from your application that doesn't support JSONP (because of Cross domain origin policy), and the latter would be to run applications on different machine and use your app as a reverse proxy, though chances are you want to use frontend web servers like nginx, lighttpd or perlbal to do the job. 61 | 62 | Anyway, Plack::App::Proxy is the middleware to do this: 63 | 64 | use Plack::App::Proxy; 65 | use Plack::Builder; 66 | 67 | my $app = Plack::App::Proxy->new(host => '192.168.0.2:8080')->to_app; 68 | 69 | builder { 70 | mount "/app" => $app; 71 | }; 72 | 73 | Proxy middleware is developed by Lee Aylward on [github](http://github.com/leedo/Plack-App-Proxy). 74 | 75 | ### More 76 | 77 | There are more middleware components available in the Plack distribution, and on [CPAN](http://search.cpan.org/search?query=plack+middleware&mode=dist). Not all middleware components are supposed to be great, but certainly they can be shared and used by most frameworks that support PSGI. 78 | -------------------------------------------------------------------------------- /en/23_write_your_own_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 23: Write your own middleware 2 | 3 | Let's finish up this middleware discovery with "Do It Yourself" tutorial now. 4 | 5 | ### Writing Middleware 6 | 7 | PSGI middleware behaves like a normal PSGI application but wraps the original PSGI application, so from the server it looks like an application but from an application it looks like a server (plays both sides). 8 | 9 | A simple middleware that fakes HTTP user-agent would be like this: 10 | 11 | # Wrapped application 12 | my $app = sub { 13 | my $env = shift; 14 | my $who = $env->{HTTP_USER_AGENT} =~ /Mobile Safari/ ? 'iPhone' : 'non-iPhone'; 15 | return [ 200, ['Content-Type','text/html'], ["Hello $who"] ]; 16 | }; 17 | 18 | # Middleware to wrap $app 19 | my $mw = sub { 20 | my $env = shift; 21 | $env->{HTTP_USER_AGENT} .= " (Mobile Safari)"; 22 | $app->($env); 23 | }; 24 | 25 | The app would display "Hello iPhone" only if a request comes with iPhone browser (*Mobile Safari*), but the middleware adds that phrase to all incoming requests, so if you run this application and open the page with any browsers, you'll always see "Hello iPhone". And the default Access Log would say: 26 | 27 | 127.0.0.1 - - [23/Dec/2009 12:34:31] "GET / HTTP/1.1" 200 12 "-" "Mozilla/5.0 28 | (Macintosh; U; Intel Mac OS X 10_6_2; en-us) AppleWebKit/531.21.8 (KHTML, like 29 | Gecko) Version/4.0.4 Safari/531.21.10 (Mobile Safari)" 30 | 31 | You can see " (Mobile Safari)" is added to the tail of User-Agent string. 32 | 33 | ### Make it a reusable Middleware 34 | 35 | So that was a good example of writing your own middleware in `.psgi`. If it is one-time middleware that you can quickly whip up then that's great, but you often want to make it generic enough or reusable in other applications too. Then you should use Plack::Middleware. 36 | 37 | package Plack::Middleware::FakeUserAgent; 38 | use strict; 39 | use parent qw(Plack::Middleware); 40 | use Plack::Util::Accessor qw(agent); 41 | 42 | sub call { 43 | my($self, $env) = @_; 44 | $env->{HTTP_USER_AGENT} = $self->agent; 45 | $self->app->($env); 46 | }; 47 | 48 | 1; 49 | 50 | That's it. All you have to do is to inherit from Plack::Middleware and defines options that your middleware would take, and implement `call` method that would delegate to `$self->app` which is a wrapped application. This middleware is now compatible to [Plack::Builder DSL](http://advent.plackperl.org/2009/12/day-11-using-plackbuilder.html) so you can say: 51 | 52 | use Plack::Builder; 53 | 54 | builder { 55 | enable "FakeUserAgent", agent => "Mozilla/3.0 (MSIE 4.0)"; 56 | $app; 57 | }; 58 | 59 | to fake all incoming requests as it comes with the good old Internet Explorer, and you can also use `enable_if` to [conditionally enable](http://advent.plackperl.org/2009/12/day-18-load-middleware-conditionally.html) this middleware. 60 | 61 | ### Post process requests 62 | 63 | The previous examples does pre-processing of PSGI request `$env` hash, what to do about the response? It's almost the same: 64 | 65 | my $app = sub { ... }; 66 | 67 | # Middleware to fake status code to 500 68 | my $mw = sub { 69 | my $env = shift; 70 | my $res = $app->($env); 71 | $res->[0] = 500 unless $res->[2] == 200; 72 | $res; 73 | }; 74 | 75 | This is an *evil* middleware component that changes all the status code to 500 unless it's 200 OK. Not sure if there is any use for this but it's simple enough for a quick example. 76 | 77 | Because some servers implement special [streaming interface](http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html) to delay HTTP response, this middleware doesn't really work with such an interface. Dealing with this special callback interface in individual middleware is not efficient, so we have a special callback interface in Plack::Middleware to make this easy: 78 | 79 | package Plack::Middleware::BadStatusCode; 80 | use strict; 81 | use parent qw(Plack::Middleware); 82 | 83 | sub call { 84 | my($self, $env) = @_; 85 | my $res = $self->app->($env); 86 | $self->response_cb($res, sub { 87 | my $res = shift; 88 | $res->[0] = 500 unless $res->[0] == 200; 89 | }); 90 | } 91 | 92 | 1; 93 | 94 | Pass the response `$res` to `response_cb` and set the callback to wrap the real response, and the method takes care of the direct response and delayed response. 95 | 96 | ### Namespaces 97 | 98 | In this example we use Plack::Middleware namespace to make middleware, but it doesn't really have to be. If you think your middleware is generic enough for all PSGI apps can benefit, feel free to use the namespace, but if the middleware is too specific for your own needs, or works only with a particular application framework, then use whatever namespace, like: 99 | 100 | package MyFramework::Middleware::Foo; 101 | use parent qw(Plack::Middleware); 102 | 103 | and then use the + (plus) sign to indicate the fully qualified namespace, 104 | 105 | enable '+MyFramework::Middleware::Foo', ...; 106 | 107 | or use the non-DSL API, 108 | 109 | $app = MyFramework::Middleware::Foo->wrap($app, ...); 110 | 111 | and they should work just fine. 112 | -------------------------------------------------------------------------------- /en/24_wrap_up.md: -------------------------------------------------------------------------------- 1 | ## Day 24: Wrap up 2 | 3 | 24 days have passed so fast and this is the last entry for this Plack advent calendar. 4 | 5 | ### Best Practices 6 | 7 | Plack and PSGI are still really young projects but we've already discovered a couple of suggestions and advices to write a new PSGI application or a framework. 8 | 9 | When you write a new framework, be sure to have an access to the PSGI environment hash from end users applications or plugin developers, either directly or with an accessor method. This allows your framework to share and extend functionality with middleware components like Debug or Session. 10 | 11 | Do not write your application logic in `.psgi` files using Plack::Request. It's like writing a 1000 lines of CGI script using CGI.pm, so if you think that's your favorite I won't give you any further advice, but usually you want to make your application [testable](http://advent.plackperl.org/2009/12/day-13-use-placktest-to-test-your-application.html) and reusable by making it a class or an object. Then your `.psgi` code is just a few lines of code to create a PSGI application out of it and apply some middleware components. 12 | 13 | Think twice before using Plack::App::* namespace. Plack::App namespace is for middleware components that do not act as a *wrapper* but rather an *endpoint*. Proxy, File, Cascade and URLMap are the good examples. If you write a blog application using Plack, **Never** call it Plack::App::Blog, okay? Name your software by what it does, not how it's written. 14 | 15 | ### Explore more stuff 16 | 17 | Most of the Plack gangs use [github](http://github.com/) for the source control and [searching for repositories with "Plack"](http://github.com/search?langOverride=&q=plack&repo=&start_value=1&type=Repositories) would give you a fresh look of what would look like an interesting idea. You can also search for modules on CPAN with [Plack](http://search.cpan.org/search?query=plack&mode=module) or [PSGI](http://search.cpan.org/search?query=psgi&mode=module). I keep track of good blog posts and stuff on delicious, so you can see them tagged with [psgi](http://delicious.com/miyagawa/psgi) or [Plack](http://delicious.com/miyagawa/plack). 18 | 19 | ### Getting in touch with the dev team 20 | 21 | Again, Plack is a fairly young project. It's just been 3 months since we gave this project a birth. There are many things that could get more improvements, so if you come across one of them, don't stop there. Let us know what you think is a problem, give us an insight how it could be improved, or if you're impatient, fork the project on github and send us patches. 22 | 23 | We're chatting on IRC channel #plack on irc.perl.org and there's a [mailing list](http://groups.google.com/group/psgi-plack) and [an issue tracker on github](http://github.com/plack/Plack/issues) to communicate with us. 24 | 25 | ### On a final note... 26 | 27 | It's been an interesting experiment of writing 24 articles for 24 days, and I'm glad that I finished this myself. Next year, i'm looking forward to having your own advent entries to make the community based advent calendar. 28 | 29 | I wish you a Very Merry Christmas and a Happy New Year. 30 | -------------------------------------------------------------------------------- /images/pylons_as_onion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miyagawa/plack-handbook/f4aaf90213335f8d27f3638b4ee7c58b15908d0e/images/pylons_as_onion.png -------------------------------------------------------------------------------- /ja/00_intro.md: -------------------------------------------------------------------------------- 1 | ## イントロダクション 2 | 3 | この本は[Plack Advent Calendar](http://advent.plackperl.org/)の内容をベースにしています。このカレンダーでは24個の短いポストでPSGIのコンセプトやPlackをプロジェクトに導入するTipsを紹介しました。 4 | 5 | カレンダーの内容は人気となり、しばらくの間、Plackを導入しようとするビギナーやPSGIに対応したいフレームワーク作者のためのリファレンスとして参照されてきました。 6 | 7 | 本書の中身は3年前に書かれたもので、少し古くなっているものもありますが、内容については最新に追随するよう書き換えました。また読者からのフィードバックによってさらに最新に更新していく予定です。 8 | 9 | 本のソースコードはMarkdownフォーマットで[github レポジトリ](https://github.com/miyagawa/plack-handbook)ダウンロードでき、Creative Commonsでライセンスされます。詳細はLICENSEファイルを参照してください。 10 | 11 | 2012年9月 宮川達彦 12 | 13 | 14 | -------------------------------------------------------------------------------- /ja/01_getting_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 1: Plackを入手 2 | 3 | まずはじめに、[Plack](http://search.cpan.org/dist/Plack)とその他ユーティリティをインストールしましょう。PSGIやPlackは通常のPerlモジュールとして配布されているので、インストールはCPANシェルをたちあげて以下のようにタイプするだけです。 4 | 5 | ``` 6 | cpan> install PSGI Plack 7 | ``` 8 | 9 | [PSGI](http://search.cpan.org/dist/PSGI) はPSGIインタフェースの仕様を記述したドキュメントをモジュール化したものです。インストールすることによってシェルから`perldoc PSGI` や `perldoc PSGI::FAQ`としてドキュメントを参照することができます。Plackは標準のサーバ実装、コアのミドルウェアやplackup, Plack::Testといったユーティリティが付属します。 10 | 11 | PlackはコアではないXSモジュールに依存していないため、Perl 5.8.1 以上のバージョンであれば特に問題なくリリースできるはずですし、CコンパイラのないWin32やDeveloper ToolsのないMac OS X環境でも利用が可能です (*とはいえ、makeなどのツールがないとCPANからのインストールが実行できないかもしれません)。 12 | 13 | Webアプリケーションやフレームワークのデベロッパーであれば、オプションのバンドル [Task::Plack](http://search.cpan.org/dist/Task-Plack)もインストールすることをおすすめします。こちらも、以下のコマンドでインストールが可能です。 14 | 15 | ``` 16 | cpan> install Task::Plack 17 | ``` 18 | 19 | CPANシェルを対話モードで起動している場合、いくつかの質問を聞かれるかもしれません。不明な場合はデフォルトを選択して問題ありません。オプションのXSモジュールについてはデフォルトでインストールされますが、非同期用のサーバなどは標準ではインストールされません。 20 | 21 | `perldoc Plack` としてドキュメントの概観を読むことができます。 22 | 23 | -------------------------------------------------------------------------------- /ja/02_hello_world.md: -------------------------------------------------------------------------------- 1 | ## Day 2: Hello World 2 | 3 | プログラミング言語の学習で最初に書くプログラムは "Hello World" を表示することです。PSGIでもそうしてみましょう。 4 | 5 | **注意:** 今日のコードは理解のためにPSGIの生インタフェースを利用して書かれていますが、あなたがWebアプリケーションフレームワークの作者でない限り、実際にはこうしたコードを書く必要はありません。[PSGIをサポートしているフレームワーク](http://plackperl.org/#frameworks)を参照してください。 6 | 7 | ### Hello, World 8 | 9 | "Hello World" を表示するための最小限のコードは以下のようになります。 10 | 11 | my $app = sub { 12 | return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ]; 13 | }; 14 | 15 | PSGIアプリケーションはPerlのサブルーチンリファレンス(コードリファレンス)を利用して記述し、ここでは`$app`という変数名に格納しています(が、名前はなんでも構いません)。このサブルーチンは1つの引数`$env`を受け取り(今回のコードでは省略されています)、ステータス、ヘッダとボディを格納した配列リファレンスを返します。 16 | 17 | このコードを`hello.psgi`というファイルに保存し、plackupコマンドで起動しましょう: 18 | 19 | > plackup hello.psgi 20 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ 21 | 22 | plackup はアプリケーションをデフォルトのHTTPサーバ HTTP::Server::PSGI を利用して localhost の5000番ポートで起動します。http://127.0.0.1:5000/ をブラウザで開くと、Hello Worldが表示されましたか? 23 | 24 | ### 違うものを表示 25 | 26 | Hello Worldはとてもシンプルなものですが、もう少し違ったことをやってみましょう。クライアント情報をPSGI環境変数から取得し、表示します。 27 | 28 | my $app = sub { 29 | my $env = shift; 30 | return [ 31 | 200, 32 | ['Content-Type' => 'text/plain'], 33 | [ "Hello stranger from $env->{REMOTE_ADDR}!"], 34 | ]; 35 | }; 36 | 37 | このコードはクライアントのリモートアドレスをPSGI環境変数ハッシュから表示します。ローカルホストで起動している場合、値は127.0.0.1 になるはずです。PSGI環境変数にはHTTPの接続やクライアントに関する情報、たとえばHTTPヘッダやリクエストパスなどが格納されていて、CGIの環境変数によく似ています。 38 | 39 | テキスト以外のデータをファイルから表示するには、以下のようにします。 40 | 41 | my $app = sub { 42 | my $env = shift; 43 | if ($env->{PATH_INFO} eq '/favicon.ico') { 44 | open my $fh, "<:raw", "/path/to/favicon.ico" or die $!; 45 | return [ 200, ['Content-Type' => 'image/x-icon'], $fh ]; 46 | } elsif ($env->{PATH_INFO} eq '/') { 47 | return [ 200, ['Content-Type' => 'text/plain'], [ "Hello again" ] ]; 48 | } else { 49 | return [ 404, ['Content-Type' => 'text/html'], [ '404 Not Found' ] ]; 50 | } 51 | }; 52 | 53 | このアプリケーションはリクエストパスが`/favicon.ico`となっている場合に`favicon.ico`を表示し、ルート(/)については"Hello World"、その他のパスには404を返します。Perlの標準ファイルハンドルである`$fh`はPSGIのボディにそのまま設定することができますし、ステータスコードについても妥当な数字をいれることができます。 54 | -------------------------------------------------------------------------------- /ja/03_using_plackup.md: -------------------------------------------------------------------------------- 1 | ## Day 3: plackupを使う 2 | 3 | Day 2の記事ではplackupコマンドを利用してHello World PSGIアプリケーションを起動しました。 4 | 5 | plackup はPSGIアプリケーションを起動するためのコマンドラインランチャーで、Rackのrackupにインスパイアされました。.psgiファイルに保存されたPSGIアプリケーションであれば、Plackハンドラーに対応したWebサーババックエンドの上で動かすことができます。使い方はシンプルで、.psgiファイルのパスをコマンドに渡すだけです。 6 | 7 | > plackup hello.psgi 8 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ 9 | 10 | カレントディレクトリの`app.psgi`という名前のファイルを起動する場合、ファイル名も省略可能です。 11 | 12 | デフォルトで起動するバックエンドは以下の方法で選ばれます。 13 | 14 | * 環境変数`PLACK_SERVER`が定義されている場合、その値 15 | * 環境特有の環境変数、たとえば `GATEWAY_INTERFACE` や `FCGI_ROLE` などが定義されている場合、CGIやFCGIバックエンドが自動で選ばれます 16 | * ロードされた`.psgi`ファイルがAnyEvent, CoroやPOEなどのモジュールをロードしている場合、それに対応したバックエンドが自動で選ばれます 17 | * その他の場合、"Standalone" バックエンドが選択され、HTTP::Server::PSGIモジュールによって起動します 18 | 19 | コマンドラインスイッチ`-s`か`--server`でバックエンドを指定することもできます。 20 | 21 | > plackup -s Starman hello.psgi 22 | 23 | plackupコマンドはデフォルトで3つのミドルウェアを有効にします。Lint, AccessLog と StackTrace で、開発の際にログやスタックトレースを表示してくれて便利ですが、これを無効にするには、`-E`または`--environment`スイッチで`development`以外の値をセットします: 24 | 25 | > plackup -E production -s Starman hello.psgi 26 | 27 | Plack environment に`development`を利用したいが、デフォルトのミドルウェアは無効にしたい場合、`--no-default-middleware` オプションも用意されています。 28 | 29 | その他のコマンドラインオプションをサーババックエンドに渡すこともでき、サーバのリッスンするポートは以下のように設定できます: 30 | 31 | > plackup -s Starlet --host 127.0.0.1 --port 8080 hello.psgi 32 | Plack::Handler::Starlet: Accepting connections at http://127.0.0.1:8080/ 33 | 34 | FCGIバックエンドでUNIXドメインソケットを指定するには: 35 | 36 | > plackup -s FCGI --listen /tmp/fcgi.sock app.psgi 37 | 38 | その他のオプションについては、コマンドラインから`perldoc plackup`を実行して参照してください。明日もplackupについて解説をつづけます。 39 | -------------------------------------------------------------------------------- /ja/04_reloading_applications.md: -------------------------------------------------------------------------------- 1 | ## Day 4: アプリケーションのリロード 2 | 3 | 昨日はplackupの基本とコマンドラインオプションについて解説しました。今日もつづけましょう! 4 | 5 | ### 必要に応じてアプリケーションをリロードする 6 | 7 | 開発中のコードは`.psgi`や`.pm`に書かれたPerlのコードを編集します。plackupによって起動されたサーバは永続プロセスのため、最初に一度だけコンパイルされたPSGIコードが何度も実行されます。コードの変更を反映するにはサーバプロセスを再起動する必要があり、面倒です。 8 | 9 | 作業ディレクトリ以下のファイルを監視し、変更があったらアプリケーションをリロードするオプションが用意されています。`-r`または`--reload`です。 10 | 11 | plackup -r hello.psgi 12 | 13 | デフォルトではカレントディレクトリ以下のファイルをモニターしますが、他のパスを指定するには`-R`(大文字)で指定します。 14 | 15 | plackup -R lib,/path/to/scripts hello.psgi 16 | 17 | 複数のパスを指定するにはカンマ(,)で区切ります。 18 | 19 | デフォルトではタイマーを利用してディレクトリの中身をスキャンしますが、LinuxではLinux::Inotify2, Mac OS Xでは Mac::FSEventsがインストールされている場合、ファイルシステムのイベント通知を利用するため、より効率的です。 20 | 21 | ### -r とサーバ自動選択 22 | 23 | Day 3でplackupのサーバ自動選択について紹介しました。アプリケーションがAnyEvent, CoroやPOEを利用している場合、それに対応したサーバが自動で選択される機能です。この機能は`-r`や`-R`オプションが有効の場合、正常に機能しません。アプリケーションのロードが起動時には行われなくなるためです。リロード機能を使う場合、`-s`で明示的にバックエンドサーバを指定してください。 24 | 25 | ### リロードがうまく動かない場合: Shotgun 26 | 27 | 永続環境のperlプロセスでモジュールやアプリケーションを読み直す場合、問題が起こることがあります。モジュールのパッケージ変数を更新したり、初期化する場合です。 28 | 29 | PlackにあるShotgunローダーは、Rackの[shotgun](http://github.com/rtomayko/shotgun)にインスパイアされたハンドラで、リクエストごとに子プロセスをforkして、そこでアプリケーションを読み直します。 30 | 31 | Shotgunローダーの利用は簡単です。 32 | 33 | > plackup -L Shotgun myapp.psgi 34 | 35 | アプリケーションのコンパイルがランタイムまで遅延され、新しいリクエストが来た際、新しい子プロセスをforkし、PSGIのレスポンスをパイプ上で返します。開発中には変更することのないモジュールはプリロードしておき、レスポンスのボトルネックにならないようにすることも可能です。 36 | 37 | たとえば、アプリケーションがMooseとDBIx::Classを使っているなら、 38 | 39 | > plackup -MMoose -MDBIx::Class -L Shotgun myapp.psgi 40 | 41 | とすればリクエストごとに読み直すコードが減少するため、スピードアップが期待できます。 42 | -------------------------------------------------------------------------------- /ja/05_run_a_static_web_server_with_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 5: Plackで静的サーバを起動する 2 | 3 | Plackディストリビューションにはすぐに使えるPSGIアプリケーションがPlack::Appネームスペースの下に用意されています。いくつかはとても便利で、一例がここで紹介するPlack::App::FileとPlack::App::Directoryです。 4 | 5 | Plack::App::Fileは`/foo/bar.html`といったリクエストパスを、`/path/to/htdocs/foo/bar.html`といったローカルのファイルにマップし、ファイルを開いてファイルハンドルをPSGIのレスポンスとして返します。lighttpd, nginx やApacheといった既成のWebサーバと同様です。 6 | 7 | Plack::App::DirectoryはPlack::App::Fileのラッパーで、Apacheのmod_autoindexのようなディレクトリインデックスを表示します。 8 | 9 | これらのアプリケーションの利用はとても簡単です。以下のような`.psgi`ファイルを記述します。 10 | 11 | use Plack::App::File; 12 | my $app = Plack::App::File->new(root => "$ENV{HOME}/public_html"); 13 | 14 | これをplackupで起動します。 15 | 16 | > plackup file.psgi 17 | 18 | これで`~/public_html`以下のファイルはURL http://localhost:5000/somefile.html でアクセスできるようになります。 19 | 20 | Plack::App::Directory についても同様ですが、plackupのコマンドラインから直接起動する例を紹介します。 21 | 22 | > plackup -MPlack::App::Directory \ 23 | -e 'Plack::App::Directory->new(root => "$ENV{HOME}/Sites")' 24 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ 25 | 26 | plackupコマンドは、perlコマンド同様、`-I`(インクルードパス), `-M`(ロードするモジュール)や`-e`(実行するコード)を指定できるため、ワンライナーでPSGIアプリを書くことができます。 27 | 28 | 他にもPlack::Appにはいくつかアプリケーションが用意されていますが、それはまた別の日に。 29 | -------------------------------------------------------------------------------- /ja/06_convert_cgi_apps_to_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 6: CGIアプリケーションをPSGIに変換 2 | 3 | Perlでウェブアプリケーションを書く方法として長い間もっとも人気があったのがCGI, FastCGIとmod_perlでした。CGI.pmはPerlに付属するコアモジュールで、この3つの環境で同時に動くコードを記述することが(少しの変更で)できます。これによって、多くのWebアプリケーションやフレームワークはCGI.pmを使って環境の差異を吸収してきました。 4 | 5 | [CGI::PSGI](http://search.cpan.org/perldoc?CGI::PSGI) を使うと、既存のCGI.pmベースのアプリケーションをPSGIに簡単に変換できます。以下のようなCGIアプリケーションがあるとします: 6 | 7 | use CGI; 8 | 9 | my $q = CGI->new; 10 | print $q->header('text/plain'), 11 | "Hello ", $q->param('name'); 12 | 13 | とてもシンプルなCGIスクリプトですが、これをPSGIに変換するには以下のようにします: 14 | 15 | use CGI::PSGI; 16 | 17 | my $app = sub { 18 | my $env = shift; 19 | my $q = CGI::PSGI->new($env); 20 | return [ 21 | $q->psgi_header('text/plain'), 22 | [ "Hello ", $q->param('name') ], 23 | ]; 24 | }; 25 | 26 | `CGI::PSGI->new($env)` はPSGIの環境変数ハッシュを受け取り、CGI.pmのサブクラスであるCGI::PSGIのインスタンスをつくります。`param`, `query_string`といったメソッドは今までどおり動作しますが、CGIの環境変数ではなく、PSGI環境変数から値を取得します。 27 | 28 | `psgi_header` はCGIの`header`メソッドのように動作するユーティリティで、ステータスコードとHTTPヘッダの配列リファレンスをリストで返します。 29 | 30 | 明日は既存のCGI.pmを利用したフレームワークをPSGIに変換します。 31 | -------------------------------------------------------------------------------- /ja/07_use_web_application_framework_in_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 7: WebアプリケーションフレームワークをPSGIで利用する 2 | 3 | PlackとPSGIプロジェクトを2009年9月にはじめて以来、Catalyst, Jifty やCGI::Applicationといった人気のあるフレームワークのデベロッパーから多くのフィードバックをもらいました。 4 | 5 | [CGI::Application](http://cgi-app.org/) はもっとも「伝統的」なCGIベースのWebアプリケーションフレームワークで、昨日も紹介したようにCGI.pmを利用して様々な環境の差異を吸収しています。 6 | 7 | CGI::Applicationの現在のメンテナであるMark Stosbergと、CGI::ApplicationにおけるPSGIサポートについて検討してきました。いくつかのアプローチを検討し、その中にはPSGIサポートを直接CGI::Applicationに記述するものも含まれましたが、昨日も紹介したCGI::PSGIをラッパーとして開発し、[CGI::Application::PSGI](http://search.cpan.org/perldoc?CGI::Application::PSGI)を既存のCGI::Applicationを変更することなくPSGI互換モードで起動させるように実装しました。 8 | 9 | CGI::Application::PSGIをCPANからインストールして、`.psgi`ファイルを以下のように記述します。 10 | 11 | use CGI::Application::PSGI; 12 | use WebApp; 13 | 14 | my $app = sub { 15 | my $env = shift; 16 | my $app = WebApp->new({ QUERY => CGI::PSGI->new($env) }); 17 | CGI::Application::PSGI->run($app); 18 | }; 19 | 20 | そして[plackup](http://advent.plackperl.org/2009/12/day-3-using-plackup.html)を使ってスタンドアロンや各種バックエンドのサーバ上で起動することができます。 21 | 22 | 同様に、多くのWebフレームワークではPSGIサポートをするためのプラグイン、エンジンやアダプターを提供し、PSGIモードで起動するための機能が用意されています。たとえば[Catalyst](http://www.catalystframework.org/)ではCatalyst::EngineというレイヤーでWebサーバエンジンの抽象化を行っていて、[Catalyst::Engine::PSGI](http://search.cpan.org/perldoc?Catalyst::Engine::PSGI) でCatalystをPSGI上で起動することができます。(**注**: 2011年にCatalyst 5.8 がリリースされ、PSGIサポートはCatalyst本体に組み込まれており、エンジンを別途インストールする必要はありません) 23 | 24 | 重要なのは、"PSGIサポート"しているフレームワークを利用する場合、利用者のアプリケーションをPSGI用に書き換える必要はないということです。多くの場合、1行のコードの変更も必要ありません。かつ、PSGIを利用することでplackup, Plack::Testやミドルウェアなど多くのエコシステムを利用することができます。これらについては、のちほど紹介していきます。 25 | -------------------------------------------------------------------------------- /ja/08_adapting_web_frameworks_to_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 8: WebフレームワークをPSGIに対応させる 2 | 3 | Webアプリケーションフレームワーク作者にとって、PSGIの最大のメリットは、一度PSGIに対応すれば、FastCGI、CGIといったWebサーバ固有の環境の差異といった問題に対応する必要がなくなるということです。 4 | 5 | オープンソースあるいはプロプライエタリの大規模なWebアプリケーションを開発している場合、自家製のフレームワークを作っているという場合も多いでしょう。 6 | 7 | 今日のエントリではこうしたフレームワークをどのようにPSGIインタフェースに対応させるかを紹介します。 8 | 9 | ### CGI.pm ベースのフレームワーク 10 | 11 | Day 7ではCGI::ApplicationベースのアプリケーションをCGI::Application::PSGI経由で、PSGIで起動させる方法を紹介しました。CGI::Applicationはその名前からわかるようにCGI.pmを利用していますので、ここをCGI::PSGIにすり替えてしまうのがもっとも手っ取り早い方法です。 12 | 13 | package CGI::Application::PSGI; 14 | use strict; 15 | use CGI::PSGI; 16 | 17 | sub run { 18 | my($class, $app) = @_; 19 | 20 | # HACK: deprecate HTTP header generation 21 | # -- CGI::Application should support some flag to turn this off cleanly 22 | my $body = do { 23 | no warnings 'redefine'; 24 | local *CGI::Application::_send_headers = sub { '' }; 25 | local $ENV{CGI_APP_RETURN_ONLY} = 1; 26 | $app->run; 27 | }; 28 | 29 | my $q = $app->query; 30 | my $type = $app->header_type; 31 | 32 | my @headers = $q->psgi_header($app->header_props); 33 | return [ @headers, [ $body ] ]; 34 | } 35 | 36 | メインとなる実装はたったこれだけです。CGI::Applicationの`run`メソッドは通常、HTTPヘッダとボディを含む全体の出力を文字列で返します。ご覧のとおり、このモジュールはちょっと行儀の悪いハックでHTTPヘッダ生成をオーバーライドして、CGI::PSGIモジュールの`psgi_header`メソッドを使ってPSGIのレスポンスを返しています。 37 | 38 | [Mason](http://search.cpan.org/perldoc?HTML::Mason) や [Maypole](http://search.cpan.org/perldoc?Maypole) 用のPSGIアダプターも実装してみましたが、おおむねコードは同様です。 39 | 40 | * `$env`からCGI::PSGIインスタンスを作り、それをCGI.pmインスタンスの代わりにセットする 41 | * 必要ならHTTPヘッダ出力を抑制 42 | * アプリのメインディスパッチャーを実行 43 | * 送信するHTTPヘッダを抽出、`psgi_header` を使ってステータスとヘッダを生成 44 | * レスポンスボディを抽出 45 | 46 | ### アダプターベースのフレームワーク 47 | 48 | フレームワークがすでにアダプターベースのアプローチでWebサーバ環境の差異を吸収している場合、PSGIサポートを追加するのはさらに簡単になります。CGI用のコードを少し変更するだけですみます。以下のコードは[Squatting](http://search.cpan.org/perldoc?Squatting) をPSGI対応させるためのコードです。SquattingはSquatting::On::* ネームスペースでmod_perl, FastCGIやその他のフレームワーク(Catalyst, HTTP::Engine)などへのアダプターを記述します。[Squatting::On::PSGI](http://search.cpan.org/perldoc?Squatting::On::PSGI) でPSGI対応のコードを書くのはとても簡単でした。 49 | 50 | package Squatting::On::PSGI; 51 | use strict; 52 | use CGI::Cookie; 53 | use Plack::Request; 54 | use Squatting::H; 55 | 56 | my %p; 57 | $p{init_cc} = sub { 58 | my ($c, $env) = @_; 59 | my $cc = $c->clone; 60 | $cc->env = $env; 61 | $cc->cookies = $p{c}->($env->{HTTP_COOKIE} || ''); 62 | $cc->input = $p{i}->($env); 63 | $cc->headers = { 'Content-Type' => 'text/html' }; 64 | $cc->v = { }; 65 | $cc->status = 200; 66 | $cc; 67 | }; 68 | 69 | # \%input = i($env) # Extract CGI parameters from an env object 70 | $p{i} = sub { 71 | my $r = Plack::Request->new($_[0]); 72 | my $p = $r->params; 73 | +{%$p}; 74 | }; 75 | 76 | # \%cookies = $p{c}->($cookie_header) # Parse Cookie header(s). 77 | $p{c} = sub { 78 | +{ map { ref($_) ? $_->value : $_ } CGI::Cookie->parse($_[0]) }; 79 | }; 80 | 81 | sub psgi { 82 | my ($app, $env) = @_; 83 | 84 | $env->{PATH_INFO} ||= "/"; 85 | $env->{REQUEST_PATH} ||= do { 86 | my $script_name = $env->{SCRIPT_NAME}; 87 | $script_name =~ s{/$}{}; 88 | $script_name . $env->{PATH_INFO}; 89 | }; 90 | $env->{REQUEST_URI} ||= do { 91 | ($env->{QUERY_STRING}) 92 | ? "$env->{REQUEST_PATH}?$env->{QUERY_STRING}" 93 | : $env->{REQUEST_PATH}; 94 | }; 95 | 96 | my $res; 97 | eval { 98 | no strict 'refs'; 99 | my ($c, $args) = &{ $app . "::D" }($env->{REQUEST_PATH}); 100 | my $cc = $p{init_cc}->($c, $env); 101 | my $content = $app->service($cc, @$args); 102 | 103 | $res = [ 104 | $cc->status, 105 | [ %{ $cc->{headers} } ], 106 | [ $content ], 107 | ]; 108 | }; 109 | 110 | if ($@) { 111 | $res = [ 500, [ 'Content-Type' => 'text/plain' ], [ "
$@
" ] ]; 112 | } 113 | 114 | return $res; 115 | } 116 | 117 | 多少のコード量がありますが、ほとんどは [Squatting::On::CGI](http://cpansearch.perl.org/src/BEPPU/Squatting-0.70/lib/Squatting/On/CGI.pm) と共通で、CGI.pmを利用している箇所をPlack::Requestに置き換えただけの単純なコードです。 118 | 119 | 昨日紹介した[Catalyst::Engine::PSGI](http://search.cpan.org/perldoc?Catalyst::Engine::PSGI) もほとんどがCGI用と共通です。 120 | 121 | ### mod_perl 中心のフレームワーク 122 | 123 | いくつかのフレームワークは mod\_perl のAPIを多用して実装されていることがあり、こうした場合はCGI.pmを置き換えるといったアプローチは利用できません。Apache::RequestのAPIをfake/mock objectなどでモックする必要があるでしょう。WebGUIデベロッパーであるPatrick Donelanが mod_perl ライクなAPIからPSGIへポートした際の事例を[ブログ記事](http://blog.patspam.com/2009/plack-roundup-at-sf-pm)で紹介しています。実際にリンクされている[モッククラス](http://github.com/pdonelan/webgui/blob/plebgui/lib/WebGUI/Session/Plack.pm) を見てみるのもよいでしょう。 124 | -------------------------------------------------------------------------------- /ja/09_running_cgi_scripts_on_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 9: CGIスクリプトをPlackで走らせる 2 | 3 | 既存のCGIベースのアプリケーションをPSGIに変換する方法を幾つか紹介してきました。今日のエントリでは、すべてのCGIスクリプトを、多くの場合なにも変更せずに、PSGIアプリケーションとして実行する究極の方法を紹介します。 4 | 5 | [CGI::PSGI](http://search.cpan.org/perldoc?CGI::PSGI)はCGI.pmのサブクラスとして実装されていて、CGI.pmからのマイグレーションは、多くの場合数行の変更だけで可能ですが、これは元のコードがある程度エントリポイントが整理されているなどの前提が必要です。レガシーなCGIスクリプトで、いろんな箇所で環境変数を直接参照したり、STDOUTに出力がされていて変更が難しい場合はどうでしょうか。 6 | 7 | [CGI::Emulate::PSGI](http://search.cpan.org/perldoc?CGI::Emulate::PSGI) はCGIベースのPerlプログラムをPSGI環境で実行するモジュールです。CGI::Emulate::PSGIでは環境変数やSTDIN/STODOUTをCGI向けにエミュレートしてから実行するため、上で書いたようなレガシーなCGIスクリプトでSTDOUTにいろいろな箇所で出力をしていても問題ありません。 8 | 9 | use CGI::Emulate::PSGI; 10 | CGI::Emulate::PSGI->handler(sub { 11 | do "/path/to/foo.cgi"; 12 | CGI::initialize_globals() if &CGI::initialize_globals; 13 | }); 14 | 15 | このコードで既存のCGIスクリプトをPSGIとして実行できます。CGI.pm を使っている場合、CGI.pmは多くのグローバル変数にキャッシュをつくるため、`initialize_globals`をリクエストごとに手動で実行する必要があります。 16 | 17 | San FranciscoからLondon Perl Workshopに向かうフライトの途中で、これよりもさらにスマートな方法を思いついてハックしていました。`do`でスクリプトを都度実行するのではなく、CGIスクリプトをサブルーチンにコンパイルしてしまうものです。このモジュールは[CGI::Compile](http://search.cpan.org/perldoc?CGI::Compile) として公開されていて、CGI::Emulate::PSGIと組み合わせて使うと最適です。 18 | 19 | my $sub = CGI::Compile->compile("/path/to/script.cgi"); 20 | my $app = CGI::Emulate::PSGI->handler($sub); 21 | 22 | [Plack::App::CGIBin](http://search.cpan.org/perldoc?Plack::App::CGIBin) がPlackに付属していて、このアプリケーションは`/pat/to/cgi-bin`といったディレクトリにあるCGIスクリプトをそのままPSGIアプリケーションとして起動することができます。 23 | 24 | > plackup -MPlack::App::CGIBin -e 'Plack::App::CGIBin->new(root => "/path/to/cgi-bin"))' 25 | 26 | こうして`/path/to/cgi-bin`にあるCGIスクリプトをマウントします。cgi-binディレクトリにある`foo.pl`は http://localhost:5000/foo.pl でアクセスできます。最初の実行時にコンパイルされるため、mod_perlのApache::Registryと似たような感じで動作します。 27 | -------------------------------------------------------------------------------- /ja/10_using_plack_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 10: Plackミドルウェアの利用 2 | 3 | ### ミドルウェア 4 | 5 | ミドルウェアはPSGIにおけるコンセプト(いつものように、PythonのWSGIやRubyのRackからのパクリです)で、サーバとアプリケーション両側の動作をするコンポーネントです。 6 | 7 | ![WSGI middleware onion](../images/pylons_as_onion.png) 8 | 9 | (画像は Pylons project) 10 | 11 | この画像はミドルウェアのコンセプトをうまく解説しています。玉ねぎの真ん中にPSGIアプリケーションがあり、それをミドルウェアがラップし、リクエストが来るごとに(外側から内側に)前処理を行い、レスポンスが出力されたら(内側から外側に)後処理を行います。 12 | 13 | HTTP認証、エラーの補足、JSONPなど数多くの機能をミドルウェアとして実装することによって、PSGIアプリケーションやフレームワークに動的に機能追加していくことが可能になります。 14 | 15 | ### Plack::Middleware 16 | 17 | [Plack::Middleware](http://search.cpan.org/perldoc?Plack::Middleware) はミドルウェアを記述するためのベースクラスで、シンプルにかつ再利用可能な形でミドルウェアを書くことができます。 18 | 19 | Plack::Middlewareで書かれたミドルウェアの利用は簡単で、元のアプリケーションを`wrap`メソッドでラップするだけです。 20 | 21 | my $app = sub { [ 200, ... ] }; 22 | 23 | use Plack::Middleware::StackTrace; 24 | $app = Plack::Middleware::StackTrace->wrap($app); 25 | 26 | この例は元のアプリケーションをStackTraceミドルウェア(実際にはplackupのデフォルトで有効)の`wrap`メソッドでラップします。ラップされたアプリケーションが例外を投げた場合、ミドルウェアがエラーを捕捉して、Devel::StackTrace::AsHTMLを利用して美しいHTMLページを表示します。 27 | 28 | ミドルウェアの中にはパラメータをとるものもあります。その場合、`$app`の後ろにハッシュでパラメータを渡すことができます。: 29 | 30 | my $app = sub { ... }; 31 | 32 | use Plack::Middleware::MethodOverride; 33 | $app = Plack::Middleware::MethodOverride->wrap($app, header => 'X-Method'); 34 | 35 | 多くのミドルウェアをラップするのは、とくにそのミドルウェアを実装したモジュールを先にuseする必要もあるため、退屈になりがちです。この対策として、DSL風の記述法が用意されています。 36 | 37 | use Plack::Builder; 38 | my $app = sub { ... }; 39 | 40 | builder { 41 | enable "StackTrace"; 42 | enable "MethodOverride", header => 'X-Method'; 43 | enable "Deflater"; 44 | $app; 45 | }; 46 | 47 | Plack::Builderの利用方法については明日解説します。 48 | 49 | ### Middleware and Frameworks 50 | 51 | ミドルウェアの美しい点は、どんなPSGIアプリケーションにも適用できるところです。このコード例からは当たり前かもしれませんが、実際にはラップされたアプリケーションはどんなものでも構いません。既存のWebアプリケーションをPSGIモードで起動し、それにPlackミドルウェアを追加することだってできます。例えば、CGI::Applicationで、 52 | 53 | use CGI::Application::PSGI; 54 | use WebApp; 55 | 56 | my $app = sub { 57 | my $env = shift; 58 | my $app = WebApp->new({ QUERY => CGI::PSGI->new($env) }); 59 | CGI::Application::PSGI->run($app); 60 | }; 61 | 62 | use Plack::Builder; 63 | builder { 64 | enable "Auth::Basic", authenticator => sub { $_[1] eq 'foobar' }; 65 | $app; 66 | }; 67 | 68 | このようにすると、CGI::ApplicationベースのアプリケーションにBasic認証をつけることができます。[PSGIをサポートしているフレームワーク](http://plackperl.org/#frameworks)であればなんでもこのようなことが可能です。 69 | -------------------------------------------------------------------------------- /ja/11_using_plack_builder.md: -------------------------------------------------------------------------------- 1 | ## Day 11: Plack::Builderを使う 2 | 3 | 昨日のエントリではPlackミドルウェアを.psgiで利用する方法を紹介しました。ミドルウェアを`use`して、そのあと`$app`を`wrap`メソッドでラップしていくのは退屈ですし、直感的ではありません。そこで、それをより簡単にするDSL (Domain Specific Language) 風シンタックスを用意しています。それが、Plack::Builderです。 4 | 5 | Plack::Builderの利用はとても簡単です。`builder`と`enable`キーワードを使います。 6 | 7 | my $app = sub { 8 | return [ 200, [], [ "Hello World" ] ]; 9 | }; 10 | 11 | use Plack::Builder; 12 | builder { 13 | enable "JSONP"; 14 | enable "Auth::Basic", authenticator => sub { ... }; 15 | enable "Deflater"; 16 | $app; 17 | }; 18 | 19 | このコードは元のアプリケーション`$app`に対して、Deflater, Auth::Basic と JSONP ミドルウェアを内側から外側に向けてラップしていきます。つまり、以下のコードと同様です。 20 | 21 | $app = Plack::Middleware::Deflater->wrap($app); 22 | $app = Plack::Middleware::Auth::Basic->wrap($app, authenticator => sub { }); 23 | $app = Plack::Middleware::JSONP->wrap($app); 24 | 25 | ただし、各モジュールを先に`use`する必要がないので、よりDRYになっています。 26 | 27 | ### 外側から内側へ、上から下へ 28 | 29 | ラップされるミドルウェアの順番が逆であることに気づいたでしょうか?builder/enableのDSLでは、ラップされる`$app`に近い行が、*内側*で、最初の行が*外側*にくるようにラップされます。昨日紹介した玉ねぎの図と比較するとよりわかりやすくなります。アプリケーションに近い行ほど、レイヤーの内側になるということです。 30 | 31 | `enable`を使う場合Plack::Middleware::をミドルウェアの名前から省略できます。Plack::Middleware以外の名前空間を使う場合、たとえば MyFramework::PSGI::MW::Foo であれば、 32 | 33 | enable "+MyFramework::PSGI::MW::Foo"; 34 | 35 | とすることができます。重要なのはプラス(+)でモジュール名がFully Qualified であることを指定できます。 36 | 37 | ### 裏で起こっていること 38 | 39 | もしPlack::Builderの実装に興味があれば、コードを見て何をしているか追ってみてください。`builder`はコードブロックを受け取り、それを実行した結果のコードリファレンスを元のアプリケーション(`$app`)として受け取り、そしてenableされたミドルウェアを逆順にラップしていきます。つまり、`builder`ブロックの最後に`$app`またはPSGIアプリケーションを配置することが重要で、また`builder`ブロックは.psgiファイルの最後になければなりません。 40 | 41 | ### Thanks, Rack 42 | 43 | Plack::BuilderはRubyのRack::Builderにインスパイアされています。Rack::Builderでは`use`キーワードを使っていますが、Perlではこれは使えないため、`enable`で代用しています:) Rackには`map`キーワードでアプリケーションをパスにマップする機能がありますが、Plackでこれをどうするかは明日解説します。 44 | -------------------------------------------------------------------------------- /ja/12_maps_multiple_apps_with_mount_and_urlmap.md: -------------------------------------------------------------------------------- 1 | ## Day 12: 複数のアプリケーションをmountとURLMapでマウントする 2 | 3 | ### Hello World! but anyone else? 4 | 5 | 本書では、シンプルなアプリケーションの例として "Hello World" を使っていました。 6 | 7 | my $app = sub { 8 | return [ 200, [], [ "Hello World" ] ]; 9 | }; 10 | 11 | より複雑な例として、Webアプリケーションフレームワークを使って書かれた複数のアプリケーションを、1つのサーバ内でmod_aliasなどでマップしているような例を考えてみましょう。 12 | 13 | ### Plack::App::URLMap 14 | 15 | Plack::App::URLMap は複数のPSGIアプリケーションを*合成*して1つのPSGIアプリケーションのように振る舞います。リクエストパスやヴァーチャルホストのようにホスト名ベースでアプリケーションをディスパッチすることができます。 16 | 17 | my $app1 = sub { 18 | return [ 200, [], [ "Hello John" ] ]; 19 | }; 20 | 21 | my $app2 = sub { 22 | return [ 200, [], [ "Hello Bob" ] ]; 23 | }; 24 | 25 | このように2つのアプリがあり、1つはJohnもう1つはBobにHelloを返します。この2つのPSGIアプリを1つのサーバで動作させたい場合、どうすればよいでしょう。Plack::App::URLMapを利用すると以下のように出来ます。 26 | 27 | use Plack::App::URLMap; 28 | my $app = Plack::App::URLMap->new; 29 | $app->mount("/john" => $app1); 30 | $app->mount("/bob" => $app2); 31 | 32 | たったこれだけです。リクエストパスに応じて、`/john`には`$app1`つまり"Hello John", `/bob`には`$app2`つまり"Hello Bob"へディスパッチします。またマップされいていないパス、たとえばルートの "/" などは404が返ります。 33 | 34 | `PATH_INFO`や`SCRIPT_NAME`といったPSGI環境変数は自動的に調整され、Apacheのmod_aliasやCGIスクリプトを起動したときのように、そのまま動きます。アプリケーションやフレームワークは、`PATH_INFO`を使ってリクエストを処理し、`SCRIPT_NAME`をベースパスとしてURLを生成する必要があります。 35 | 36 | ### mount DSL 37 | 38 | Plack::App::URLMapの`mount`はとても便利なので、Plack::BuilderのDSLにも追加してあります。Rack::Builderでは`map`を使っていますが、これもPerlでは使えないので、`mount`として使います。 39 | 40 | use Plack::Builder; 41 | builder { 42 | mount "/john" => $app1; 43 | mount "/bob" => builder { 44 | enable "Auth::Basic", authenticator => ...; 45 | $app2; 46 | }; 47 | }; 48 | 49 | '/john' へのリクエストはURLMapのときと同様、`$app1`にディスパッチされます。この例では"/bob"に対して`builder`をネストさせ、"Hello Bob"を表示するアプリケーションにBasic認証を追加しています。この例は以下のコードと同様です。 50 | 51 | $app = Plack::App::URLMap->new; 52 | $app->mount("/john", $app1); 53 | 54 | $app2 = Plack::Middleware::Auth::Basic->wrap($app2, authenticator => ...); 55 | $app->mount("/bob", $app2); 56 | 57 | が、DSLを利用した方がより短いコードで、簡潔になっています。 58 | 59 | ### マルチテナントフレームワーク 60 | 61 | もちろん、このURLMapやmount APIをつかって複数のフレームワークのアプリケーションを1つのサーバにマウントすることができます。3つのアプリケーションがあって、"Foo"がCatalyst, "Bar"がCGI::Application, "Baz"がSquattingで書かれているとしましょう。 62 | 63 | # Catalyst 64 | use Foo; 65 | my $app1 = Foo->psgi_app; 66 | 67 | # CGI::Application 68 | use Bar; 69 | use CGI::Application::PSGI; 70 | my $app2 = sub { 71 | my $app = Bar->new({ QUERY => CGI::PSGI->new(shift) }); 72 | CGI::Application::PSGI->run($app); 73 | }; 74 | 75 | # Squatting 76 | use Baz 'On::PSGI'; 77 | Baz->init; 78 | my $app3 = sub { Baz->psgi(shift) }; 79 | 80 | builder { 81 | mount "/foo" => $app1; 82 | mount "/bar" => $app2; 83 | mount "/baz" => $app3; 84 | }; 85 | 86 | こうすると、別々のフレームワークで書かれた3つのアプリケーションが、plackupなどを利用して同一サーバ上で異なるパスにマップされて起動します。 87 | -------------------------------------------------------------------------------- /ja/13_use_plack_test_to_test_your_application.md: -------------------------------------------------------------------------------- 1 | ## Day 13: Plack::Test でアプリケーションをテストする 2 | 3 | ### Testing 4 | 5 | Webアプリケーションのテストにはいろいろな方法があり、ライブのサーバを使ったり、モックリクエストを使う方法などがあります。こうしたテスト手法を提供しているフレームワークもありますが、どのようにテストを記述するかはフレームワークごとに異なっていることが多いです。 6 | 7 | Plack::TestはどんなPSGI対応のWebアプリケーションフレームワークでも、共通のインタフェースを使って、それぞれモックとライブのサーバを使ったテスト手法を導入できます。 8 | 9 | ### Plack::Testを利用 10 | 11 | Plack::Testの利用はとても簡単で、Perlのテストプロトコル標準である[TAP](http://testanything.org/wiki/) や[Test::More](http://search.cpan.org/perldoc?Test::More)と互換性があります。 12 | 13 | use Plack::Test; 14 | use Test::More; 15 | use HTTP::Request; 16 | 17 | my $app = sub { 18 | return [ 200, [ 'Content-Type', 'text/plain' ], [ "Hello" ] ]; 19 | }; 20 | 21 | test_psgi $app, sub { 22 | my $cb = shift; 23 | 24 | my $req = HTTP::Request->new(GET => 'http://localhost/'); 25 | my $res = $cb->($req); 26 | 27 | is $res->code, 200; 28 | is $res->content, "Hello"; 29 | }; 30 | 31 | done_testing; 32 | 33 | PSGIアプリを作成またはロード([Plack::Util](http://search.cpan.org/perldoc?Plack::Util)の `load_psgi` 関数をつかって`.psgi`ファイルからアプリケーションをロードできます)し、`test_psgi`関数でアプリケーションをテストします。2個目の引数はコールバックで、テスト用クライアントコードを記述します。 34 | 35 | 名前付き引数をつかって、以下のようにも書けます: 36 | 37 | test_psgi app => $app, client => sub { ... } 38 | 39 | クライアントコードはコールバック `$cb` を受け取り、これに対して HTTP::Request オブジェクトを渡すと HTTP::Response オブジェクトを返します。1つのクライアントコード内で複数のリクエストを投げて、リクエストやレスポンスのテストを記述できます。 40 | 41 | このファイルを`.t`で保存し、`prove`などでテストを実行します。 42 | 43 | ### HTTP::Request::Common 44 | 45 | これは必須ではありませんが、[HTTP::Request::Common](http://search.cpan.org/perldoc?HTTP::Request::Common) を利用してHTTPリクエストを作成することをおすすめします。コードがより簡潔になります。 46 | 47 | use HTTP::Request::Common; 48 | 49 | test_psgi $app, sub { 50 | my $cb = shift; 51 | my $res = $cb->(GET "/"); 52 | # ... 53 | }; 54 | 55 | スキームやホスト名は省略可能で、http://localhost/ (ライブ・テストの場合ポート番号は自動補完されます) になります。 56 | 57 | ### ライブ/モックモード 58 | 59 | デフォルトでは`test_psgi`のコールバックはモックHTTPリクエストモードで実行され、受け取ったHTTP::RequestオブジェクトをPSGI環境変数ハッシュに変換し、PSGIアプリケーションを実行し、レスポンスをHTTP::Responseに変換します。 60 | 61 | これをライブHTTPモードに変更して実行するには、a) パッケージ変数`$Plack::Test::Impl` または b) 環境変数 `PLACK_TEST_IMPL` を `Server` に設定します。 62 | 63 | use Plack::Test; 64 | $Plack::Test::Impl = "Server"; 65 | 66 | test_psgi ... # the same code 67 | 68 | 環境変数を使えば、`.t`コードを変更する必要がありません。 69 | 70 | env PLACK_TEST_IMPL=Server prove -l t/test.t 71 | 72 | Serverモードでは、PSGIアプリケーションをスタンドアロンサーバで記述し、LWP::UserAgent を利用してHTTPリクエストを送信します。テスト内のクライアントコードを変更する必要はありませんし、ホスト名やポート番号はテスト環境によって自動で設定されます。 73 | 74 | ### フレームワークをPlack::Testでテスト 75 | 76 | 繰り返しになりますが、PSGIとPlackの素晴らしいところは、PSGIをターゲットにして書かれたアプリケーションフレームワークであればなんでも利用が可能であるということです。Plack::Testも同様に、PSGIに対応したWebアプリケーションフレームワークをテストすることができます。 77 | 78 | use Plack::Test; 79 | use MyCatalystApp; 80 | 81 | my $app = MyCatalystApp->psgi_app; 82 | 83 | test_psgi $app, sub { 84 | my $cb = shift; 85 | # ... 86 | }; 87 | done_testing; 88 | 89 | -------------------------------------------------------------------------------- /ja/14_use_plack_request.md: -------------------------------------------------------------------------------- 1 | ## Day 14: Plack::Requestを利用する 2 | 3 | Plack自身はWebフレームワークではありません。むしろ、PSGIサーバとミドルウェアの実装にplackup, Plack::Testなどのユーティリティが入ったツールキットのようなものです。 4 | 5 | Plackプロジェクトは[HTTP::Engine](http://search.cpan.org/perldoc?HTTP::Engine)プロジェクトから派生した側面もあり、リクエスト・レスポンススタイルのAPIを使ってウェブアプリケーションを開発する需要はあるようです。Plack::Requestは、PSGI環境変数やレスポンス配列に対して、簡単なオブジェクト指向APIを提供します。新しいミドルウェアを記述する際のライブラリとしても利用できますし、PlackをベースにしたWebフレームワークを記述する際のリクエスト/レスポンスのベースクラスとしても使えます。 6 | 7 | ### Plack::Request と Response を使う 8 | 9 | Plack::Request はPSGI環境変数へのラッパーであり、コードは以下のようになります。 10 | 11 | use Plack::Request; 12 | 13 | my $app = sub { 14 | my $req = Plack::Request->new(shift); 15 | 16 | my $name = $req->param('name'); 17 | my $res = $req->new_response(200); 18 | $res->content_type('text/html'); 19 | $res->content("Hello World"); 20 | 21 | return $res->finalize; 22 | }; 23 | 24 | HTTP::Engineからマイグレートする場合、変更する箇所は`shift`で取得したPSGI環境変数をPlack::Requestに渡し、最後に`finalize`を読んでPSGIレスポンスを取得することだけです。 25 | 26 | その他、`path_info`, `uri`, `param` などはHTTP::Engine::RequestやResponseとほぼ同等に動作します。 27 | 28 | ### Plack::Request と Plack 29 | 30 | Plack::Request はPlackディストリビューションに同梱されており、CPANから入手可能です。Plack::Requestをフレームワークで利用した場合、Plack標準のサーバ以外でも、PSGIに対応したサーバ実装であれば、どのサーバでも動作させることができます。 31 | 32 | ### Plack::Request を使うか否か 33 | 34 | Plack::Requestを利用したコードを`.psgi`に直接記述するのは、簡単なプロトタイピングやテストには便利ですが、ある程度の規模アプリケーション開発にはおすすめしません。1000行のコードをCGIスクリプトに直接書くようなもので、多くの場合はモジュールに分割していくのが正しい方法です。PSGIでも同様で、アプリケーションをクラスにまとめ、リクエストクラスとしてPlack::Requestをサブクラスするなどして、`.psgi`ファイルにはPlack::BuilderのDSLでミドルウェアを設定するようなコードとエントリポイントだけが含まれるようになるはずです。 35 | 36 | Plack::Request を利用して既存のフレームワークをPSGIインタフェースに対応させるためのライブラリとして使うのもよいでしょう。 37 | -------------------------------------------------------------------------------- /ja/15_authenticate_your_app_with_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 15: ミドルウェアでアプリケーションの認証 2 | 3 | Plackのミドルウェアは数多くリリースされていて、Plackに含まれているものや、CPANに単独でリリースされているものもあります。このAdvent Calendarをアップデートしている最中にも、多くのデベロッパーがミドルウェアを開発してCPANにアップロードしていました。 4 | 5 | 今日から、PSGI対応のアプリケーションにすぐ適用可能な、いくつかのおすすめミドルウェアを紹介します。 6 | 7 | ### Basic認証 8 | 9 | Plackミドルウェアはアプリケーションをラップするため、真価を発揮するのはHTTPレイヤでの前処理、後処理です。今日紹介するのはBasic認証を行うミドルウェアです。 10 | 11 | アプリケーションへのBasic認証の追加はいくつかの方法があります。フレームワークが対応していれば、提供されている機能で可能でしょう。例えばCatalystではCatalyst::Authentication::Credential::HTTPで対応がされています。多くのCatalyst拡張と同様、認証方法やユーザのストレージなど、様々な設定が可能になっています。 12 | 13 | また、認証をウェブサーバレイヤーで行うこともできます。たとえば、Apacheとmod\_perlでアプリケーションを動かしている場合、Apacheデフォルトのmod_authモジュールで認証を追加するのはとても簡単ですが、「ユーザをどのように認証するか」の設定は、カスタムのApacheモジュールを書くなどしない限り、限界があります。 14 | 15 | Plackミドルウェアでは、Webアプリケーションフレームワークがこうした機能を共有し、多くの場合シンプルなPerlコールバックで拡張することが可能です。Plack::Middleware::Auth::BasicはBasic認証に対してこうしたインタフェースを提供します。 16 | 17 | ### Plack::Middleware::Auth::Basic 18 | 19 | その他のミドルウェアと同様、Auth::Basicミドルウェアの利用はとても簡単です。 20 | 21 | use Plack::Builder; 22 | 23 | my $app = sub { ... }; 24 | 25 | builder { 26 | enable "Auth::Basic", authenticator => sub { 27 | my($username, $password) = @_; 28 | return $username eq 'admin' && $password eq 'foobar'; 29 | }; 30 | $app; 31 | }; 32 | 33 | 34 | このコードでアプリケーション`$app`にBaisic認証機能が提供されます。ユーザ名*admin*がパスワード*foobar*でサインインすることができます。認証に成功したユーザはPSGI環境変数`REMOTE_USER`にセットされ、アプリケーションから利用したりAccessLogミドルウェアからログに追加されます。 35 | 36 | コールバックベースの設定になるため、Kerberosのような認証システムと連携するのはAuthen::Simpleモジュールを使うと簡単にできます。 37 | 38 | use Plack::Builder; 39 | use Authen::Simple; 40 | use Authen::Simple::Kerberos; 41 | 42 | my $auth = Authen::Simple->new( 43 | Authen::Simple::Kerberos->new(realm => ...), 44 | ); 45 | 46 | builder { 47 | enable "Auth::Basic", authenticator => sub { 48 | $auth->authenticate(@_): 49 | }; 50 | $app; 51 | }; 52 | 53 | 同様に [Authen::Simpleバックエンド](http://search.cpan.org/search?query=authen+simple&mode=all) を使ってLDAPなどと連携することも可能です。 54 | 55 | ### URLMap 56 | 57 | URLMap は複数のアプリケーションを1つのアプリケーションに合成することができます。Authミドルウェアと組み合わせると、同一のアプリを認証モードと非認証モードで走らせることもできます。 58 | 59 | use Plack::Builder; 60 | my $app = sub { 61 | my $env = shift; 62 | if ($env->{REMOTE_USER}) { 63 | # Authenticated 64 | } else { 65 | # Unauthenticated 66 | } 67 | }; 68 | 69 | builder { 70 | mount "/private" => builder { 71 | enable "Auth::Basic", authenticator => ...; 72 | $app; 73 | }; 74 | mount "/public" => $app; 75 | }; 76 | 77 | このようにして同一の`$app`を/publicと/privateにマップし、/privateではBasic認証を必須とします。アプリケーションでは`$env->{REMOTE_USER}`をチェックすることで認証済みアクセスかどうか判別します。 78 | -------------------------------------------------------------------------------- /ja/16_adding_jsonp_support_to_your_app.md: -------------------------------------------------------------------------------- 1 | ## Day 16: アプリケーションにJSONPサポートを追加する 2 | 3 | 今日はとてもシンプルですが便利な例として、HTTPのベーシック機能だけではないミドルウェアを紹介します。 4 | 5 | ### JSONP 6 | 7 | [JSONP](http://ajaxian.com/archives/jsonp-json-with-padding) (JSON-Padding) はJSONをJavaScriptのコールバック関数にラップするテクノロジーの名前です。JSONベースのコンテンツをサードパーティサイトから`script`タグでクロスドメイン読み込みさせるために利用されています。 8 | 9 | ### Middleware::JSONP 10 | 11 | JSONでエンコードされたデータを`application/json`コンテンツ・タイプで返すアプリケーションがあったとします。簡単なインラインPSGIアプリケーションでは以下のようになります。 12 | 13 | use JSON; 14 | my $app = sub { 15 | my $env = shift; 16 | if ($env->{PATH_INFO} eq '/whatever.json') { 17 | my $body = JSON::encode_json({ 18 | hello => 'world', 19 | }); 20 | return [ 200, ['Content-Type', 'application/json'], [ $body ] ]; 21 | } 22 | return [ 404, ['Content-Type', 'text/html'], ['Not Found']]; 23 | }; 24 | 25 | これにJSONPサポートを追加するには、Middleware::JSONPを利用します: 26 | 27 | use Plack::Builder; 28 | builder { 29 | enable "JSONP"; 30 | $app; 31 | }; 32 | 33 | たった1行だけです!このミドルウェアは、レスポンスのcontent-typeが`application/json`であるかチェックし、かつ`callback`パラメータがURLにあるかどうかチェックします。"/whatever.json"といったリクエストはそのままJSONとして返されますが、"/whatever.json?callback=myCallback"のようなリクエストには、 34 | 35 | myCallback({"hello":"world"}); 36 | 37 | というデータが Content-Type `text/javascript` で返され、 Content-Length は自動で調整されます(すでに設定されていた場合)。 38 | 39 | ### フレームワーク 40 | 41 | JSONに加えてJSONPをサポートするのは、多くのフレームワークでは簡単なことですが、Middleware::JSONP はPSGI/Plackのレイヤで共通動作させるものを簡単につくれるよい例でしょう。 42 | 43 | もちろん、JSONPミドルウェアはJSONを出力するフレームワークであればどんなものでも適用することができます。Catalystであれば、 44 | 45 | package MyApp::View::JSON; 46 | use base qw( Catalyst::View::JSON ); 47 | 48 | package MyApp::Controller::Foo; 49 | sub hello : Local { 50 | my($self, $c) = @_; 51 | $c->stash->{message} = 'Hello World!'; 52 | $c->forward('MyApp::View::JSON'); 53 | } 54 | 55 | これに、Plack::BuilderでJSONPサポートを追加します。 56 | 57 | use MyApp; 58 | my $app = MyApp->psgi_app; 59 | 60 | use Plack::Builder; 61 | builder { 62 | enable "JSONP"; 63 | $app; 64 | }; 65 | 66 | JSONを出力する[Catalyst::View::JSON](http://search.cpan.org/perldoc?Catalyst::View::JSON) は私が書いたもので、JSONPコールバックはネイティブでサポートされていますが、やり方は1つではありません! 67 | -------------------------------------------------------------------------------- /ja/17_serving_static_files_from_your_application.md: -------------------------------------------------------------------------------- 1 | ## Day 17: 静的ファイルを配信する 2 | 3 | Day 5 ではplackupでカレントディレクトリからファイルを配信する方法を紹介しました。ミドルウェアの利用方法や、URLMapを使ったアプリケーションの合成方法を学んだので、Webアプリケーション開発に必須の機能を追加するのもとても簡単です。静的ファイルの配信です。 4 | 5 | ### あるパスから静的ファイルを配信する 6 | 7 | ほとんどのフレームワークが静的ファイルを配信する機能を備えています。PSGIに対応したフレームワークであれば、この機能をつける必要はもうありません。Staticミドルウェアを利用します。 8 | 9 | use Plack::Builder; 10 | 11 | my $app = sub { ... }; 12 | 13 | builder { 14 | enable "Static", path => qr!^/static!, root => './htdocs'; 15 | $app; 16 | } 17 | 18 | /staticではじまるリクエストにマッチすると、そのパスを"htdocs"にマップします。つまり、"/static/images/foo.jpg" は "./htdocs/static/images/foo.jpg" のファイルをレスポンスとして返します。 19 | 20 | 多くの場合、ディレクトリ名を変更したり、ローカルのパス名とオーバーラップしている場合があります。たとえば、/static/index.css へのリクエストを "./static-files/index.css" にマッピングするといった具合です。以下のようにします。 21 | 22 | builder { 23 | enable "Static", path => sub { s!^/static/!! }, root => './static-files'; 24 | $app; 25 | } 26 | 27 | 重要なのは、pathに正規表現 (`qr`) ではなく、コールバックを利用し、`sub { s/// }`で文字列を置換しています。コールバックはリクエストパスに対して実行され、その値は `$_` に保存されています。この例では、リクエストパスが "/static/" で始まるかテスト、その場合パスから削除し、残りのパスを "./static-files/" 以下に追加しています。 28 | 29 | 結果として、"/static/foo.jpg" は "./static-files/foo.jpg" となります。このパターンマッチに失敗したリクエストはそのまま元の `$app`にパススルーされます。 30 | 31 | ### URLMap と App::File でDIY 32 | 33 | Perlですから、やり方は一つではありません。Day 12で紹介したmountやURLMapの使い方を覚えていれば、App::Fileと`mount`を使う方法はより直感的に書けます。前の例は、以下のように書けます。 34 | 35 | use Plack::Builder; 36 | 37 | builder { 38 | mount "/static" => Plack::App::File->new(root => "./static-files"); 39 | mount "/" => $app; 40 | }; 41 | 42 | これをどう見るかは個人の主観でしょうが、個人的にはこちらのほうが簡潔だと思います。Staticミドルウェアのコールバックでは、リクエストパスのマッチングや置換がより柔軟に行えるため、こちらを使う方がよい場合もあるでしょう。どちらでも好きな方法を使ってください。 43 | -------------------------------------------------------------------------------- /ja/18_load_middleware_conditionally.md: -------------------------------------------------------------------------------- 1 | ## Day 18: ミドルウェアを条件ロードする 2 | 3 | いくつかのミドルウェアを紹介しました。便利なので全体に有効にできるものもあれば、ある条件下でのみ有効にしたいものもあるでしょう。どうやってそれを実現するかを解説します。 4 | 5 | ### ミドルウェアを条件分岐でロードする 6 | 7 | Conditional ミドルウェアはメタミドルウェアで、1つのミドルウェアを受け取り、それをランタイムの条件によって適用させるか決定するミドルウェアを返します。例をとってみましょう。 8 | 9 | * JSONP ミドルウェアをパスが /public で始まるときだけ有効にしたい 10 | * Basic認証をローカルIPからのリクエストについては無効にしたい 11 | 12 | WSGIやRackでこうした問題をどのように対処するかを調査していましたが、きれいな方法は見つかりませんでした。各ミドルウェアで有効にする条件を設定するのは、あまりいい方法だとは思えませんでした。 13 | 14 | ### Middleware::Conditional 15 | 16 | Conditional ミドルウェアはこうした問題を柔軟に解決します。 17 | 18 | use Plack::Builder; 19 | 20 | builder { 21 | enable_if { $_[0]->{REMOTE_ADDR} !~ /^192\.168\.0\./ } 22 | "Auth::Basic", authenticator => ...; 23 | $app; 24 | }; 25 | 26 | Plack::Builder に新しいキーワード `enable_if` が追加されています。ブロックを受け取り、リクエスト時に評価され(`$_[0]`がPSGI環境変数ハッシュ) ブロックがtrueを返した場合、ミドルウェアでラップされたアプリケーションを、そうでない場合はなにもせずにパススルーします。 27 | 28 | この例ではリクエストがローカルネットワークから来ているかチェックし、そうでない場合にBasic認証ミドルウェアを適用しています。 29 | 30 | Conditional は普通のミドルウェアとして実装されているので、内部的には以下のコードと同等です: 31 | 32 | use Plack::Middleware::Conditional; 33 | use Plack::Middleware::Auth::Basic; 34 | 35 | my $app = sub { ... }; 36 | 37 | $app = Plack::Middleware::Conditional->wrap($app, 38 | builder => sub { 39 | Plack::Middleware::Auth::Basic->wrap( 40 | $_[0], authenticator => ..., 41 | ); 42 | }, 43 | condition => sub { 44 | my $env = shift; 45 | $env->{REMOTE_ADDR} !~ /^192\.168\.0\./; 46 | }, 47 | ); 48 | 49 | ですがこれを毎回書くのは退屈ですので、DSL版をおすすめします :) 50 | -------------------------------------------------------------------------------- /ja/19_cascade_multiple_applications.md: -------------------------------------------------------------------------------- 1 | ## Day 19: 複数のアプリケーションをカスケードする 2 | 3 | Conditionalミドルウェア(Day 18)とURLMapアプリケーション(Day 12)には共通点があります。これら自体がPSGIアプリケーションですが、既存のPSGIアプリケーションやミドルウェアを受け取ってそれらにディスパッチするものです。これがPSGIのアプリケーションやミドルウェアの美しい点で、今日紹介するのもその一例です。 4 | 5 | ### 複数のアプリケーションをカスケードする 6 | 7 | 複数のアプリケーションを順番に実行し、成功が返ってくるまでカスケードするのは便利なことがあります。デザインパターンではChain of responsibilityと呼ばれ、mod_perlハンドラなどのWebアプリケーションでも応用されています。 8 | 9 | ### Cascade Application 10 | 11 | Plack::App::Cascade は複数のPSGIアプリケーションを合成し、順番に実行して404以外のレスポンスが返ってくるまでトライします。 12 | 13 | use Plack::App::Cascade; 14 | use Plack::App::File; 15 | use Plack::App::URLMap; 16 | 17 | my @paths = qw( 18 | /home/www/static 19 | /virtualhost/example.com/htdocs/static 20 | /users/miyagawa/public_html/images 21 | ); 22 | 23 | my $app = Plack::App::Cascade->new; 24 | for my $path (@paths) { 25 | my $file = Plack::App::File->new(root => $path); 26 | $app->add($file); 27 | } 28 | 29 | my $map = Plack::App::URLMap->new; 30 | $map->mount("/static" => $app); 31 | $map->to_app; 32 | 33 | このアプリケーションを URLMap を使って /static にマップしています。すべてのリクエストは`@paths`に設定された3つのディレクトリをApp::Fileで順番にトライし、ファイルが見つかったらそれをレスポンスとして返します。スタティックファイルを複数のディレクトリからカスケードして返すのに便利です。 34 | 35 | ### アプリケーションをカスケード 36 | 37 | use CatalystApp; 38 | my $app1 = CatalystApp->psgi_app; 39 | 40 | use CGI::Application::PSGI; 41 | use CGIApp; 42 | my $app2 = sub { 43 | my $app = CGIApp->new({ 44 | QUERY => CGI::PSGI->new($_[0]), 45 | }); 46 | CGI::Application::PSGI->run($app); 47 | }; 48 | 49 | use Plack::App::Cascade; 50 | Plack::App::Cascade->new(apps => [ $app1, $app2 ])->to_app; 51 | 52 | この例では2つのアプリケーション、1つはCatalyst もう1つはCGI::Applicationを用意し、順番に実行します。/what/ever.cat をCatalystアプリケーション、/what/ever.cgiapp をCGI::Application側で処理する、といった際にこのようにカスケードさせることができます。 53 | 54 | とはいえ、クレイジーなアイデアのように聞こえるかもしれません。実際にはURLMapで違うパスにmountするほうが現実的でしょうが、こういうこともできるということで :) 55 | -------------------------------------------------------------------------------- /ja/20_access_your_local_app_from_the_internet.md: -------------------------------------------------------------------------------- 1 | ## Day 20: ローカルアプリケーションにインターネットからアクセスする 2 | 3 | (**注意**: reverseHTTP サービスは2012年現在停止しています) 4 | 5 | 最近では、ノートPC上でアプリケーションを開発し、ローカルのIPアドレスを使ってテストをするのが簡単です。こうして開発してローカルで動いているアプリケーションに対して、リモートで仕事をしている同僚や、webhookなどのテストをするために、インターネットからアクセスしたいことがあります。 6 | 7 | ### Reverse HTTP 8 | 9 | この問題を解決するにはいくつかのソリューションがありますが、一つ面白いやりかたが[ReverseHTTP](http://www.reversehttp.net/)を使った方法です。ReverseHTTPはクライアント・サーバ・ゲートウェイ間のシンプルなプロトコルで、HTTP/1.1の拡張を用いています。便利なことに、reversehttp.netでデモゲートウェイが動作しているため、自分でサーバを立てたりすることなく、デモに利用することができます。 10 | 11 | これが実際どのように動作するのか興味が有る人は、[仕様書](http://www.reversehttp.net/specs.html)に目を通してみるとよいでしょう。*Reverse* HTTP という名前の通り、動作させるアプリケーション・サーバがロングポールのHTTPクライアントになり、ゲートウェイ・サーバがインターネット経由でアクセスされたリクエストをレスポンスとして返します。 12 | 13 | ### Plack::Server::ReverseHTTP 14 | 15 | [Plack::Server::ReverseHTTP](http://search.cpan.org/~miyagawa/Plack-Server-ReverseHTTP-0.01/) はPlackのバックエンドサーバ実装で、ReverseHTTPプロトコル上でPSGIアプリケーションを実行し、外部インターネットから、ローカルで動作しているPSGIアプリケーションへのアクセスを可能にします。 16 | 17 | ReverseHTTPを利用するには、必要なモジュールをCPANからインストールし、以下のコマンドを実行します。 18 | 19 | > plackup -s ReverseHTTP -o yourhostname --token password \ 20 | -e 'sub { [200, ["Content-Type","text/plain"], ["Hello"]] }' 21 | Public Application URL: http://yourhostname.www.reversehttp.net/ 22 | 23 | `-o` は`--host`のエイリアスで、利用するサブドメイン(ラベル)を指定します。`--token`は登録したラベルを使うためのパスワードとして利用します。指定しないこともできますが、この場合、登録したラベルを後から誰でも利用可能になります。 24 | 25 | コンソールに出力されたアドレス(URL)をブラウザで開くと、Helloが表示されます。 26 | 27 | ### フレームワークからの利用 28 | 29 | もちろんPSGIサーバのバックエンドですから、どんなフレームワークでも利用することができます。Catalystアプリに対して実行するには、 30 | 31 | > catalyst.pl MyApp 32 | > cd MyApp 33 | > plackup -o yourhost --token password myapp.psgi 34 | 35 | たったこれだけです。 デフォルトのCatalystアプリケーションが http://yourhost.reversehttp.net/ というURLで、インターネット経由でどこからでもアクセス可能です。 36 | 37 | ### 注意 38 | 39 | ReverseHTTP.netのゲートウェイサービスは実験的なものであり、SLAのような保証はありません。プロダクション環境などでの利用は避けたほうが良いでしょう。ちょっとしたアプリを友達に見せるときなどに、SSHやVPNトンネリングなどを必要とせず利用できるのは便利ですね。 40 | -------------------------------------------------------------------------------- /ja/21_lint_your_application_and_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 21: アプリケーションとミドルウェアのLintチェック 2 | 3 | 既存のWebフレームワークをPSGIに対応させたり(Day 8)、新しくアプリケーションをPSGIインタフェースで開発する話をしてきましたが、エラー処理についてはまだ解説していませんでした。 4 | 5 | ### エラー処理 6 | 7 | plackupではデフォルトで素敵なスタックトレース(Day 3)が有効になっています。ユーザのアプリケーションが例外を投げた場合、これを捕捉してエラーページを表示します。もし、ミドルウェアやアプリケーションフレームワークのアダプターそのものにバグやエラーがあった場合はどうでしょう? 8 | 9 | 以下のコードを走らせてみます: 10 | 11 | > plackup -e 'sub { return [ 0, {"Content-Type","text/html"}, "Hello" ] }' 12 | 13 | 繰り返しですが、生のPSGIインタフェースでアプリケーションを書くことは本来ありませんが、ミドルウェアやフレームワークのアダプターにバグがあった場合のシミュレーションには十分です。 14 | 15 | このアプリケーションにブラウザからアクセスすると、サーバは以下のようなエラーとおもにクラッシュします。 16 | 17 | Not an ARRAY reference at lib/Plack/Util.pm line 145. 18 | 19 | これは、レスポンスのフォーマットがPSGIインタフェース的に不正であるためです。ステータスコードが不正ですし、ヘッダが配列でなくハッシュリファレンスになっており、またボディも配列でなくプレーンな文字列になっています。 20 | 21 | ### Lint ミドルウェア 22 | 23 | こうしたエラーを各サーバでチェックすることは可能ですが、あまり綺麗な方法ではありません。コードの重複が起きますし、実際にプロダクション環境でこうしたチェックをすることはパフォーマンスの観点からもよくありません。こうしたアプリケーション、サーバやミドルウェアの妥当性はテストや開発時に検証し、プロダクションでは無効にするのがよいでしょう。 24 | 25 | Middleware::Lint はリクエストやレスポンスが妥当なPSGIインタフェースかをチェックします。PSGIアプリケーションをLintミドルウェアを有効にして実行します。 26 | 27 | > plackup -e 'enable "Lint"; sub { return [ 0, { "Content-Type"=>"text/html" }, ["Hello"] ] }' 28 | 29 | (**注意**: 執筆時はLintがplackupデフォルトではありませんでしたが、現在はdevelopmentモードでは有効になるため、別途指定する必要はありません) 30 | 31 | こうすると、アプリケーションにアクセスすると以下のようなエラーとスタックトレースが表示されます。 32 | 33 | status code needs to be an integer greater than or equal to 100 at ... 34 | 35 | Lintミドルウェアがレスポンスが正しくPSGI仕様に沿っているかチェックするためです。 36 | 37 | フレームワークのアダプタやミドルウェアを開発する際は、必ずLintミドルウェアを有効にすることを忘れないでください。 38 | 39 | ### 新しくPSGIサーバを書く 40 | 41 | Lintミドルウェアはリクエスト、レスポンス両方のバリデートを実装していますので、新しくPSGIサーバを開発する際にもチェックが利用できます。ただし、サーバの開発者でさらにPSGI仕様を正しく実装しているかテストしたいには、Plack::Test::Suiteを利用するとよいでしょう。 42 | 43 | 実際にPlack::Test::Suiteを利用しているコードがPlackの`t/Plack-Handler`ディレクトリ内に含まれています。ここにはPSGIサーババックエンドをテストするためのテストスイートが用意されていて、既存のPlack::Handlerはすべてこのテストをパスするように書かれています。 44 | -------------------------------------------------------------------------------- /ja/22_discover_more_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 22: さらにミドルウェアの紹介 2 | 3 | この連載も終了に近づいて来ましたが、すべてのミドルウェアを紹介するには紙面が足りないようです。今日はまだ紹介していないいくつかの素晴らしいミドルウェアを簡単に紹介しましょう。 4 | 5 | ### ErrorDocument 6 | 7 | アプリケーションが500エラーを出した際や、認証が失敗した際のForbiddenページを出力する際、レスポンスコードに応じて、カスタムのエラーページを表示したいことがあります。ErrorDocumentは、Apacheの同名ディレクティブと同様、この処理を実装しています。 8 | 9 | builder { 10 | enable "ErrorDocument", 500 => "/path/to/error.html"; 11 | $app; 12 | }; 13 | 14 | 任意のエラーコードをスタティックファイルのパスにマップします。開発時にはStackTraceミドルウェア、プロダクションではErrorDocumentできれいなエラーページを表示するとよいでしょう。 15 | 16 | このミドルウェアはPlackディストリビューションに付属しています。 17 | 18 | ### Session 19 | 20 | Rackでは`rack.session`をRackの環境変数で標準定義しており、Hashオブジェクトのインタフェースを提供しています。PSGIではこれを標準には取り入れていませんが、アイデアと実装についてはかなりインスパイアされています。 21 | 22 | builder { 23 | enable "Session", store => "File"; 24 | $app; 25 | }; 26 | 27 | デフォルトではセッションデータはオンメモリのハッシュに保存されますので、preforkやマルチプロセス型のサーバではうまく動作しません。[CHI](http://search.cpan.org/perldoc?CHI)などのエンジンが付属していて、またその他のストレージエンジンに対するインタフェースを記述することで、Authミドルウェアなどと同様、拡張を簡単に行うことができます。 28 | 29 | セッションデータは`psgix.session`というPSGI環境変数にハッシュリファレンスとして格納されています。アプリケーションやフレームワークはこのハッシュに直接アクセスしてもよいですし、Plack::Sessionモジュールを使ってラッパーを定義することも可能です。例えば、Tatsumakiフレームワークでは以下のように利用できます。 30 | 31 | # Tatsumaki app 32 | sub get { 33 | my $self = shift; 34 | my $uid = $self->request->session->get('uid'); 35 | $self->request->session->set(last_access => time); 36 | ... 37 | } 38 | 39 | Sessionミドルウェアの利点として、PSGIアプリケーション間でのセッション共有が可能になるということです。フレームワークのアダプターによっては、ユーザアプリケーションからPSGI環境変数へのアクセスが許可されていない場合がありますが、こうした問題は徐々に解消されていくはずです。 40 | 41 | SessionミドルウェアはStevan Littleによって開発され、[github](http://github.com/stevan/plack-middleware-session) とCPANから入手できます。 42 | 43 | ### Debug 44 | 45 | こちらも[Rack-bug](http://github.com/brynary/rack-bug)と[django debug toolbar](http://github.com/robhudson/django-debug-toolbar)からのインスパイアで、このミドルウェアを有効にすると、デバッグ用の「パネル」が表示され、リクエストに関するデータやアナリティクスが表示されます。 46 | 47 | 標準のパネルにはTimer(リクエスト時間)、Memory(メモリー使用量)、Request(リクエストヘッダ情報)、Response(レスポンスヘッダ)などが含まれています。 48 | 49 | builder { 50 | enable "Debug"; 51 | $app; 52 | }; 53 | 54 | 利用はたったこれだけで、利用するパネルを制限したり、標準以外のものを追加するには、`panels`パラメータを指定します。 55 | 56 | DBIのクエリプロファイラや、Catalystのログなどの拡張パネルはCPANと[github](http://github.com/miyagawa/plack-middleware-debug/)から入手可能です。 57 | 58 | ### Proxy 59 | 60 | HTTPリクエストをインターネットまたはローカルネットワークで動いている別のアプリケーションにプロキシしたいことがあります。たとえば、JSONPをサポートしていないJSON APIをプロキシしたい場合など、Cross Origin Policy上アクセスできないため、プロキシを立てる必要があります。また、ローカルで動く別のアプリケーションに対してフロントエンド的に動作させるリバースプロキシ的な利用法もあるでしょう。 61 | 62 | Plack::App::Proxyはこれらを可能にします。 63 | 64 | use Plack::App::Proxy; 65 | use Plack::Builder; 66 | 67 | my $app = Plack::App::Proxy->new(host => '192.168.0.2:8080')->to_app; 68 | 69 | builder { 70 | mount "/app" => $app; 71 | }; 72 | 73 | Plack::App::ProxyはLee Aylwardによって[github](http://github.com/leedo/Plack-App-Proxy)で開発されています。 74 | 75 | ### さらに 76 | 77 | さらに多くのミドルウェアがPlackディストリビューションに付属し、またCPANから検索することができます。すべてのミドルウェアがすぐに使えるというわけではないかもしれませんが、PSGIをサポートするフレームワークで共有できるというすばらしいメリットがあります。 78 | -------------------------------------------------------------------------------- /ja/23_write_your_own_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 23: ミドルウェアを書く 2 | 3 | ミドルウェアの紹介の最後として、自分で書いてみることにしましょう。 4 | 5 | ### ミドルウェアを書く 6 | 7 | PSGIミドルウェアは、通常のPSGIアプリケーションと同様に動作しますが、実際のPSGIアプリケーションをラップするため、サーバからみるとアプリケーションのように見え、ラップされたアプリケーションからはサーバのように振る舞います。 8 | 9 | User-Agentを偽装するためのシンプルなミドルウェアは以下のようになります。 10 | 11 | # Wrapped application 12 | my $app = sub { 13 | my $env = shift; 14 | my $who = $env->{HTTP_USER_AGENT} =~ /Mobile Safari/ ? 'iPhone' : 'non-iPhone'; 15 | return [ 200, ['Content-Type','text/html'], ["Hello $who"] ]; 16 | }; 17 | 18 | # Middleware to wrap $app 19 | my $mw = sub { 20 | my $env = shift; 21 | $env->{HTTP_USER_AGENT} .= " (Mobile Safari)"; 22 | $app->($env); 23 | }; 24 | 25 | アプリケーションはリクエストがiPhoneブラウザ (*Mobile Safari*) からきた場合のみ、"Hello iPhone" を表示します。このミドルウェアは Mobile Safari 文字列をすべてのリクエストに追加します。このアプリケーションを実行して、任意のブラウザで表示すると、"Hello iPhone" が表示され、アクセスログには以下のように表示されるでしょう。 26 | 27 | 127.0.0.1 - - [23/Dec/2009 12:34:31] "GET / HTTP/1.1" 200 12 "-" "Mozilla/5.0 28 | (Macintosh; U; Intel Mac OS X 10_6_2; en-us) AppleWebKit/531.21.8 (KHTML, like 29 | Gecko) Version/4.0.4 Safari/531.21.10 (Mobile Safari)" 30 | 31 | " (Mobile Safari)" がUser-Agent文字列に追加されています。 32 | 33 | ### 再利用可能なMiddlewareにする 34 | 35 | `.psgi`にインラインで記述する方法は、一度きりの利用をするには大変便利ですが、多くの場合、一般化して他のアプリケーションでも再利用できるようにしたいでしょう。Plack::Middlewareを利用すると、これが可能になります。 36 | 37 | package Plack::Middleware::FakeUserAgent; 38 | use strict; 39 | use parent qw(Plack::Middleware); 40 | use Plack::Util::Accessor qw(agent); 41 | 42 | sub call { 43 | my($self, $env) = @_; 44 | $env->{HTTP_USER_AGENT} = $self->agent; 45 | $self->app->($env); 46 | }; 47 | 48 | 1; 49 | 50 | たったこれだけです。Plack::Middlewareを継承し、必要とするオプションをAccessorで定義し、`call`メソッドを実装して`$self->app`に処理をデリゲートします。このミドルウェアはPlack::BuilderのDSL(Day 11)と互換性があるので、 51 | 52 | use Plack::Builder; 53 | 54 | builder { 55 | enable "FakeUserAgent", agent => "Mozilla/3.0 (MSIE 4.0)"; 56 | $app; 57 | }; 58 | 59 | としてすべてのリクエストをInternet Explorer 4から来たように偽装することができます。また、条件によって有効にするには`enable_if` (Day 18)を使うことができます。 60 | 61 | ### リクエストの後処理 62 | 63 | 上で紹介した例はリクエストの`$env`に対して前処理を行いました。レスポンスに対しての場合はどうでしょう。ほとんど同じで、 64 | 65 | my $app = sub { ... }; 66 | 67 | # Middleware to fake status code to 500 68 | my $mw = sub { 69 | my $env = shift; 70 | my $res = $app->($env); 71 | $res->[0] = 500 unless $res->[2] == 200; 72 | $res; 73 | }; 74 | 75 | これはとても実用的でないミドルウェアで、すべての200以外のステータスコードを500に変更します。使う場面は想像できませんが、例としては十分でしょう。 76 | 77 | 多くのサーバがPSGIの[streaming interface](http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html)を実装して、レスポンスのストリームを可能にしていますが、このミドルウェアはそのインタフェースには対応していません。このインタフェースに対応するコードを各ミドルウェアで書くのは効率的でないので、Plack::Middlewareではそのユーティリティを用意しています。 78 | 79 | package Plack::Middleware::BadStatusCode; 80 | use strict; 81 | use parent qw(Plack::Middleware); 82 | 83 | sub call { 84 | my($self, $env) = @_; 85 | my $res = $self->app->($env); 86 | $self->response_cb($res, sub { 87 | my $res = shift; 88 | $res->[0] = 500 unless $res->[0] == 200; 89 | }); 90 | } 91 | 92 | 1; 93 | 94 | レスポンス `$res` を`response_cb` に渡し、レスポンスをラップするコールバックを渡します。このユーティリティが、ストリーミングインタフェースへの対応を面倒みてくれます。 95 | 96 | ### 名前空間 97 | 98 | この例では Plack::Middleware 名前空間にモジュールを定義しましたが、必ずしもこの名前空間を使う必要はありません。多くのPSGIアプリケーションで利用できる一般的なものであれば、この名前空間で問題ありませんが、特殊な用途に利用するものや、特定のフレームワークのみで動作するものなどは、その他の名前空間を利用し、 99 | 100 | package MyFramework::Middleware::Foo; 101 | use parent qw(Plack::Middleware); 102 | 103 | +(プラス)記号をつかって名前空間を指定します。 104 | 105 | enable '+MyFramework::Middleware::Foo', ...; 106 | 107 | DSLでないAPIを使う場合には、 108 | 109 | $app = MyFramework::Middleware::Foo->wrap($app, ...); 110 | 111 | とすれば問題なく動作します。 112 | -------------------------------------------------------------------------------- /ja/24_wrap_up.md: -------------------------------------------------------------------------------- 1 | ## Day 24: まとめ 2 | 3 | これが最後のカレンダーエントリになります。 4 | 5 | ### Best Practices 6 | 7 | PlackとPSGIはまだ始まって間もないプロジェクトですが、新しくPSGI対応のアプリケーションやフレームワークを書く際のベストプラクティスがまとまってきています。 8 | 9 | 新しくフレームワークを書く際には、ユーザアプリケーションからPSGI環境変数へアクセス出来る方法を提供してください。直接アクセスできるのでも、アクセサ経由でもかまいません。DebugやSessionなど、PSGI環境変数を利用するミドルウェアを利用できるようになります。 10 | 11 | `.psgi`ファイルでPlack::Requestを使ってアプリケーションを記述することは避けてください。スタイルの問題もありますが、アプリケーションは適切なクラスやオブジェクトに分割し、再利用やテスト(Day 13)可能にするのがよい開発手法です。`.psgi`ファイルは数行のブートストラップコードと、Builderによるミドルウェア設定のみになるでしょう。 12 | 13 | Plack::App名前空間を使用することは避けてください。Plack::App名前空間はラッパーとして動作しないミドルウェア用に予約されたもので、Proxy, File, CascadeやURLMapなどが良い例です。Plackを使ってブログアプリを書いたからといって、Plack::App::Blogのようなものは**決して**使わないでください。ソフトウェアの名前は何をするかによって決められるべきで、何を使って書かれたかは関係ないはずです。 14 | 15 | ### 探索 16 | 17 | Plackデベロッパーは[github](http://github.com/)でコードを開発しています。[github レポジトリを"Plack"で検索](http://github.com/search?langOverride=&q=plack&repo=&start_value=1&type=Repositories)すると面白いものが見つかるかもしれません。CPANをPlackやPSGIで検索しても、ミドルウェアやPSGIに対応しているツールを見つけることができるでしょう。 18 | 19 | ### 開発チームにコンタクトする 20 | 21 | Plackはまだまだ若いプロジェクトで、まだまだ改善の余地があります。改善したい点、要望、バグをみつけたら、開発チームにコンタクトするなり、githubでforkしてpull requestを送ってください! 22 | 23 | IRCチャンネル irc.perl.org の #plack でチャットをしていますし、[メーリングリスト](http://groups.google.com/group/psgi-plack) や[githubのissue tracker](http://github.com/plack/Plack/issues)でコンタクトすることが可能です。 24 | -------------------------------------------------------------------------------- /ja/README.md: -------------------------------------------------------------------------------- 1 | See [the Plack Handbook website](http://handbook.plackperl.org/) for more details about this book. --------------------------------------------------------------------------------