Plack-1.0030/000755 000765 000024 00000000000 12244057435 013577 5ustar00miyagawastaff000000 000000 Plack-1.0030/benchmarks/000755 000765 000024 00000000000 12244057435 015714 5ustar00miyagawastaff000000 000000 Plack-1.0030/Changes000644 000765 000024 00000134367 12244057435 015110 0ustar00miyagawastaff000000 000000 Go to http://github.com/plack/Plack/issues for the roadmap and known issues. 1.0030 2013-11-23 08:54:01 CET [IMPROVEMENTS] - Middleware::LogDispatch stringifies objects (oalders) #427 - Encode wide character strings as UTF-8 in HTTP::Server::PSGI #429 - Optimize Plack::Response->finalize performance (kazeburo) #433 - Optimize mount() performance in Plack::App::URLMap [BUG FIXES] - ErrorDocument: remove Content-Encoding and Transfer-Encoding (kazeburo) #430 - Fix harakiri test hang on win32 (wchristian) #431 - Handler::FCGI: Restore --keep-stderr option (mauzo) #432 1.0029 2013-08-22 14:05:44 PDT [NEW FEATURES] - Plack::Test now has a simpler object-oriented interface that doesn't take multiple callbacks. #420 [IMPROVEMENTS] - bump dependencies for Test::TCP and HTTP::Tiny - Set no_proxy for HTTP::Tiny in tests (kazeburo) [INCOMPATIBLE CHANGES] - Split HTTP::Server::Simple handler from Plack distribution and merge to HTTP-Server-Simple-PSGI distribution 1.0028 2013-06-15 01:42:52 PDT [IMPROVEMENTS] - Skip cgi related tests for Win32 (chorny) #413 - Skip tests that could potentially write empty bytes, which could cause issues on some servers on local sockets with HTTP::Tiny - Skip tests that require HTTP::Cookies, if not available #414 1.0027 2013-06-13 21:30:12 PDT [IMPROVEMENTS] - Not a dev release, including XS free version of Plack::Test* - Fix cgibin tests that often fail on Win32 #375 1.0026 2013-06-12 23:00:21 PDT [INCOMPATIBLE CHANGES] - use HTTP::Tiny in Plack::Test::Suite and Plack::Test::Server rather than skipping it. 1.0025 2013-06-12 13:08:58 PDT [INCOMPATIBLE CHANGES] - No XS! Eliminates dependency to LWP::UserAgent by making it completely optional for testing. If you run Plack::Test with Server implemenetation or run Plack::Test::Suite (for PSGI handlers) without LWP installed, the tests will automatically be skipped. This removes the eventual sub-dependency to HTML::Parser, which is the only XS dependency in Plack. #408 [IMPROVEMENTS] - Fixed the warning in OO usage of Plack::Builder (doy) #407 - Shotgun loader now dies if used in Win32 since it leaks memory #320, #400 - Suppress warnings for Test::TCP (kazeburo) #406 - $res->to_app shortcut (ether) #409 1.0024 2013-05-01 10:05:56 PDT [IMPROVEMENTS] - Fix warnings for Plack::App::WrapCGI (frioux) - Ignore emacs lock file from restarter (maio) - Add documentation for environment variable in Auth::Basic - Some Metadata cleanup and Travis CI 1.0023 2013-04-08 11:13:11 PDT [IMPROVEMENTS] - Use Apache::LogFormat::Compiler in AccessLog (kazeburo) 1.0022 2013-04-02 12:37:42 PDT [BUG FIXES] - Fixed a major bug in 1.0020-1.0021 where posix_default prevents arbitrary arguments for plackup-compat (e.g. starman) to handle them (Thanks to justnoxx) Starman#66 [IMPROVEMENTS] - Fixed test warnings (Keedi Kim) 1.0021 2013-04-02 11:20:00 PDT - Repackage with Milla v0.9.6 #392 1.0020 2013-04-01 19:34:54 PDT [INCOMPATIBLE CHANGES] - Enable posix_default and gnu_compat in plackup Getopt, so that ambiguous option names do not match with long options accidentally [IMPROVEMENTS] - Document fix for the AccessLog (ether) - Special-case Content-Length and Content-Type for %{}i in AccessLog format #387 1.0019 2013-04-01 17:58:25 PDT - Trial release with Milla 1.0018 Fri Mar 8 10:43:45 PST 2013 [IMPROVEMENTS] - Performance boost in Plack::Request#query_parameters (lestrrat) - Added custom log formats for %m, %U, %q and %H (Hiroshi Sakai) - Fixed warnings in SimpleContentFilter (earino) [DOCUMENTATION] - Added docs about plackup --path - Added docs about using manager object in Plack::Handler::FCGI 1.0017-TRIAL Thu Feb 7 19:21:24 PST 2013 [INCOMPATIBLE CHANGES] - Gives you warnings when you use one of Plack::App objects in `plackup -e` or in .psgi files but forgot to call ->to_app to make it a PSGI application (#369) Still automatically converts them for backward compatibility, but in the loading time inside Plack::Builder. [BUG FIXES] - chdir to the CGI path when executing CGIBin (#338, #368) 1.0016 Thu Jan 31 13:21:14 PST 2013 [SECURITY] - Fixed directory traversal bug in Plack::App::File on win32 environments [INCOMPATIBLE CHANGES] - Updated Plack::Builder OO interface to be more natural. Still keeps backward compatible to the old ->mount() and ->to_app() interface. [NEW FEATURES] - Static middleware 'path' callback now takes $env as a 2nd argument (avar) - Static middleware takes 'content_type' callback to determine custom MIME (pstadt) [IMPROVEMENTS] - Fixed regexp warning for blead (doy) - Documentation update for AccessLog::Timed to suggest Runtime (ether) - Ignore vim swap files on restarter (nihen) - Major doucmentation overhaul on Apache2 startup files (rkitover, avar) 1.0015 Thu Jan 10 15:19:17 PST 2013 [BUG FIXES] - Fixed Lint complaining about Latin-1 range characters stored internally with utf8 flag on (Mark Fowler) - HTTP::Message::PSGI::res_from_psgi now always returns empty string for an empty response body, so streamed responses are consistent with non-streamed (ether) 1.0014 Mon Dec 3 10:27:43 PST 2012 [BUG FIXES] - Fixed Hash order in tests for perl 5.17 (doy) - Fixed StackTrace tests to run with Devel::StackTrace [IMPROVEMENTS] - Plack::Middleware::AccessLog can now log the worker pid and server port (ether) 1.0013 Wed Nov 14 19:46:49 PST 2012 [BUG FIXES] - Make sure psgi.input is seeked even when the input is buffered (Getty, leedo) - Delete invalid (empty) CONTENT_LENGTH and CONTENT_TYPE in FCGI (Getty, leedo) 1.0012 Wed Nov 14 12:00:17 PST 2012 [IMPROVEMENTS] - Make conditional middleware work with initialization without an app (doy) - Added force option to BufferedStreaming 1.0011 Sun Nov 11 11:05:30 PST 2012 [BUG FIXES] - Fix bad Content-Length that could be caused with mod_perl (avar) - Allow an empty PATH_INFO in Lint per PSGI spec 1.0010 Fri Nov 2 13:30:50 PDT 2012 [IMPROVEMENTS] - Added vim .swp files to the default ignore list in Restarter - Check if PATH_INFO begins with / in Lint 1.0009 Tue Oct 23 00:57:16 PDT 2012 [BUG FIXES] - Correct fix to address drive letters for Win32 1.0008 Mon Oct 22 18:52:29 PDT 2012 [BUG FIXES] - Allow drive letters for absolute paths for plackup and load_psgi #343 1.0007 Sat Oct 20 23:20:20 PDT 2012 [IMPROVEMENTS] - Fix test failures with HTTP::Message 6.06. #345 - relaxed plackup -R ignore files and directoris. #260 1.0006 Thu Oct 18 16:06:15 PDT 2012 [INCOMPATIBLE CHANGES] - plackup foo.psgi will not search the file in @INC anymore before the current directory See https://github.com/plack/Plack/pull/343 for details (miyagawa) [NEW FEATURES] - plackup --path /foo will mount the application under /path (mattn) [BUG FIXES] - AccessLog: Fix the timezon offset for certain timezones - ErrorDocument: support streaming interface 1.0005 Tue Oct 9 13:33:47 PDT 2012 [NEW FEATURES] - Support psgix.cleanup handlers in Apache2 (avar) - Added REMOTE_PORT environment variable to HTTP::Server::PSGI (dex4er) [IMPROVEMENTS] - Documentation fix for multiple cookie values (miyagawa) - Delete MOD_PERL environment variable for better compatibilities (avar) - Split out Plack::TempBuffer as a standalone Stream::Buffered module (doy) - Bump Test::TCP dep 1.0004 Thu Sep 20 08:36:11 JST 2012 [NEW FEATURES] - Added psgix.harakiri support in HTTP::Server::PSGI [IMPROVEMENTS] - Preload TempBuffer modules (avar) - Documentation fixes (autarch) 1.0003 Wed Aug 29 13:44:53 PDT 2012 [BUG FIXES] - Fix Basic authentication error in case password contains a colon #319 - Fix AccessLog middleware in platforms where %z strftime is not supported #318 - Escape $_ in Plack::Request path method due to a possible URI::Escape bug 1.0002 Mon Aug 13 17:04:25 PDT 2012 [NEW FEATURES] - Added --no-default-middleware option to plackup #290 [BUG FIXES] - Use C locale for AccessLog strftime #313 - Escape Plack::Request URI path using RFC 3986 definition (ssmccoy) [IMPROVEMENTS] - Documentation improvements (ether, Tom Heady) - Skip displaying ".." in Plack::App::Directory #277 - Document load_class() doesn't validate user input. #285 1.0001 Thu Jul 26 16:24:13 PDT 2012 [INCOMPATIBLE CHANGES] - Deleted lots of code, methods and warnings that have been deprecated since 0.99 (which should have been done in the 1.0000 release) [DEVELOPERS] - Added bootstrap script to install devel dependencies [IMPROVEMENTS] - Fixed version numbers in some of the modules that have their own $VERSION 1.0000 Thu Jul 19 18:59:18 PDT 2012 - This be 1.0! (Same as 0.9991) 0.9991 Thu Jul 19 17:27:52 PDT 2012 [NEW FEATURES] - Added IIS7 fix middleware (t0m) 0.9990 Wed Jul 18 11:12:07 PDT 2012 [INCOMPATIBILE CHANGES] - Plack::Request changes the way it parses QUERY_STRING for valueless keys such as "?a&b=1". Now "a" becomes part of query_parameters with empty string as its value (yannk) [IMPROVEMENTS] - Support max-age options in Plack::Response cookies (remorse) - Pass correct protocol from HTTP::Server::PSGI to display https URL correctly (siracusa) - Copy Authorization header from FastCGI handler (ray1729) - Stop special casing COOKIE environment variable in Plack::Request headers (doy) 0.9989 Thu Jun 21 13:39:11 PDT 2012 [IMPROVEMENTS] - Support streaming in Head middleware (wreis) - Document middleware prefixing (Jon Swartz) - Make Basic authentication detection case insensitive per RFC (Mark Fowler) - Added backlog option to FCGI handler (xaicron) 0.9988 Fri May 11 12:25:09 CEST 2012 [BUG FIXES] - Fixes HTTP_HOST in HTTP::Message::PSGI #287 (doy) 0.9987 Thu May 10 07:06:32 CEST 2012 [IMPROVEMENTS] - Support streaming in AccessLog::Timed (Peter Makholm) - Support streaming in ErrorDocument - Removed UTF8 hack in HTTP::Message::PSGI. Depends on URI.pm 1.59 (wreis) - Set Host headers correctly in HTTP::Message::PSGI #177 - Added documentation on supported %-flags in AccessLog (ether) - Skip unnecessary tests on non-developer environment 0.9986 Mon Mar 12 11:26:59 PDT 2012 [IMPROVEMENTS] - Use I/O handles to FCGI::Request instead of global STDIN, STDOUT etc. (chansen) - Improved FastCGI docs (osfameron) - Cascade app now returns the last response code (aristotle) 0.9985 Mon Oct 31 13:11:19 PDT 2011 [BUG FIXES] - Short circuit Plack::Handler fallback to avoid %INC bugs in perl 5.8 (mst) - Fixed Makefile.PL to avoid Test::SharedFork interferring with Module::Install (ambs) 0.9984 Mon Oct 3 09:55:05 PDT 2011 [BUG FIXES] - WrapCGI: Close wrapped CGI's STDIN handle (rwstauner) [IMPROVEMENTS] - WrapCGI: improved docs (chromatic) - Request: Do not destroy HTTP::Body upload headers (mst) 0.9983 Tue Sep 27 09:55:48 PDT 2011 [BUG FIXES] - Fixed a typo in nginx FastCGI configuration - Clone HTTP headers in Response->finalize #237 (chip) - Fixed Directory app not displaying the right path in its title - Changed IPv6 default listen address to wildcard (ollyg) - Fixed the FastCGI handler with web-server mode on Win32 [NEW FEATURES] - Added psgix.harakiri for Apache handlers [IMPROVEMENTS] - Prefer Corona when Coro is detected #236 (chip) - Increased Pod::Usage dependency - Improved Plack::Test documentation (chromatic) - Lint now checks if SCRIPT_NAME eq '/' which is forbidden in the spec (chromatic) 0.9982 Tue Jul 19 13:07:35 PDT 2011 [BUG FIXES] - Fixed the bug in restarter introduced in 0.9980 (nihen) #223 #234 - Removed a debug statement left over in Plack::Util - Fixed warnings in Lint 0.9981 Mon Jul 18 17:24:11 PDT 2011 [BUG FIXES] - Plack::Request: Added a sanity check to remove newlines from headers to follow the PSGI specification #224 - HTTPParser::PP: Fixed warnings #225 - plackup now prints errors to psgi.errors rather than STDERR - Fixes issues with undef returned from streaming handler in middleware #231 - ContentLength: Do not auto-add Content-Length from block devices, pipes and character files [NEW FEATURES] - HTTPExceptions: Support ->as_psgi method on exceptions (doy) - FastCGI: Support psgix.harakiri [IMPROVEMENTS] - Lint: Added more checks to validate header values - StackTrace: Strip caller information since it is not useful anyway - HTTPExceptions: Added rethrow option (doy) - Misc. doc fixes on plackup (chromatic) - binmode STDIN for CGI handler for Win32 #218 - Remove the test that tests Server specific handling of Transfer-Encoding - Fixed POD link (audreyt) 0.9980 Mon Jun 6 20:24:25 PDT 2011 [BUG FIXES] - Fixed a bug where restarting loader doesn't terminate children (#209) - Strip URI fragments off of PATH_INFO and QUERY_STRING (#213) [IMPROVEMENTS] - Documented -r vs auto server detection caveat - Documented a default AccessLog format (ask) - Support %V in AccessLog formats (ask) - Document PLACK_HTTP_PARSER_PP (melo) [NEW FEATURES] - Added experimental IPv6 and SSL support for the built-in HTTP::Server::PSGI 0.9979 Tue May 17 09:54:03 PDT 2011 [BUG FIXES] - Fixed Middleware::AccessLog's default %t format to match Apache's format - Fixed a warning in Apache1 handler where PATH_INFO doesn't exist #204 - Fixed a bad test relying on new Test::More versions [IMPROVEMENTS] - Fixed Lint to accept bare in-memory filehandle per http://stackoverflow.com/questions/6011793/ - Added setup_env() to Plack::Handler::CGI (markstos) - Added a non-blocking Hello World example in eg/dot-psgi - Doc cleanup 0.9978 Wed May 4 11:29:12 PDT 2011 [TEST FIXES] - Fixed a failing output_encoding.t because of FCGI dependencies - Improved Plack::Test::Suite documentation 0.9977 Sun May 1 12:16:08 PDT 2011 [BUG FIXES] - Fixed ConditionalGET to not die with streaming interface (reported by Paul Ervamaa) - Add a reason string to CGI/FastCGI Status header to comply with RFC 3875 (Stephen Clouse) - Fixed a CGI/FastCGI handler to ensure newlines are not mangled on Win32 platforms (Christian Walde) [IMPROVEMENTS] - localize @ARGV to empty when evaluating a PSGI application (https://github.com/sukria/Dancer/issues/473) - Fixed the use of Getopt::Long to make the pass_through flag local - Middleware::JSONP now supports more response types such as IO::Handle (reported by Theory) 0.9976 Fri Apr 8 18:07:11 PDT 2011 [NEW FEATURES] - Support setting content_type in App::File (ajgb) [IMPROVEMENTS] - Document fixes (jhannah) - Skip bad tests failing on LWP 6 (daxim) 0.99_75 Thu Mar 24 11:29:22 PDT 2011 [INCOMPATIBLE CHANGES] - builder {} now always returns a PSGI code reference, instead of inconsistently returning URLMap object when mount() is used. (reported by hoelzro) - Plack::Runner now automatically calls ->parse_options() if it hasn't been called, so the sane defaults for plackup can be applied. (reported by arcanez) [BUG FIXES] - Fixed the way to override %ENV to avoid test breakages in Win32 #179 - Properly append '/' when linking to a directory in Plack::App::Directory (theory) [IMPROVEMENTS] - Skips the current directory in Plack::App::Directory - Plack::App::Directory now redirects to a canonical URL that has a trailing slash just like Apache (hobbs) - Fixed some typos and outdated information in the PODs 0.9974 Thu Mar 3 20:55:28 PST 2011 - Added a documentation about using relative URI paths beginning with // - Added IIS6ScriptNameFix that fixes SCRIPT_NAME for IIS6 FastCGI, extracted from Catalyst (rafl) - Moved the wrapcgi/exec tests for Win32 #174 - Fixed a warning for the new Test::TCP in FCGI testing - Clear %ENV when running the Plack::Test::Suite with Server implementation (hachi) 0.9973 Sat Feb 26 09:40:15 PST 2011 - Fixed the regexp in the code check added in 0.9972 (leedo) 0.9972 Thu Feb 24 10:50:01 PST 2011 - Fixed the Plack::Runner docs to avoid the cargo cult issue of __FILE__ eq $0 - Added a silly check to give warnings if the idiom __FILE__ eq $0 is used in .psgi 0.9971 Wed Feb 23 14:02:35 PST 2011 [INCOMPATIBLE CHANGES] - Localize $0 to the given .psgi path when evaluating it in Plack::Util::load_psgi() This fixes the unexpected values and/or crashes with Starman when your application uses FindBin module. 0.9970 Tue Feb 22 08:35:50 PST 2011 - Apache2: Fixed a bug where dispatcher fails to parse first path when it begins with two or more slashes (clkao) 0.9969 Fri Feb 18 21:35:29 PST 2011 - Suppress the use of unlocalized $_ in Plack::Runner (mst) - Plack::Handler::Net::FastCGI is now removed from Plack core dist. It will be released as a separate distribution on CPAN. - Fixed Plack::Handler::Apache2 so that it can safely call log (Andy Wardley) - StackTrace: Display graceful fallback errors when $SIG{__DIE__} is overridden in the application (mkanat) 0.9968 Wed Feb 9 19:07:48 PST 2011 - Fixed Recursive middleware to rethrow unknown exceptions. #166 (reported by waba) - Document response_cb. #121 - Plack::Loader to print errors if it is really a compilation error - Fixed the Cascade app to work with all 404 responses with the streaming interface. #171 (reported by eevee) 0.9967 Tue Jan 25 14:26:37 PST 2011 - Fixed StackTrace to require D::ST::WithLexicals 0.08 that supports 'message' (doy) 0.9966 Tue Jan 25 12:00:25 PST 2011 - Fixed a memory leak in SimpleLogger (vti) - Support %v in AccessLog (Ranguard) - Force set CONTENT_LENGTH in req_to_psgi when $content is given to HTTP::Request (timbunce) #150 - Fixed a case where SCRIPT_NAME and PATH_INFO can both get empty in req_to_psgi (doy) #163 0.9965 Mon Jan 24 23:08:04 PST 2011 - Requires Devel::StackTrace 0.11 - Fixed a regression where StackTrace wasn't able to get the thrown exception as an error message (hachi) 0.9964 Mon Jan 24 16:29:08 PST 2011 - Various documentation improvements (miyagawa, schwern) - Improved the way it eliminates Plack::Middleware::StackTrace from its own stacktrace (Jonathan Swartz) 0.9963 Mon Jan 10 16:46:33 PST 2011 - Fixed fcgi.t for lighttpd < 1.4.23 (confound) 0.9962 Sat Jan 8 21:07:30 PST 2011 - Same fix as 0.9961 but works around the issues with Strawberry unarchiver 0.9961 Fri Jan 7 21:54:04 PST 2011 - Skip directory.t on win32 since the directory "stuff.." can't be created [RT:64545] 0.9960 Sat Dec 25 11:16:08 PST 2010 - FCGI: Fixed the regression in 0.9958 where PATH_INFO gets wrong value when hosted under a non-root path (ambs) - Improved the FastCGI and Apache2 test infrastructure to test SCRIPT_NAME values 0.9959 Tue Dec 21 11:38:08 PST 2010 - Apache2: Fixed the regression bug around LocationMatch caused by fixes in 0.9958 (cho45) 0.9958 Mon Dec 20 15:18:54 PST 2010 - Plack::Handler::Apache[12] now handles Authorization: header automatically, no need for mod_rewrite workaround anymore (cho45) - Fixed Apache[12] and FCGI where multiple forward slashes were munged (cho45) - Static: Added pass_through option to pass non-existent paths to the app. Fixing the docs to match with the code (beanz) #154 0.9957 Thu Dec 16 11:27:29 PST 2010 - Fixed warnings in Plack::Request cookie parsing (typester) - removed MethodOverride middleware. Now it is a standalone distribution on CPAN (theory) 0.9956 Thu Dec 9 19:32:46 PST 2010 - FastCGI: Fixed an empty PATH_INFO with mod_fastcgi (and possibly others) - FastCGI: Improved the automatic detection of the case when invoked from web server. #141 (reported by LeoNerd) - plackup: Document that -e 'enable ...' doesn't assume app.psgi when there's no argument. #106 (clkao) - Plack::App::FCGIDispatcher: Remove the Status: header #123 (reported by Htbaa) - Apache2: Work around issues where SCRIPT_NAME gets wrong when LocationMatch is used. #136 (reported by atiking) 0.9955 Thu Dec 9 18:02:50 PST 2010 - More fixes to a possible directory traversal 0.9954 Thu Dec 9 17:45:59 PST 2010 - Fixed a directory traversal bug in Plack::App::File etc. RT:63020 0.9953 Fri Dec 3 14:50:09 PST 2010 - Include the original error message in the StackTrace text output on console. This requires Devel::StackTrace 1.23 and Devel::StackTrace::WithLexicals 0.08 (optional) - Fixed AccessLog middleware to handle multiple dashes in %{} (Jiro) 0.9952 Thu Dec 2 14:03:48 PST 2010 - Fixed the potential deadlocks in WrapCGI's read/write pipe (typester) - Improved documentations on plackup -e - Fixed a potential DoS vulnerability in HTTP::Server::PSGI (kazuho) - Allows setting names of FCGI process with proc_title option (rafl) 0.9951 Mon Oct 25 13:50:33 PDT 2010 - Added Feersum to the benchmark script (stash) - Lint: fixed the body handle check to see if the file has getline() method (tokuhirom) - StackTrace: store the stacktrace in $env->{'plack.stacktrace.text'} and $env->{'plack.stacktrace.html'} (theory) - Added ->mount method to the Plack::Builder OO interface (franckcuny) - HTTPExceptions: Don't set an invalid Content-Length when the exception is not an object (ask) - ErrorDocument: Fixed wrong Content-Length header be set (ask) 0.9950 Thu Sep 30 14:11:33 PDT 2010 - Fixed typos in middleware docs (miyagawa, theory, tokuhirom) - App::Directory: fixed URL generation escape bug (chiba) - Middleware::JSONP: support callback parameter name (franck) 0.9949 Tue Sep 14 11:59:36 PDT 2010 - Fixed FCGI handler docs - Auth::Basic: Pass $env to the callback so .htpasswd based auth can be implemented with PATH_INFO (doy) 0.9948 Thu Sep 9 16:01:53 PDT 2010 - Fixed a bug introduced in 0.9947 where $req->upload loses the temporary files when Plack::Request object is instantiated multiple times. It could happen if one of the pre-processing middleware uses Plack::Request and then again in the application or frameworks. 0.9947 Thu Sep 9 02:26:14 PDT 2010 - Plack::Loader: Fixed a typo in ENV that prevents warnings messages in development - Added flymake temporary file in Restarter (hirose31) - Plack::Request: Fixed a bug that HTTP::Body temporary files were not cleaned up (plu) - Middleware::AccessLog: Fixed a bug where %{key}i ignores the value '0' (nekoya) 0.9946 Sat Aug 28 22:32:16 PDT 2010 - Fixes UUV warnings in Apache2 handler RT:60472 - Fixed various test failures due to dependencies 0.9945 Thu Aug 19 16:24:30 PDT 2010 - Support executing (non-perl) CGI scripts in CGIBin and WrapCGI - Fixed tests for win32 0.9944 Sun Aug 8 23:35:52 PDT 2010 - Fixed Restarter for Starlet where SIGTERM doesn't quit the process (chiba) 0.9943 Fri Jul 30 13:24:15 PDT 2010 - Updated Apache* handler so it could duck type on Loader (jnap) - Added --access-log to plackup (grantm) - Added support for streaming stdio in Net::FastCGI handler (chansen) 0.9942 Fri Jul 23 23:42:43 PDT 2010 - Allow passing FCGI manager object to Handler::FCGI (confound) - Call FCGI::Request::Finish() before pm_post_dispatch (confound) - Moved response_cb() to Plack::Util (confound) - re-enable WithLexicals now that PadWalker segfaults with 5.12 is fixed #98 0.9941 Thu Jul 8 18:17:30 PDT 2010 - Makes Lint not warn about ASCII-only strings with UTF8 flag because they're safe 0.9940 Fri Jul 2 23:37:51 PDT 2010 - Fixed META.yml 0.9939 Fri Jul 2 17:56:10 PDT 2010 - Improved middleware documentation (miyagawa, leedo, bobtfish) - Added a test about Transfer-Encoding with Content-Length: 0 (chiba) - Fixed NullLogger middleware (haarg) - Fixed Plack::Util inline object's can() (haarg) - Middleware::HTTPException now honors ->location method of the exception (frodwith) - Middleware::AccessLog: Fixes %D to be microsec so it's compatible to Apache #119 (cho45) - Fixed Plack::Request->uri when PATH_INFO conatins URI reserved characters #118 (leedo) 0.9938 Sun May 23 17:13:05 PDT 2010 - ErrorDocument: Added Content-Length to error responses (hachi) - Improved docs about conditional middleware loading - XSendfile: Updated (undocumented) environment key to switch frontend - Auth::Basic: Added notes about how to use it with Apache (mod_perl and CGI) [RT #57436] 0.9937 Fri May 14 23:11:27 PDT 2010 - Fixed -I broken in 0.9936 (juster) #114 0.9936 Fri May 14 15:58:02 PDT 2010 - Remove 'use lib "lib"' from plackup - Remove HTTP_CONTENT_* environment variables in FastCGI handlers to deal with buggy web servers. (Justin Davis) 0.9935 Wed May 5 15:17:06 PDT 2010 - Set an empty PATH_INFO if CGI environment doesn't set so (hachi) #109 - Fixed a possible weird combination of SCRIPT_NAME and PATH_INFO in CGI handlers - localize PATH_INFO and SCRIPT_NAME in App::File and subclasses #100 - updated COPYRIGHT notice for Debian - Middleware::StackTrace now displays text trace to psgi.errors like Rack::ShowExceptions (castaway, theorbtwo) - Middleware::StackTrace: Fixed the text stack trace format to be more readable 0.9934 Tue May 4 15:47:33 PDT 2010 - Added a test in CGIBin where binmode ":utf8" causes bad content-length #110 - Doc fix for the deprecated servers - Initialize Module::Refresh (hiratara) - Added mime_type to ErorrDocument (kakuno) 0.9933 Tue Apr 27 14:32:23 PDT 2010 - refactored the app.psgi loading error handling - Enable type checking of the app in Lint->wrap - allow plackup -e'...' - Disable FCGI::Client/Net::FastCGI test by default 0.9932 Mon Apr 19 15:23:55 JST 2010 - Enable Lint middleware by default in the development env - Lint middleware now validates $app on startup - Fixed documentations on middleware and handlers 0.9931 Fri Apr 16 23:52:27 PDT 2010 - replace kyoto.jpg test image file with smaller baybridge.jpg to strip down the tarball size from 2.5MB to 212KB. 0.9930 Tue Apr 13 20:18:06 PDT 2010 - Added Plack::Handler::Net::FastCGI (chansen) - Made Test::TCP a hard dependency since Plack::Test needs it - Added Delayed loader for Starlet and Starman (clkao) - Hide logger middleware from log4perl's caller stack (haarg) 0.9929 Wed Mar 31 00:33:10 PDT 2010 - Middleware::JSONP: Simplified code and does not support IO response body type - fcgi.t: skip tests with lighttpd < 1.4.17 per CPAN Testers #7040400 0.9928 Mon Mar 29 17:02:42 PDT 2010 - log_dispatch.t: require Log::Dispatch::Array 0.9927 Mon Mar 29 12:43:44 PDT 2010 - require newer Log::Dispatch (confound) - StackTrace: Encode exceptions in utf-8 in case they include wide characters #95 (tokuhirom) - StackTrace: Depends on a new Devel::StackTrace::AsHTML that escapes wide characters - StackTrace: Display stacktrace only if the thrown exception is a direct error #91 (frodwith) - StackTrace: Added 'force' option to force stacktrace in 500 errors - Avoid warnings when response_cb filter returns undef in ARRAY response body #92 (hiratara) - URLMap: Ignore port number if it matches with SERVER_PORT #90 (omega) - URLMap: Enable debug print with PLACK_URLMAP_DEBUG=1 #94 - JSONP: Fixed possible infinite-loop when using with IO response body (hiratara) - Fixed the compatiblity issues with FastCGI docs and tests with lighttpd 1.4.26 (tadam) - LighttpdScriptNameFix: Added 'script_name' option (tadam) 0.9926 Sun Mar 28 14:37:03 PDT 2010 - Added -v|--version option to plackup and the ability for Plack::Runner users to override 0.9925 Sat Mar 27 19:03:57 PDT 2010 - Make this a non-devel release 0.99_24 Sat Mar 27 13:31:51 PDT 2010 - Disable Devel::StackTrace::WithLexicals for now until PadWalker RT #55242 is fixed 0.99_23 Sat Mar 27 01:02:24 PDT 2010 - Dropped keep-alive code from HTTP::Server::PSGI now that Starlet clones the code - Special case --disable-* and --enable-* command line options in plackup and Plack::Runner 0.99_22 Thu Mar 25 19:48:08 PDT 2010 - INCOMPATIBLE: removed --max-workers option from the default standalone server. Now it gives you warnings and falls back to the single process mode. 0.99_21 Thu Mar 25 15:05:53 PDT 2010 - INCOMPATIBLE: removed a workaround for lighttpd SCRIPT_NAME bug in FCGI handler See http://github.com/plack/Plack/issues#issue/68 for details. - HTTPException now logs standard exceptions to psgi.errors - micro optimization for Plack::Request content() method 0.9920 Thu Mar 18 23:48:06 PDT 2010 - Fixed URL path prefix matching in URLMap (hiratara) - Fixed Plack::Request->content on GET with FastCGI servers (sunnavy) - Added new middleware Middleware::Head - Fixed localization bug in Plack-Util/load.t 0.9919 Wed Mar 17 22:50:09 PDT 2010 - Properly rethrow .psgi compilation errors 0.9918 Wed Mar 17 22:35:00 PDT 2010 - Load .psgi file in an unique package rather than Plack::Util to avoid namespace pollution gh-88 0.9917 Wed Mar 17 15:33:43 PDT 2010 - Added Plack::Handler::Apache2::Registry (hiratara) - Set default PLACK_ENV in Plack::Util::load_psgi 0.9916 Fri Mar 12 12:52:39 JST 2010 - Added support for a new (renamed) web server Corona - Document enable coderef in Plack::Middleware (clkao) - Middleware::StackTrace: Send plain text errors to clients that probably do not understand HTML like curl 0.9915 Mon Mar 8 18:22:33 JST 2010 - Fixed a dumb bug in Plack::Handler::Apache2, broken in 0.9914 (hiratara) - Added a warning if you misuse mount() 0.9914 Wed Mar 3 16:02:38 PST 2010 - Fixed psgix.io and nested closure for perl 5.8 (hiratara) - Added an inheritance friendly Apache2 interface (frodwith) - HTTP::Server::PSGI: Close client connection in the first run (hirose31) - Fixed Loader/auto.t to reset env var (gugod) 0.9913 Thu Feb 25 19:14:40 PST 2010 - Revive psgix.io in HTTP::Server::PSGI (hiratara) - Fix packaging issue 0.9912 Thu Feb 25 01:28:21 PST 2010 - Fixed the possible source of memory leak in middleware + streamer + HTTP::Server::PSGI with perl 5.8.x (hiratara) 0.9911 Tue Feb 23 01:55:04 PST 2010 - Removed psgix.io extension to fix streaming choke issue on HTTP::Server::PSGI (tomyhero) 0.9910 Mon Feb 22 19:03:17 PST 2010 - This is the first non-dev release since 0.99. Read all the change logs below. - Support streaming in JSONP (hiratara) - Fixed various handler docs (markstos) - Added Starman and Twiggy to benchmark script - INCOMPATIBLE: Loader now prefers Twiggy when AnyEvent is loaded - Implemented (experimental) psgix.io and psgix.input.buffered extensions - Fixed Plack::Request POST parser to use psgix.input.buffered for better performance - Added PLACK_ENV environment support in plackup #63 - Added HTTPExceptions middleware - Added Recursive middleware 0.99_05 Wed Feb 10 12:46:05 PST 2010 - Changed the Loader command line options to -L from -l - Runner now folds --host, --port and --socket to --listen and vice verca - Added -D and --daemonize to plackup/Runner standard options - Fixed FCGI handler to work with the new --listen and --daemonize option - Fixed a bug in static.t where it chdir's before loading modules - Renamed Writer to BufferedStreaming middleware and added docs - Support streaming apps in Shotgun loader - Falls back to Standalone handler when auto-detected backend is not available (hiratara) - Support chunked-input in HTTP::Request->to_psgi - Make the Reloader work with preforked server (chiba) - Added 'Auto' backend in TempBuffer - Added Nomo backend to the benchmark script - Updated HTTP::Server::PSGI to support experimental psgix.input.buffered - Plack::Request now honors psgix.input.buffered to see psgi.input is seekable - Renamed Standalone handler to HTTP::Server::PSGI for consistency while keeping 'Standalone' as a nickname 0.99_04 Fri Feb 5 23:10:48 PST 2010 - Updated Test suite for multiple request headers to relax a bit for AE::HTTPD - Added a test for large POST body which revealed FCGI::Client bug - Added a handler for HTTP::Server::Simple::PSGI - Depend on a decent version of URI (tomyhero) - Reworked Loader API so the default loader can autodetect the backend again - run_app now doesn't use Try::Tiny but use plain eval {} 0.99_03 Wed Feb 3 16:09:14 PST 2010 - Use 0 as a default address in the server_ready hook in Plack::Runner - Document Plack::Handler naming scheme - Fixed how Plack::Server::Standalone saves args - Supported streaming interface in Cascade and URLMap - mentions awesome WSGI Paste in Plack documentation - Removed URI caching in Plack::Request since it's fast enough - Fixed packaging issue due to Module::Install::Share bug (rafl) - Support 'file' option in App::File and its subclasses - Fixed SCRIPT_NAME and PATH_INFO in App::CGIBin - Fixed App::Directory and ::File not to use Path::Class and its canonicalization. It's now 300% faster! 0.99_02 Sat Jan 30 22:10:45 PST 2010 - Fixed Plack::TempBuffer to work with 5.8 and 5.11.3 - Do not use <$input> in FCGIDispatcher - Skip fcgi_client.t unless explicitly stated (clkao) - clarify and drop some CPAN dependencies (andk) 0.99_01 Fri Jan 29 14:02:04 PST 2010 Incompatible Changes - Rename Standalone servers to HTTP::Server::PSGI - Rename Plack::Server adapters to Plack::Handler. These changes should be transparnt since we have a compatible code to work with the older names as well. - Dropped sendfile(2) AIO support from Standalone server - Plack::Request and Response are now in core, deprecating many methods. Read `perldoc Plack::Request` and its INCOMPATIBILITIES section New Features - New middleware: WrapCGI to convert a single CGI script into a PSGI applciation - Support psgix.logger and psgix.session in Plack::Request - New logger middleware: NullLogger, SimpleLogger, Log4perl and LogDispatch - Refactored Loader classes and added a new Shotgun loader (like rack's Shotgun) - Added -l option to plackup which specifies the Loader backend - New middleware: Refresh reloads modules in %INC in every N seconds - Wraps -e code with 'builder { }' by default. You can also use with *.psgi to add middleware components without editing the file! Bug Fixes and Improvements - Do not call ->canonical in HTTP::Message::PSGI to keep the URI encoded params in Plack::Test tests (rafl, t0m) - Fixed a bug in stupid corner case in HTTP::Message::PSGI where passed URI has UTF-8 encoded strings *and* URI escaped UTF-8 bytes. (chmrr) - Depend on new HTTP::Request::AsCGI that has better REQUEST_URI - Plack::Runner/plackup does not autoload AccessLog in CGI mode anymore - Added server_ready hook to PSGI servers so you can disable them in tests etc. (clkao, rafl) - Escape user-supplied values in AccessLog to avoid control sequence injection (tokuhirom, kazuho) - Support -foo (single dash) style option in Plack::Runner and plackup - Relax the runtime.t check since it still fails on low-res time environments - Now depends on Digest::MD5, HTTP::Body and Hash::MultiValue - Revert the 'require' in load_psgi to do 'do' - Delay load unnecessary modules in Plack::Runner - Fixed psgi.multiprocess value on HTTP::Server::PSGI - PSGI/1.1 support in Lint 0.9031 Mon Jan 11 11:29:04 PST 2010 - Fixed Plack::App::Directory directory listing by switching to Plack::MIME (tokuhirom) This has been broken since 0.9025 - Fixed body filtering middleware such as Plack::Middleware::Deflater (hiratara) This has been broken since 0.9026 0.9030 Sat Jan 9 13:13:17 PST 2010 - Support streaming interface in HTTP::Message::PSGI, Lint and Plack::Test (rafl) - plackup -e doesn't enable strict and warnings by default, like perl - Improved Middleware::Auth::Basic performance and error check 0.9029 Thu Jan 7 19:09:17 PST 2010 - Fixed runtime.t to relax test condition to avoid failures on Win32 (xaicron) - Fixed a bug in FCGI engine where it creates a bogus response when running under a buffered I/O with lighttpd. (fcharlier, typester) - FCGI and CGI server now autoflushes STDOUT to do non-buffering output - Fixed a Plack::MIME bug where extensions like .mp3 fails 0.9028 Tue Jan 5 18:42:07 PST 2010 - Fixed a long standing bug where errors are not printed correctly when the application dies. (tokuhirom) - Fixed FCGIClient passing bogus psgi.* environment values to the backend - Implemented psgi.streaming in all blocking servers (miyagawa, rafl) 0.9027 Sun Jan 3 16:33:23 PST 2010 - Added new middleware Runtime that adds X-Runtime header - Delay load Pod::Usage in Plack::Runner and plackup 0.9026 Fri Jan 1 10:35:26 JST 2010 - Auth::Basic now accepts an object that duck types to ->authenticate (e.g. Authen::Simple) - Reworked how response_cb body callback works, so Content-Length will be updated correctly 0.9025 Sat Dec 26 10:11:59 JST 2009 - Server::Standalone::* should now display the correct Server: value - Fixed a bug in AccessLog::Timed where %D and %T do not work - Fixed a bug in AccessLog::Timed to work with filehandles - Removed a dependency to MIME::Types and include Plack::MIME - Refactored plackup into Plack::Runner - Fixed a failing test under stupid Win32 filesystem - Fixed ConditionalGET to work with delayed response 0.9024 Sat Dec 19 12:25:52 PST 2009 - Overwhauled how -r and -R works in plackup. Looks at .psgi and lib/ under that by default. 0.9023 Thu Dec 17 13:16:38 PST 2009 - Document the use of Plack:: namespace - Use safer Unicode characters in tests to silence warnings #66 - Plack::Util::load_psgi now takes a class name as well. Added notes about the security of its use - Set default host in MockHTTP and keep them if explicitly set (nihen) 0.9022 Sun Dec 13 10:53:01 PST 2009 - Added more assertions to Middleware::Lint - Added a new test to test big HTTP header, which reveals the FCGI::Client bug (zrail, tokuhirom) - plackup -e now automatically loads Plack::Builder - Fixed fcgi tests (tokuhirom) - Fixed Test::MockHTTP to make 500 response when the app died - Fixed a memory leak in StackTrace when WithLexicals is used (chiba, Sartak) - Fixed Middleware::ConditionalGET to deal with stupid IE headers (chiba) - Fixed lots of typos (Sartak) 0.9021 Tue Dec 8 14:29:08 PST 2009 - Doc patches to Plack about CONTRIBUTING (stevan) - Remove Class::Accessor::Fast and added Plack::Util::Accessor (stevan) - Added Plack::Component the common base class for both App:: and Middleware (stevan) - Plack::Test test_psgi now accepts $app, $client in positional args - Plack::Test client callback can now omit host names like $cb->(GET "/") 0.9020 Mon Dec 7 10:38:37 GMT 2009 - Fixed a test (psgibin.t) failure in case sensitive filesystem - Fixed a warning in Plack::Util::header_set 0.9019 Sun Dec 6 05:56:30 GMT 2009 - Fixed a bug in Plack::Util::header_set when to clear multiple headers (chiba) - Added Plack::App::CGIBin that runs cgi-bin scripts as a PSGI application - Added Plack::App::PSGIBin that loads .psgi files from local filesystem 0.9018 Thu Dec 3 00:48:04 PST 2009 - Allow Plack::Middleware->new to accept plain hashes - Added Plack::App::Cascade to create a compound apps that cascade requests - Added POE backend to benchmarks/ab.pl - Implemented Plack::Server::Apache[12]->preload to preload apps in or startup file 0.9017 Sun Nov 29 17:33:36 JST 2009 - Fixed more tests that fail on Win32 (charsbar) 0.9016 Sun Nov 29 16:39:40 JST 2009 - removed Middleware::Deflater from the dist. - Fixed Standalone so as not to use Time::HiRes::Alarm on Win32 systems (charsbar, kazuho) - Fixed App::File to set file path using forward slashes on Win32 (charsbar) #49 0.9015 Thu Nov 26 17:31:33 JST 2009 - Fixed a bug in URLMap where $env is shallow copied and some middleware don't work - Added -e and -M to plackup - plackup -r with args (directories to watch) is deprecated. Use -R instead - plackup foo.psgi now DWIMs. -a (--app) continues to work - Optimizaitons to Middleware and docs to explicitly call to_app because overloading happens every request and is inefficient. - The abilitiy to auto-select server backends for POE/AnyEvent/Coro is restored. Doesn't work with -r though. #50 - Display server package name in the Standalone/Prefork startup - Fixed a bug in Plack::Test::MockHTTP where $res doesn't return the request (teejay) - Fixed a bug in URLMap where requests to / fails in some cases (chiba) 0.9014 Fri Nov 20 21:51:47 PST 2009 - Updated docs for Standalone keep-alive options - Added Auth::Basic middleware - Fixed dependencies and MakeMaker issues in the archive 0.9013 Wed Nov 18 18:26:31 PST 2009 - Disable keep-alive in Standalone by default (kazuho, frew) - Fixed a bug in Standalone where 'timeout' property is ignored in the first request (kazuho) - Fixed a documentation bug in Middleware::Conditional (confound, scook) 0.9012 Tue Nov 17 13:38:38 PST 2009 - Added Middleware::Conditional and enable_if DSL for dynamic builder (scook) 0.9011 Thu Nov 12 03:53:28 PST 2009 - Added Apache1 support (Aaron Trevena) 0.9010 Wed Nov 11 23:18:37 PST 2009 - You can now omit Plack::Middleware:: in Builder DSL's enable() 0.9009 Sat Nov 7 20:43:17 PST 2009 - Fixed dependencies for tests 0.9008 Tue Oct 27 14:15:28 PDT 2009 - Removed optional deps from Makefile.PL and moved them to Task::Plack (mst) - Make some middleware deps as required to make it simple, for now 0.9007 Sat Oct 24 17:41:33 PDT 2009 - Fixed Server::CGI to really inline fuctions to avoid warnings - Fixed Middleware::AccessLog to let %{..}t strftime log format work (beppu) - Fixed a flush bug in gzip encoding in Middleware::Deflater - Fixed a bug in Middleware::AccessLog so POSIX strftime always works in English (fayland) - Added Middleware::ContentMD5 (Fayland) - Fixed plackup -r to actually reload the application code (robinsmidsrod) 0.9006 Fri Oct 23 01:21:13 PDT 2009 - Support streaming interface in most middlewares - Added Middleware::Deflater (not recommended to use: see POD) - Document FCGI configuration in Server::FCGI pod (dhoss) - Inline Plack::Util functions in Server::CGI to speed up (mst) 0.9005 Wed Oct 21 20:53:19 PDT 2009 - Switch to Filesys::Notify::Simple to watch directory to trim down deps - Made some dependencies optional since they're actually optional 0.9004 Tue Oct 20 22:57:48 PDT 2009 - Fixed File::ShareDir dependency (mst) - App::File and Middleware::Static now auto follows symlinks (chiba) - Implemented plackup -r as Plack::Loader::Reloadable (nothingmuch) - Removed poll_cb from Writer middleware - Added plackup common command line options: -o for --host and -p for --port 0.9003 Sun Oct 18 19:16:26 PDT 2009 - Added POE to Plack::Loader autoload - Implemented callback style streaming in selected middlewares - Bump up HTTP::Parser::XS to fix memory leaks - Added Middleware::Chunked - Added Middleware::JSONP - Added twitter-stream.psgi example to do streaming server push - Fixed Middleware::StackTrace to DWIM in framework generated 500 errors - Fixed Restarter to do what doc says 0.9002 Wed Oct 14 11:26:28 PDT 2009 - Added a workaround in Server::Apache2 when Location and SCRIPT_NAME don't match - Use Try::Tiny and parent for smaller memory footprint and better error handling 0.9001 Tue Oct 13 00:55:34 PDT 2009 - Downgrade EUMM in inc/ 0.9000 Tue Oct 13 00:14:01 PDT 2009 - original version Plack-1.0030/cpanfile000644 000765 000024 00000002301 12244057435 015277 0ustar00miyagawastaff000000 000000 requires 'perl', '5.008001'; requires 'Devel::StackTrace', '1.23'; requires 'Devel::StackTrace::AsHTML', '0.11'; requires 'File::ShareDir', '1.00'; requires 'Filesys::Notify::Simple'; requires 'HTTP::Body', '1.06'; requires 'HTTP::Message', '5.814'; requires 'Hash::MultiValue', '0.05'; requires 'Pod::Usage', '1.36'; requires 'Stream::Buffered', '0.02'; requires 'Test::TCP', '2.00'; requires 'Try::Tiny'; requires 'URI', '1.59'; requires 'parent'; requires 'Apache::LogFormat::Compiler', '0.12'; requires 'HTTP::Tiny', 0.034; on test => sub { requires 'Test::More', '0.88'; requires 'Test::Requires'; suggests 'Authen::Simple::Passwd'; suggests 'MIME::Types'; suggests 'CGI::Emulate::PSGI'; suggests 'CGI::Compile'; suggests 'IO::Handle::Util'; suggests 'LWP::Protocol::http10'; suggests 'Log::Log4perl'; suggests 'HTTP::Server::Simple::PSGI'; suggests 'HTTP::Request::AsCGI'; suggests 'LWP::UserAgent', '5.814'; suggests 'Module::Refresh'; }; on runtime => sub { suggests 'FCGI'; suggests 'FCGI::ProcManager'; suggests 'CGI::Emulate::PSGI'; suggests 'CGI::Compile'; suggests 'IO::Handle::Util'; suggests 'LWP::UserAgent', '5.814'; }; Plack-1.0030/dist.ini000644 000765 000024 00000000107 12244057435 015241 0ustar00miyagawastaff000000 000000 [@Milla] installer = MakeMaker [Metadata] x_authority = cpan:MIYAGAWA Plack-1.0030/eg/000755 000765 000024 00000000000 12244057435 014172 5ustar00miyagawastaff000000 000000 Plack-1.0030/lib/000755 000765 000024 00000000000 12244057435 014345 5ustar00miyagawastaff000000 000000 Plack-1.0030/LICENSE000644 000765 000024 00000043714 12244057435 014615 0ustar00miyagawastaff000000 000000 This software is copyright (c) 2009-2013 by Tatsuhiko Miyagawa. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2009-2013 by Tatsuhiko Miyagawa. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2009-2013 by Tatsuhiko Miyagawa. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Plack-1.0030/Makefile.PL000644 000765 000024 00000004655 12244057435 015563 0ustar00miyagawastaff000000 000000 use strict; use warnings; use 5.008001; use ExtUtils::MakeMaker 6.30; use File::ShareDir::Install; install_share dist => "share"; my %WriteMakefileArgs = ( "ABSTRACT" => "Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)", "AUTHOR" => "Tatsuhiko Miyagawa", "BUILD_REQUIRES" => {}, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "6.30", "File::ShareDir::Install" => "0.03" }, "DISTNAME" => "Plack", "EXE_FILES" => [ "script/plackup" ], "LICENSE" => "perl", "NAME" => "Plack", "PREREQ_PM" => { "Apache::LogFormat::Compiler" => "0.12", "Devel::StackTrace" => "1.23", "Devel::StackTrace::AsHTML" => "0.11", "File::ShareDir" => "1.00", "Filesys::Notify::Simple" => 0, "HTTP::Body" => "1.06", "HTTP::Message" => "5.814", "HTTP::Tiny" => "0.034", "Hash::MultiValue" => "0.05", "Pod::Usage" => "1.36", "Stream::Buffered" => "0.02", "Test::TCP" => "2.00", "Try::Tiny" => 0, "URI" => "1.59", "parent" => 0 }, "TEST_REQUIRES" => { "Test::More" => "0.88", "Test::Requires" => 0 }, "VERSION" => "1.0030", "test" => { "TESTS" => "t/*.t t/HTTP-Message-PSGI/*.t t/HTTP-Server-PSGI/*.t t/Plack-Builder/*.t t/Plack-HTTPParser-PP/*.t t/Plack-Handler/*.t t/Plack-Loader/*.t t/Plack-MIME/*.t t/Plack-Middleware/*.t t/Plack-Middleware/cascade/*.t t/Plack-Middleware/recursive/*.t t/Plack-Middleware/stacktrace/*.t t/Plack-Request/*.t t/Plack-Response/*.t t/Plack-Runner/*.t t/Plack-TempBuffer/*.t t/Plack-Test/*.t t/Plack-Util/*.t" } ); my %FallbackPrereqs = ( "Apache::LogFormat::Compiler" => "0.12", "Devel::StackTrace" => "1.23", "Devel::StackTrace::AsHTML" => "0.11", "File::ShareDir" => "1.00", "Filesys::Notify::Simple" => 0, "HTTP::Body" => "1.06", "HTTP::Message" => "5.814", "HTTP::Tiny" => "0.034", "Hash::MultiValue" => "0.05", "Pod::Usage" => "1.36", "Stream::Buffered" => "0.02", "Test::More" => "0.88", "Test::Requires" => 0, "Test::TCP" => "2.00", "Try::Tiny" => 0, "URI" => "1.59", "parent" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); { package MY; use File::ShareDir::Install qw(postamble); } Plack-1.0030/MANIFEST000644 000765 000024 00000016556 12244057435 014745 0ustar00miyagawastaff000000 000000 Changes LICENSE MANIFEST META.json META.yml Makefile.PL README benchmarks/ab.pl benchmarks/fcgi.pl cpanfile dist.ini eg/dot-psgi/Dumper.psgi eg/dot-psgi/Hello.psgi eg/dot-psgi/cgi-pm.psgi eg/dot-psgi/cgi-script.psgi eg/dot-psgi/echo-stream-sync.psgi eg/dot-psgi/echo-stream.psgi eg/dot-psgi/echo.psgi eg/dot-psgi/error.psgi eg/dot-psgi/image.psgi eg/dot-psgi/nonblock-hello.psgi eg/dot-psgi/plack-req.psgi eg/dot-psgi/runnable.psgi eg/dot-psgi/slowapp.psgi eg/dot-psgi/static.psgi eg/dot-psgi/static/index.html eg/dot-psgi/static/test.css eg/dot-psgi/static/test.js eg/dot-psgi/twitter-stream.psgi lib/HTTP/Message/PSGI.pm lib/HTTP/Server/PSGI.pm lib/Plack.pm lib/Plack/App/CGIBin.pm lib/Plack/App/Cascade.pm lib/Plack/App/Directory.pm lib/Plack/App/File.pm lib/Plack/App/PSGIBin.pm lib/Plack/App/URLMap.pm lib/Plack/App/WrapCGI.pm lib/Plack/Builder.pm lib/Plack/Component.pm lib/Plack/HTTPParser.pm lib/Plack/HTTPParser/PP.pm lib/Plack/Handler.pm lib/Plack/Handler/Apache1.pm lib/Plack/Handler/Apache2.pm lib/Plack/Handler/Apache2/Registry.pm lib/Plack/Handler/CGI.pm lib/Plack/Handler/FCGI.pm lib/Plack/Handler/HTTP/Server/PSGI.pm lib/Plack/Handler/Standalone.pm lib/Plack/LWPish.pm lib/Plack/Loader.pm lib/Plack/Loader/Delayed.pm lib/Plack/Loader/Restarter.pm lib/Plack/Loader/Shotgun.pm lib/Plack/MIME.pm lib/Plack/Middleware.pm lib/Plack/Middleware/AccessLog.pm lib/Plack/Middleware/AccessLog/Timed.pm lib/Plack/Middleware/Auth/Basic.pm lib/Plack/Middleware/BufferedStreaming.pm lib/Plack/Middleware/Chunked.pm lib/Plack/Middleware/Conditional.pm lib/Plack/Middleware/ConditionalGET.pm lib/Plack/Middleware/ContentLength.pm lib/Plack/Middleware/ContentMD5.pm lib/Plack/Middleware/ErrorDocument.pm lib/Plack/Middleware/HTTPExceptions.pm lib/Plack/Middleware/Head.pm lib/Plack/Middleware/IIS6ScriptNameFix.pm lib/Plack/Middleware/IIS7KeepAliveFix.pm lib/Plack/Middleware/JSONP.pm lib/Plack/Middleware/LighttpdScriptNameFix.pm lib/Plack/Middleware/Lint.pm lib/Plack/Middleware/Log4perl.pm lib/Plack/Middleware/LogDispatch.pm lib/Plack/Middleware/NullLogger.pm lib/Plack/Middleware/RearrangeHeaders.pm lib/Plack/Middleware/Recursive.pm lib/Plack/Middleware/Refresh.pm lib/Plack/Middleware/Runtime.pm lib/Plack/Middleware/SimpleContentFilter.pm lib/Plack/Middleware/SimpleLogger.pm lib/Plack/Middleware/StackTrace.pm lib/Plack/Middleware/Static.pm lib/Plack/Middleware/XFramework.pm lib/Plack/Middleware/XSendfile.pm lib/Plack/Request.pm lib/Plack/Request/Upload.pm lib/Plack/Response.pm lib/Plack/Runner.pm lib/Plack/TempBuffer.pm lib/Plack/Test.pm lib/Plack/Test/MockHTTP.pm lib/Plack/Test/Server.pm lib/Plack/Test/Suite.pm lib/Plack/Util.pm lib/Plack/Util/Accessor.pm script/plackup share/#foo share/baybridge.jpg share/face.jpg t/FCGIUtils.pm t/HTTP-Message-PSGI/content_length.t t/HTTP-Message-PSGI/empty_streamed_response.t t/HTTP-Message-PSGI/host.t t/HTTP-Message-PSGI/path_info.t t/HTTP-Message-PSGI/utf8_req.t t/HTTP-Server-PSGI/harakiri.t t/HTTP-Server-PSGI/post.t t/Plack-Builder/builder.t t/Plack-Builder/mount.t t/Plack-Builder/oo_interface.t t/Plack-HTTPParser-PP/simple.t t/Plack-Handler/apache1.t t/Plack-Handler/apache2-registry.t t/Plack-Handler/apache2.t t/Plack-Handler/cgi.t t/Plack-Handler/fcgi.t t/Plack-Handler/output_encoding.t t/Plack-Handler/standalone.t t/Plack-Handler/try_mangle.pl t/Plack-Loader/auto.t t/Plack-Loader/auto_fallback.t t/Plack-Loader/delayed.t t/Plack-Loader/restarter.t t/Plack-Loader/restarter_valid.t t/Plack-Loader/shotgun.t t/Plack-MIME/add_type.t t/Plack-MIME/basic.t t/Plack-MIME/fallback.t t/Plack-Middleware/access_log.t t/Plack-Middleware/access_log_timed.t t/Plack-Middleware/access_log_value_zero.t t/Plack-Middleware/auth_basic.t t/Plack-Middleware/auth_basic_env.t t/Plack-Middleware/auth_basic_simple.t t/Plack-Middleware/bufferedstreaming.t t/Plack-Middleware/cascade/basic.t t/Plack-Middleware/cascade/streaming.t t/Plack-Middleware/cgi-bin/cgi_dir.cgi t/Plack-Middleware/cgi-bin/hello.cgi t/Plack-Middleware/cgi-bin/hello.py t/Plack-Middleware/cgi-bin/hello2.cgi t/Plack-Middleware/cgi-bin/hello3.cgi t/Plack-Middleware/cgi-bin/utf8.cgi t/Plack-Middleware/cgibin.t t/Plack-Middleware/cgibin_exec.t t/Plack-Middleware/chunked.t t/Plack-Middleware/component-leak.t t/Plack-Middleware/component.t t/Plack-Middleware/conditional.t t/Plack-Middleware/conditional_new.t t/Plack-Middleware/conditionalget.t t/Plack-Middleware/conditionalget_writer.t t/Plack-Middleware/content_length.t t/Plack-Middleware/directory.t t/Plack-Middleware/error_document.t t/Plack-Middleware/error_document_streaming_app.t t/Plack-Middleware/errors/404.html t/Plack-Middleware/errors/500.html t/Plack-Middleware/file.t t/Plack-Middleware/head.t t/Plack-Middleware/head_streaming.t t/Plack-Middleware/htpasswd t/Plack-Middleware/httpexceptions.t t/Plack-Middleware/httpexceptions_streaming.t t/Plack-Middleware/iis6_script_name_fix.t t/Plack-Middleware/iis7_keep_alive_fix.t t/Plack-Middleware/jsonp.t t/Plack-Middleware/lint.t t/Plack-Middleware/lint_env.t t/Plack-Middleware/lint_utf8_false_alarm.t t/Plack-Middleware/log4perl.t t/Plack-Middleware/log_dispatch.t t/Plack-Middleware/order.t t/Plack-Middleware/prefix.t t/Plack-Middleware/psgibin.t t/Plack-Middleware/rearrange_headers.t t/Plack-Middleware/recursive/base.t t/Plack-Middleware/recursive/streaming.t t/Plack-Middleware/recursive/throw.t t/Plack-Middleware/recursive/throw_streaming.t t/Plack-Middleware/refresh-init.t t/Plack-Middleware/runtime.t t/Plack-Middleware/simple_content_filter.t t/Plack-Middleware/simple_logger.t t/Plack-Middleware/stacktrace/basic.t t/Plack-Middleware/stacktrace/force.t t/Plack-Middleware/stacktrace/sigdie.t t/Plack-Middleware/stacktrace/streaming.t t/Plack-Middleware/stacktrace/utf8.t t/Plack-Middleware/static.foo t/Plack-Middleware/static.t t/Plack-Middleware/static.txt t/Plack-Middleware/static_env.t t/Plack-Middleware/urlmap.t t/Plack-Middleware/urlmap_builder.t t/Plack-Middleware/urlmap_env.t t/Plack-Middleware/urlmap_ports.t t/Plack-Middleware/wrapcgi.t t/Plack-Middleware/wrapcgi_exec.t t/Plack-Middleware/xframework.t t/Plack-Middleware/xsendfile.t t/Plack-Request/base.t t/Plack-Request/body.t t/Plack-Request/content-on-get.t t/Plack-Request/content.t t/Plack-Request/cookie.t t/Plack-Request/double_port.t t/Plack-Request/foo1.txt t/Plack-Request/foo2.txt t/Plack-Request/hostname.t t/Plack-Request/many_upload.t t/Plack-Request/multi_read.t t/Plack-Request/new.t t/Plack-Request/parameters.t t/Plack-Request/params.t t/Plack-Request/path_info.t t/Plack-Request/path_info_escaped.t t/Plack-Request/readbody.t t/Plack-Request/request_uri.t t/Plack-Request/upload-basename.t t/Plack-Request/upload-large.t t/Plack-Request/upload.t t/Plack-Request/uri.t t/Plack-Request/uri_utf8.t t/Plack-Response/body.t t/Plack-Response/compatible.t t/Plack-Response/cookie.t t/Plack-Response/new.t t/Plack-Response/redirect.t t/Plack-Response/response.t t/Plack-Response/to_app.t t/Plack-Runner/options.t t/Plack-Runner/path.t t/Plack-TempBuffer/print.t t/Plack-Test/2args.t t/Plack-Test/cookie.t t/Plack-Test/hello.t t/Plack-Test/hello_server.t t/Plack-Test/suite.t t/Plack-Util/Hello.pm t/Plack-Util/bad.psgi t/Plack-Util/bad2.psgi t/Plack-Util/bin/findbin.psgi t/Plack-Util/error.psgi t/Plack-Util/foreach.t t/Plack-Util/headers.t t/Plack-Util/headers_obj.t t/Plack-Util/hello.psgi t/Plack-Util/inc/hello.psgi t/Plack-Util/inline_object.t t/Plack-Util/io_with_path.t t/Plack-Util/is_real_fh.t t/Plack-Util/load.t t/Plack-Util/response_cb.t t/release-pod-syntax.t t/test.txt xt/author-downstream.t Plack-1.0030/META.json000644 000765 000024 00000015616 12244057435 015231 0ustar00miyagawastaff000000 000000 { "abstract" : "Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)", "author" : [ "Tatsuhiko Miyagawa" ], "dynamic_config" : 0, "generated_by" : "Dist::Milla version v1.0.4, Dist::Zilla version 5.006, CPAN::Meta::Converter version 2.132830", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Plack", "no_index" : { "directory" : [ "t", "xt", "inc", "share", "eg", "examples" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.30", "File::ShareDir::Install" : "0.03" } }, "develop" : { "requires" : { "Test::Pod" : "1.41" } }, "runtime" : { "requires" : { "Apache::LogFormat::Compiler" : "0.12", "Devel::StackTrace" : "1.23", "Devel::StackTrace::AsHTML" : "0.11", "File::ShareDir" : "1.00", "Filesys::Notify::Simple" : "0", "HTTP::Body" : "1.06", "HTTP::Message" : "5.814", "HTTP::Tiny" : "0.034", "Hash::MultiValue" : "0.05", "Pod::Usage" : "1.36", "Stream::Buffered" : "0.02", "Test::TCP" : "2.00", "Try::Tiny" : "0", "URI" : "1.59", "parent" : "0", "perl" : "5.008001" }, "suggests" : { "CGI::Compile" : "0", "CGI::Emulate::PSGI" : "0", "FCGI" : "0", "FCGI::ProcManager" : "0", "IO::Handle::Util" : "0", "LWP::UserAgent" : "5.814" } }, "test" : { "requires" : { "Test::More" : "0.88", "Test::Requires" : "0" }, "suggests" : { "Authen::Simple::Passwd" : "0", "CGI::Compile" : "0", "CGI::Emulate::PSGI" : "0", "HTTP::Request::AsCGI" : "0", "HTTP::Server::Simple::PSGI" : "0", "IO::Handle::Util" : "0", "LWP::Protocol::http10" : "0", "LWP::UserAgent" : "5.814", "Log::Log4perl" : "0", "MIME::Types" : "0", "Module::Refresh" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/plack/Plack/issues" }, "homepage" : "https://github.com/plack/Plack", "repository" : { "type" : "git", "url" : "https://github.com/plack/Plack.git", "web" : "https://github.com/plack/Plack" } }, "version" : "1.0030", "x_authority" : "cpan:MIYAGAWA", "x_contributors" : [ "Aaron Trevena ", "Alex J. G. Burzy\u0144ski ", "Alexandr Ciornii ", "Andrew Rodland ", "Andy Wardley ", "Aristotle Pagaltzis ", "Ask Bj\u00f8rn Hansen ", "Ben Morrow ", "Bernhard Graf ", "Chia-liang Kao ", "Christian Walde ", "Cosimo Streppone ", "Daisuke Maki ", "Daisuke Murase ", "Dave Rolsky ", "David E. Wheeler ", "David Steinbrunner ", "Eduardo Arino de la Rubia ", "Florian Ragwitz ", "Graham Knop ", "Grant McLean ", "HIROSE Masaaki ", "Hans Dieter Pearcey ", "Henry Baragar ", "Hiroshi Sakai ", "Jakob Voss ", "Jay Hannah ", "Jesse Luehrs ", "Jiro Nishiguchi ", "Johannes Plunien ", "John Beppu ", "John Napiorkowski ", "Jonathan Swartz ", "Justin Davis ", "Kang-min Liu ", "Karen Etheridge ", "Kazuho Oku ", "Keedi Kim ", "Lee Aylward ", "Leo Lapworth ", "Marian Schubert ", "Mark Fowler ", "Mark Stosberg ", "Masahiro Chiba ", "Masahiro Nagano ", "Michael G. Schwern ", "Nick Wellnhofer ", "Nobuo Danjou ", "Olaf Alders ", "Oliver Gorwits ", "Oliver Paukstadt ", "Olivier Mengu\u00e9 ", "Panu Ervamaa ", "Paul Driver ", "Pedro Melo ", "Peter Flanigan ", "Peter Makholm ", "Piotr Roszatycki ", "Rafael Kitover ", "Randy Stauner ", "Ray Miller ", "Ricky Morse ", "Rob Hoelz ", "Ryo Miyake ", "Scott S. McCoy ", "Shawn M Moore ", "Stephen Clouse ", "Stevan Little ", "Stuart A Johnston ", "Takeshi OKURA ", "Tokuhiro Matsuno ", "Tom Heady ", "Tomas Doran ", "Wallace Reis ", "Yann Kerherve ", "Yury Zavarin ", "Yuval Kogman ", "chansen ", "cho45 ", "chromatic ", "fREW Schmidt ", "fayland ", "franck cuny ", "hiratara ", "kakuno ", "leedo ", "mala ", "osfameron ", "punytan ", "vti ", "xaicron ", "yappo ", "\u00c6var Arnfj\u00f6r\u00f0 Bjarmason ", "\u5510\u9cf3 " ] } Plack-1.0030/META.yml000644 000765 000024 00000011665 12244057435 015061 0ustar00miyagawastaff000000 000000 --- abstract: 'Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)' author: - 'Tatsuhiko Miyagawa' build_requires: Test::More: 0.88 Test::Requires: 0 configure_requires: ExtUtils::MakeMaker: 6.30 File::ShareDir::Install: 0.03 dynamic_config: 0 generated_by: 'Dist::Milla version v1.0.4, Dist::Zilla version 5.006, CPAN::Meta::Converter version 2.132830' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Plack no_index: directory: - t - xt - inc - share - eg - examples requires: Apache::LogFormat::Compiler: 0.12 Devel::StackTrace: 1.23 Devel::StackTrace::AsHTML: 0.11 File::ShareDir: 1.00 Filesys::Notify::Simple: 0 HTTP::Body: 1.06 HTTP::Message: 5.814 HTTP::Tiny: 0.034 Hash::MultiValue: 0.05 Pod::Usage: 1.36 Stream::Buffered: 0.02 Test::TCP: 2.00 Try::Tiny: 0 URI: 1.59 parent: 0 perl: 5.008001 resources: bugtracker: https://github.com/plack/Plack/issues homepage: https://github.com/plack/Plack repository: https://github.com/plack/Plack.git version: 1.0030 x_authority: cpan:MIYAGAWA x_contributors: - 'Aaron Trevena ' - 'Alex J. G. BurzyÅ„ski ' - 'Alexandr Ciornii ' - 'Andrew Rodland ' - 'Andy Wardley ' - 'Aristotle Pagaltzis ' - 'Ask Bjørn Hansen ' - 'Ben Morrow ' - 'Bernhard Graf ' - 'Chia-liang Kao ' - 'Christian Walde ' - 'Cosimo Streppone ' - 'Daisuke Maki ' - 'Daisuke Murase ' - 'Dave Rolsky ' - 'David E. Wheeler ' - 'David Steinbrunner ' - 'Eduardo Arino de la Rubia ' - 'Florian Ragwitz ' - 'Graham Knop ' - 'Grant McLean ' - 'HIROSE Masaaki ' - 'Hans Dieter Pearcey ' - 'Henry Baragar ' - 'Hiroshi Sakai ' - 'Jakob Voss ' - 'Jay Hannah ' - 'Jesse Luehrs ' - 'Jiro Nishiguchi ' - 'Johannes Plunien ' - 'John Beppu ' - 'John Napiorkowski ' - 'Jonathan Swartz ' - 'Justin Davis ' - 'Kang-min Liu ' - 'Karen Etheridge ' - 'Kazuho Oku ' - 'Keedi Kim ' - 'Lee Aylward ' - 'Leo Lapworth ' - 'Marian Schubert ' - 'Mark Fowler ' - 'Mark Stosberg ' - 'Masahiro Chiba ' - 'Masahiro Nagano ' - 'Michael G. Schwern ' - 'Nick Wellnhofer ' - 'Nobuo Danjou ' - 'Olaf Alders ' - 'Oliver Gorwits ' - 'Oliver Paukstadt ' - 'Olivier Mengué ' - 'Panu Ervamaa ' - 'Paul Driver ' - 'Pedro Melo ' - 'Peter Flanigan ' - 'Peter Makholm ' - 'Piotr Roszatycki ' - 'Rafael Kitover ' - 'Randy Stauner ' - 'Ray Miller ' - 'Ricky Morse ' - 'Rob Hoelz ' - 'Ryo Miyake ' - 'Scott S. McCoy ' - 'Shawn M Moore ' - 'Stephen Clouse ' - 'Stevan Little ' - 'Stuart A Johnston ' - 'Takeshi OKURA ' - 'Tokuhiro Matsuno ' - 'Tom Heady ' - 'Tomas Doran ' - 'Wallace Reis ' - 'Yann Kerherve ' - 'Yury Zavarin ' - 'Yuval Kogman ' - 'chansen ' - 'cho45 ' - 'chromatic ' - 'fREW Schmidt ' - 'fayland ' - 'franck cuny ' - 'hiratara ' - 'kakuno ' - 'leedo ' - 'mala ' - 'osfameron ' - 'punytan ' - 'vti ' - 'xaicron ' - 'yappo ' - 'Ævar Arnfjörð Bjarmason ' - 'å”é³³ ' Plack-1.0030/README000644 000765 000024 00000014357 12244057435 014471 0ustar00miyagawastaff000000 000000 NAME Plack - Perl Superglue for Web frameworks and Web Servers (PSGI toolkit) DESCRIPTION Plack is a set of tools for using the PSGI stack. It contains middleware components, a reference server and utilities for Web application frameworks. Plack is like Ruby's Rack or Python's Paste for WSGI. See PSGI for the PSGI specification and PSGI::FAQ to know what PSGI and Plack are and why we need them. MODULES AND UTILITIES Plack::Handler Plack::Handler and its subclasses contains adapters for web servers. We have adapters for the built-in standalone web server HTTP::Server::PSGI, CGI, FCGI, Apache1, Apache2 and HTTP::Server::Simple included in the core Plack distribution. There are also many HTTP server implementations on CPAN that have Plack handlers. See Plack::Handler when writing your own adapters. Plack::Loader Plack::Loader is a loader to load one Plack::Handler adapter and run a PSGI application code reference with it. Plack::Util Plack::Util contains a lot of utility functions for server implementors as well as middleware authors. .psgi files A PSGI application is a code reference but it's not easy to pass code reference via the command line or configuration files, so Plack uses a convention that you need a file named "app.psgi" or similar, which would be loaded (via perl's core function "do") to return the PSGI application code reference. # Hello.psgi my $app = sub { my $env = shift; # ... return [ $status, $headers, $body ]; }; If you use a web framework, chances are that they provide a helper utility to automatically generate these ".psgi" files for you, such as: # MyApp.psgi use MyApp; my $app = sub { MyApp->run_psgi(@_) }; It's important that the return value of ".psgi" file is the code reference. See "eg/dot-psgi" directory for more examples of ".psgi" files. plackup, Plack::Runner plackup is a command line launcher to run PSGI applications from command line using Plack::Loader to load PSGI backends. It can be used to run standalone servers and FastCGI daemon processes. Other server backends like Apache2 needs a separate configuration but ".psgi" application file can still be the same. If you want to write your own frontend that replaces, or adds functionalities to plackup, take a look at the Plack::Runner module. Plack::Middleware PSGI middleware is a PSGI application that wraps an existing PSGI application and plays both side of application and servers. From the servers the wrapped code reference still looks like and behaves exactly the same as PSGI applications. Plack::Middleware gives you an easy way to wrap PSGI applications with a clean API, and compatibility with Plack::Builder DSL. Plack::Builder Plack::Builder gives you a DSL that you can enable Middleware in ".psgi" files to wrap existent PSGI applications. Plack::Request, Plack::Response Plack::Request gives you a nice wrapper API around PSGI $env hash to get headers, cookies and query parameters much like Apache::Request in mod_perl. Plack::Response does the same to construct the response array reference. Plack::Test Plack::Test is a unified interface to test your PSGI application using standard HTTP::Request and HTTP::Response pair with simple callbacks. Plack::Test::Suite Plack::Test::Suite is a test suite to test a new PSGI server backend. CONTRIBUTING Patches and Bug Fixes Small patches and bug fixes can be either submitted via nopaste on IRC or the github issue tracker . Forking on github is another good way if you intend to make larger fixes. See also when you think this document is terribly outdated. Module Namespaces Modules added to the Plack:: sub-namespaces should be reasonably generic components which are useful as building blocks and not just simply using Plack. Middleware authors are free to use the Plack::Middleware:: namespace for their middleware components. Middleware must be written in the pipeline style such that they can chained together with other middleware components. The Plack::Middleware:: modules in the core distribution are good examples of such modules. It is recommended that you inherit from Plack::Middleware for these types of modules. Not all middleware components are wrappers, but instead are more like endpoints in a middleware chain. These types of components should use the Plack::App:: namespace. Again, look in the core modules to see excellent examples of these (Plack::App::File, Plack::App::Directory, etc.). It is recommended that you inherit from Plack::Component for these types of modules. DO NOT USE Plack:: namespace to build a new web application or a framework. It's like naming your application under CGI:: namespace if it's supposed to run on CGI and that is a really bad choice and would confuse people badly. AUTHOR Tatsuhiko Miyagawa COPYRIGHT The following copyright notice applies to all the files provided in this distribution, including binary files, unless explicitly noted otherwise. Copyright 2009-2013 Tatsuhiko Miyagawa CORE DEVELOPERS Tatsuhiko Miyagawa (miyagawa) Tokuhiro Matsuno (tokuhirom) Jesse Luehrs (doy) Tomas Doran (bobtfish) Graham Knop (haarg) CONTRIBUTORS Yuval Kogman (nothingmuch) Kazuhiro Osawa (Yappo) Kazuho Oku Florian Ragwitz (rafl) Chia-liang Kao (clkao) Masahiro Honma (hiratara) Daisuke Murase (typester) John Beppu Matt S Trout (mst) Shawn M Moore (Sartak) Stevan Little Hans Dieter Pearcey (confound) mala Mark Stosberg Aaron Trevena SEE ALSO The PSGI specification upon which Plack is based. The Plack wiki: The Plack FAQ: LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Plack-1.0030/script/000755 000765 000024 00000000000 12244057435 015103 5ustar00miyagawastaff000000 000000 Plack-1.0030/share/000755 000765 000024 00000000000 12244057435 014701 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/000755 000765 000024 00000000000 12244057435 014042 5ustar00miyagawastaff000000 000000 Plack-1.0030/xt/000755 000765 000024 00000000000 12244057435 014232 5ustar00miyagawastaff000000 000000 Plack-1.0030/xt/author-downstream.t000644 000765 000024 00000000751 12244057435 020105 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More 0.98; use File::Temp qw(tempdir); plan skip_all => "AUTHOR_TESTING is required." unless $ENV{AUTHOR_TESTING}; my @downstream = qw( Starman Starlet Twiggy Monoceros Feersum Corona Amon2 Tatsumaki OX Dancer2 Catalyst Web::Machine Web::Request ); for my $module (@downstream) { my $tmp = tempdir(CLEANUP => 1); is(system("cpanm --notest -l $tmp ."), 0); is(system("cpanm -l $tmp --test-only $module"), 0, $module); } done_testing; Plack-1.0030/t/FCGIUtils.pm000644 000765 000024 00000004730 12244057435 016135 0ustar00miyagawastaff000000 000000 package t::FCGIUtils; use strict; use warnings; use File::Temp (); use FindBin; use Test::More; use IO::Socket; use File::Spec; use Test::TCP qw/test_tcp empty_port/; use parent qw/Exporter/; # this file is copied from Catalyst. thanks! our @EXPORT = qw/ test_lighty_external /; # test for FCGI External Server sub test_lighty_external (&@) { my ($callback, $lighty_port, $fcgi_port) = @_; $lighty_port ||= empty_port(); $fcgi_port ||= empty_port($lighty_port); my $lighttpd_bin = $ENV{LIGHTTPD_BIN} || `which lighttpd`; chomp $lighttpd_bin; plan skip_all => 'Please set LIGHTTPD_BIN to the path to lighttpd' unless $lighttpd_bin && -x $lighttpd_bin; my $ver = (`$lighttpd_bin -v` =~ m!lighttpd[-/]1.(\d+\.\d+)!)[0]; if ($ver < 4.17) { plan skip_all => "Too old lighttpd (1.$ver), known to be broken"; } diag "Testing with lighttpd 1.$ver"; my $tmpdir = File::Temp::tempdir( CLEANUP => 1 ); test_tcp( client => sub { $callback->($lighty_port, $fcgi_port, ($ver && $ver < 4.23)); warn `cat $tmpdir/error.log` if $ENV{DEBUG}; }, server => sub { my $conffname = File::Spec->catfile($tmpdir, "lighty.conf"); _write_file($conffname => _render_conf($tmpdir, $lighty_port, $fcgi_port)); my $pid = open my $lighttpd, "$lighttpd_bin -D -f $conffname 2>&1 |" or die "Unable to spawn lighttpd: $!"; $SIG{TERM} = sub { kill 'INT', $pid; close $lighttpd; exit; }; sleep 60; # waiting tests. die "server timeout"; }, port => $lighty_port, ); } sub _write_file { my ($fname, $src) = @_; open my $fh, '>', $fname or die $!; print {$fh} $src or die $!; close $fh; } sub _render_conf { my ($tmpdir, $port, $fcgiport) = @_; my $script_name = $ENV{PLACK_TEST_SCRIPT_NAME} || '/'; <<"END"; # basic lighttpd config file for testing fcgi(external server)+Plack server.modules += ("mod_fastcgi") server.document-root = "$tmpdir" server.bind = "127.0.0.1" server.port = $port # HTTP::Engine app specific fcgi setup fastcgi.server = ( "$script_name" => (( "check-local" => "disable", "host" => "127.0.0.1", "port" => $fcgiport, "idle-timeout" => 20, "fix-root-scriptname" => "enable", # for 1.4.23 or later )) ) END } 1; Plack-1.0030/t/HTTP-Message-PSGI/000755 000765 000024 00000000000 12244057435 016743 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/HTTP-Server-PSGI/000755 000765 000024 00000000000 12244057435 016625 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Builder/000755 000765 000024 00000000000 12244057435 016460 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Handler/000755 000765 000024 00000000000 12244057435 016447 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-HTTPParser-PP/000755 000765 000024 00000000000 12244057435 017343 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Loader/000755 000765 000024 00000000000 12244057435 016300 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Middleware/000755 000765 000024 00000000000 12244057435 017147 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-MIME/000755 000765 000024 00000000000 12244057435 015621 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Request/000755 000765 000024 00000000000 12244057435 016522 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Response/000755 000765 000024 00000000000 12244057435 016670 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Runner/000755 000765 000024 00000000000 12244057435 016343 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-TempBuffer/000755 000765 000024 00000000000 12244057435 017131 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Test/000755 000765 000024 00000000000 12244057435 016011 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Util/000755 000765 000024 00000000000 12244057435 016007 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/release-pod-syntax.t000644 000765 000024 00000000450 12244057435 017752 0ustar00miyagawastaff000000 000000 #!perl BEGIN { unless ($ENV{RELEASE_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for release candidate testing'); } } use Test::More; eval "use Test::Pod 1.41"; plan skip_all => "Test::Pod 1.41 required for testing POD" if $@; all_pod_files_ok(); Plack-1.0030/t/test.txt000644 000765 000024 00000000004 12244057435 015554 0ustar00miyagawastaff000000 000000 foo Plack-1.0030/t/Plack-Util/bad.psgi000644 000765 000024 00000000112 12244057435 017413 0ustar00miyagawastaff000000 000000 use strict; eval { load_class("CGI") }; sub { [ 200, [], ["Hello"] ] }; Plack-1.0030/t/Plack-Util/bad2.psgi000644 000765 000024 00000000100 12244057435 017472 0ustar00miyagawastaff000000 000000 sub load_class { die "woohaa" } sub { [ 200, [], ["Hello"] ] }; Plack-1.0030/t/Plack-Util/bin/000755 000765 000024 00000000000 12244057435 016557 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Util/error.psgi000644 000765 000024 00000000050 12244057435 020017 0ustar00miyagawastaff000000 000000 use strict; sub { $env = shift; }; Plack-1.0030/t/Plack-Util/foreach.t000644 000765 000024 00000001454 12244057435 017607 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Util; { package Foo; sub new { bless {}, shift } } do { for my $body ('error', \'error', qr//, +{}, sub {}, Foo->new()) { eval { Plack::Util::foreach($body, sub {}); }; like $@, qr/Can't (call|locate object) method "getline"/; } }; do { my @x = (0, 1); Plack::Util::foreach([0, 1], sub { my $line = shift; is($line, $x[$line]) }); }; { package Bar; sub new { bless { i => 0 }, shift } my @x = (2, 3); sub getline { my $self = shift; $x[$self->{i}++]; } sub current { $x[shift->{i}-1] } sub close { ::ok(1, 'close') } } do { my $bar = Bar->new; Plack::Util::foreach($bar, sub { my $line = shift; is($line, $bar->current) }); }; done_testing; Plack-1.0030/t/Plack-Util/headers.t000644 000765 000024 00000002743 12244057435 017615 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Util; { my $headers = [ Foo => 'bar' ]; Plack::Util::header_set($headers, Bar => 'baz'); is_deeply $headers, [ Foo => 'bar', Bar => 'baz' ]; } { my $headers = [ Foo => 'bar' ]; Plack::Util::header_set($headers, Foo => 'baz'); is_deeply $headers, [ Foo => 'baz' ]; } { my $headers = [ Foo => 'bar' ]; Plack::Util::header_set($headers, foo => 'baz'); is_deeply $headers, [ Foo => 'baz' ], 'header_set case-insensitive'; } { my $headers = [ Foo => 'bar' ]; is Plack::Util::header_get($headers, 'Foo'), 'bar'; } { my $headers = [ Foo => 'bar' ]; is Plack::Util::header_get($headers, 'foo'), 'bar', 'header_get case-insensitive' } { my $headers = [ Foo => 'bar', Bar => 'baz' ]; Plack::Util::header_push($headers, Foo => 'quox'); is_deeply $headers, [ Foo => 'bar', Bar => 'baz', Foo => 'quox' ]; } { my $headers = [ Foo => 'bar', Bar => 'baz' ]; Plack::Util::header_remove($headers, 'Foo'); is_deeply $headers, [ Bar => 'baz' ]; } { my $headers = [ Foo => 'bar', Bar => 'baz' ]; Plack::Util::header_remove($headers, 'foo'); is_deeply $headers, [ Bar => 'baz' ], 'header_remove case-insensitive'; } { my $headers = [ Foo => 'bar', Bar => 'baz' ]; is Plack::Util::header_exists($headers, 'Foo'), 1; } { my $headers = [ Foo => 'bar', Foo => 'baz' ]; Plack::Util::header_set($headers, Foo => 'quox'); is_deeply $headers, [ Foo => 'quox' ]; } done_testing; Plack-1.0030/t/Plack-Util/headers_obj.t000644 000765 000024 00000001061 12244057435 020437 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Util; { my $headers = [ Foo => 'bar' ]; my $h = Plack::Util::headers($headers); $h->set(Bar => 'baz'); is_deeply $headers, [ Foo => 'bar', Bar => 'baz' ]; is_deeply $h->headers, [ Foo => 'bar', Bar => 'baz' ]; is $h->get('Foo'), 'bar'; $h->push('Foo' => 'xxx'); is $h->get('Foo'), 'bar'; my @v = $h->get('Foo'); is_deeply \@v, [ 'bar', 'xxx' ]; ok $h->exists('Bar'); $h->remove('Bar'); ok ! $h->exists('Bar'); is_deeply $headers, $h->headers; } done_testing; Plack-1.0030/t/Plack-Util/Hello.pm000644 000765 000024 00000000220 12244057435 017402 0ustar00miyagawastaff000000 000000 package Hello; sub to_app { return sub { return [200, ['Content-Type', 'text/plain'], ['Hello']]; }; } __PACKAGE__->to_app; Plack-1.0030/t/Plack-Util/hello.psgi000644 000765 000024 00000000101 12244057435 017766 0ustar00miyagawastaff000000 000000 sub { return [200, ['Content-Type', 'text/plain'], ['Hello']] }; Plack-1.0030/t/Plack-Util/inc/000755 000765 000024 00000000000 12244057435 016560 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Util/inline_object.t000644 000765 000024 00000001012 12244057435 020772 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Util; use Try::Tiny; my $counter; my $object = Plack::Util::inline_object( method1 => sub { $counter++ }, ); $object->method1; is $counter, 1, 'method call works'; my $sub = $object->can('method1'); ok $sub, 'can returns true value for method'; try { $sub->($object) }; is $counter, 2, 'can returns sub ref for method'; ok ! try { $object->method2; 1 }, 'croaks if nonexistant method called'; is $object->can('method2'), undef, 'can returns undef for nonexistant method'; done_testing; Plack-1.0030/t/Plack-Util/io_with_path.t000644 000765 000024 00000000360 12244057435 020651 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Util; open my $fh, "<", "t/test.txt"; Plack::Util::set_io_path($fh, "/path/to/test.txt"); is $fh->path, "/path/to/test.txt"; like scalar <$fh>, qr/foo/; ok fileno $fh; isa_ok $fh, 'IO::Handle'; done_testing; Plack-1.0030/t/Plack-Util/is_real_fh.t000644 000765 000024 00000000722 12244057435 020270 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Util; { open my $fh, __FILE__; ok Plack::Util::is_real_fh($fh); } { my $fh = IO::File->new(__FILE__); ok Plack::Util::is_real_fh($fh); } { open my $fh, "<", "/dev/null"; ok ! Plack::Util::is_real_fh($fh); } { open my $fh, "<", \"foo"; ok ! Plack::Util::is_real_fh($fh); } { use IO::File; my $fh = IO::File->new("/dev/null"); ok ! Plack::Util::is_real_fh($fh); } done_testing; Plack-1.0030/t/Plack-Util/load.t000644 000765 000024 00000002475 12244057435 017123 0ustar00miyagawastaff000000 000000 use strict; use Plack::Util; use Plack::Test; use HTTP::Request::Common; use Test::More; { my $app = Plack::Util::load_psgi("t/Plack-Util/hello.psgi"); ok $app; test_psgi $app, sub { is $_[0]->(GET "/")->content, "Hello"; }; } { my $app = Plack::Util::load_psgi("t/Plack-Util/bad.psgi"); ok $app; ok !$INC{"CGI.pm"}; } { my $app = Plack::Util::load_psgi("t/Plack-Util/bad2.psgi"); ok $app; eval { Plack::Util::load_class("Plack") }; is $@, ''; } { use lib "t/Plack-Util"; my $app = Plack::Util::load_psgi("Hello"); ok $app; test_psgi $app, sub { is $_[0]->(GET "/")->content, "Hello"; }; } { eval { Plack::Util::load_psgi("t/Plack-Util/error.psgi") }; like $@, qr/Global symbol/; } { eval { Plack::Util::load_psgi("t/Plack-Util/nonexistent.psgi") }; unlike $@, qr/Died/; } { my $app = Plack::Util::load_psgi("t/Plack-Util/bin/findbin.psgi"); test_psgi $app, sub { like $_[0]->(GET "/")->content, qr!t[/\\]Plack-Util[/\\]bin$!; } } { require Cwd; my $cwd = Cwd::cwd(); chdir "t/Plack-Util"; local @INC = ("./inc", @INC); my $app = Plack::Util::load_psgi("hello.psgi"); ok $app; test_psgi $app, sub { is $_[0]->(GET "/")->content, "Hello"; }; chdir $cwd; } done_testing; Plack-1.0030/t/Plack-Util/response_cb.t000644 000765 000024 00000001351 12244057435 020476 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Plack::Util; use Plack::Test; use Test::More; use HTTP::Request::Common; $Plack::Test::Impl = "Server"; my $app = sub { my $env = shift; return sub { my $respond = shift; my $writer = $respond->([200, [ 'Content-Type' => 'text/plain' ]]); $writer->write('foo'); $writer->write('bar'); $writer->close; }; }; my $mw = sub { my $env = shift; my $res = $app->($env); Plack::Util::response_cb($res, sub { my $res = shift; return sub { my $chunk = shift; return $chunk; } }); }; test_psgi $mw, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, 'foobar'; }; done_testing; Plack-1.0030/t/Plack-Util/inc/hello.psgi000644 000765 000024 00000000035 12244057435 020545 0ustar00miyagawastaff000000 000000 die "Do not load this file"; Plack-1.0030/t/Plack-Util/bin/findbin.psgi000644 000765 000024 00000000125 12244057435 021052 0ustar00miyagawastaff000000 000000 use FindBin; sub { [ 200, [ "Content-Type", "text/plain" ], [ "$FindBin::Bin" ] ] }; Plack-1.0030/t/Plack-Test/2args.t000644 000765 000024 00000000472 12244057435 017217 0ustar00miyagawastaff000000 000000 use Plack::Test; use Test::More; use HTTP::Request::Common; $Plack::Test::Impl = "Server"; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my $app = sub { return [ 200, [], [ "Hello" ] ] }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, "Hello"; }; done_testing; Plack-1.0030/t/Plack-Test/cookie.t000644 000765 000024 00000001041 12244057435 017443 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use HTTP::Request::Common; use Test::More; use Test::Requires qw(HTTP::Cookies); my $app = sub { return [ 200, [ 'Content-Type' => 'text/html', 'Set-Cookie' => "ID=123; path=/" ], [ "Hi" ] ]; }; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/"); my $cookie_jar = HTTP::Cookies->new; $cookie_jar->extract_cookies($res); my @cookies; $cookie_jar->scan( sub { @cookies = @_ }); ok @cookies; is $cookies[1], 'ID'; }; done_testing; Plack-1.0030/t/Plack-Test/hello.t000644 000765 000024 00000000750 12244057435 017303 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Test; $Plack::Test::Impl = "MockHTTP"; test_psgi client => sub { my $cb = shift; my $req = HTTP::Request->new(GET => "http://localhost/hello"); my $res = $cb->($req); is $res->content, 'Hello World'; is $res->content_type, 'text/plain'; is $res->code, 200; }, app => sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello World" ] ]; }; done_testing; Plack-1.0030/t/Plack-Test/hello_server.t000644 000765 000024 00000001027 12244057435 020667 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Test; $Plack::Test::Impl = "Server"; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; test_psgi client => sub { my $cb = shift; my $req = HTTP::Request->new(GET => "http://localhost/hello"); my $res = $cb->($req); is $res->content, 'Hello World'; is $res->content_type, 'text/plain'; is $res->code, 200; }, app => sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello World" ] ]; }; done_testing; Plack-1.0030/t/Plack-Test/suite.t000644 000765 000024 00000000723 12244057435 017331 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use FindBin; use HTTP::Message::PSGI; use Plack; use Plack::Test::Suite; use Plack::Util; Plack::Test::Suite->runtests(sub { my ($name, $test, $handler) = @_; note $name; my $cb = sub { my $req = shift; my $env = req_to_psgi($req); my $res = res_from_psgi(Plack::Util::run_app $handler, $env); $res->request($req); return $res; }; $test->($cb); }); done_testing; Plack-1.0030/t/Plack-TempBuffer/print.t000644 000765 000024 00000001646 12244057435 020461 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::TempBuffer; my $warn = ''; $SIG{__WARN__} = sub { $warn .= $_[0] }; { my $b = Plack::TempBuffer->new(-1); $b->print("foo"); is $b->size, 3; my $fh = $b->rewind; is do { local $/; <$fh> }, 'foo'; $fh->seek(0, 0); } { local $Plack::TempBuffer::MaxMemoryBufferSize = 12; my $b = Plack::TempBuffer->new; is $b->size, 0; $b->print("foo") for 1..5; is $b->size, 15; my $fh = $b->rewind; isa_ok $fh, 'IO::File'; is do { local $/; <$fh> }, ('foo' x 5); like $warn, qr/MaxMemoryBufferSize.*deprecated/; $warn = ''; } { local $Plack::TempBuffer::MaxMemoryBufferSize = 0; my $b = Plack::TempBuffer->new(3); $b->print("foo\n"); is $b->size, 4; my $fh = $b->rewind; isa_ok $fh, 'IO::File'; is do { local $/; <$fh> }, "foo\n"; like $warn, qr/MaxMemoryBufferSize.*deprecated/; $warn = ''; } done_testing; Plack-1.0030/t/Plack-Runner/options.t000644 000765 000024 00000002712 12244057435 020225 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Runner; sub p { my $r = Plack::Runner->new; $r->parse_options(@_); return {@{$r->{options}}}; } my %defaults = ( host => undef, port => 5000, listen => [ ':5000' ], socket => undef ); is_deeply p(), { %defaults }; is_deeply p('-l', '/tmp/foo.sock'), { host => undef, port => undef, listen => [ '/tmp/foo.sock' ], socket => '/tmp/foo.sock' }; is_deeply p('-o', '0.0.0.0', '--port', 9000), { host => '0.0.0.0', port => 9000, listen => [ '0.0.0.0:9000' ], socket => undef }; is_deeply p('-S', 'foo.sock'), { host => undef, port => undef, listen => [ 'foo.sock' ], socket => 'foo.sock' }; is_deeply p('-l', ':80'), { host => undef, port => 80, listen => [ ':80' ], socket => undef }; is_deeply p('-l', '10.0.0.1:80', '-l', 'unix.sock'), { host => '10.0.0.1', port => 80, listen => [ '10.0.0.1:80', 'unix.sock' ], socket => 'unix.sock' }; is_deeply p('-l', ':80', '--disable-foo', '--enable-bar'), { host => undef, port => 80, listen => [ ':80' ], socket => undef, foo => '', bar => 1 }; { my $r = Plack::Runner->new; $r->parse_options('-D', '--workers=50', '-E', 'development', 'foo.psgi', '--list=4000'); is $r->{env}, 'development'; is $r->{daemonize}, 1; is_deeply $r->{argv}, [ 'foo.psgi' ]; my $options = {@{$r->{options}}}; is $options->{daemonize}, 1; is $options->{workers}, 50; is_deeply $options->{listen}, [ ':5000' ]; is $options->{list}, '4000'; } done_testing; Plack-1.0030/t/Plack-Runner/path.t000644 000765 000024 00000002024 12244057435 017462 0ustar00miyagawastaff000000 000000 use strict; use Cwd; use File::Spec; use File::Temp; use Test::Requires qw(LWP::UserAgent); use Test::More; use Test::TCP qw(empty_port); plan skip_all => "release test only" unless $ENV{RELEASE_TESTING}; sub write_file($$){ my ( $path, $content ) = @_; open my $out, '>', $path or die "$path: $!"; print $out $content; close $out; } my $tmpdir = File::Temp::tempdir( CLEANUP => 1 ); my $psgi_file = File::Spec->catfile($tmpdir, 'app.psgi'); write_file $psgi_file, qq/my \$app = sub {return [200, [], ["hello world"]]}\n/; my $port = empty_port(); my $pid = fork; if ($pid == 0) { close STDERR; exec($^X, '-Ilib', 'script/plackup', '-p', $port, '--path', '/app/', '-a', $psgi_file) or die $@; } else { $SIG{INT} = 'IGNORE'; sleep 1; my $ua = LWP::UserAgent->new; my $res = $ua->get("http://localhost:$port/"); is $res->code, 404; $res = $ua->get("http://localhost:$port/app/"); is $res->code, 200; is $res->content, 'hello world'; kill 'INT', $pid; wait; } done_testing; Plack-1.0030/t/Plack-Response/body.t000644 000765 000024 00000001264 12244057435 020015 0ustar00miyagawastaff000000 000000 use strict; use warnings; use FindBin; use Test::More; use Plack::Response; use URI; use File::Temp; sub r($) { my $res = Plack::Response->new(200); $res->body(@_); return $res->finalize->[2]; } is_deeply r "Hello World", [ "Hello World" ]; is_deeply r [ "Hello", "World" ], [ "Hello", "World" ]; { open my $fh, "$FindBin::Bin/body.t"; is_deeply r $fh, $fh; } { my $foo = "bar"; open my $io, "<", \$foo; is_deeply r $io, $io; } { my $uri = URI->new("foo"); # stringified object is_deeply r $uri, [ $uri ]; } { my $tmp = File::Temp->new; # File::Temp has stringify method, but it is-a IO::Handle. is_deeply r $tmp, $tmp; } done_testing; Plack-1.0030/t/Plack-Response/compatible.t000644 000765 000024 00000002146 12244057435 021177 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Response; { my $res = Plack::Response->new; $res->code(200); $res->header("Foo\000Bar" => "baz"); $res->header("Qux\177Quux" => "42"); $res->body("Hello"); is_deeply $res->finalize, [ 200, [ "Foo\000Bar" => 'baz', "Qux\177Quux" => '42' ], ["Hello"] ]; } { my $res = Plack::Response->new; $res->code(200); $res->header("X-LWS-I" => "Bar\r\n true"); $res->header("X-LWS-II" => "Bar\r\n\t\ttrue"); $res->body("Hello"); is_deeply $res->finalize, [ 200, [ 'X-LWS-I' => 'Bar true', 'X-LWS-II' => 'Bar true' ], ["Hello"] ]; } { my $res = Plack::Response->new; $res->code(200); $res->header("X-CR-LF" => "Foo\nBar\rBaz"); $res->body("Hello"); is_deeply $res->finalize, [ 200, [ 'X-CR-LF' => 'FooBarBaz' ], ["Hello"] ]; } { my $res = Plack::Response->new; $res->code(200); $res->header("X-CR-LF" => "Foo\nBar\rBaz"); $res->body("Hello"); is_deeply $res->finalize, [ 200, [ 'X-CR-LF' => 'FooBarBaz' ], ["Hello"] ]; } done_testing; Plack-1.0030/t/Plack-Response/cookie.t000644 000765 000024 00000001322 12244057435 020324 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::More; use Plack::Response; use HTTP::Request::Common; my $app = sub { my $res = Plack::Response->new(200); $res->cookies->{t1} = { value => "bar", domain => '.example.com', path => '/cgi-bin' }; $res->cookies->{t2} = { value => "xxx yyy", expires => time + 3600 }; $res->cookies->{t3} = { value => "123123", "max-age" => 15 }; $res->finalize; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); my @v = sort $res->header('Set-Cookie'); is $v[0], "t1=bar; domain=.example.com; path=/cgi-bin"; like $v[1], qr/t2=xxx%20yyy; expires=\w+, \d+-\w+-\d+ \d\d:\d\d:\d\d GMT/; is $v[2], "t3=123123; max-age=15"; }; done_testing; Plack-1.0030/t/Plack-Response/new.t000644 000765 000024 00000001226 12244057435 017647 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Response; { my $res = Plack::Response->new(302); is $res->status, 302; is $res->code, 302; } { my $res = Plack::Response->new(200, [ 'Content-Type' => 'text/plain' ]); is $res->content_type, 'text/plain'; } { my $res = Plack::Response->new(200, { 'Content-Type' => 'text/plain' }); is $res->content_type, 'text/plain'; } { my $res = Plack::Response->new(200); $res->content_type('image/png'); is $res->content_type, 'image/png'; } { my $res = Plack::Response->new(200); $res->header('X-Foo' => "bar"); is $res->header('X-Foo'), "bar"; } done_testing; Plack-1.0030/t/Plack-Response/redirect.t000644 000765 000024 00000001347 12244057435 020663 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Response; { my $res = Plack::Response->new; $res->redirect('http://www.google.com/'); is $res->location, 'http://www.google.com/'; is $res->code, 302; is_deeply $res->finalize, [ 302, [ 'Location' => 'http://www.google.com/' ], [] ]; } { my $res = Plack::Response->new; $res->redirect('http://www.google.com/', 301); is_deeply $res->finalize, [ 301, [ 'Location' => 'http://www.google.com/' ], [] ]; } { my $uri_invalid = "http://www.google.com/\r\nX-Injection: true\r\n\r\nHello World"; my $res = Plack::Response->new; $res->redirect($uri_invalid, 301); my $psgi_res = $res->finalize; ok $psgi_res->[1][1] !~ /\n/; } done_testing; Plack-1.0030/t/Plack-Response/response.t000644 000765 000024 00000002313 12244057435 020712 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Response; sub res { my $res = Plack::Response->new; my %v = @_; while (my($k, $v) = each %v) { $res->$k($v); } $res->finalize; } is_deeply( res( status => 200, body => 'hello', ), [ 200, +[], [ 'hello' ] ] ); my $res = res( status => 200, cookies => +{ 'foo_sid' => +{ value => 'ASDFJKL:', expires => 'Thu, 25-Apr-1999 00:40:33 GMT', domain => 'example.com', path => '/', }, 'poo_sid' => +{ value => 'QWERTYUI', expires => 'Thu, 25-Apr-1999 00:40:33 GMT', domain => 'example.com', path => '/', }, }, body => 'hello', ); is($res->[0], 200); is(scalar(@{ $res->[1] }), 4); is($res->[1][0], 'Set-Cookie'); is($res->[1][2], 'Set-Cookie'); my @cookies = sort($res->[1][1], $res->[1][3]); is($cookies[0], 'foo_sid=ASDFJKL%3A; domain=example.com; path=/; expires=Thu, 25-Apr-1999 00:40:33 GMT'); is($cookies[1], 'poo_sid=QWERTYUI; domain=example.com; path=/; expires=Thu, 25-Apr-1999 00:40:33 GMT'); is(scalar(@{ $res->[2] }), 1); is($res->[2][0], 'hello'); done_testing; Plack-1.0030/t/Plack-Response/to_app.t000644 000765 000024 00000000535 12244057435 020342 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use HTTP::Request::Common; use Plack::Test; use Plack::Response; my $res = Plack::Response->new(200); $res->body("hello"); test_psgi $res->to_app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200, 'response code'; is $res->content, 'hello', 'content'; }; done_testing; Plack-1.0030/t/Plack-Request/base.t000644 000765 000024 00000002535 12244057435 017626 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Plack::Request; use Test::More; my @tests = ( { host => 'localhost', base => 'http://localhost/' }, { script_name => '/foo', host => 'localhost', base => 'http://localhost/foo' }, { script_name => '/foo bar', host => 'localhost', base => 'http://localhost/foo%20bar' }, { scheme => 'http', host => 'localhost:91', base => 'http://localhost:91/' }, { scheme => 'http', host => 'example.com', base => 'http://example.com/' }, { scheme => 'https', host => 'example.com', base => 'https://example.com/' }, { scheme => 'http', server_name => 'example.com', server_port => 80, base => 'http://example.com/' }, { scheme => 'http', server_name => 'example.com', server_port => 8080, base => 'http://example.com:8080/' }, { host => 'foobar.com', server_name => 'example.com', server_port => 8080, base => 'http://foobar.com/' }, ); plan tests => 1*@tests; for my $block (@tests) { my $env = { 'psgi.url_scheme' => $block->{scheme} || 'http', HTTP_HOST => $block->{host} || undef, SERVER_NAME => $block->{server_name} || undef, SERVER_PORT => $block->{server_port} || undef, SCRIPT_NAME => $block->{script_name} || '', }; my $req = Plack::Request->new($env); is $req->base, $block->{base}; } Plack-1.0030/t/Plack-Request/body.t000644 000765 000024 00000000656 12244057435 017653 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Test; use Plack::Request; use HTTP::Request::Common; my $app = sub { my $req = Plack::Request->new(shift); is_deeply $req->body_parameters, { foo => 'bar' }; is $req->content, 'foo=bar'; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(POST "/", { foo => "bar" }); ok $res->is_success; }; done_testing; Plack-1.0030/t/Plack-Request/content-on-get.t000644 000765 000024 00000000677 12244057435 021562 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Test; use Plack::Request; use HTTP::Request::Common; my $app = sub { my $req = Plack::Request->new(shift); is $req->content, ''; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); ok $res->is_success or diag $res->content; $res = $cb->(HEAD "/"); ok $res->is_success or diag $res->content; }; done_testing; Plack-1.0030/t/Plack-Request/content.t000644 000765 000024 00000001225 12244057435 020361 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Test; use Plack::Request; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); is $req->content, 'body'; # emulate other PSGI apps that reads from input, but not reset $env->{'psgi.input'}->read(my($dummy), $env->{CONTENT_LENGTH}, 0); $req = Plack::Request->new($env); is $req->content, 'body'; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; my $req = HTTP::Request->new(POST => "/"); $req->content("body"); $req->content_type('text/plain'); $req->content_length(4); $cb->($req); }; done_testing; Plack-1.0030/t/Plack-Request/cookie.t000644 000765 000024 00000002422 12244057435 020160 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use HTTP::Request; use Plack::Test; use Plack::Request; my $app = sub { my $req = Plack::Request->new(shift); is $req->cookies->{undef}, undef, "non-existing keys return undef"; is $req->cookies->{Foo}, 'Bar'; is $req->cookies->{Bar}, 'Baz'; is $req->cookies->{XXX}, 'Foo Bar'; is $req->cookies->{YYY}, 0, "When we get multiple values we return the first one (which e.g. Apache does too)"; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; my $req = HTTP::Request->new(GET => "/"); $req->header(Cookie => 'Foo=Bar; Bar=Baz; XXX=Foo%20Bar; YYY=0; YYY=3'); $cb->($req); }; $app = sub { my $req = Plack::Request->new(shift); is_deeply $req->cookies, {}; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; $cb->(HTTP::Request->new(GET => "/")); }; $app = sub { my $warn = 0; local $SIG{__WARN__} = sub { $warn++ }; my $req = Plack::Request->new(shift); is $req->cookies->{Foo}, 'Bar'; is $warn, 0; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; my $req = HTTP::Request->new(GET => "/"); $req->header(Cookie => 'Foo=Bar,; Bar=Baz;'); $cb->($req); }; done_testing; Plack-1.0030/t/Plack-Request/double_port.t000644 000765 000024 00000000651 12244057435 021227 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Test; use Plack::Request; use HTTP::Request::Common; $Plack::Test::Impl = 'Server'; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my $app = sub { my $req = Plack::Request->new(shift); return [200, [], [ $req->uri ]]; }; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/foo"); ok $res->content !~ /:\d+:\d+/; }; done_testing; Plack-1.0030/t/Plack-Request/foo1.txt000644 000765 000024 00000000004 12244057435 020121 0ustar00miyagawastaff000000 000000 foo Plack-1.0030/t/Plack-Request/foo2.txt000644 000765 000024 00000000004 12244057435 020122 0ustar00miyagawastaff000000 000000 foo Plack-1.0030/t/Plack-Request/hostname.t000644 000765 000024 00000000500 12244057435 020520 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Test; use Plack::Request; plan tests => 2; my $req = Plack::Request->new({ REMOTE_HOST => "foo.example.com" }); is $req->remote_host, "foo.example.com"; $req = Plack::Request->new({ REMOTE_HOST => '', REMOTE_ADDR => '127.0.0.1' }); is $req->address, "127.0.0.1"; Plack-1.0030/t/Plack-Request/many_upload.t000644 000765 000024 00000003544 12244057435 021225 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Request; my $content = qq{------BOUNDARY Content-Disposition: form-data; name="test_upload_file"; filename="yappo.txt" Content-Type: text/plain SHOGUN ------BOUNDARY Content-Disposition: form-data; name="test_upload_file"; filename="yappo2.txt" Content-Type: text/plain SHOGUN2 ------BOUNDARY Content-Disposition: form-data; name="test_upload_file3"; filename="yappo3.txt" Content-Type: text/plain SHOGUN3 ------BOUNDARY Content-Disposition: form-data; name="test_upload_file4"; filename="yappo4.txt" Content-Type: text/plain SHOGUN4 ------BOUNDARY Content-Disposition: form-data; name="test_upload_file4"; filename="yappo5.txt" Content-Type: text/plain SHOGUN4 ------BOUNDARY Content-Disposition: form-data; name="test_upload_file6"; filename="yappo6.txt" Content-Type: text/plain SHOGUN6 ------BOUNDARY-- }; $content =~ s/\r\n/\n/g; $content =~ s/\n/\r\n/g; { open my $in, '<', \$content; my $req = Plack::Request->new({ 'psgi.input' => $in, CONTENT_LENGTH => length($content), CONTENT_TYPE => 'multipart/form-data; boundary=----BOUNDARY', REQUEST_METHOD => 'POST', SCRIPT_NAME => '/', SERVER_PORT => 80, }); my @undef = $req->upload('undef'); is @undef, 0; my $undef = $req->upload('undef'); is $undef, undef; my @uploads = $req->upload('test_upload_file'); like slurp($uploads[0]), qr|^SHOGUN|; like slurp($uploads[1]), qr|^SHOGUN|; is slurp($req->upload('test_upload_file4')), 'SHOGUN4'; my $test_upload_file3 = $req->upload('test_upload_file3'); is slurp($test_upload_file3), 'SHOGUN3'; my @test_upload_file6 = $req->upload('test_upload_file6'); is slurp($test_upload_file6[0]), 'SHOGUN6'; } done_testing; sub slurp { my $up = shift; open my $fh, "<", $up->path or die; join '', <$fh>; } Plack-1.0030/t/Plack-Request/multi_read.t000644 000765 000024 00000000764 12244057435 021043 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Test; use Plack::Request; use HTTP::Request::Common; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); is $req->content, 'foo=bar'; is $req->content, 'foo=bar'; $req = Plack::Request->new($env); is $req->content, 'foo=bar'; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(POST "/", { foo => "bar" }); ok $res->is_success; }; done_testing; Plack-1.0030/t/Plack-Request/new.t000644 000765 000024 00000001313 12244057435 017476 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Request; my $req = Plack::Request->new({ REQUEST_METHOD => 'GET', SERVER_PROTOCOL => 'HTTP/1.1', SERVER_PORT => 80, SERVER_NAME => 'example.com', SCRIPT_NAME => '/foo', REMOTE_ADDR => '127.0.0.1', 'psgi.version' => [ 1, 0 ], 'psgi.input' => undef, 'psgi.errors' => undef, 'psgi.url_scheme' => 'http', }); isa_ok($req, 'Plack::Request'); is($req->address, '127.0.0.1', 'address'); is($req->method, 'GET', 'method'); is($req->protocol, 'HTTP/1.1', 'protocol'); is($req->uri, 'http://example.com/foo', 'uri'); is($req->port, 80, 'port'); is($req->scheme, 'http', 'url_scheme'); done_testing(); Plack-1.0030/t/Plack-Request/parameters.t000644 000765 000024 00000000770 12244057435 021056 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Request; use Plack::Test; use HTTP::Request::Common; my $app = sub { my $req = Plack::Request->new(shift); my $b = $req->body_parameters; is $b->{foo}, 'bar'; my $q = $req->query_parameters; is $q->{bar}, 'baz'; is_deeply $req->parameters, { foo => 'bar', 'bar' => 'baz' }; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; $cb->(POST "/?bar=baz", { foo => "bar" }); }; done_testing; Plack-1.0030/t/Plack-Request/params.t000644 000765 000024 00000001470 12244057435 020174 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Request; my $req = Plack::Request->new({ QUERY_STRING => "foo=bar" }); is_deeply $req->parameters, { foo => "bar" }; is $req->param('foo'), "bar"; is_deeply [ $req->param ], [ 'foo' ]; $req = Plack::Request->new({ QUERY_STRING => "foo=bar&foo=baz" }); is_deeply $req->parameters, { foo => "baz" }; is $req->param('foo'), "baz"; is_deeply [ $req->param('foo') ] , [ qw(bar baz) ]; is_deeply [ $req->param ], [ 'foo' ]; $req = Plack::Request->new({ QUERY_STRING => "foo=bar&foo=baz&bar=baz" }); is_deeply $req->parameters, { foo => "baz", bar => "baz" }; is_deeply $req->query_parameters, { foo => "baz", bar => "baz" }; is $req->param('foo'), "baz"; is_deeply [ $req->param('foo') ] , [ qw(bar baz) ]; is_deeply [ sort $req->param ], [ 'bar', 'foo' ]; done_testing; Plack-1.0030/t/Plack-Request/path_info.t000644 000765 000024 00000001375 12244057435 020664 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use Plack::App::URLMap; use Plack::Test; use Plack::Request; use HTTP::Request::Common; my $path_app = sub { my $req = Plack::Request->new(shift); my $res = $req->new_response(200); $res->content_type('text/plain'); $res->content($req->path_info); return $res->finalize; }; my $app = Plack::App::URLMap->new; $app->map("/foo" => $path_app); $app->map("/" => $path_app); test_psgi app => $app->to_app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/foo"); is $res->content, ''; $res = $cb->(GET "http://localhost/foo/bar"); is $res->content, '/bar'; $res = $cb->(GET "http://localhost/xxx/yyy"); is $res->content, '/xxx/yyy'; }; done_testing; Plack-1.0030/t/Plack-Request/path_info_escaped.t000644 000765 000024 00000001631 12244057435 022343 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use Plack::Request; use HTTP::Request::Common; use Data::Dumper; my $path_app = sub { my $req = Plack::Request->new(shift); my $res = $req->new_response(200); $res->content_type('text/plain'); $res->content('my ' . Dumper([ $req->uri, $req->parameters ])); return $res->finalize; }; test_psgi $path_app, sub { my $cb = shift; my $res = $cb->(GET "http://localhost/foo.bar-baz?a=b"); is_deeply eval($res->content), [ URI->new("http://localhost/foo.bar-baz?a=b"), { a => 'b' } ]; $res = $cb->(GET "http://localhost/foo%2fbar#ab"); is_deeply eval($res->content), [ URI->new("http://localhost/foo/bar"), {} ], "%2f vs / can't be distinguished - that's alright"; $res = $cb->(GET "http://localhost/%23foo?a=b"); is_deeply eval($res->content), [ URI->new("http://localhost/%23foo?a=b"), { a => 'b' } ]; }; done_testing; Plack-1.0030/t/Plack-Request/readbody.t000644 000765 000024 00000000706 12244057435 020503 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More tests => 1; use Plack::Test; use Plack::Request; use Try::Tiny; { try { my $data = 'a'; open my $input, "<", \$data; my $req = Plack::Request->new({ 'psgi.input' => $input, CONTENT_LENGTH => 3, CONTENT_TYPE => 'application/octet-stream' }); $req->body_parameters; } catch { like $_, qr/Bad Content-Length/; } } Plack-1.0030/t/Plack-Request/request_uri.t000644 000765 000024 00000000704 12244057435 021257 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Request; use Plack::Test; use HTTP::Request::Common; my $app = sub { my $req = Plack::Request->new(shift); return [ 200, [], [ $req->request_uri ] ]; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "http://localhost/foo%20bar"); is $res->content, '/foo%20bar'; $res = $cb->(GET "http://localhost:2020/FOO/bar,baz"); is $res->content, '/FOO/bar,baz'; }; done_testing; Plack-1.0030/t/Plack-Request/upload-basename.t000644 000765 000024 00000000312 12244057435 021740 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More tests => 1; use Plack::Request::Upload; my $upload = Plack::Request::Upload->new( filename => '/tmp/foo/bar/hoge.txt', ); is $upload->basename, 'hoge.txt'; Plack-1.0030/t/Plack-Request/upload-large.t000644 000765 000024 00000001346 12244057435 021267 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Request; use Plack::Test; use HTTP::Request::Common; my $file = "share/baybridge.jpg"; my @backends = qw( Server MockHTTP ); sub flip_backend { $Plack::Test::Impl = shift @backends } local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my $app = sub { my $req = Plack::Request->new(shift); is $req->uploads->{image}->size, -s $file; is $req->uploads->{image}->content_type, 'image/jpeg'; is $req->uploads->{image}->basename, 'baybridge.jpg'; $req->new_response(200)->finalize; }; test_psgi $app, sub { my $cb = shift; $cb->(POST "/", Content_Type => 'form-data', Content => [ image => [ $file ], ]); } while flip_backend; done_testing; Plack-1.0030/t/Plack-Request/upload.t000644 000765 000024 00000002602 12244057435 020173 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Request; use Plack::Test; use HTTP::Request::Common; my @temp_files = (); my $app = sub { my $env = shift; my $req = Plack::Request->new($env); isa_ok $req->uploads->{foo}, 'HASH'; is $req->uploads->{foo}->{filename}, 'foo2.txt'; my @files = $req->upload('foo'); is scalar(@files), 2; is $files[0]->filename, 'foo1.txt'; is $files[1]->filename, 'foo2.txt'; ok -e $files[0]->tempname; is join(', ', sort { $a cmp $b } $req->upload()), 'bar, foo'; for (qw(foo bar)) { my $temp_file = $req->upload($_)->path; ok -f $temp_file; push @temp_files, $temp_file; } my $res = $req->new_response(200); undef $req; # Simulate when we instantiate Plack::Request multiple times # redo the test with the same $env $req = Plack::Request->new($env); @files = $req->upload('foo'); is scalar(@files), 2; is $files[0]->filename, 'foo1.txt'; ok -e $files[0]->tempname; $res->finalize; }; test_psgi $app, sub { my $cb = shift; $cb->(POST "/", Content_Type => 'form-data', Content => [ foo => [ "t/Plack-Request/foo1.txt" ], foo => [ "t/Plack-Request/foo2.txt" ], bar => [ "t/Plack-Request/foo1.txt" ], ]); }; # Check if the temp files got cleaned up properly ok !-f $_ for @temp_files; done_testing; Plack-1.0030/t/Plack-Request/uri.t000644 000765 000024 00000006264 12244057435 017516 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Request; my @tests = ( { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "", }, uri => 'http://example.com/', parameters => {} }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "", PATH_INFO => "/foo bar", }, uri => 'http://example.com/foo%20bar', parameters => {} }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => '/test.c', }, uri => 'http://example.com/test.c', parameters => {} }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => '/test.c', PATH_INFO => '/info', }, uri => 'http://example.com/test.c/info', parameters => {} }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => '/test', QUERY_STRING => 'dynamic=daikuma', }, uri => 'http://example.com/test?dynamic=daikuma', parameters => { dynamic => 'daikuma' } }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => '/exec/' }, uri => 'http://example.com/exec/', parameters => {} }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => '/exec/' }, uri => 'http://example.com/exec/', parameters => {} }, { add_env => { SERVER_NAME => 'example.com' }, uri => 'http://example.com/', parameters => {} }, { add_env => {}, uri => 'http:///', parameters => {} }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "", QUERY_STRING => 'aco=tie' }, uri => 'http://example.com/?aco=tie', parameters => { aco => 'tie' } }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "", QUERY_STRING => "foo_only" }, uri => 'http://example.com/?foo_only', parameters => { foo_only => '' } }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "", QUERY_STRING => "foo&bar=baz" }, uri => 'http://example.com/?foo&bar=baz', parameters => { foo => '', bar => 'baz' } }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "", QUERY_STRING => 0 }, uri => 'http://example.com/?0', parameters => { 0 => '' } }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "/foo bar", PATH_INFO => "/baz quux", }, uri => 'http://example.com/foo%20bar/baz%20quux', parameters => {} }, { add_env => { HTTP_HOST => 'example.com', SCRIPT_NAME => "/path", PATH_INFO => "/parameters;path=one,two", QUERY_STRING => "query=foobar", }, uri => 'http://example.com/path/parameters;path=one,two?query=foobar', parameters => { query => "foobar" } }, ); plan tests => 2 * @tests; for my $block (@tests) { my $env = {SERVER_PORT => 80}; while (my($key, $val) = each %{ $block->{add_env} || {} }) { $env->{$key} = $val; } my $req = Plack::Request->new($env); is $req->uri, $block->{uri}; is_deeply $req->query_parameters, $block->{parameters}; }; Plack-1.0030/t/Plack-Request/uri_utf8.t000644 000765 000024 00000000512 12244057435 020452 0ustar00miyagawastaff000000 000000 use strict; use utf8; use Plack::Request; use HTTP::Request; use HTTP::Message::PSGI; use Test::More; my $path = "/Платежи"; my $hreq = HTTP::Request->new(GET => "http://localhost" . $path); my $req = Plack::Request->new($hreq->to_psgi); is $req->uri->path, '/%D0%9F%D0%BB%D0%B0%D1%82%D0%B5%D0%B6%D0%B8'; done_testing; Plack-1.0030/t/Plack-MIME/add_type.t000644 000765 000024 00000000412 12244057435 017574 0ustar00miyagawastaff000000 000000 use Plack::MIME; use Test::More; Plack::MIME->add_type(".foo" => "text/foo"); is( Plack::MIME->mime_type("bar.foo"), "text/foo" ); Plack::MIME->add_type(".c" => "application/c-source"); is( Plack::MIME->mime_type("FOO.C"), "application/c-source" ); done_testing; Plack-1.0030/t/Plack-MIME/basic.t000644 000765 000024 00000000346 12244057435 017072 0ustar00miyagawastaff000000 000000 use Plack::MIME; use Test::More; sub x($) { Plack::MIME->mime_type($_[0]) } is x ".gif", "image/gif"; is x "foo.png", "image/png"; is x "foo.GIF", "image/gif"; is x "foo.bar", undef; is x "foo.mp3", "audio/mpeg"; done_testing; Plack-1.0030/t/Plack-MIME/fallback.t000644 000765 000024 00000000427 12244057435 017550 0ustar00miyagawastaff000000 000000 use Test::More; use Test::Requires qw(MIME::Types); use Plack::MIME; use MIME::Types 'by_suffix'; is( Plack::MIME->mime_type(".vcd"), undef ); Plack::MIME->set_fallback(sub { (by_suffix $_[0])[0] }); is( Plack::MIME->mime_type(".vcd"), "application/x-cdlink" ); done_testing; Plack-1.0030/t/Plack-Middleware/access_log.t000644 000765 000024 00000003322 12244057435 021436 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use HTTP::Request::Common; use Plack::Test; use Plack::Builder; use POSIX; my $log; my $test = sub { my $format = shift; return sub { my $req = shift; my $app = builder { enable "Plack::Middleware::AccessLog", logger => sub { $log = "@_" }, format => $format; sub { [ 200, [ 'Content-Type' => 'text/plain', 'Content-Length', 2 ], [ 'OK' ] ] }; }; test_psgi $app, sub { $_[0]->($req) }; }; }; { my $req = GET "http://example.com/"; $req->header("Host" => "example.com", "X-Forwarded-For" => "192.0.2.1"); my $fmt = "%P %{Host}i %p %{X-Forwarded-For}i %{Content-Type}o %{%m %y}t %v"; $test->($fmt)->($req); chomp $log; my $month_year = POSIX::strftime('%m %y', localtime); is $log, "$$ example.com 80 192.0.2.1 text/plain [$month_year] example.com"; } { $test->("%D")->(GET "/"); chomp $log; is $log, '-'; } { my $req = GET "http://example.com/"; my $fmt = "%r == %m %U%q %H"; $test->($fmt)->($req); chomp $log; my ($r, $rs) = split / == /, $log; is $r, $rs; } { my $req = GET "http://example.com/foo?bar=baz"; my $fmt = "%r == %m %U%q %H"; $test->($fmt)->($req); chomp $log; my ($r, $rs) = split / == /, $log; is $r, $rs; } { my $req = POST "http://example.com/foo", [ "bar", "baz" ]; my $fmt = "cti=%{Content-Type}i cli=%{Content-Length}i cto=%{Content-Type}o clo=%{Content-Length}o"; $test->($fmt)->($req); chomp $log; my %vals = split /[= ]/, $log; is_deeply \%vals, { cti => "application/x-www-form-urlencoded", cli => 7, cto => 'text/plain', clo => 2 }; } done_testing; Plack-1.0030/t/Plack-Middleware/access_log_timed.t000644 000765 000024 00000005113 12244057435 022620 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use HTTP::Request::Common; use Plack::Test; use Plack::Builder; my $log; my $handler = builder { enable "Plack::Middleware::AccessLog::Timed", logger => sub { $log .= "@_" }; sub { [ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }; }; my $test_req = sub { my $req = shift; test_psgi app => $handler, client => sub { my $cb = shift; $cb->($req); }; }; { $test_req->(GET "http://localhost/"); like $log, qr@^127\.0\.0\.1 - - \[.*?\] "GET / HTTP/1\.1" 200 2@; } { $log = ""; $test_req->(POST "http://localhost/foo", { foo => "bar" }); like $log, qr@^127\.0\.0\.1 - - \[.*?\] "POST /foo HTTP/1\.1" 200 2@; } { $log = ""; $test_req->(GET "http://localhost/foo%20bar?baz=baz"); like $log, qr@GET /foo%20bar\?baz=baz HTTP/1\.1@; } # Testing delayed responses $log = ""; $handler = builder { enable "Plack::Middleware::AccessLog::Timed", logger => sub { $log .= "@_" }; sub { return sub { $_[0]->( [ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ] ) } }; }; $test_req = sub { my $req = shift; test_psgi app => $handler, client => sub { my $cb = shift; $cb->($req); }; }; { $test_req->(GET "http://localhost/"); like $log, qr@^127\.0\.0\.1 - - \[.*?\] "GET / HTTP/1\.1" 200 2@; } { $log = ""; $test_req->(POST "http://localhost/foo", { foo => "bar" }); like $log, qr@^127\.0\.0\.1 - - \[.*?\] "POST /foo HTTP/1\.1" 200 2@; } { $log = ""; $test_req->(GET "http://localhost/foo%20bar?baz=baz"); like $log, qr@GET /foo%20bar\?baz=baz HTTP/1\.1@; } # Testing streaming responses $log = ""; $handler = builder { enable "Plack::Middleware::AccessLog::Timed", logger => sub { $log .= "@_" }; sub { return sub { my $writer = $_[0]->( [ 200, [ 'Content-Type' => 'text/plain' ] ] ); $writer->write("OK"); $writer->close; } }; }; $test_req = sub { my $req = shift; test_psgi app => $handler, client => sub { my $cb = shift; $cb->($req); }; }; { $test_req->(GET "http://localhost/"); like $log, qr@^127\.0\.0\.1 - - \[.*?\] "GET / HTTP/1\.1" 200 2@; } { $log = ""; $test_req->(POST "http://localhost/foo", { foo => "bar" }); like $log, qr@^127\.0\.0\.1 - - \[.*?\] "POST /foo HTTP/1\.1" 200 2@; } { $log = ""; $test_req->(GET "http://localhost/foo%20bar?baz=baz"); like $log, qr@GET /foo%20bar\?baz=baz HTTP/1\.1@; } done_testing; Plack-1.0030/t/Plack-Middleware/access_log_value_zero.t000644 000765 000024 00000001372 12244057435 023674 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use HTTP::Request::Common; use Plack::Test; use Plack::Builder; use POSIX; my $log; my $test = sub { my $format = shift; return sub { my $req = shift; my $app = builder { enable "Plack::Middleware::AccessLog", logger => sub { $log = "@_" }, format => $format; sub { [ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }; }; test_psgi $app, sub { $_[0]->($req) }; }; }; { my $req = GET "http://example.com/"; $req->header("Zero" => "0"); my $fmt = "%{zero}i %{undef}i"; $test->($fmt)->($req); chomp $log; is $log, "0 -"; } { $test->("%D")->(GET "/"); chomp $log; is $log, '-'; } done_testing; Plack-1.0030/t/Plack-Middleware/auth_basic.t000644 000765 000024 00000001623 12244057435 021440 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Test; use Plack::Builder; use HTTP::Request::Common; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello $_[0]->{REMOTE_USER}" ] ] }; $app = builder { enable "Auth::Basic", authenticator => \&cb; $app; }; my %map = ( admin => 's3cr3t', john => 'foo:bar', ); sub cb { my($username, $password) = @_; return $map{$username} && $password eq $map{$username}; } test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/"); is $res->code, 401; my $req = GET "http://localhost/", "Authorization" => "Basic YWRtaW46czNjcjN0"; $res = $cb->($req); is $res->code, 200; is $res->content, "Hello admin"; $req = GET "http://localhost/", "Authorization" => "Basic am9objpmb286YmFy"; $res = $cb->($req); is $res->code, 200; is $res->content, "Hello john"; }; done_testing; Plack-1.0030/t/Plack-Middleware/auth_basic_env.t000644 000765 000024 00000002700 12244057435 022305 0ustar00miyagawastaff000000 000000 use Test::More; use Plack::Test; use Plack::Builder; use Plack::Request; use HTTP::Request::Common; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello $_[0]->{REMOTE_USER}" ] ] }; $app = builder { enable "Auth::Basic", authenticator => \&cb; $app; }; sub cb { my($username, $password, $env) = @_; my $req = Plack::Request->new($env); if ($req->path eq '/') { return $username eq 'admin' && $password eq 's3cr3t'; } else { return $username eq 'user' && $password eq 's0m3th1ngel5e'; } } test_psgi app => $app, client => sub { my $cb = shift; { my $res = $cb->(GET "http://localhost/"); is $res->code, 401; } { my $req = GET "http://localhost/", "Authorization" => "Basic YWRtaW46czNjcjN0"; my $res = $cb->($req); is $res->code, 200; is $res->content, "Hello admin"; } { my $req = GET "http://localhost/", "Authorization" => "Basic dXNlcjpzMG0zdGgxbmdlbDVl"; my $res = $cb->($req); is $res->code, 401; } { my $req = GET "http://localhost/foo", "Authorization" => "Basic YWRtaW46czNjcjN0"; my $res = $cb->($req); is $res->code, 401; } { my $req = GET "http://localhost/foo", "Authorization" => "Basic dXNlcjpzMG0zdGgxbmdlbDVl"; my $res = $cb->($req); is $res->code, 200; is $res->content, "Hello user"; } }; done_testing; Plack-1.0030/t/Plack-Middleware/auth_basic_simple.t000644 000765 000024 00000001455 12244057435 023014 0ustar00miyagawastaff000000 000000 use Test::More; use Test::Requires qw(Authen::Simple::Passwd); use Plack::Test; use Plack::Builder; use HTTP::Request::Common; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello $_[0]->{REMOTE_USER}" ] ] }; $app = builder { enable "Auth::Basic", authenticator => Authen::Simple::Passwd->new(path => "t/Plack-Middleware/htpasswd"); $app; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "http://localhost/"); is $res->code, 401; my $req = GET "http://localhost/", "Authorization" => "Basic YWRtaW46czNjcjN0"; $res = $cb->($req); is $res->code, 200; is $res->content, "Hello admin"; local $^W = 0; $req = GET "http://localhost/", "Authorization" => "Basic bogus"; $res = $cb->($req); is $res->code, 401; }; done_testing; Plack-1.0030/t/Plack-Middleware/bufferedstreaming.t000644 000765 000024 00000002065 12244057435 023033 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Builder; my @tests = ( { app => sub { return sub { $_[0]->([ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ]); }, }, env => { REQUEST_METHOD => 'GET' }, headers => [ 'Content-Type' => 'text/plain' ], body => 'OK', }, { app => sub { return sub { my $writer = $_[0]->([ 200, [ 'Content-Type' => 'text/plain' ]]); $writer->write("O"); $writer->write("K"); $writer->close(); }, }, env => { REQUEST_METHOD => 'GET' }, headers => [ 'Content-Type', 'text/plain' ], body => 'OK', }, ); plan tests => 2 * @tests; for my $block (@tests) { my $handler = builder { enable "BufferedStreaming"; $block->{app}; }; my $res = $handler->($block->{env}); is_deeply $res->[1], $block->{headers}, "headers passed through"; is join("", @{ $res->[2] }), $block->{body}, "body accumulated"; }; Plack-1.0030/t/Plack-Middleware/cascade/000755 000765 000024 00000000000 12244057435 020532 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Middleware/cgi-bin/000755 000765 000024 00000000000 12244057435 020457 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Middleware/cgibin.t000644 000765 000024 00000003333 12244057435 020571 0ustar00miyagawastaff000000 000000 use strict; use Test::More; plan skip_all => "release test only" unless $ENV{RELEASE_TESTING}; use Test::Requires { 'CGI::Emulate::PSGI' => 0.10, 'CGI::Compile' => 0.03 }; use Plack::Test; use HTTP::Request::Common; use Plack::App::CGIBin; my $app = Plack::App::CGIBin->new(root => "t/Plack-Middleware/cgi-bin")->to_app; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/hello.cgi?name=foo"); is $res->code, 200; is $res->content, "Hello foo counter=1"; $res = $cb->(GET "http://localhost/hello.cgi?name=bar"); is $res->code, 200; is $res->content, "Hello bar counter=2"; $res = $cb->(GET "http://localhost/hello2.cgi?name=foo"); is $res->code, 200; is $res->content, "Hello foo counter=1"; $res = $cb->(GET "http://localhost/hello3.cgi"); my $env = eval $res->content; is $env->{SCRIPT_NAME}, '/hello3.cgi'; is $env->{REQUEST_URI}, '/hello3.cgi'; $res = $cb->(GET "http://localhost/hello3.cgi/foo%20bar/baz"); is $res->code, 200; $env = eval $res->content || {}; is $env->{SCRIPT_NAME}, '/hello3.cgi'; is $env->{PATH_INFO}, '/foo bar/baz'; is $env->{REQUEST_URI}, '/hello3.cgi/foo%20bar/baz'; $res = $cb->(GET "http://localhost/hello4.cgi"); is $res->code, 404; $res = $cb->(GET "http://localhost/utf8.cgi"); is $res->code, 200; is length $res->content, 4; is $res->content, "\xe1\x83\xb7\n"; }; $app = Plack::App::CGIBin->new( root => "t/Plack-Middleware/cgi-bin", exec_cb => sub { 1 } )->to_app; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/cgi_dir.cgi"); is $res->code, 200; is $res->content, "MATCH"; }; done_testing; Plack-1.0030/t/Plack-Middleware/cgibin_exec.t000644 000765 000024 00000003513 12244057435 021575 0ustar00miyagawastaff000000 000000 use strict; use Test::More; plan skip_all => "release test only" unless $ENV{RELEASE_TESTING}; use Test::Requires { 'CGI::Emulate::PSGI' => 0.10, 'CGI::Compile' => 0.03 }; use Plack::Test; use HTTP::Request::Common; use Plack::App::CGIBin; unless (-e "/usr/bin/python" && -x _) { plan skip_all => "You don't have /usr/bin/python"; } if (`/usr/bin/python --version 2>&1` =~ /^Python 3/) { plan skip_all => "This test doesn't support python 3 yet"; } my $app = Plack::App::CGIBin->new(root => "t/Plack-Middleware/cgi-bin")->to_app; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/hello.py?name=foo"); is $res->code, 200; like $res->content, qr/Hello foo/; like $res->content, qr/QUERY_STRING is name=foo/; }; # test that current directory is same the script directory { use File::Basename qw/basename dirname/; my $tmp = File::Temp->new(CLEANUP => 1); print $tmp <<"..."; #!$^X use CGI; use File::Basename qw/dirname/; use Cwd; my \$cgi_dir = Cwd::abs_path( dirname( __FILE__ ) ); my \$exec_dir = Cwd::abs_path( Cwd::getcwd ); my \$result = \$cgi_dir eq \$exec_dir ? "MATCH" : "DIFFERENT"; if (\$result ne "MATCH") { \$result .= "\nCGI_DIR: \$cgi_dir\nEXEC_DIR: \$exec_dir\n"; } my \$q = CGI->new; print \$q->header(-type => "text/plain"), \$result; ... close $tmp; chmod(oct("0700"), $tmp->filename) or die "Cannot chmod"; my $cgi_dir = dirname( $tmp->filename ); my $cgi_name = basename( $tmp->filename ); my $app_exec = Plack::App::CGIBin->new( root => $cgi_dir, exec_cb => sub { 1 } )->to_app; test_psgi app => $app_exec, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/$cgi_name?"); is $res->code, 200; is $res->content, "MATCH"; }; undef $tmp; }; done_testing; Plack-1.0030/t/Plack-Middleware/chunked.t000644 000765 000024 00000003100 12244057435 020747 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Test::Requires qw(IO::Handle::Util LWP::UserAgent LWP::Protocol::http10); use IO::Handle::Util qw(:io_from); use HTTP::Request::Common; use Plack::Test; use Plack::Middleware::Chunked; $Plack::Test::Impl = "Server"; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my @app = ( sub { [ 200, [], [ 'Hello World' ] ] }, sub { [ 200, [], [ 'Hello ', 'World' ] ] }, sub { [ 200, [], [ 'Hello ', '', 'World' ] ] }, sub { [ 200, [], io_from_array [ 'Hello World' ] ] }, sub { [ 200, [], io_from_array [ 'Hello', ' World' ] ] }, sub { [ 200, [], io_from_array [ 'Hello', '', ' World' ] ] }, ); @app = (@app, @app); # for 1.0 and 1.1 my $app = sub { (shift @app)->(@_) }; test_psgi ua => LWP::UserAgent->new, # force LWP app => Plack::Middleware::Chunked->wrap($app), client => sub { my $cb = shift; for my $proto (qw( HTTP/1.1 HTTP/1.0 )) { my $is_http_10 = $proto eq 'HTTP/1.0'; if ($is_http_10) { LWP::Protocol::implementor('http', 'LWP::Protocol::http10'); } for (1..@app/2) { my $req = GET "http://localhost/"; $req->protocol($proto); my $res = $cb->($req); is $res->content, 'Hello World'; is $res->decoded_content, 'Hello World'; if ($is_http_10) { isnt $res->header('client-transfer-encoding'), 'chunked', 'Chunked shouldn\'t be used in HTTP/1.0'; } else { is $res->header('client-transfer-encoding'), 'chunked'; } } } }; done_testing; Plack-1.0030/t/Plack-Middleware/component-leak.t000644 000765 000024 00000002421 12244057435 022247 0ustar00miyagawastaff000000 000000 package MyComponent; use strict; use warnings; use Test::More; use Scalar::Util qw/weaken/; use parent 'Plack::Component'; sub call { my $self = shift; my $env = shift; if( $env->{PATH_INFO} eq '/run_response_cb' ){ my $my; # Record $res and $cb $self->{res} = [200, ['Content-Type' => 'text/plain'], ['OK']]; $self->{cb} = sub { $my }; # Contain $my to be regard as a closure. return $self->response_cb($self->{res}, $self->{cb}); }else{ # Decrease REFCNT weaken $self->{res}; weaken $self->{cb}; # Check if references are released. return [ 200, [ 'Content-Type' => 'text/plain', 'X-Res-Freed' => ! $self->{res}, 'X-Cb-Freed' => ! $self->{cb}, ], ['HELLO'] ]; } } package main; use strict; use warnings; use Test::More; use Plack::Test; use HTTP::Request::Common; $Plack::Test::Impl = "Server"; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my $app = MyComponent->new; test_psgi( $app->to_app, sub { my $cb = shift; $cb->(GET '/run_response_cb'); my $req = $cb->(GET '/check'); ok $req->header('X-Res-Freed'), '$res has been released.'; ok $req->header('X-Cb-Freed') , '$cb has been released.'; } ); done_testing; Plack-1.0030/t/Plack-Middleware/component.t000644 000765 000024 00000003047 12244057435 021342 0ustar00miyagawastaff000000 000000 use strict; use Test::Requires qw(IO::Handle::Util); package MyComponent; use parent 'Plack::Component'; use Plack::Util::Accessor qw( res cb ); sub call { return $_[0]->response_cb( $_[0]->res, $_[0]->cb ); } package main; use IO::Handle::Util qw(:io_from); use HTTP::Request::Common; use Test::More; use Plack::Test; # Various kinds of PSGI responses. sub generate_responses { [200, ['Content-Type' => 'text/plain'], ['Hello']], [200, ['Content-Type' => 'text/plain'], io_from_array ['Hello']], sub { $_[0]->([ 200, ['Content-Type' => 'text/plain'], ['Hello'] ]) }, sub { my $writer = $_[0]->([ 200, ['Content-Type' => 'text/plain'] ]); $writer->write( 'Hello' ); $writer->close; }, } # $body filters can return undef with no warnings. for my $res ( generate_responses ) { my @warns; local $SIG{__WARN__} = sub { push @warns, @_ }; my $app = MyComponent->new( res => $res, cb => sub { sub { $_[0] } }, ); test_psgi( $app, sub { $_[0]->(GET '/') } ); is_deeply \@warns, []; } for my $res ( generate_responses ) { my $app = MyComponent->new( res => $res, cb => sub { my $done; sub { return if $done; if (defined $_[0]) { return $_[0]; } else { $done = 1; return 'END'; } }, }, ); test_psgi( $app, sub { my $res = $_[0]->(GET '/'); is $res->content, 'HelloEND'; } ); } done_testing; Plack-1.0030/t/Plack-Middleware/conditional.t000644 000765 000024 00000001653 12244057435 021644 0ustar00miyagawastaff000000 000000 use strict; no warnings; use Plack::Test; use Plack::Builder; use Test::More; use HTTP::Request::Common; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello' ] ] }; $app = builder { enable_if { $_[0]->{HTTP_X_FOO} =~ /Bar/i } 'XFramework', framework => 'Testing'; enable_if { $_[0]->{HTTP_X_ALLCAPS} } sub { my $app = shift; sub { my $res = $app->($_[0]); $res->[2] = [ map uc $_, @{$res->[2]} ]; $res }; }; $app; }; test_psgi app => $app, client => sub { my $cb = shift; my($req, $res); $req = GET "http://localhost/"; $res = $cb->($req); ok !$res->header('X-Framework'); $req = GET "http://localhost/", 'X-Foo' => 'Bar'; $res = $cb->($req); like $res->header('X-Framework'), qr/Testing/; $req = GET "http://localhost/", 'X-AllCaps' => 1; $res = $cb->($req); is $res->content, 'HELLO'; }; done_testing; Plack-1.0030/t/Plack-Middleware/conditional_new.t000644 000765 000024 00000002254 12244057435 022513 0ustar00miyagawastaff000000 000000 use strict; no warnings; use Plack::Test; use Plack::Builder; use Test::More; use HTTP::Request::Common; use Plack::Middleware::Conditional; use Plack::Middleware::XFramework; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello' ] ] }; my $mw1 = Plack::Middleware::Conditional->new( condition => sub { $_[0]->{HTTP_X_FOO} =~ /Bar/i }, builder => sub { Plack::Middleware::XFramework->new(framework => 'Testing')->wrap($_[0]) }, ); my $mw2 = Plack::Middleware::Conditional->new( condition => sub { $_[0]->{HTTP_X_ALLCAPS} }, builder => sub { my $app = shift; sub { my $res = $app->($_[0]); $res->[2] = [ map uc $_, @{$res->[2]} ]; $res }; }, ); $app = $mw2->wrap($app); $app = $mw1->wrap($app); test_psgi app => $app, client => sub { my $cb = shift; my($req, $res); $req = GET "http://localhost/"; $res = $cb->($req); ok !$res->header('X-Framework'); $req = GET "http://localhost/", 'X-Foo' => 'Bar'; $res = $cb->($req); like $res->header('X-Framework'), qr/Testing/; $req = GET "http://localhost/", 'X-AllCaps' => 1; $res = $cb->($req); is $res->content, 'HELLO'; }; done_testing; Plack-1.0030/t/Plack-Middleware/conditionalget.t000644 000765 000024 00000004211 12244057435 022335 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Plack::Builder; use Test::More; my @tests = ( { app => sub { [ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }, env => { REQUEST_METHOD => 'GET' }, status => 200, headers => [ 'Content-Type', 'text/plain' ], }, { app => sub { [ 200, [ 'ETag' => 'Foo', 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }, env => { REQUEST_METHOD => "GET", HTTP_IF_NONE_MATCH => "Foo" }, status => 304, headers => [ ETag => 'Foo' ], }, { app => sub { [ 200, [ 'Last-Modified' => 'Wed, 23 Sep 2009 13:36:33 GMT', 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }, env => { REQUEST_METHOD => "GET", HTTP_IF_MODIFIED_SINCE => "Wed, 23 Sep 2009 13:36:33 GMT" }, status => 304, headers => [ "Last-Modified" => "Wed, 23 Sep 2009 13:36:33 GMT" ], }, { app => sub { [ 200, [ 'Last-Modified' => 'Wed, 23 Sep 2009 13:36:33 GMT', 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }, env => { REQUEST_METHOD => "GET", HTTP_IF_MODIFIED_SINCE => "Wed, 23 Sep 2009 13:36:32 GMT" }, status => 200, headers => [ "Last-Modified", "Wed, 23 Sep 2009 13:36:33 GMT", "Content-Type", "text/plain", ], }, { app => sub { [ 200, [ 'Last-Modified' => 'Wed, 23 Sep 2009 13:36:33 GMT', 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }, env => { REQUEST_METHOD => "GET", HTTP_IF_MODIFIED_SINCE => "Wed, 23 Sep 2009 13:36:33 GMT; length=2" }, status => 304, headers => [ "Last-Modified", "Wed, 23 Sep 2009 13:36:33 GMT" ], }, { app => sub { [ 200, [ 'ETag' => 'Foo', 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }, env => { REQUEST_METHOD => "POST", HTTP_IF_NONE_MATCH => "Foo" }, status => 200, headers => [ ETag => "Foo", "Content-Type" => "text/plain" ], } ); plan tests => 2*@tests; for my $block (@tests) { my $handler = builder { enable "Plack::Middleware::ConditionalGET"; $block->{app}; }; my $res = $handler->($block->{env}); is $res->[0], $block->{status}; is_deeply $res->[1], $block->{headers}; } Plack-1.0030/t/Plack-Middleware/conditionalget_writer.t000644 000765 000024 00000001632 12244057435 023735 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Builder; use HTTP::Request::Common; use Plack::Test; my $handler = builder { enable 'ConditionalGET'; sub { my $env = shift; return sub { my $writer = shift->( [ 200, [ 'Content-Type' => 'text/plain', 'ETag' => 'DEADBEEF', ] ] ); $writer->write($_) for ( qw( kling klang klong ) ); $writer->close; }; }; }; test_psgi $handler, sub { my $cb = shift; my $res = $cb->( GET "http://localhost/streaming-klingklangklong" ); is $res->code, 200, 'Response HTTP status'; is $res->content, 'klingklangklong', 'Response content'; $res = $cb->( GET "http://localhost/streaming-klingklangklong", 'If-None-Match' => 'DEADBEEF' ); is $res->code, 304, 'Response HTTP status'; ok(!$res->content); }; done_testing; Plack-1.0030/t/Plack-Middleware/content_length.t000644 000765 000024 00000002775 12244057435 022362 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Builder; my @tests = ( { app => sub { [ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }, env => { REQUEST_METHOD => 'GET' }, headers=> [ 'Content-Type' => 'text/plain', 'Content-Length' => 2 ], }, { app => sub { open my $fh, "<", "share/baybridge.jpg"; [ 200, [ 'Content-Type' => 'image/jpeg' ], $fh ]; }, env => { REQUEST_METHOD => 'GET' }, headers => [ 'Content-Type' => 'image/jpeg', 'Content-Length' => 79838 ], }, { app => sub { [ 304, [ ETag => 'Foo' ], [] ]; }, env => { REQUEST_METHOD => 'GET' }, headers => [ ETag => 'Foo' ], }, { app => sub { my $body = "Hello World"; open my $fh, "<", \$body; [ 200, [ 'Content-Type' => 'text/plain' ], $fh ]; }, env => { REQUEST_METHOD => 'GET' }, headers => [ 'Content-Type' => 'text/plain' ], }, { app => sub { [ 200, [ 'Content-Type' => 'text/plain', 'Content-Length' => 11 ], [ "Hello World" ] ]; }, env => { REQUEST_METHOD => 'GET' }, headers => [ 'Content-Type' => 'text/plain', 'Content-Length', 11 ], }, ); plan tests => 1 * @tests; for my $block (@tests) { my $handler = builder { enable "Plack::Middleware::ContentLength"; $block->{app}; }; my $res = $handler->($block->{env}); is_deeply $res->[1], $block->{headers}; }; Plack-1.0030/t/Plack-Middleware/directory.t000644 000765 000024 00000002324 12244057435 021341 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use HTTP::Request::Common; use HTTP::Response; use Plack::Test; use Plack::App::Directory; my $handler = Plack::App::Directory->new({ root => 'share' }); my %test = ( client => sub { my $cb = shift; # URI-escape my $res = $cb->(GET "http://localhost/"); my($ct, $charset) = $res->content_type; ok $res->content =~ m{/%23foo}; $res = $cb->(GET "/.."); is $res->code, 403; $res = $cb->(GET "/..%00foo"); is $res->code, 400; $res = $cb->(GET "/..%5cfoo"); is $res->code, 403; $res = $cb->(GET "/"); like $res->content, qr/Index of \//; SKIP: { skip "Filenames can't end with . on windows", 2 if $^O eq "MSWin32"; mkdir "share/stuff..", 0777; open my $out, ">", "share/stuff../Hello.txt" or die $!; print $out "Hello\n"; close $out; $res = $cb->(GET "/stuff../Hello.txt"); is $res->code, 200; is $res->content, "Hello\n"; unlink "share/stuff../Hello.txt"; rmdir "share/stuff.."; } }, app => $handler, ); test_psgi %test; done_testing; Plack-1.0030/t/Plack-Middleware/error_document.t000644 000765 000024 00000002133 12244057435 022362 0ustar00miyagawastaff000000 000000 use strict; use warnings; use FindBin; use Test::More; use HTTP::Request::Common; use Plack::Test; use Plack::Builder; my $handler = builder { enable "Plack::Middleware::ErrorDocument", 500 => "$FindBin::Bin/errors/500.html"; enable "Plack::Middleware::ErrorDocument", 404 => "/errors/404.html", subrequest => 1; enable "Plack::Middleware::Static", path => qr{^/errors}, root => $FindBin::Bin; sub { my $env = shift; my $status = ($env->{PATH_INFO} =~ m!status/(\d+)!)[0] || 200; [ $status, [ 'Content-Type' => 'text/plain' ], [ "Error: $status" ] ]; }; }; test_psgi app => $handler, client => sub { my $cb = shift; { my $res = $cb->(GET "http://localhost/"); is $res->code, 200; $res = $cb->(GET "http://localhost/status/500"); is $res->code, 500; like $res->content, qr/fancy 500/; $res = $cb->(GET "http://localhost/status/404"); is $res->code, 404; like $res->header('content_type'), qr!text/html!; like $res->content, qr/fancy 404/; } }; done_testing; Plack-1.0030/t/Plack-Middleware/error_document_streaming_app.t000644 000765 000024 00000004307 12244057435 025300 0ustar00miyagawastaff000000 000000 use strict; use warnings; use FindBin; use Test::More; use HTTP::Request::Common; use Plack::Test; use Plack::Builder; $Plack::Test::Impl = undef; my @impl = ('Server', 'MockHTTP'); sub flip_backend { push @impl, $Plack::Test::Impl; $Plack::Test::Impl = shift @impl; } { my $handler = builder { enable "Plack::Middleware::ErrorDocument", 404 => "$FindBin::Bin/errors/404.html"; sub { my $env = shift; my $status = ($env->{PATH_INFO} =~ m!status/(\d+)!)[0] || 200; return sub { my $r = shift; my $w = $r->([ $status, [ 'Content-Type' => 'text/plain' ]]); $w->write("Error: $status\n"); $w->close; }; }; }; test_psgi app => $handler, client => sub { my $cb = shift; { my $res = $cb->(GET "http://localhost/"); is $res->code, 200; $res = $cb->(GET "http://localhost/status/404"); is $res->code, 404; like $res->header('content_type'), qr!text/html!; like $res->content, qr/fancy 404/; } } while flip_backend; } { my $handler = builder { enable "Plack::Middleware::ErrorDocument", 404 => "/404", subrequest => 1; mount '/404' => sub { [200, ['Content-Type' => 'text/html'], [< sub { my $env = shift; my $status = ($env->{PATH_INFO} =~ m!status/(\d+)!)[0] || 200; return sub { my $r = shift; my $w = $r->([ $status, [ 'Content-Type' => 'text/plain' ]]); $w->write("Error: $status\n"); $w->close; }; }; }; test_psgi app => $handler, client => sub { my $cb = shift; { my $res = $cb->(GET "http://localhost/"); is $res->code, 200; $res = $cb->(GET "http://localhost/status/404"); is $res->code, 404; like $res->header('content_type'), qr!text/html!; like $res->content, qr/fancy 404/; } } while flip_backend; } done_testing; Plack-1.0030/t/Plack-Middleware/errors/000755 000765 000024 00000000000 12244057435 020463 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Middleware/file.t000644 000765 000024 00000001375 12244057435 020261 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::More; use HTTP::Request::Common; use Plack::App::File; my $app = Plack::App::File->new(file => 'Changes'); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200; like $res->content, qr/Plack/; $res = $cb->(GET "/whatever"); is $res->content_type, 'text/plain'; is $res->code, 200; }; my $app_content_type = Plack::App::File->new( file => 'Changes', content_type => 'text/x-changes' ); test_psgi $app_content_type, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200; like $res->content, qr/Plack/; $res = $cb->(GET "/whatever"); is $res->content_type, 'text/x-changes'; is $res->code, 200; }; done_testing; Plack-1.0030/t/Plack-Middleware/head.t000644 000765 000024 00000000776 12244057435 020247 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use Plack::Builder; use HTTP::Request::Common; my $app = sub { my $env = shift; my $body = "Hello World"; [ 200, [ 'Content-Type', 'text/plain', 'Content-Length', length($body) ], [ $body ] ]; }; $app = builder { enable "Head"; $app }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, "Hello World"; $res = $cb->(HEAD "/"); ok !$res->content; is $res->content_length, 11; }; done_testing; Plack-1.0030/t/Plack-Middleware/head_streaming.t000644 000765 000024 00000001070 12244057435 022304 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use Plack::Builder; use HTTP::Request::Common; my $app = sub { my $env = shift; return sub { my $writer = shift->( [ 200, [ 'Content-Type' => 'text/plain', ] ] ); $writer->write($_) for qw{Hello World}; $writer->close; }; }; $app = builder { enable "Head"; $app }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, "HelloWorld"; $res = $cb->(HEAD "/"); ok !$res->content; ok(!$res->content_length); }; done_testing; Plack-1.0030/t/Plack-Middleware/htpasswd000644 000765 000024 00000000024 12244057435 020723 0ustar00miyagawastaff000000 000000 admin:6iSeSVcVHgNQw Plack-1.0030/t/Plack-Middleware/httpexceptions.t000644 000765 000024 00000002777 12244057435 022432 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use HTTP::Request::Common; use Test::More; package HTTP::Error; sub new { bless {}, shift } sub throw { my $class = shift; die $class->new; } package HTTP::Error::InternalServerError; use base qw(HTTP::Error); sub code { 500 } package HTTP::Error::Forbidden; use base qw(HTTP::Error); sub code { 403 } sub as_string { "blah blah blah" } package HTTP::Error::Redirect; use base qw(HTTP::Error); sub code { 302 } sub location { "http://somewhere/else" } package main; my $psgi_errors; my $app = sub { my $env = shift; $env->{'psgi.errors'} = do { open my $io, ">>", \$psgi_errors; $io }; if ($env->{PATH_INFO} eq '/secret') { HTTP::Error::Forbidden->throw; } if ($env->{PATH_INFO} eq '/redirect') { HTTP::Error::Redirect->throw; } if ($env->{PATH_INFO} eq '/uncaught') { die 'ugly stack trace here'; } HTTP::Error::InternalServerError->throw; }; use Plack::Middleware::HTTPExceptions; $app = Plack::Middleware::HTTPExceptions->wrap($app); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 500; is $res->content, 'Internal Server Error'; $res = $cb->(GET "/secret"); is $res->code, 403; is $res->content, 'blah blah blah'; $res = $cb->(GET '/redirect'); is $res->code, 302; is $res->header('Location'), 'http://somewhere/else'; $res = $cb->(GET '/uncaught'); is $res->code, 500; like $psgi_errors, qr/ugly stack trace here/; }; done_testing; Plack-1.0030/t/Plack-Middleware/httpexceptions_streaming.t000644 000765 000024 00000002362 12244057435 024471 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use HTTP::Request::Common; use Test::More; package HTTP::Error; sub new { bless {}, shift } sub throw { my $class = shift; die $class->new; } package HTTP::Error::InternalServerError; use base qw(HTTP::Error); sub code { 500 } package HTTP::Error::Forbidden; use base qw(HTTP::Error); sub code { 403 } sub as_string { "blah blah blah" } package main; my $app = sub { my $env = shift; if ($env->{PATH_INFO} eq '/secret') { return sub { HTTP::Error::Forbidden->throw }; } elsif ($env->{PATH_INFO} eq '/ok') { return sub { my $res = shift; my $w = $res->([ 200, [ 'Content-Type', 'text/plain' ] ]); $w->write("Hello"); $w->close; }; } return sub { HTTP::Error::InternalServerError->throw }; }; use Plack::Middleware::HTTPExceptions; $app = Plack::Middleware::HTTPExceptions->wrap($app); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 500; is $res->content, 'Internal Server Error'; $res = $cb->(GET "/secret"); is $res->code, 403; is $res->content, 'blah blah blah'; $res = $cb->(GET "/ok"); is $res->code, 200; is $res->content, 'Hello'; }; done_testing; Plack-1.0030/t/Plack-Middleware/iis6_script_name_fix.t000644 000765 000024 00000004164 12244057435 023445 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Middleware::IIS6ScriptNameFix; my %env = ( 'SCRIPT_NAME' => '/koo/blurb', 'PATH_INFO' => '/koo/blurb', 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'REQUEST_METHOD' => 'GET', 'SCRIPT_FILENAME' => 'C:\\Foo\\script\\blurb', 'INSTANCE_META_PATH' => '/LM/W3SVC/793536', 'SERVER_SOFTWARE' => 'Microsoft-IIS/6.0', 'AUTH_PASSWORD' => '', 'AUTH_TYPE' => '', 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows; U; Windows NT 5.2; de; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4 (.NET CLR 3.5.30729)', 'REMOTE_PORT' => '1281', 'QUERY_STRING' => '', 'URL' => '/koo/blurb', 'HTTP_ACCEPT_LANGUAGE' => 'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3', 'FCGI_ROLE' => 'RESPONDER', 'HTTP_KEEP_ALIVE' => '300', 'CONTENT_TYPE' => '', 'LOCAL_ADDR' => '127.0.0.1', 'GATEWAY_INTERFACE' => 'CGI/1.1', 'HTTPS' => 'off', 'DOCUMENT_ROOT' => 'C:\\Foo\\script', 'REMOTE_HOST' => '127.0.0.1', 'PATH_TRANSLATED' => 'C:\\Foo\\script\\blurb', 'APPL_PHYSICAL_PATH' => 'C:\\Foo\\script\\', 'SERVER_NAME' => '127.0.0.1', 'HTTP_ACCEPT_ENCODING' => 'gzip,deflate', 'HTTP_CONNECTION' => 'keep-alive', 'INSTANCE_ID' => '793536', 'CONTENT_LENGTH' => '0', 'AUTH_USER' => '', 'APPL_MD_PATH' => '/LM/W3SVC/793536/Root/koo', 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'REMOTE_USER' => '', 'SERVER_PORT_SECURE' => '0', 'SERVER_PORT' => 83, 'REMOTE_ADDR' => '127.0.0.1', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'REQUEST_URI' => '/koo/blurb', 'APP_POOL_ID' => 'DefaultAppPool', 'HTTP_HOST' => '127.0.0.1:83' ); sub test_fix { my ($input_env) = @_; my $mangled_env; Plack::Middleware::IIS6ScriptNameFix->wrap(sub { my ($env) = @_; $mangled_env = $env; return [ 200, ['Content-Type' => 'text/plain'], [''] ]; })->($input_env); return $mangled_env; } my $fixed_env = test_fix({ %env }); is($fixed_env->{PATH_INFO}, '//blurb', 'check PATH_INFO'); is($fixed_env->{SCRIPT_NAME}, '/koo', 'check SCRIPT_NAME'); done_testing; Plack-1.0030/t/Plack-Middleware/iis7_keep_alive_fix.t000644 000765 000024 00000001617 12244057435 023246 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use HTTP::Request::Common; use Plack::Middleware::IIS7KeepAliveFix; my $app=Plack::Middleware::IIS7KeepAliveFix->wrap( sub { my $env = shift; my $location='/go/?'.join('|', (0..1000)); return [ 302, [ 'Content-Type' => 'text/html', 'Content-Length' => 285, 'Location' => $location, ],[qq~ Moved

This item has moved here.

~] ]; }); test_psgi(app=>$app,client=> sub { my $cb = shift; my $res = $cb->(GET "/"); ok(!$res->content); ok(!$res->content_length); ok(!$res->content_type); }); done_testing; Plack-1.0030/t/Plack-Middleware/jsonp.t000644 000765 000024 00000002606 12244057435 020471 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use Plack::Builder; my @json = ('{"foo":', '"bar"}'); my $json = join '', @json; my @tests = ( { callback_key => 'json.p', app => sub { return [ 200, [ 'Content-Type' => 'application/json' ], [@json] ]; }, }, { app => sub { return sub { my $respond = shift; $respond->( [ 200, [ 'Content-Type' => 'application/json' ], [$json] ] ); }; }, } ); for my $test ( @tests ) { my $app = $test->{app}; if ( exists $test->{callback_key} ) { $app = builder { enable "Plack::Middleware::JSONP", callback_key => $test->{callback_key}; $app; }; } else { $app = builder { enable "Plack::Middleware::JSONP"; $app; }; } my $callback_key = $test->{callback_key} || 'callback'; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(HTTP::Request->new(GET => 'http://localhost/')); is $res->content_type, 'application/json'; is $res->content, $json; $res = $cb->(HTTP::Request->new(GET => 'http://localhost/?'.$callback_key.'=foo')); is $res->content_type, 'text/javascript'; is $res->content, "foo($json)"; }; } done_testing; Plack-1.0030/t/Plack-Middleware/lint.t000644 000765 000024 00000003452 12244057435 020306 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::More; use HTTP::Request::Common; use Plack::Builder; use Plack::Middleware::Lint; my @bad = map { Plack::Middleware::Lint->wrap($_) } ( sub { return {} }, sub { return [ 200, [], [], [] ] }, sub { return [ 200, {}, [] ] }, sub { return [ 0, [], "Hello World" ] }, sub { return [ 200, [], [ "\x{1234}" ] ] }, sub { return [ 200, [], {} ] }, sub { return [ 200, [], undef ] }, sub { return [ 200, [ "Foo:", "bar" ], [ "Hello" ] ] }, sub { return [ 200, [ "Foo-", "bar" ], [ "Hello" ] ] }, sub { return [ 200, [ "0xyz", "bar" ], [ "Hello" ] ] }, sub { return [ 200, [ "Status", "201" ], [ "Hi" ] ] }, sub { return [ 200, [ "Foo\nBar", "baz" ], [ '' ] ] }, sub { return [ 200, [ "Location", "Foo\nBar" ], [] ] }, sub { return [ 200, [ "Foo" ], [ "Hello" ] ] }, sub { return sub { shift->([ 200, [], {} ]) } }, sub { return sub { shift->([ 200, [], undef ]) } }, sub { return [ 200, [ "X-Foo", undef ], [ "Hi" ] ] }, ); my @good = map { Plack::Middleware::Lint->wrap($_) } ( sub { open my $io, "<", \"foo"; return [ 200, [ "Content-Type", "text/plain" ], $io ]; }, sub { my $body = "L\x{e9}on"; utf8::upgrade $body; return [ 200, [ "Content-Type", "text/html; charset=latin-1" ], [ $body ] ]; }, ); push @bad, builder { enable sub { my $app = shift; sub { $_[0]->{SCRIPT_NAME} = '/'; $app->(@_) } }; enable "Lint"; $good[0]; }; for my $app (@bad) { test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 500, $res->content; }; } for my $app (@good) { test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200, $res->content; }; } done_testing; Plack-1.0030/t/Plack-Middleware/lint_env.t000644 000765 000024 00000002257 12244057435 021160 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Middleware::Lint; use HTTP::Message::PSGI qw(req_to_psgi); use HTTP::Request; my $app = sub { [ 200, [ 'Content-Type' => 'text/plain' ], [ "OK" ] ]; }; $app = Plack::Middleware::Lint->wrap($app); my @good_env = ( { PATH_INFO => '' }, ); my @bad_env = ( [ { REQUEST_METHOD => undef }, qr/Missing env param: REQUEST_METHOD/ ], [ { REQUEST_METHOD => "foo" },, qr/Invalid env param: REQUEST_METHOD/ ], [ { PATH_INFO => 'foo' }, qr/PATH_INFO must begin with \// ], [ { SERVER_PORT => undef }, qr/Missing mandatory .*SERVER_PORT/ ], [ { SERVER_PROTOCOL => 'HTTP/2.2' }, qr/Invalid SERVER_PROTOCOL/ ], [ { "psgi.version" => 2 }, qr/psgi\.version should be ArrayRef/ ], [ { HTTP_CONTENT_TYPE => "text/plain" }, qr/HTTP_CONTENT_TYPE should not exist/ ], ); for my $good (@good_env) { my $env = req_to_psgi( HTTP::Request->new(GET => "/") ); eval { $app->({ %$env, %$good }); }; ok !$@; } for my $bad (@bad_env) { my($inject, $err) = @$bad; my $env = req_to_psgi( HTTP::Request->new(GET => "/") ); eval { $app->({ %$env, %$inject }); }; like $@, $err, $err; } done_testing; Plack-1.0030/t/Plack-Middleware/lint_utf8_false_alarm.t000644 000765 000024 00000000752 12244057435 023602 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::More; use HTTP::Request::Common; use Plack::Middleware::Lint; my @good = map { Plack::Middleware::Lint->wrap($_) } ( sub { my $body = "abc"; utf8::upgrade($body); return [ 200, [ "Content-Type", "text/plain;charset=utf-8"], [ $body ] ]; }, ); for my $app (@good) { test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200, $res->content; }; } done_testing; Plack-1.0030/t/Plack-Middleware/log4perl.t000644 000765 000024 00000002033 12244057435 021062 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::Requires qw(Log::Log4perl); use Test::More; use Plack::Middleware::Log4perl; use HTTP::Request::Common; my $test_file = "t/Plack-Middleware/log4perl.log"; my $conf = <{'psgix.logger'}->({ level => "debug", message => "This is debug" }); $env->{'psgix.logger'}->({ level => "info", message => "This is info" }); return [ 200, [], [] ]; }; $app = Plack::Middleware::Log4perl->wrap($app, category => 'plack.test'); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); my $log = do { open my $fh, "<", $test_file; join '', <$fh>; }; like $log, qr/INFO - This is info/; unlike $log, qr/debug/; }; END { unlink $test_file } done_testing; Plack-1.0030/t/Plack-Middleware/log_dispatch.t000644 000765 000024 00000002312 12244057435 021772 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::Requires { 'Log::Dispatch' => 2.25, 'Log::Dispatch::Array' => 1.001 }; use Test::More; use Plack::Middleware::LogDispatch; use HTTP::Request::Common; use Log::Dispatch; use Log::Dispatch::Array; package Stringify; use overload q{""} => sub { 'stringified object' }; sub new { bless {}, shift } package main; my @logs; my $logger = Log::Dispatch->new; $logger->add(Log::Dispatch::Array->new( min_level => 'debug', array => \@logs, )); my $app = sub { my $env = shift; $env->{'psgix.logger'}->({ level => "debug", message => "This is debug" }); $env->{'psgix.logger'}->({ level => "info", message => sub { 'code ref' } }); $env->{'psgix.logger'}->({ level => "notice", message => Stringify->new() }); return [ 200, [], [] ]; }; $app = Plack::Middleware::LogDispatch->wrap($app, logger => $logger); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is @logs, 3; is $logs[0]->{level}, 'debug'; is $logs[0]->{message}, 'This is debug'; is $logs[1]->{level}, 'info'; is $logs[1]->{message}, 'code ref'; is $logs[2]->{level}, 'notice'; is $logs[2]->{message}, 'stringified object'; }; done_testing; Plack-1.0030/t/Plack-Middleware/order.t000644 000765 000024 00000000607 12244057435 020452 0ustar00miyagawastaff000000 000000 use strict; use Plack::Builder; use Test::More; my $handler = builder { enable "Plack::Middleware::XFramework", framework => 'Dog'; enable "Plack::Middleware::StackTrace"; sub { die "Oops"; }; }; open my $io, ">", \my $err; my $res = $handler->({ 'psgi.errors' => $io }); is $res->[0], 500; my %hdrs = @{$res->[1]}; is $hdrs{'X-Framework'}, 'Dog'; done_testing; Plack-1.0030/t/Plack-Middleware/prefix.t000644 000765 000024 00000000564 12244057435 020636 0ustar00miyagawastaff000000 000000 use strict; use Plack::Builder; use Test::More; my $handler = builder { enable "XFramework", framework => 'Dog'; enable "Plack::Middleware::StackTrace"; sub { die "Oops"; }; }; open my $io, ">", \my $err; my $res = $handler->({ 'psgi.errors' => $io }); is $res->[0], 500; my %hdrs = @{$res->[1]}; is $hdrs{'X-Framework'}, 'Dog'; done_testing; Plack-1.0030/t/Plack-Middleware/psgibin.t000644 000765 000024 00000000562 12244057435 020772 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use HTTP::Request::Common; use Plack::App::PSGIBin; my $app = Plack::App::PSGIBin->new(root => "eg/dot-psgi")->to_app; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/Hello.psgi?name=foo"); is $res->code, 200; is $res->content, "Hello World"; }; done_testing; Plack-1.0030/t/Plack-Middleware/rearrange_headers.t000644 000765 000024 00000002026 12244057435 022775 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Builder; my $app = sub { return [ 200, [ 'Last-Modified' => 'Wed, 23 Sep 2009 13:36:33 GMT', 'Content-Type' => 'text/plain', 'ETag' => 'foo bar', ], [ 'Hello Foo' ] ]; }; { my $test = "Pre-test: test that header order is not changed by default."; # Don't use Plack::Test since it uses HTTP::Headers to reorder itself my $res = $app->({}); is_deeply $res->[1], [ 'Last-Modified' => 'Wed, 23 Sep 2009 13:36:33 GMT', 'Content-Type' => 'text/plain', 'ETag' => 'foo bar', ], $test; } { my $test = "Rearrange Middleware changes the header order"; $app = builder { enable "Plack::Middleware::RearrangeHeaders"; $app; }; # Don't use Plack::Test since it uses HTTP::Headers to reorder itself my $res = $app->({}); is_deeply $res->[1], [ 'ETag' => 'foo bar', 'Content-Type' => 'text/plain', 'Last-Modified' => 'Wed, 23 Sep 2009 13:36:33 GMT', ], $test; } done_testing; Plack-1.0030/t/Plack-Middleware/recursive/000755 000765 000024 00000000000 12244057435 021156 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Middleware/refresh-init.t000644 000765 000024 00000002167 12244057435 021741 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::Requires qw(Module::Refresh); use File::Spec; use File::Temp; use HTTP::Request::Common; use Plack::Middleware::Refresh; use Plack::Test; use Test::More; sub write_file($$){ my ( $path, $content ) = @_; open my $out, '>', $path or die "$path: $!"; print $out $content; } my $tmpdir = File::Temp::tempdir( CLEANUP => 1 ); my $pm_file = File::Spec->catfile($tmpdir, 'SomeModule.pm'); write_file $pm_file, qq/sub SomeModule::hello {'...'}; 1;\n/; # Load SomeModule unshift @INC, $tmpdir; require SomeModule; my $app = Plack::Middleware::Refresh->wrap(sub { [200, [ 'X-SomeModule' => SomeModule->hello ], ["OK\n"]] }, cooldown => 0 ); test_psgi $app, sub { my $cb = shift; # Change SomeModule before the server gets requests. sleep 1; write_file $pm_file, qq/sub SomeModule::hello {'Hi.'}; 1;\n/; my $res = $cb->(GET "/"); is $res->header('X-SomeModule'), 'Hi.'; # Change again. sleep 1; write_file $pm_file, qq/sub SomeModule::hello {'Good-bye.'}; 1;\n/; $res = $cb->(GET "/"); is $res->header('X-SomeModule'), 'Good-bye.'; }; done_testing; Plack-1.0030/t/Plack-Middleware/runtime.t000644 000765 000024 00000000664 12244057435 021025 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::More; use HTTP::Request::Common; plan skip_all => "Skipping on $^O platform" if $^O eq 'MSWin32'; use Plack::Builder; my $app = builder { enable "Runtime"; sub { sleep 1; return [200, ['Content-Type'=>'text/html'], ["Hello"]]; }; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); ok $res->header('X-Runtime') >= 0.5; }; done_testing; Plack-1.0030/t/Plack-Middleware/simple_content_filter.t000644 000765 000024 00000001034 12244057435 023722 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use Plack::Builder; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain', 'Content-Length' => 9 ], [ 'Hello ', 'Foo' ] ]; }; $app = builder { enable "ContentLength"; enable "SimpleContentFilter", filter => sub { s/Foo/FooBar/g; }; $app; }; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(HTTP::Request->new(GET => 'http://localhost/')); is $res->content, 'Hello FooBar'; is $res->content_length, 12; }; done_testing; Plack-1.0030/t/Plack-Middleware/simple_logger.t000644 000765 000024 00000001246 12244057435 022167 0ustar00miyagawastaff000000 000000 use strict; use Plack::Test; use Test::More; use Plack::Middleware::SimpleLogger; use HTTP::Request::Common; my $app = sub { my $env = shift; my $errors; $env->{'psgi.errors'} = do { open my $io, ">", \$errors; $io }; $env->{'psgix.logger'}->({ level => "debug", message => "This is debug" }); $env->{'psgix.logger'}->({ level => "info", message => "This is info" }); return [ 200, [], [$errors] ]; }; $app = Plack::Middleware::SimpleLogger->wrap($app, level => "info"); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); like $res->content, qr/This is info/; unlike $res->content, qr/This is debug/; }; done_testing; Plack-1.0030/t/Plack-Middleware/stacktrace/000755 000765 000024 00000000000 12244057435 021273 5ustar00miyagawastaff000000 000000 Plack-1.0030/t/Plack-Middleware/static.foo000644 000765 000024 00000000004 12244057435 021135 0ustar00miyagawastaff000000 000000 bar Plack-1.0030/t/Plack-Middleware/static.t000644 000765 000024 00000005401 12244057435 020623 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Middleware::Static; use Plack::Builder; use Plack::Util; use HTTP::Request::Common; use HTTP::Response; use Cwd; use Plack::Test; my $base = cwd; Plack::MIME->add_type(".foo" => "text/x-fooo"); my $handler = builder { enable "Plack::Middleware::Static", path => sub { s!^/share/!!}, root => "share"; enable "Plack::Middleware::Static", path => sub { s!^/more_share/!! if $_[1]->{PATH_INFO} =~ m!^/more_share/! }, root => "share"; enable "Plack::Middleware::Static", path => sub { s!^/share-pass/!!}, root => "share", pass_through => 1; enable "Plack::Middleware::Static", path => qr{\.(t|PL|txt)$}i, root => '.'; enable "Plack::Middleware::Static", path => qr{\.foo$}i, root => '.', content_type => sub { substr Plack::MIME->mime_type($_[0]),0,-1 } ; sub { [200, ['Content-Type' => 'text/plain', 'Content-Length' => 2], ['ok']] }; }; my %test = ( client => sub { my $cb = shift; { my $path = "t/test.txt"; my $res = $cb->(GET "http://localhost/$path"); is $res->content_type, 'text/plain', 'ok case'; like $res->content, qr/foo/; is -s $path, length($res->content); my $content = do { open my $fh, "<", $path; binmode $fh; join '', <$fh> }; is $content, $res->content; } { my $res = $cb->(GET "http://localhost/..%2f..%2f..%2fetc%2fpasswd.t"); is $res->code, 403; } { my $res = $cb->(GET "http://localhost/..%2fMakefile.PL"); is $res->code, 403, 'directory traversal'; } { my $res = $cb->(GET "http://localhost/foo/not_found.t"); is $res->code, 404, 'not found'; is $res->content, 'not found'; } { my $res = $cb->(GET "http://localhost/share/face.jpg"); is $res->content_type, 'image/jpeg'; } { my $res = $cb->(GET "http://localhost/more_share/face.jpg"); is $res->content_type, 'image/jpeg'; } { my $res = $cb->(GET "http://localhost/share-pass/faceX.jpg"); is $res->code, 200, 'pass through'; is $res->content, 'ok'; } { my $res = $cb->(GET "http://localhost/t/Plack-Middleware/static.txt"); is $res->content_type, 'text/plain'; my($ct, $charset) = $res->content_type; is $charset, 'charset=utf-8'; } { my $res = $cb->(GET "http://localhost/t/Plack-Middleware/static.foo"); is $res->content_type, 'text/x-foo'; } }, app => $handler, ); test_psgi %test; done_testing; Plack-1.0030/t/Plack-Middleware/static.txt000644 000765 000024 00000000004 12244057435 021171 0ustar00miyagawastaff000000 000000 foo Plack-1.0030/t/Plack-Middleware/static_env.t000644 000765 000024 00000001427 12244057435 021477 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::Test; use HTTP::Request::Common; use Plack::Middleware::AccessLog; use Plack::Middleware::Auth::Basic; use Plack::Middleware::Static; my $app = sub { my $env = shift; return [ 200, ['Content-Type' => 'text/plain'], ["Hello $env->{REMOTE_USER}"] ]; }; $app = Plack::Middleware::Static->wrap($app, path => qr!^/t/!, root => "."); $app = Plack::Middleware::Auth::Basic->wrap($app, authenticator => sub { 1 }); my $line; $app = Plack::Middleware::AccessLog->wrap($app, logger => sub { $line = shift }); test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/t/test.txt", Authorization => "Basic YWRtaW46czNjcjN0"); like $res->content, qr/foo/; like $line, qr/ admin /; }; done_testing; Plack-1.0030/t/Plack-Middleware/urlmap.t000644 000765 000024 00000003312 12244057435 020633 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::App::URLMap; use Plack::Test; use HTTP::Request::Common; my $make_app = sub { my $name = shift; sub { my $env = shift; my $body = join "|", $name, $env->{SCRIPT_NAME}, $env->{PATH_INFO}; return [ 200, [ 'Content-Type' => 'text/plain' ], [ $body ] ]; }; }; my $app1 = $make_app->("app1"); my $app2 = $make_app->("app2"); my $app3 = $make_app->("app3"); my $app4 = $make_app->("app4"); my $app = Plack::App::URLMap->new; $app->map("/" => $app1); $app->map("/foo" => $app2); $app->map("/foobar" => $app3); $app->map("http://bar.example.com/" => $app4); test_psgi app => $app, client => sub { my $cb = shift; my $res ; $res = $cb->(GET "http://localhost/"); is $res->content, 'app1||/'; $res = $cb->(GET "http://localhost/foo"); is $res->content, 'app2|/foo|'; $res = $cb->(GET "http://localhost/foo/bar"); is $res->content, 'app2|/foo|/bar'; $res = $cb->(GET "http://localhost/foox"); is $res->content, 'app1||/foox'; $res = $cb->(GET "http://localhost/foox/bar"); is $res->content, 'app1||/foox/bar'; $res = $cb->(GET "http://localhost/foobar"); is $res->content, 'app3|/foobar|'; $res = $cb->(GET "http://localhost/foobar/baz"); is $res->content, 'app3|/foobar|/baz'; $res = $cb->(GET "http://localhost/bar/foo"); is $res->content, 'app1||/bar/foo'; $res = $cb->(GET "http://bar.example.com/"); is $res->content, 'app4||/'; $res = $cb->(GET "http://bar.example.com/foo"); is $res->content, 'app4||/foo'; # Fix a bug where $location eq '' $_ = "bar"; /bar/; $res = $cb->(GET "http://localhost/"); is $res->content, 'app1||/'; }; done_testing; Plack-1.0030/t/Plack-Middleware/urlmap_builder.t000644 000765 000024 00000003037 12244057435 022345 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::App::URLMap; use Plack::Builder; use Plack::Test; use HTTP::Request::Common; my $make_app = sub { my $name = shift; sub { my $env = shift; my $body = join "|", $name, $env->{SCRIPT_NAME}, $env->{PATH_INFO}; return [ 200, [ 'Content-Type' => 'text/plain' ], [ $body ] ]; }; }; my $app1 = $make_app->("app1"); my $app2 = $make_app->("app2"); my $app3 = $make_app->("app3"); my $app4 = $make_app->("app4"); my $app = builder { mount "/" => $app1; mount "/foo" => builder { enable "Plack::Middleware::XFramework", framework => "Bar"; $app2; }; mount "/foobar" => builder { $app3 }; mount "http://bar.example.com/" => $app4; }; test_psgi app => $app, client => sub { my $cb = shift; my $res ; $res = $cb->(GET "http://localhost/"); is $res->content, 'app1||/'; $res = $cb->(GET "http://localhost/foo"); is $res->header('X-Framework'), 'Bar'; is $res->content, 'app2|/foo|'; $res = $cb->(GET "http://localhost/foo/bar"); is $res->content, 'app2|/foo|/bar'; $res = $cb->(GET "http://localhost/foox"); is $res->content, 'app1||/foox'; $res = $cb->(GET "http://localhost/foobar"); is $res->content, 'app3|/foobar|'; $res = $cb->(GET "http://localhost/foobar/baz"); is $res->content, 'app3|/foobar|/baz'; $res = $cb->(GET "http://bar.example.com/"); is $res->content, 'app4||/'; $res = $cb->(GET "http://bar.example.com/foo"); is $res->content, 'app4||/foo'; }; done_testing; Plack-1.0030/t/Plack-Middleware/urlmap_env.t000644 000765 000024 00000001403 12244057435 021502 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::App::URLMap; use Plack::Test; use HTTP::Request::Common; use Plack::Middleware::Auth::Basic; use Plack::Middleware::AccessLog; my $app1 = sub { my $env = shift; return [ 200, ['Content-Type' => 'text/plain'], ["Hello $env->{REMOTE_USER}"] ]; }; $app1 = Plack::Middleware::Auth::Basic->wrap($app1, authenticator => sub { 1 }); my $app = Plack::App::URLMap->new; $app->map("/foo" => $app1); my $line; $app = Plack::Middleware::AccessLog->wrap($app, logger => sub { $line = shift }); test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/foo", Authorization => "Basic YWRtaW46czNjcjN0"); is $res->content, 'Hello admin'; like $line, qr/ admin /; }; done_testing; Plack-1.0030/t/Plack-Middleware/urlmap_ports.t000644 000765 000024 00000001354 12244057435 022066 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Plack::App::URLMap; use Plack::Test; use HTTP::Request::Common; $Plack::Test::Impl = "Server"; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my $make_app = sub { my $name = shift; sub { my $env = shift; my $body = join "|", $name, $env->{SCRIPT_NAME}, $env->{PATH_INFO}; return [ 200, [ 'Content-Type' => 'text/plain' ], [ $body ] ]; }; }; my $app1 = $make_app->("app1"); my $app2 = $make_app->("app2"); my $app = Plack::App::URLMap->new; $app->map("http://127.0.0.1/" => $app1); $app->map("/" => $app2); test_psgi app => $app, client => sub { my $cb = shift; my $res; $res = $cb->(GET "http://127.0.0.1/"); is $res->content, 'app1||/'; }; done_testing; Plack-1.0030/t/Plack-Middleware/wrapcgi.t000644 000765 000024 00000001701 12244057435 020767 0ustar00miyagawastaff000000 000000 use strict; use Test::More; plan skip_all => "Hangs on Windows" if $^O eq 'MSWin32'; use Test::Requires { 'CGI::Emulate::PSGI' => 0.06, 'CGI::Compile' => 0.03 }; use Plack::Test; use HTTP::Request::Common; use Plack::App::WrapCGI; use IO::File; use File::Temp; my $app = Plack::App::WrapCGI->new(script => "t/Plack-Middleware/cgi-bin/hello.cgi")->to_app; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/?name=foo"); is $res->code, 200; is $res->content, "Hello foo counter=1"; $res = $cb->(GET "http://localhost/?name=bar"); is $res->code, 200; is $res->content, "Hello bar counter=2"; }; $app = Plack::App::WrapCGI->new( script => "t/Plack-Middleware/cgi-bin/cgi_dir.cgi", execute => 1)->to_app; test_psgi app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/?"); is $res->code, 200; is $res->content, "MATCH"; }; done_testing; Plack-1.0030/t/Plack-Middleware/wrapcgi_exec.t000644 000765 000024 00000007057 12244057435 022005 0ustar00miyagawastaff000000 000000 use strict; use Test::More; use Test::Requires { 'CGI::Emulate::PSGI' => 0.06, 'CGI::Compile' => 0.03 }; use Plack::Test; use HTTP::Request::Common; use Plack::App::WrapCGI; use IO::File; use File::Temp; plan skip_all => $^O if $^O eq "MSWin32"; { my $tmp = File::Temp->new(CLEANUP => 1); print $tmp <<"..."; #!$^X use CGI; my \$q = CGI->new; print \$q->header, "Hello ", \$q->param('name'), " counter=", ++\$COUNTER; ... close $tmp; chmod(oct("0700"), $tmp->filename) or die "Cannot chmod"; my $app_exec = Plack::App::WrapCGI->new(script => "$tmp", execute => 1)->to_app; test_psgi app => $app_exec, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/?name=foo"); is $res->code, 200; is $res->content, "Hello foo counter=1"; $res = $cb->(POST "http://localhost/", ['name' => 'bar']); is $res->code, 200; is $res->content, "Hello bar counter=1"; }; undef $tmp; }; { my $tmp = File::Temp->new(CLEANUP => 1); print $tmp <<"..."; #!$^X use CGI; my \$q = CGI->new; print \$q->header, "Hello " x 10000; ... close $tmp; chmod(oct("0700"), $tmp->filename) or die "Cannot chmod"; my $app_exec = Plack::App::WrapCGI->new(script => "$tmp", execute => 1)->to_app; test_psgi app => $app_exec, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/"); is $res->code, 200; }; undef $tmp; } # test that wrapped cgi doesn't wait indefinitely for STDIN { my $tmp = File::Temp->new(CLEANUP => 1); print $tmp <<"..."; #!$^X print "Content-type: text/plain\\n\\nYou said: "; local \$/; print ; ... close $tmp; chmod(oct("0700"), $tmp->filename) or die "Cannot chmod"; my $app_exec = Plack::App::WrapCGI->new(script => "$tmp", execute => 1)->to_app; test_psgi app => $app_exec, client => sub { my $cb = shift; eval { # without the fix $res->content seems to be "alarm\n" which still fails local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required alarm(10); my $res = $cb->(GET "http://localhost/?name=foo"); alarm(0); is $res->code, 200; is $res->content, "You said: "; alarm(10); $res = $cb->(POST "http://localhost/", Content => "doing things\nthe hard way"); alarm(0); is $res->code, 200; is $res->content, "You said: doing things\nthe hard way"; }; if ( $@ ) { die unless $@ eq "alarm\n"; # propagate unexpected errors ok 0, "request timed out waiting for STDIN"; } }; undef $tmp; }; # test that current directory is same the script directory { my $tmp = File::Temp->new(CLEANUP => 1); print $tmp <<"..."; #!$^X use CGI; use File::Basename qw/dirname/; use Cwd; my \$cgi_dir = Cwd::abs_path( dirname( __FILE__ ) ); my \$exec_dir = Cwd::abs_path( Cwd::getcwd ); my \$result = \$cgi_dir eq \$exec_dir ? "MATCH" : "DIFFERENT"; if (\$result ne "MATCH") { \$result .= "\nCGI_DIR: \$cgi_dir\nEXEC_DIR: \$exec_dir\n"; } my \$q = CGI->new; print \$q->header(-type => "text/plain"), \$result; ... close $tmp; chmod(oct("0700"), $tmp->filename) or die "Cannot chmod"; my $app_exec = Plack::App::WrapCGI->new(script => "$tmp", execute => 1)->to_app; test_psgi app => $app_exec, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/?"); is $res->code, 200; is $res->content, "MATCH"; }; undef $tmp; }; done_testing; Plack-1.0030/t/Plack-Middleware/xframework.t000644 000765 000024 00000000462 12244057435 021523 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Plack::Builder; use Test::More; my $handler = builder { enable "Plack::Middleware::XFramework", framework => 'Dog'; sub { [200, [], ['ok']] }; }; my $res = $handler->(+{}); is_deeply $res, [200, ['X-Framework' => 'Dog'], ['ok']]; done_testing; Plack-1.0030/t/Plack-Middleware/xsendfile.t000644 000765 000024 00000002012 12244057435 021310 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use HTTP::Request::Common; use Plack::Builder; use Plack::Test; use Cwd; sub is_wo_case($$;$) { is lc $_[0], lc $_[1], $_[2]; } my $handler = builder { enable "Plack::Middleware::XSendfile"; enable "Plack::Middleware::Static", path => qr/./, root => "."; sub { }; }; test_psgi app => $handler, client => sub { my $cb = shift; { my $req = GET "http://localhost/t/test.txt", 'X-Sendfile-Type' => 'X-Sendfile'; my $res = $cb->($req); is $res->content_type, 'text/plain';; is_wo_case $res->header('X-Sendfile'), Cwd::realpath("t/test.txt"); # wo_case for Win32-- is $res->content, ''; } }; test_psgi( app => sub { return [ 200, [ 'X-Sendfile' => '/foo/bar.txt' ], [] ] }, client => sub { my $cb = shift; my $res = $cb->(GET "http://localhost/foo", 'X-Sendfile-Type' => 'X-Sendfile'); is $res->header('X-Sendfile'), '/foo/bar.txt', 'pass through app header'; }, ); done_testing; Plack-1.0030/t/Plack-Middleware/stacktrace/basic.t000644 000765 000024 00000001655 12244057435 022550 0ustar00miyagawastaff000000 000000 use strict; use warnings; use Test::More; use Plack::Middleware::StackTrace; use Plack::Test; use HTTP::Request::Common; my $traceapp = Plack::Middleware::StackTrace->wrap(sub { die "orz" }, no_print_errors => 1); my $app = sub { my $env = shift; my $ret = $traceapp->($env); like $env->{'plack.stacktrace.text'}, qr/orz/; return $ret; }; my @backends = ($Plack::Middleware::StackTrace::StackTraceClass); push @backends, "Devel::StackTrace" if $backends[0] eq 'Devel::StackTrace::WithLexicals'; for my $be (@backends) { local $Plack::Middleware::StackTrace::StackTraceClass = $be; test_psgi $app, sub { my $cb = shift; my $req = GET "/"; $req->header(Accept => "text/html,*/*"); my $res = $cb->($req); ok $res->is_error; is_deeply [ $res->content_type ], [ 'text/html', 'charset=utf-8' ]; like $res->content, qr/Error: orz/; } } done_testing; �����������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/stacktrace/force.t��������������������������������������������������000644 �000765 �000024 �00000001376 12244057435 022565� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack::Middleware::StackTrace; use Plack::Test; use HTTP::Request::Common; my $app = sub { eval { die "Blah" }; return [ 500, [ 'Content-Type', 'text/html' ], [ "Fancy Error" ] ]; }; my $default_app = Plack::Middleware::StackTrace->wrap($app, no_print_errors => 1); test_psgi $default_app, sub { my $cb = shift; my $req = GET "/"; my $res = $cb->($req); is $res->code, 500; like $res->content, qr/Fancy Error/; }; my $force_app = Plack::Middleware::StackTrace->wrap($app, force => 1, no_print_errors => 1); test_psgi $force_app, sub { my $cb = shift; my $req = GET "/"; my $res = $cb->($req); is $res->code, 500; like $res->content, qr/Blah/; }; done_testing; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/stacktrace/sigdie.t�������������������������������������������������000644 �000765 �000024 �00000001033 12244057435 022721� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack::Middleware::StackTrace; use Plack::Test; use HTTP::Request::Common; $Plack::Test::Impl = "Server"; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my $app = sub { $SIG{__DIE__} = sub {}; die "meh"; }; my $wrapped = Plack::Middleware::StackTrace->wrap($app, no_print_errors => 1); test_psgi $wrapped, sub { my $cb = shift; my $req = GET "/"; my $res = $cb->($req); is $res->code, 500; like $res->content, qr/The application raised/; }; done_testing; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/stacktrace/streaming.t����������������������������������������������000644 �000765 �000024 �00000001031 12244057435 023444� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack::Middleware::StackTrace; use Plack::Test; use HTTP::Request::Common; my $app = sub { eval { require DooBar }; return sub { my $respond = shift; $respond->([ 200, [ "Content-Type", "text/plain" ], [ "Hello World" ] ]); }; }; $app = Plack::Middleware::StackTrace->wrap($app); test_psgi $app, sub { my $cb = shift; my $req = GET "/"; my $res = $cb->($req); ok $res->is_success; like $res->content, qr/Hello World/; }; done_testing; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/stacktrace/utf8.t���������������������������������������������������000644 �000765 �000024 �00000001227 12244057435 022350� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Test::Requires { 'Devel::StackTrace::AsHTML' => 0.08 }; use Plack::Middleware::StackTrace; use Plack::Test; use HTTP::Request::Common; $Plack::Test::Impl = "Server"; local $ENV{PLACK_SERVER} = "HTTP::Server::PSGI"; my $app = Plack::Middleware::StackTrace->wrap(sub { die "Foo \x{30c6}" }, no_print_errors => 1); test_psgi $app, sub { my $cb = shift; my $req = GET "/"; $req->header(Accept => "text/html,*/*"); my $res = $cb->($req); like $res->content, qr/Foo テ/; $req = GET "/"; $res = $cb->($req); is $res->code, 500; like $res->content, qr/Foo/; }; done_testing; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/recursive/base.t����������������������������������������������������000644 �000765 �000024 �00000001171 12244057435 022255� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use Plack::Test; use HTTP::Request::Common; use Plack::Middleware::Recursive; my $app = sub { my $env = shift; if ($env->{PATH_INFO} eq '/forwarded') { is_deeply $env->{'plack.recursive.old_path_info'}, [ '/' ]; return [ 200, [ 'Content-Type', 'text/plain' ], [ "Hello $env->{QUERY_STRING}" ] ]; } return $env->{'plack.recursive.include'}->("/forwarded?q=bar"); }; $app = Plack::Middleware::Recursive->wrap($app); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200; is $res->content, "Hello q=bar"; }; done_testing; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/recursive/streaming.t�����������������������������������������������000644 �000765 �000024 �00000001377 12244057435 023344� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use Plack::Test; use HTTP::Request::Common; use Plack::Middleware::Recursive; my $app = sub { my $env = shift; if ($env->{PATH_INFO} eq '/forwarded') { is_deeply $env->{'plack.recursive.old_path_info'}, [ '/' ]; return sub { $_[0]->([ 200, [ 'Content-Type', 'text/plain' ], [ "Hello $env->{QUERY_STRING}" ] ]) }; } return sub { my $respond = shift; my $r = $env->{'plack.recursive.include'}->("/forwarded?q=bar"); ref $r eq 'CODE' ? $r->($respond) : $respond->($r); }; }; $app = Plack::Middleware::Recursive->wrap($app); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200; is $res->content, "Hello q=bar"; }; done_testing; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/recursive/throw.t���������������������������������������������������000644 �000765 �000024 �00000001627 12244057435 022514� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use Plack::Test; use HTTP::Request::Common; use Plack::Middleware::Recursive; my $app = sub { my $env = shift; if ($env->{PATH_INFO} eq '/forwarded2') { is_deeply $env->{'plack.recursive.old_path_info'}, [ '/', '/forwarded' ]; return [ 200, [ 'Content-Type', 'text/plain' ], [ "Hello $env->{QUERY_STRING}" ] ]; } elsif ($env->{PATH_INFO} eq '/forwarded') { Plack::Recursive::ForwardRequest->throw("/forwarded2?q=bar"); } elsif ($env->{PATH_INFO} eq '/die') { die "Foo"; } Plack::Recursive::ForwardRequest->throw("/forwarded?q=bar"); }; $app = Plack::Middleware::Recursive->wrap($app); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200; is $res->content, "Hello q=bar"; $res = $cb->(GET "/die"); is $res->code, 500; like $res->content, qr/Foo at /; }; done_testing; ���������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/recursive/throw_streaming.t�����������������������������������������000644 �000765 �000024 �00000001500 12244057435 024553� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use Plack::Test; use HTTP::Request::Common; use Plack::Middleware::Recursive; my $app = sub { my $env = shift; if ($env->{PATH_INFO} eq '/forwarded2') { is_deeply $env->{'plack.recursive.old_path_info'}, [ '/', '/forwarded' ]; return sub { $_[0]->([ 200, [ 'Content-Type', 'text/plain' ], [ "Hello $env->{QUERY_STRING}" ] ]) }; } elsif ($env->{PATH_INFO} eq '/forwarded') { Plack::Recursive::ForwardRequest->throw("/forwarded2?q=bar"); } return sub { my $respond = shift; Plack::Recursive::ForwardRequest->throw("/forwarded"); }; }; $app = Plack::Middleware::Recursive->wrap($app); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->code, 200; is $res->content, "Hello q=bar"; }; done_testing; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/errors/404.html�����������������������������������������������������000644 �000765 �000024 �00000000041 12244057435 021653� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������a b c This is a fancy 404 page. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/errors/500.html�����������������������������������������������������000644 �000765 �000024 �00000000032 12244057435 021650� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������This is a fancy 500 page! ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cgi-bin/cgi_dir.cgi�������������������������������������������������000755 �000765 �000024 �00000000603 12244057435 022545� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use CGI; use File::Basename qw/dirname/; use Cwd; my $cgi_dir = Cwd::abs_path( dirname( __FILE__ ) ); my $exec_dir = Cwd::abs_path( Cwd::getcwd ); my $result = $cgi_dir eq $exec_dir ? "MATCH" : "DIFFERENT"; if ($result ne "MATCH") { $result .= "\nCGI_DIR: $cgi_dir\nEXEC_DIR: $exec_dir\n"; } my $q = CGI->new; print $q->header(-type => "text/plain"), $result; �����������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cgi-bin/hello.cgi���������������������������������������������������000755 �000765 �000024 �00000000163 12244057435 022251� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use CGI; my $q = CGI->new; print $q->header, "Hello ", $q->param('name'), " counter=", ++$COUNTER; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cgi-bin/hello.py����������������������������������������������������000755 �000765 �000024 �00000000274 12244057435 022142� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import os print "Content-Type: text/plain" print for item in ([ "foo", "bar" ]): print "Hello " + item + ". " print "QUERY_STRING is " + os.environ['QUERY_STRING'] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cgi-bin/hello2.cgi��������������������������������������������������000755 �000765 �000024 �00000000163 12244057435 022333� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use CGI; my $q = CGI->new; print $q->header, "Hello ", $q->param('name'), " counter=", ++$COUNTER; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cgi-bin/hello3.cgi��������������������������������������������������000755 �000765 �000024 �00000000150 12244057435 022330� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use Data::Dumper; print "Content-Type: text/plain\r\n\r\n"; print 'my ' . Dumper \%ENV; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cgi-bin/utf8.cgi����������������������������������������������������000755 �000765 �000024 �00000000152 12244057435 022032� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!perl use CGI; binmode STDOUT, ":utf8"; print CGI::header("text/html;charset=utf-8"), chr(4343), "\n"; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cascade/basic.t�����������������������������������������������������000644 �000765 �000024 �00000001457 12244057435 022007� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Plack::Test; use Test::More; use Plack::App::Cascade; use Plack::App::File; use HTTP::Request::Common; my $cascade = Plack::App::Cascade->new; test_psgi $cascade, sub { my $cb = shift; $res = $cb->(GET "http://localhost/"); is $res->code, 404; }; $cascade->add( Plack::App::File->new(root => "t/Plack-Middleware")->to_app ); $cascade->add( Plack::App::File->new(root => "t/Plack-Util")->to_app ); $cascade->add( sub { [ 404, [], [ 'Custom 404 Page' ] ] } ); test_psgi $cascade, sub { my $cb = shift; my $res = $cb->(GET "http://localhost/access_log.t"); is $res->code, 200; $res = $cb->(GET "http://localhost/foo"); is $res->code, 404; is $res->content, 'Custom 404 Page'; $res = $cb->(GET "http://localhost/foreach.t"); is $res->code, 200; }; done_testing; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Middleware/cascade/streaming.t�������������������������������������������������000644 �000765 �000024 �00000002210 12244057435 022703� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Plack::Test; use Test::More; use Plack::App::Cascade; use HTTP::Request::Common; my $cascade = Plack::App::Cascade->new; $cascade->add( sub { return sub { my $respond = shift; $respond->([ 404, [], [ "Duh" ] ]) } } ); $cascade->add( sub { return [ 403, [ 'Content-Type', 'text/plain' ], [ "Forbidden" ] ] } ); $cascade->add( sub { my $env = shift; return sub { my $r = shift; if ($env->{PATH_INFO} eq '/') { my $w = $r->([ 200, [ 'Content-Type', 'text/plain' ] ]); $w->write("Hello"); $w->close; } else { $r->([ 404, [ 'Content-Type', 'text/plain' ], [ 'Custom 404 Page' ] ]); } } }); $cascade->catch([ 403, 404 ]); test_psgi $cascade, sub { my $cb = shift; my $res = $cb->(GET "http://localhost/"); is $res->code, 200; is $res->content, "Hello"; $res = $cb->(GET "http://localhost/xyz"); is $res->code, 404; is $res->content, 'Custom 404 Page'; }; done_testing; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Loader/auto.t������������������������������������������������������������������000644 �000765 �000024 �00000001012 12244057435 017427� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack::Loader; my $builder = sub { require AnyEvent; my $app = sub { return [ 200, [], [ "Hi" ] ]; }; }; $INC{"Plack/Handler/Twiggy.pm"} = __FILE__; sub Plack::Handler::Twiggy::new { bless {}, shift } no warnings 'redefine'; local *Plack::Loader::env = sub { return {} }; eval { my $loader = Plack::Loader->new; $loader->preload_app($builder); my $server = $loader->auto; like ref $server, qr/Twiggy/; }; ok 1 if $@; done_testing; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Loader/auto_fallback.t���������������������������������������������������������000644 �000765 �000024 �00000000644 12244057435 021260� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; no warnings 'redefine'; use Test::More; use Plack::Loader; my $builder = sub { my $app = sub { return [ 200, [], [ "Hi" ] ]; }; }; local *Plack::Loader::guess = sub { 'NonExistent' }; local $SIG{__WARN__} = sub { like $_[0], qr/Autoloading/ }; my $loader = Plack::Loader->new; $loader->preload_app($builder); my $server = $loader->auto; like ref $server, qr/Standalone/; done_testing; ��������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Loader/delayed.t���������������������������������������������������������������000644 �000765 �000024 �00000000660 12244057435 020076� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use Plack::Loader; my $compiled; my $builder = sub { $compiled = 1; my $app = sub { return [ 200, [], [ "Hi" ] ]; }; }; # The following eval might not fail if you set PLACK_SEVER delete $ENV{PLACK_SERVER}; eval { my $loader = Plack::Loader::Delayed->new; $loader->preload_app($builder); my $server = $loader->auto; ok(!$compiled); }; ok 1 if $@; done_testing; ��������������������������������������������������������������������������������Plack-1.0030/t/Plack-Loader/restarter.t�������������������������������������������������������������000644 �000765 �000024 �00000003121 12244057435 020475� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use Test::TCP; use Test::Requires qw(LWP::UserAgent); use HTTP::Request::Common; use Plack::Loader::Restarter; plan skip_all => "release test only" unless $ENV{AUTHOR_TESTING}; my @return_bodies = ('Hi first', 'Hi second', 'Hi third'); my @restartertestfiles = ('t/restartertestfile1.pl', 't/restartertestfile2.pl'); unlink $_ for @restartertestfiles; my $builder = sub { my $idx = 0; for my $file (@restartertestfiles) { $idx++ if -e $file; } my $return_body = $return_bodies[$idx]; my $app = sub { return [ 200, [], [ $return_body ] ]; }; }; test_tcp( client => sub { my $port = shift; my $ua = LWP::UserAgent->new; my $cb = sub { my $req = HTTP::Request->new(GET => sprintf('http://127.0.0.1:%s/', $port)); return $ua->request($req); }; is $cb->()->content, $return_bodies[0]; touch($restartertestfiles[0]); sleep 2; wait_port($port); is $cb->()->content, $return_bodies[1]; touch($restartertestfiles[1]); sleep 2; wait_port($port); is $cb->()->content, $return_bodies[2]; }, server => sub { my $port = shift; my $loader = Plack::Loader::Restarter->new; my $server = $loader->auto(port => $port); $loader->preload_app($builder); $loader->watch('t'); $loader->run($server); }, ); sub touch { my $file = shift; open my $fh, ">", $file or die $!; print $fh time; close $fh; } unlink $_ for @restartertestfiles; done_testing; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Loader/restarter_valid.t�������������������������������������������������������000644 �000765 �000024 �00000001146 12244057435 021661� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Plack::Loader::Restarter; use Test::More; my $r = Plack::Loader::Restarter->new; my @match = qw( Foo.pm foo.t lib/Bar.pm view/index.tt _myapp/foo.psgi .www/bar.pl _sass.css /Users/joe/foo/bar.pm /path/to/4912 /path/to/5037 ); my @ignore = qw( .git/123 .svn/abc Foo.pm~ _flymake.pl /Users/joe/foo.pl~ /foo/bar/x.txt.bak /path/to/foo.swp /path/to/foo.swpx /path/to/foo.swx /path/to/4913 /path/to/5036 /path/to/.#Foo.pm ); ok $r->valid_file({ path => $_ }), "$_ is valid" for @match; ok !$r->valid_file({ path => $_ }), "$_ should be ignored" for @ignore; done_testing; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Loader/shotgun.t���������������������������������������������������������������000644 �000765 �000024 �00000000727 12244057435 020162� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack::Test::Suite; plan skip_all => "Skip on Win32" if $^O eq 'MSWin32'; require Plack::Loader::Shotgun; Plack::Test::Suite->run_server_tests( sub { my($port, $app) = @_; my $loader = Plack::Loader::Shotgun->new; $loader->preload_app(sub { $app }); my $server = $loader->load('Standalone', port => $port, host => '127.0.0.1'); $loader->run($server); }, ); done_testing(); �����������������������������������������Plack-1.0030/t/Plack-HTTPParser-PP/simple.t���������������������������������������������������������000644 �000765 �000024 �00000005322 12244057435 021023� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use Plack::HTTPParser::PP; *parse_http_request = \&Plack::HTTPParser::PP::parse_http_request; my $req; my %env; $req = "GET /abc?x=y HTTP/1.0\r\n\r\n"; %env = (); is(parse_http_request($req, \%env), length($req), 'simple get'); is_deeply(\%env, { PATH_INFO => '/abc', QUERY_STRING => 'x=y', REQUEST_METHOD => "GET", SCRIPT_NAME => '', SERVER_PROTOCOL => 'HTTP/1.0', REQUEST_URI => '/abc?x=y', }, 'result of GET /'); $req = <<"EOT"; POST /hoge HTTP/1.1\r Content-Type: text/plain\r Content-Length: 15\r Host: example.com\r User-Agent: hoge\r \r EOT %env = (); is(parse_http_request($req, \%env), length($req), 'POST'); is_deeply(\%env, { CONTENT_LENGTH => 15, CONTENT_TYPE => 'text/plain', HTTP_HOST => 'example.com', HTTP_USER_AGENT => 'hoge', PATH_INFO => '/hoge', REQUEST_METHOD => "POST", REQUEST_URI => '/hoge', QUERY_STRING => '', SERVER_PROTOCOL => 'HTTP/1.1', SCRIPT_NAME => '', }, 'result of GET with headers'); $req = <<"EOT"; GET / HTTP/1.0\r Foo: \r Foo: \r abc\r de\r Foo: fgh\r \r EOT %env = (); is(parse_http_request($req, \%env), length($req), 'multiline header'); is_deeply(\%env, { HTTP_FOO => ', abc de, fgh', PATH_INFO => '/', QUERY_STRING => '', REQUEST_METHOD => 'GET', REQUEST_URI => '/', SCRIPT_NAME => '', SERVER_PROTOCOL => 'HTTP/1.0', }, 'multiline'); # dumb HTTP client: https://github.com/plack/Plack/issues/213 $req = <<"EOT"; GET /a/b#c HTTP/1.0\r \r EOT %env = (); is(parse_http_request($req, \%env), length($req), 'URI fragment'); is_deeply(\%env, { SCRIPT_NAME => '', PATH_INFO => '/a/b', REQUEST_METHOD => 'GET', REQUEST_URI => '/a/b#c', QUERY_STRING => '', SCRIPT_NAME => '', SERVER_PROTOCOL => 'HTTP/1.0', }); $req = <<"EOT"; GET /a/b%23c HTTP/1.0\r \r EOT %env = (); is(parse_http_request($req, \%env), length($req), '%23 -> #'); is_deeply(\%env, { SCRIPT_NAME => '', PATH_INFO => '/a/b#c', REQUEST_METHOD => 'GET', REQUEST_URI => '/a/b%23c', QUERY_STRING => '', SCRIPT_NAME => '', SERVER_PROTOCOL => 'HTTP/1.0', }); $req = <<"EOT"; GET /a/b?c=d#e HTTP/1.0\r \r EOT %env = (); is(parse_http_request($req, \%env), length($req), 'URI fragment after query string'); is_deeply(\%env, { SCRIPT_NAME => '', PATH_INFO => '/a/b', REQUEST_METHOD => 'GET', REQUEST_URI => '/a/b?c=d#e', QUERY_STRING => 'c=d', SCRIPT_NAME => '', SERVER_PROTOCOL => 'HTTP/1.0', }); my $w; { local $SIG{__WARN__} = sub { $w = shift }; $req = "GET /foo HTTP/1.0\r\n\r\n"; parse_http_request($req, \%env); } ok !$w; done_testing; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/apache1.t��������������������������������������������������������������000644 �000765 �000024 �00000003342 12244057435 020140� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack; use Test::TCP; use Test::Requires qw(LWP::UserAgent); use FindBin; use File::Path; use Plack::Test::Suite; plan skip_all => "TEST_APACHE1 is not set" unless $ENV{TEST_APACHE1}; Plack::Test::Suite->run_server_tests(\&run_httpd); done_testing(); my $log_filename; sub run_httpd { my $port = shift; my $tmpdir = $ENV{APACHE1_TMP_DIR} || File::Temp::tempdir( CLEANUP => 1 ); my $httpd = $ENV{APACHE_BIN} || 'httpd'; write_file("$tmpdir/app.psgi", _render_psgi()); write_file("$tmpdir/httpd.conf", _render_conf($tmpdir, $port, "$tmpdir/app.psgi")); mkpath( "$tmpdir/conf" ); write_file("$tmpdir/conf/mime.types", _render_mimetypes()); $log_filename = "$tmpdir/error_log"; system ("touch $log_filename"); link($log_filename, 'err'); exec "$httpd -X -F -f $tmpdir/httpd.conf" or die "couldn't start httpd : $!\n"; } sub write_file { my($path, $content) = @_; open my $out, ">", $path or die "$path: $!"; print $out $content; } sub _render_mimetypes { return 'text/html html htm'; } sub _render_psgi { return <<'EOF'; use lib "lib"; use Plack::Test::Suite; Plack::Test::Suite->test_app_handler; EOF } sub _render_conf { my ($tmpdir, $port, $psgi_path) = @_; my $load_module = ( -f "$tmpdir/libexec/mod_perl.so" ) ? 'LoadModule perl_module libexec/mod_perl.so' : '' ; my $conf = <<"END"; $load_module ServerRoot $tmpdir ServerName 127.0.0.1 PidFile $tmpdir/httpd.pid LockFile $tmpdir/httpd.lock ErrorLog $tmpdir/error_log Listen $port <Location /> SetHandler perl-script PerlHandler Plack::Handler::Apache1 PerlSetVar psgi_app $tmpdir/app.psgi </Location> END return $conf; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/apache2-registry.t�����������������������������������������������������000644 �000765 �000024 �00000005231 12244057435 022006� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use File::Path; use Test::TCP; use Test::Requires qw(LWP::UserAgent); use HTTP::Request::Common; use Test::More; plan skip_all => "TEST_APACHE2 is not set" unless $ENV{TEST_APACHE2}; # Note: you need to load 64bit lib to test Apache2 on OS X 10.5 or later test_tcp( client => sub { my $port = shift; my $ua = LWP::UserAgent->new; my $call = sub { my $req = shift; $req->uri->port($port); return $ua->request($req); }; my $res1 = $call->( GET 'http://127.0.0.1/psgi-bin/app.psgi' ); note $res1->content; is $res1->header('X-Script-Name'), '/psgi-bin/app.psgi'; is $res1->header('X-Path-Info') , ''; my $res2 = $call->( GET 'http://127.0.0.1/psgi-bin/deep/app.psgi/deeply' ); note $res2->content; is $res2->header('X-Script-Name'), '/psgi-bin/deep/app.psgi'; is $res2->header('X-Path-Info') , '/deeply'; my $res3 = $call->( GET 'http://127.0.0.1/psgi-bin/404.psgi' ); note $res3->content; is $res3->code, 404; my $res4 = $call->( GET 'http://127.0.0.1/psgi-bin/dead.psgi' ); note $res4->content; is $res4->code, 500; }, server => sub { my $port = shift; run_httpd($port); }, ); done_testing(); sub run_httpd { my $port = shift; my $tmpdir = $ENV{APACHE2_TMP_DIR} || File::Temp::tempdir( CLEANUP => 1 ); mkpath( "$tmpdir/psgi-bin" ); write_file("$tmpdir/psgi-bin/app.psgi", _render_psgi()); mkpath( "$tmpdir/psgi-bin/deep" ); write_file("$tmpdir/psgi-bin/deep/app.psgi", _render_psgi()); write_file("$tmpdir/psgi-bin/dead.psgi", _render_dead_psgi()); write_file("$tmpdir/httpd.conf", _render_conf($tmpdir, $port)); exec "httpd -X -D FOREGROUND -f $tmpdir/httpd.conf"; } sub write_file { my($path, $content) = @_; open my $out, ">", $path or die "$path: $!"; print $out $content; } sub _render_psgi { return <<'EOF'; sub { my $env = shift; [200, [ 'Content-Type' => 'text/plain', 'X-Script-Name' => $env->{SCRIPT_NAME}, 'X-Path-Info' => $env->{PATH_INFO}, ], ['OK']] } EOF } sub _render_dead_psgi { return <<'EOF'; die 'What's happen?'; EOF } sub _render_conf { my ($tmpdir, $port) = @_; <<"END"; LoadModule perl_module libexec/apache2/mod_perl.so ServerRoot $tmpdir DocumentRoot $tmpdir PidFile $tmpdir/httpd.pid LockFile $tmpdir/httpd.lock ErrorLog $tmpdir/error_log Listen $port PerlModule Plack::Handler::Apache2::Registry; <Location /psgi-bin> SetHandler modperl PerlHandler Plack::Handler::Apache2::Registry </Location> END } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/apache2.t��������������������������������������������������������������000644 �000765 �000024 �00000005514 12244057435 020144� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack; use Test::TCP; use Test::Requires qw(LWP::UserAgent); use FindBin; use Plack::Test::Suite; plan skip_all => "TEST_APACHE2 is not set" unless $ENV{TEST_APACHE2}; # Note: you need to load 64bit lib to test Apache2 on OS X 10.5 or later Plack::Test::Suite->run_server_tests(run_httpd(\&_render_conf)); local $ENV{PLACK_TEST_SCRIPT_NAME} = '/foo/bar/baz'; Plack::Test::Suite->run_server_tests( run_httpd(\&_render_conf_location),); Plack::Test::Suite->run_server_tests( run_httpd(\&_render_conf_location_match),); done_testing(); sub run_httpd { my $render_conf = shift; sub { my $port = shift; my $tmpdir = $ENV{APACHE2_TMP_DIR} || File::Temp::tempdir( CLEANUP => 1 ); write_file("$tmpdir/app.psgi", _render_psgi()); write_file("$tmpdir/httpd.conf", $render_conf->($tmpdir, $port, "$tmpdir/app.psgi")); # This is required for failing tests. # Apache2 peep real filesystem to make SCRIPT_NAME and PATH_INFO. mkdir "$tmpdir/foo"; exec "httpd -X -D FOREGROUND -f $tmpdir/httpd.conf"; }; } sub write_file { my($path, $content) = @_; open my $out, ">", $path or die "$path: $!"; print $out $content; } sub _render_psgi { return <<'EOF'; use lib "lib"; use Plack::Test::Suite; Plack::Test::Suite->test_app_handler; EOF } sub _render_conf { my ($tmpdir, $port, $psgi_path) = @_; <<"END"; LoadModule perl_module libexec/apache2/mod_perl.so ServerRoot $tmpdir DocumentRoot $tmpdir PidFile $tmpdir/httpd.pid LockFile $tmpdir/httpd.lock ErrorLog $tmpdir/error_log Listen $port <Perl> use Plack::Handler::Apache2; Plack::Handler::Apache2->preload("$tmpdir/app.psgi"); </Perl> <Location /> SetHandler perl-script PerlHandler Plack::Handler::Apache2 PerlSetVar psgi_app $tmpdir/app.psgi </Location> END } sub _render_conf_location { my ($tmpdir, $port, $psgi_path) = @_; <<"END"; LoadModule perl_module libexec/apache2/mod_perl.so ServerRoot $tmpdir DocumentRoot $tmpdir PidFile $tmpdir/httpd.pid LockFile $tmpdir/httpd.lock ErrorLog $tmpdir/error_log Listen $port <Perl> use Plack::Handler::Apache2; Plack::Handler::Apache2->preload("$tmpdir/app.psgi"); </Perl> <Location /foo/bar/baz> SetHandler perl-script PerlHandler Plack::Handler::Apache2 PerlSetVar psgi_app $tmpdir/app.psgi </Location> END } sub _render_conf_location_match { my ($tmpdir, $port, $psgi_path) = @_; <<"END"; LoadModule perl_module libexec/apache2/mod_perl.so ServerRoot $tmpdir DocumentRoot $tmpdir PidFile $tmpdir/httpd.pid LockFile $tmpdir/httpd.lock ErrorLog $tmpdir/error_log Listen $port <Perl> use Plack::Handler::Apache2; Plack::Handler::Apache2->preload("$tmpdir/app.psgi"); </Perl> <LocationMatch /foo/bar/(baz)> SetHandler perl-script PerlHandler Plack::Handler::Apache2 PerlSetVar psgi_app $tmpdir/app.psgi </LocationMatch> END } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/cgi.t������������������������������������������������������������������000644 �000765 �000024 �00000002030 12244057435 017371� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::Requires { 'HTTP::Request::AsCGI' => 1.2, }; use Test::More; use FindBin; use HTTP::Request::AsCGI; use URI::Escape; use Plack; use Plack::Handler::CGI; use Plack::Test::Suite; Plack::Test::Suite->runtests(sub { my ($name, $test, $handler) = @_; local $ENV{PLACK_TEST_HANDLER} = 'CGI'; local $ENV{PLACK_TEST_SCRIPT_NAME} = '/plack_test.cgi'; note $name; my $cb = sub { my $req = shift; my $cgi = HTTP::Request::AsCGI->new($req); my $c = $cgi->setup; # Fix CGI container parameters $ENV{SCRIPT_NAME} = '/plack_test.cgi'; $ENV{REQUEST_URI} = "/plack_test.cgi$ENV{REQUEST_URI}"; # Apache's CGI implementation does not pass "Authorization" header by untrusted ENV. # We bow down to it under this test. delete $ENV{HTTP_AUTHORIZATION}; eval { Plack::Handler::CGI->new->run($handler) }; my $res = $c->response; $res->request($req); $res; }; $test->($cb); }); done_testing; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/fcgi.t�����������������������������������������������������������������000644 �000765 �000024 �00000003200 12244057435 017537� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; plan skip_all => "release test only" unless $ENV{RELEASE_TESTING}; use Test::Requires qw(FCGI FCGI::ProcManager); use Plack; use Plack::Handler::FCGI; use Plack::Test::Suite; use t::FCGIUtils; my $lighty_port; my $fcgi_port; for my $script_name ('', '/fastcgi') { $ENV{PLACK_TEST_SCRIPT_NAME} = $script_name; test_lighty_external( sub { ($lighty_port, $fcgi_port) = (shift, shift); my $needs_fix = $script_name eq '' ? shift : 0; Plack::Test::Suite->run_server_tests(run_server_cb($needs_fix), $fcgi_port, $lighty_port); } ); } done_testing(); { package Plack::Handler::FCGI::Manager; use parent qw(FCGI::ProcManager); sub pm_post_dispatch { my $self = shift; ${ $self->{dispatched} }++; $self->SUPER::pm_post_dispatch(@_); } } sub run_server_cb { my $needs_fix = shift; require Plack::Middleware::LighttpdScriptNameFix; return sub { my($port, $app) = @_; if ($needs_fix) { note "Applying LighttpdScriptNameFix"; $app = Plack::Middleware::LighttpdScriptNameFix->wrap($app); } $| = 0; # Test::Builder autoflushes this. reset! my $d; my $manager = Plack::Handler::FCGI::Manager->new({ dispatched => \$d, }); my $server = Plack::Handler::FCGI->new( host => '127.0.0.1', port => $port, manager => $manager, keep_stderr => 1, ); $server->run($app); ok($d > 0, "FCGI manager object state updated"); }; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/output_encoding.t������������������������������������������������������000644 �000765 �000024 �00000001354 12244057435 022045� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; package output_encoding; use Test::More; run(); done_testing; sub read_file { open my $fh, "<", shift; binmode $fh; return join '', <$fh>; } sub run { my $mangler = 'try_mangle.pl'; $mangler = 't/Plack-Handler/try_mangle.pl' if !-f $mangler; my $mangle_file = 'mangle_test.txt'; test_handler( 'CGI', $mangler, $mangle_file ); # test_handler( 'FCGI', $mangler, $mangle_file ); return; } sub test_handler { my ( $handler, $mangler, $mangle_file ) = @_; system( "$^X $mangler Plack::Handler::$handler > $mangle_file" ); like read_file( $mangle_file ), qr/test\ntest/, '\n is not converted'; unlink $mangle_file; return; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/standalone.t�����������������������������������������������������������000644 �000765 �000024 �00000000210 12244057435 020755� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack::Test::Suite; Plack::Test::Suite->run_server_tests('Standalone'); done_testing(); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Handler/try_mangle.pl����������������������������������������������������������000644 �000765 �000024 �00000000341 12244057435 021143� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; package try_mangle; my $module = $ARGV[0]; $module ||= 'Plack::Handler::CGI'; eval "require $module"; my $res = [200,[],["test\ntest"]]; $module->_handle_response( $res ); exit; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Builder/builder.t��������������������������������������������������������������000644 �000765 �000024 �00000000267 12244057435 020300� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More tests => 1; use Plack::Builder; my $app = builder { mount "/" => sub { [ 200, ["Content-Type", "text/plain"], ["Hello"] ] }; }; is ref($app), 'CODE'; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Builder/mount.t����������������������������������������������������������������000644 �000765 �000024 �00000000461 12244057435 020010� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; my $builder = sub { use Plack::Builder; builder { mount "/foo" => sub { }; sub { warn @_ }; }; }; my @warn; { local $SIG{__WARN__} = sub { push @warn, @_ }; my $app = $builder->(); ok $app; } like $warn[0], qr/mount/; done_testing; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/Plack-Builder/oo_interface.t���������������������������������������������������������000644 �000765 �000024 �00000004076 12244057435 021311� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use Plack::Builder; use HTTP::Request::Common; use Plack::Test; my $app = sub { [200, ['Content-Type', 'text/plain'], ['ok']] }; sub test_app { my $app = shift; is ref($app), 'CODE'; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/app/foo/bar"); ok $res->header('X-Runtime'); is $res->header('X-Framework'), 'Plack::Builder'; is $res->content, "ok"; }; } { # old (doucmented :/) interface - backward compatibility my $builder = Plack::Builder->new; $builder->add_middleware('Runtime'); $builder->add_middleware('XFramework', framework => 'Plack::Builder'); my $new_app = $builder->mount('/app/foo/bar' => $app); test_app $builder->to_app($new_app); } { my $builder = Plack::Builder->new; $builder->add_middleware('Runtime'); $builder->add_middleware('XFramework', framework => 'Plack::Builder'); $builder->mount('/app/foo/bar' => $app); test_app $builder->to_app; } { my $builder = Plack::Builder->new; $builder->add_middleware_if(sub { $_[0]->{HTTP_HOST} eq 'localhost' }, 'Runtime'); $builder->add_middleware('XFramework', framework => 'Plack::Builder'); $builder->mount('/app/foo/bar' => $app); test_app $builder->to_app; } { my $builder = Plack::Builder->new; $builder->add_middleware('Runtime'); eval { $builder->to_app }; like $@, qr/called without mount/, $@; } { my @warn; local $SIG{__WARN__} = sub { push @warn, @_ }; my $builder = Plack::Builder->new; $builder->mount('/bar' => sub { [ 200, [], [''] ] }); $builder->wrap($app); like $warn[0], qr/mappings to be ignored/; } { local $ENV{PLACK_ENV} = 'development'; my @warn; local $SIG{__WARN__} = sub { push @warn, @_ }; my $builder = Plack::Builder->new; $builder->add_middleware('Runtime'); $builder->add_middleware('XFramework', framework => 'Plack::Builder'); $builder->mount('/app/foo/bar' => $app); test_app $builder->to_app; is_deeply(\@warn, [], "no warnings"); } done_testing; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/HTTP-Server-PSGI/harakiri.t����������������������������������������������������������000644 �000765 �000024 �00000002417 12244057435 020610� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Plack::Runner; use Test::More; use Test::TCP; use Test::Requires qw(LWP::UserAgent); my $ua_timeout = 3; test_tcp( server => sub { my $port = shift; my $runner = Plack::Runner->new; $runner->parse_options("--port" => $port, "-E", "dev", "-s", "HTTP::Server::PSGI"); $runner->run( sub { my $env = shift; if ($env->{PATH_INFO} eq '/kill') { $env->{'psgix.harakiri.commit'} = 1; } return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hi" ], ]; }, ); sleep $ua_timeout + 2; # to block }, client => sub { my $port = shift; my $ua = LWP::UserAgent->new( timeout => $ua_timeout ); my $res = $ua->get("http://127.0.0.1:$port/"); ok $res->is_success; is $res->code, 200; is $res->content, 'Hi'; $res = $ua->get("http://127.0.0.1:$port/kill"); ok $res->is_success; is $res->code, 200; note 'check that the server is dead'; $res = $ua->get("http://127.0.0.1:$port/"); ok !$res->is_success, "no response"; }, ); done_testing; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/HTTP-Server-PSGI/post.t��������������������������������������������������������������000644 �000765 �000024 �00000003036 12244057435 020001� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Plack::Runner; use Test::More; use Test::TCP; use Test::Requires qw(LWP::UserAgent); test_tcp( server => sub { my $port = shift; my $runner = Plack::Runner->new; $runner->parse_options("--port" => $port, "-E", "dev", "-s", "HTTP::Server::PSGI"); $runner->run( sub { my $env = shift; my $buf = ''; while (length($buf) != $env->{CONTENT_LENGTH}) { my $rlen = $env->{'psgi.input'}->read( $buf, $env->{CONTENT_LENGTH} - length($buf), length($buf), ); last unless $rlen > 0; } return [ 200, [ 'Content-Type' => 'text/plain' ], [ $buf ], ]; }, ); }, client => sub { my $port = shift; note 'send a broken request'; my $sock = IO::Socket::INET->new( PeerAddr => "127.0.0.1:$port", Proto => 'tcp', ) or die "failed to connect to server:$!"; $sock->print(<< "EOT"); POST / HTTP/1.0\r Content-Length: 6\r \r EOT undef $sock; note 'send next request'; my $ua = LWP::UserAgent->new; $ua->timeout(10); my $res = $ua->post("http://127.0.0.1:$port/", { a => 1 }); ok $res->is_success; is $res->code, 200; is $res->content, 'a=1'; }, ); done_testing; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/HTTP-Message-PSGI/content_length.t���������������������������������������������������000644 �000765 �000024 �00000000603 12244057435 022142� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use HTTP::Message::PSGI qw(req_to_psgi); use HTTP::Request; my $content = "{'foo':'bar'}"; my $req = HTTP::Request->new(POST => "http://localhost/post", [ "Content-Type", "application/json" ], $content); my $env = req_to_psgi $req; is $env->{CONTENT_LENGTH}, 13; $env->{"psgi.input"}->read(my $buf, 13); is $buf, $content; done_testing; �����������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/HTTP-Message-PSGI/empty_streamed_response.t������������������������������������������000644 �000765 �000024 �00000001162 12244057435 024070� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use HTTP::Message::PSGI; use Plack::Middleware::AccessLog::Timed; use HTTP::Request; use HTTP::Response; # Plack::Middleware::AccessLog::Timed is used here as it always uses # a coderef in response_cb to wrap the response body. my $app = Plack::Middleware::AccessLog::Timed->wrap( sub { return [ 200, [], []] }, logger => sub {}, ); my $env = req_to_psgi(HTTP::Request->new(POST => "http://localhost/post", [ ], 'hello')); my $response = HTTP::Response->from_psgi($app->($env)); is($response->content, '', 'undef response body converted to empty string'); done_testing; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/HTTP-Message-PSGI/host.t�������������������������������������������������������������000644 �000765 �000024 �00000001235 12244057435 020106� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use HTTP::Message::PSGI qw(req_to_psgi); use HTTP::Request; { my $req = HTTP::Request->new(GET => "http://example.com/"); my $env = req_to_psgi $req; is $env->{HTTP_HOST}, 'example.com'; is $env->{PATH_INFO}, '/'; } { my $req = HTTP::Request->new(GET => "http://example.com:345/"); my $env = req_to_psgi $req; is $env->{HTTP_HOST}, 'example.com:345'; is $env->{PATH_INFO}, '/'; } { my $req = HTTP::Request->new(GET => "/"); $req->header('Host' => "perl.com"); my $env = req_to_psgi $req; is $env->{HTTP_HOST}, 'perl.com'; is $env->{PATH_INFO}, '/'; } done_testing; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/HTTP-Message-PSGI/path_info.t��������������������������������������������������������000644 �000765 �000024 �00000001013 12244057435 021072� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use HTTP::Message::PSGI qw(req_to_psgi); use HTTP::Request::Common; my $env = req_to_psgi GET "http://localhost/foo"; is $env->{PATH_INFO}, "/foo"; $env = req_to_psgi GET "http://localhost/"; is $env->{SCRIPT_NAME}, ""; is $env->{PATH_INFO}, "/"; $env = req_to_psgi GET "http://localhost/0"; is $env->{SCRIPT_NAME}, ""; is $env->{PATH_INFO}, "/0"; $env = req_to_psgi GET "http://localhost"; is $env->{SCRIPT_NAME}, ""; is $env->{PATH_INFO}, "/"; is $env->{REQUEST_URI}, "/"; done_testing; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/t/HTTP-Message-PSGI/utf8_req.t���������������������������������������������������������000644 �000765 �000024 �00000001051 12244057435 020662� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Test::More; use Encode; use HTTP::Request; use HTTP::Message::PSGI; my @paths = ( 'П', '%D0%9F', decode_utf8('П'), '%D0%9F', 'À', '%C3%80', decode_utf8('À'), '%C3%80', ); while (my($raw, $encoded) = splice @paths, 0, 2) { my $req = HTTP::Request->new(GET => "http://localhost/" . $raw); my $env = $req->to_psgi; is $env->{REQUEST_URI}, "/$encoded"; is $env->{PATH_INFO}, URI::Escape::uri_unescape("/$encoded"); ok !utf8::is_utf8 $env->{PATH_INFO}; ok !utf8::is_utf8 $env->{HTTP_HOST}; } done_testing; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/share/#foo�����������������������������������������������������������������������������000644 �000765 �000024 �00000000000 12244057435 015440� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/share/baybridge.jpg��������������������������������������������������������������������000644 �000765 �000024 �00000233736 12244057435 017351� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿØÿà�JFIF��H�H��ÿí�^Photoshop 3.0�8BIM�����BZ� San Francisco_� Californiad�USAe� United States���ÿá ‚XMP�://ns.adobe.com/xap/1.0/�<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 7.82'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'> <rdf:Description rdf:about='' xmlns:photoshop='http://ns.adobe.com/photoshop/1.0/'> <photoshop:City>San Francisco</photoshop:City> <photoshop:Country>United States</photoshop:Country> <photoshop:State>California</photoshop:State> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='w'?>ÿÛ�C�   ÿÛ�CÿÀ�ô�ÿÄ����������� � ÿÄ�U��!1A"Qaq#2‘¡± BÁð3RbÑ$rá 4Cs‚²ñ%56St¢´Ò7u³Â&dvw’µÃÿÄ�������������ÿÄ�G���!1AQaq‘¡ð"±ÁÑ2áñB#R3br‚Ò$’Âcs“¢²CS£ÓâÿÚ� ��?�ñ #ÉçZÉÂ]cÆ8Ç?M0%-Q3Ç�}|è‰L«8Æ=ôÀ$–X¼Ï$è…@ãêtähvJR¢?ƒôçKiH%–/×.ä‰ UNíÏßH¦J¬xÇM".!îÇ]?zP–X½ˆ`q¤%„„s‘ÏLZ™°çßÏ#LÔè„€ “ôΉ‚×L‰JaÇÏ<é€0’])ðGt‚P‰Z|�1Ο[$‰Øã ýpt‹wNŠZQŒöóôÓ80R”žHàãDZRJŠLŸÝÏ:n©Ò‘ReÀþ:!Ñ4¬¤¤=Üã?ÓJLJR†4ƒ¸p?="Ô–F”Ê“úie>IÊé¸ÀiƒL&BKN@'m1!:à'ŒdŸm?zHg„ž|i ¤…h 1d’ ùqƈ™IÑó¶‘“’‘h€ÏpÏ:¡"Ñ‚8ÒÉ‚£'8�þ£KDé�23ù>É›Ä�üôDrNm×$gøi‰›”)2˜Ïÿ�=07I"ÉÜçÛßJBu@3ÉÇùh@ä’H¦9ãΞ ,;83ýt“,@ñƒ§„’]œ{s¡"É/Œ¸'Ü{iÈN’+åŽ~úsÜX<`~ÚÓ²e‰B<Œ :KÁÏh#ž8Óå$$°ì@÷ãBRX”#œdiÈI|í äli“ÊĨƒ‘¤IcØ�à¤:„…ÖF #ü4sI&Td=>š¤Bįps¦0„‘^?Â>Ú}”›(ÉÈÇ¿å¦ rC)&AÎ :PŸdž<r?MDê j„©r Èþ¦¦”’É�œãJSÊ!Sëþz}’KÆÓéJxº]8A¥'TÅ,#ç$qí¤BIeLýyþZIB]cßòÒ´ÂId'÷Gá§)ºY"϶™©ºÇÈ䮜”¡±Ÿi¥+„RDy<.©‘Iä®t²Úé"Ò·?žŠlž))ŽyÀ?ÃQ.”#b§oqž5&mS#R”áx9öãΚ£R•¶}ôùgMSZÐçŸ®Ž™´?Q¿×Q·.¥:XPõÏ$ȘmǸ`È÷Ñ4òBåŒô�1¹ý4àOHN ‰ÉgmDîIËHWÛû§.$ÎèBo–Ç>ÞÙÔZ‚SÊo–œç<Y Jb4§A=?cùiÄê™Ð{vóüt§hNá Ÿô4å2 àåIü´Ñ(´C´ |Þ~ÚѲâú òÓÂP‘hÉ àgýgH ÓísúsãòÓ²VH˜¼‚F?Ñ1+l1óÈàxÓfLRmÀ?Ÿ4Ù ’1ûàÿ� ŽIÈXùîÁ9Ƕ†žI&OÞÈ içšb“1Žë¤4‚LÇìF7JRe� ùÓÌ&I”òp<iŠK¾=°xûi‰,;s€|MK£“Æ~ÞÚ"S¬{Ð~`éàh/…Kv’IÇ<ãBIO Ð|dŸ|çΖ·J=œ°?qΟ¢I2£ž9ü´À¤° ãóuoÌx9ûh„¥ xÿ�-&ôM+Ol¤äñd›/h {ÿ� 3Fé‰2¾xÓ¡H•$p|hz'$@Û¤ '�•3XÎ9ãéÎtÉ€K"s’9ûq£§„B¦<Æs¦ €KªÇï§ÍÉY.àã øûéeC eÁÀû"‰*±ä®qùišgDº¢V3ŽÞÞÞ|é‰Õ,#ÇØiÅôH5±.G³ž1§†éu‡‘ÀñãC;§(ÈáîÈ8-§ÍºDJ@3ÜTr])H„ltÞÜçL#cƒžF €§©ÎsÇ>ß]&Âdá18È ÿ�]8S'(é�v‚>šB|N1PƒÛ‘íôÓ—’pŽÞ8àãüô‰Cä¶ä ¡ÆqãB÷Þé‘klÇøq8:$]ÉMj=Ø�ãNå ’5ÌÉŒ{éÍD›(qlíVàž1çßA²@óMòÐ`¶GòÑæè‚h¨£<œ`{è òd´Gå~ž4Ä¢M²Ò|s¤!:J^Ohç맘Iôù�Œ}4‚P„’Ü8Æ”§7„ìsõЃ eÝ ñdð§9ÓÇ$À!^? Œþž~Úº ïÍÀÁúéI4Y ÑäiÓÂE£Èàù{iw¦�!Ìc>äãØs§€¼JHÇöß6hL ÒE2<q<€H˜ÏÛý{ètL“1{ùmDGšHÆqÊàçõÓÉ6AŒú謔$™ò8Ð¥ 2€dãI%‰\ÌóõÑ'”‘@ Çë¡ µ_ }ÈöÆšRIvpGƒÆtSºuó·<œhfé, séöHƒ&r0qôÿ�_–‘;”€ºÇ´>=ôЙ`SÈäûi"cÚ9È})æ™`WŸlcV)g¿?–š$ˆØm"wN’l£“äî=ô§É)I:dž3í¤‰IXÎxóÐ<ÁÒR‚n¦Èƒ p5"’ë±F襖0}Ž4‰;'Òè¤@| òÓºx„¦‘u“j–XŽGOoá§<ÂP—Hâ] 1ªmuD,G'Œž–ˆ‘ /ÓO¢h¨àíÇ×Dã°Nîh¨áÇ#ÏÐ6aºb'TlpþmÐˆŠ³iÆ—CØ©²3ôü´…ŠPœc¥>ÀçÏ×N ’"S”4‡ àôúiî'hh}3¦R!<CoÏoÏO2›dóMn'ÂÏLa1OZ˜€BñãÇ9( Ö+>p;[ëœiš%,Û"Å€&OåãDBby#él­ÏÉïôѰ(ÞûY 5™Ì¸ìÀü¼h\Ò e#- ªþé?ôÓĈF$ÍQkÀ<ùãL Ä'¢Ô[~ƒþ4ÐŽ5E¸ ’¹×Nœ&yèˆã·ùiÃú% ¦jL|¸þº“HÛ-(ÈÊàç¢OºJnG'ÆœÈ(ö@ÉNxñÏLtL‚’–oóΘ™Ñ< Þsí¤t€š$BáñÇ} òO!š/ ií¢Qc>€ÿ�]!Í?D‹BᦞP†„“ÜžÞ=ôÉ$LGò?}0;’M¸ÇDœÂ£ƒïõÒ”0’hüqΘ™"cä`3ã:y²rF3à‘çï¤ÞV1‘Ÿç¦¨L°hÀÏ$S9#ØÓw§Ê±(Æ1ý4Ò†e ûŸ#N9" Uú ²EcÛç'òÓ›è”,=3í>ÚÒ jEAÎFGŸÏF.œø§¯ßMÜ›¹%ØqŸm-“…ƒ({ )d , ç} 7L°dÏ<ñ¢iKhX2{úhsM’d8öÒžHâÉ"ª ŸáO$Òž¤Ds�#RŠ 01Àòt¯ªH•…€sãòÒJ脈c¦%' ‘ ¾2tAÀ‰O u„ûãçH€Ÿ*!b9R†Pî‰Xy$å½¾šM;„¢tK¤^4ì¢Q:£#€;GõÓ7TA¨ØéÉÁ�}4íx:¦Q©OŸÏH%?’Žs¥ŸiHç ($gó:`mdÀòNRgÓ“dÄõd s¦mH@Ÿimä1€~ÃÆ”¤TŽ’Ö[·äôÓ‹”*OEf<š&ºÈd)E-‹ f<é¢f²EÂ$)>Þ@r~šwuQ:�„éÚã÷3ãÈјº…ÏN´»h”'ÓÁçý JƈCRµÀMo·ˆ‘ÏgçÆ„¶e¬#ªi­³cÿ�V1íÆ„·r¦c”N²Ø3€5'ee¥F*íݧ¼é§dj;UCŽâF9öÓá1TPòp¸ûhÅ“Ç$Ç=ÉãÞiÂeš“ÆOÓI¢,J|©ªjqàŒé%8±MÒ@CŽ4ƒ)ÜÔß$þt³ºp9!N¿’<i©¬‚x2Oüt×)ÀC4}Aó÷Ò%1d;EôǹÒºHcSÆ‹2b%"ðƒÏiÝTƒE·�rH˜FxòtSÍ(HIÎFtóÉ$“EŒƒÎ>ÚwtLàh~Äc߯4Ù’#t‹Cÿ�?NtòvD“1r0¤¶)‚ÀÀqçI4“F9¶“ˆÕ(XzCØqíÆ„X&ŽI3ÇÕtà§X˜<Ζdé3’G?] y&+OƘ҄™ŒžÞ¦”óK,•‰B=»qÈÒî—zKÓϰΔ„Ð„}äže3Ž�#ï¤O$Ð’(OÓ‹hœ‹¬9ç{ñ¡BBI£çœm80,%ÙÉî�ŸÏ@â7Bаò8>}ôùµ”a‘6@ÆšNÛ'„JÅŽB÷Ë:Y¶K,˜„ˆg¸/m"‘Ñ,žx#ÛÛLŸ-î‰X¼xóôÒ&d#Á°‘à~šg8§‘) áãßH[T´’ŒHWéãNÒ bÔdpp¼`6då€jŽŠŸÇýt€%—dá1ÀÈÏßë§cÆÉ€Ø§Hi öûiCd‹ Féâ <óÚqçòѦswOTÔ$‘Æ8Ð „¡p… ¥·ð¹lçt3²“QÛ3ÛòàþZ0Ê!L-öœöü ËO(\TæÝcî ˆÏ‘ÇÓFÝ!DíTâß`-ŒÇŸÚ–âʳž¦t{eˆ§¿ÓH… $h¤m,û3ü5$…ß ê  ›Óå8ãR6¥U©Xƒ)Š}¦Xˆ™p¶…¢•µ9¨=Ïn$Éü´®0•¸XXòäþ^ú…Óª¸Ç(E}¡cÚAз’¢5–Æ_ðûñ¤O$@(ÍUPÄŽ~Úi3 n£Õ4XîÊ®šN…B`šŒäŽÑúé‹¶ É3OFsžÁÝùèÜå$&Ç£<ñŒèC®œuM’R¸À�ùi®šRÓàóÇôÓ”@I‚‚’ž}¹÷Ój“€ Ú>Uˆ`ý“ÁB¼åóÇdáˆV€œq¡Î£ˆH´ãÇñÓ)ØÕ"bç´i4ÚP†ø¤L ‘£)ˆf„ò;Aö�€·šE¡ñ€3íÆ2`Û¤Z,sÎ3¦kå"Û¤L#æ ¤è{’MAÈ#Nté6‹>söÓ“t€I4~x9Ї](%%éƒÎ24¤9, R~—�ó¢;„j±hüñΆ†D!Ê’1£ôÑlœ˜²OÓÀá}ô.tÙ#É&cÈ Œ ý|iÂ5X˜óà϶;”CšM£ù¼xçI¶LÀÇ8Àü´Ù’JÀÇõý´ù’ 3œóÒˆM’t÷:py')6N9hfFˆR!~ªOÓNà™ÝUš±=ÿ�M@ÒvS5·„P‡÷sçLçÅÑA¡ ãåϱїM‘ Q }¿,èZø”ͺ%!äá¤×sNÖÂ! Î~žÿ�m;Œ„@uE-1?UÓIÙ"ÙF%1àã?C§!+ꎙø!A9ñ¡í#t¢Œt„rA |èKÆÉÀNqQdÛÿ�ŽŸ´¿TƒL§:z#Ÿ>øÐŠ¢R!<AC“àçéôÒÎH€˜§¨(0U¾üyÐæÝ žIöž„ÿ�v¸ -R:Jœã?M\7Q:öS }´ÒTŽ>žtaÉœþ×jÏkzxý4m}¬ %Y›2ü£ÓöãF*ÙZ¾´v/ðÔ mº¦ç쬻VÖîìÄJÇòÔlª.y¿U? Ù…ÐbaçR5¥DêHÆÉqLÇÑ>>š°Æ@U»VÌ ¹íCGöl?M Ûb§k䪢õ·YK“Ï<cU]ÍhÒ©bWv²`6#üµWØB®nV…Êsýt)Ï%_Üh™Øh#¹Pªê%Ëð8Ôn;#�(­Usò§4Ù„ÁQB<`góÒ%= ™*(Ó8óü†œT­L³S®t`ÿ�ÏI§DA©¢Jsœ}s¤]4l›ä¥üñõÇúûèŽéÃ/èAç «‡TS“äqýtùÄÝ"e ôÙöùtÅæòˆ¡6¿è4ƒ€A7B½9œ¦ÏºxÒÍÏŒ>iLÏ8#ŸÓv°n–é€àü™ûiZé9¥ ðä`s§Ít£š ö�ûè‹Ä¤á)3 $çΘT.vˆŒä1§k)òÆ…$b<}yýt5t%‘ #¸ËEžéòDÅÆsÇ8Ó ]a阎>Úpy'ky$Ú r@ü¼igƒ ŽÉˆùçNtÉ`càþö–xM’F09ûéf3É9hæ±1ÿ� û6dÍéªDÄG#?å§Êw4”Ÿ¥$ ió&bSÈ9çL℈RLƒÁ9€lŒ6é&Ç禑¢Û$L~ñÓî˜ÈÙ$É“þÝt%[iMöçΪæU¸;¢’œóì8÷Òq›"!´ÃŒþšYÄBrÛ¢’1Ïw$Ä"’Ÿ'“ý4g4ZÓ{�ë§:].ŽJQî¸>9ÒÌP–’>^~þÚäe±dá1Èã>ãK6Áa”ëqçÎt26J›éÖ2t‰¤;'hh†2Usçé¡Ì¦S¼yÿ�qþºpîh2O”ÔD‘Æxþ:bã6C“e §·ƒò°:"ø6M–J”ÑÛ +ÁÇ'N".ÔæÙh¡íÉûê@UwYYv‹P%>N1£a¡*ײY)•Ç?MX¦ ª7W^ÜÛË!ŒzË:°Ð³++ÿ�l줩ôñ'頻`•R§5zØú`Ò¢þÃ’¶¬ \Ö}JêaSÒÇŠ‘»©OŒxÕ†¶Ê£ª’U;¹¶¤%&íAY‚Wpõ`J×­Ç´FLB�çTªÄ-JUIº¡7 ƒÓg9üµYÆVµ#*š¼Úʨ\Ö‹IUuÖÚA~ìðxãQw©B¯ëèð[å9þšlŒ (e]?ï|¸–…ÎÜ©Zn¢ð þ\h ÔÀ&9é\åˆãßÚb@ºA4MDß0€4‰ðI­MrQc`lŸ½6ËGùþ¾t‰ òò@ËIïƒöÒq'Te²ai ?(?M0u¡#¤ g†(U Ì‘™Ó@|ÈÄ•G’x8“pÑ%XI²*íb¨´WKo«j©TËSÕÅSW]JË27 àžÖîS†V�iWkă*Þ?†ÔÃÔìêDÛB¸A#N©–JB1ÁÇùhóÝR!ôÞ àçY“eB=>Oq8?MŸ)e(f§óÓL ` !šœy#úi³ÂE·º¡ã?èèÁ;”²I44á÷º”;Óþèúý4Ítj”n‘xsþG¾œ¾öÑ6©�’>ƒCš4LÃD›AÇ€~‡';ÉHÁ8ϸÒq$D„“AìßN×sL’0ñàcÏç§0Sd¤Ì>ݤþz~ÒÝ�’h8Áþä‚I ýï—ûxÑò²Fçû!ªA£2E£ÁÇú,Âe„›Dsœ>‡CžP–€f.8ãê~úv¹0<Òl‡è~^4¤ÌÎt¦Np1¢ä)‰Ç${q¥$ZTfUϺ8ÿ�®ªfZ¨¤ƒøçÆ–c²l¼ÑkMŒú}´TàQQÀO<sïõÓJ<‡DltŸÇ:DÂE¦QÑÒñ‘ÉÀÐæ)ZÈÕ'â4ÓÍ8b>:_¨ÉÒÌ!>K§H©>Ç>çtݧ$å §xiT@ç>‡9&É¡¥^>Sôÿ�GMÚsHÓÄT cå$ý´9ÀK/$ñOKž;{´šé6L[ÍHi(ñŒƒ9« ƒÊMED´ãÒk ]F[¤)¾Þ¹FƒãÇþ0ùUœö×nOç:•ޏUê7ª³¬¶²ÝŸ&}µ(t\*ÄB¹ll”Ì~ÿ�MY¦ûIU*‚n¶híâíPþ£WYhXõÝ{-½Ø;J)Z�ñ€3ôÕú@,ŠÏºèßEzQ¿j©-¶Ê35K€1'ÖËe/j`-’êÁ¦äÙ[uîÕ4tõ`�ï “ÿ�<~zŽŽ9®0 ŸÁžÁ™s+¨›:*ÃÈÈñ«„ÈTªÓ í¶;=`°·xÕ: Ä-l;åj¾èÛ’wÉûÜçΩTiÙmPx…Eßv̤· <ê±a‰Z ¨ UEÛlà¿yþšÀ z®.[~;‰(<ê á2¡Ê]g§RI馘¿%(F¶ßN¤Œr?ž…ϼ)›™F*©cÉà{{Ý[& é—Ÿi‹¹£‚še¦ú è?×>hN{¦ÉiFx\}þšôí@c箣ídÝ;YOÕïm§¤¨¿STíÆÙÅîÌ+hª÷ì1†/”F ®ÉQÛ+ˆþUï Ú¬.ª&p ¶åº×iÔ^:$Nϰl»>àk]{ ›TÞk¤ü7|õ5‰#'ljîëd0¢¨hç’˜åqw—aªe2FÞ>öVøs2×içÎÜþš©U_R¶¦à©Ùµ›¦[8Ø-5&’®žÍ”gtC8ŠžY¥å´J]âôäe¨v‘¤’9†Ò,ÅIĂٸƒ´‚®ó2µ±tÈ ìâvšìn;È·DÃW±¶Níª¶EþšÝxšîK·«f8µGNµ&ª)îõ_…Šyý:XªbH"s8¬ @É'LqÎÚ}ûîY_ÓS>ö·‡ì¡·¥³iQW^î6™êv„WîØw 2<–k”¤NѽyQDR¥%L‘J„¬‰Œ¤„nÙébZïÒ¡u8ýVU³Ð6r‡ O ô9ñãY&.¢ æ›ÞŒ‚F0>šl֔哠B½)Ò.S9…zcœ€3ô od9-(v¥ð{xΈžiÃd¤œŸ�i³„²òCµ3gÀüôÁ÷ºQ-ž8ÒsÄ!ÊtH½9ñΞSä $Z䌟¾˜ÄJM¡<Œ|Ãí¢6O$‹ÂÀ¨ó§Ô �j’01ù‡Ÿ}&™Fi™„‹EŽÞœ Ph8#2`f~ÙÒ ¡ ÑpÀŸã£&éeIO°Ç}ô%ÓªbË‹Ž@ÇÓE¨„ú¤#Éǰýt¤“(E£ü½ÏvDák¤ÏÌt.w$³«Ù)1”j¦a+G ;#R<Añùh…Nj@ÔZÒŽ˜¿šLJ*:3€{JqÈÓuG”£#¥äqÏÓMÚY"ÄrQ¶3ÛçíöÓg€‹*pJSŒ`Ÿ×Cš{’"BpŽ—ó¡%,©Æ:O˜qÚ=´!Äj–Té1ç�›4¢ :'ŠzLã�·ùhƒ¹%–éî 1ŸÆ4!ÞiÍH)mÜ‚Ciƒ÷C•I¨í¿LçóÒk„ÂD)] ´{çFÇ좩¥”ÚÝmÈ0G¶}Õw4ê¬;E¾?“’G·ßVíÊ«Q¤è­‹0ôò¹ÏòÕ€æªO^r–—1ç³Ií ¥f»e²[:exǤWšû^›µ[Óô§@{�ÇZ4ªÙbâ)ÕŸ…Ž¢Úv⡸WÆ^ÔÅ/n;”0##î3¨ñtKÁÕ¾\S|•¿½XëOêöæÝmºGtª­§0ªzeDYÁËwÈÇï¬Ê7彈ư°†ÞW :´cžª²DìpI8Öë] \Jrå¡{óƒ?jF8ÈÕ:õUì=JÔÖ$&\ÇØj‹ê[´)¯»†9O'íªO¬M–ƒ9x§™Œ™Ï?SªÎ©*ËiªÎçHýÌs ¨IV%¸RÜyƠΧ YAžì©îÇK4n§”b¦ƒ |gÛCHÖndžßžï”ì4Ý¢|—MæÜ[Tçí¦så§ÉöŲ&¼ÒÓߪªéìÛ%nÔVŠûÓ)¨†ØõìዺyBÇD¥bF$BT|ìŠÌê$þ}€&¾÷RU¶Ôlûm¦ói²Ac¼- Þ²ÎàE{~ê·L憷PUR°2(ünL­Þ¬ëèKGÌO¨F¶úŸ}þ)2˜25O~ÂÜnûzÃ5m&Ç£ªUºª†ór„Et –²âBfŠ5is $+;…„Šch´i;™fæçß5Eõ²ë¸/»r¶ëqº]¯7ô³ÔÕOPòÔÕÏ*J^W•‰w•ÚBÅØ–,rI:[-vŸ¿¿Uo‡ÐšÍdjcí÷N2Gg’JE´R=”tðÛ+"EQNòĤ£ÆÈMãYçN2ð4±¦™9šApæ$‰ÆHèce©ˆsj`ÃÃ`´†žD‰ƒß=GT™ÆA' ýq­Ìë%áJ,›Ówmë ÚÓ~®ŠåK¡£šnÚ– €É꧇vÈLˆÑhܳ¡Vf&Ñk¯¿5#\b6%Nh·îÁ¿ËJKéÀ®Ui+«.z´Ñ]ï÷d2=u]H© ”zîñÓAdÒ c�wBǙ͸>|û­>C»D'LzºZÉÕêæÕ¶]ª)ÍuÉ7<Og¡²Çûøt­v‘ëêQ¥˜2A�õ#§2ÅÞY¡Œ›X‹ø÷ࢠ§êTN÷Ѥح[>ùrÙŽžÑ¸@{É«ýò…Q–Jhд²FË,e°-…%ƒ*ÈÊâÄ{û£›Á´{îUt¶™Gˆ®d¼¹ä{r<~cRê7H2S{ÛÜ€‚: ûjŸ&è6£?ðž?M3j_TÙ¯GíÚqy›'ȆjSÇË€>ÚläÎfÈf¥<€_"ûjŸ&å ÔŒHÂäø9Ó{”ÎjHÒžx?ÃOÚ‹Gð¤xS}8| (r$ !#…çH>ÖHÒÔ$Z›œñù?jvI¦ó>º~Ц �¤š•°AyãCÚ›!ìÐÍNß1 Æ4aèLl“xw8Ô† §îI˜¸:|Éd)§ð1å¦BiÚɧÁÇnõÓ‡Á„%‰Oÿ�tiä(ô[)>ÞØñªAÄjVÎYÑ)àà¨O˜êŸ/4dt¹íöçŽBH¸éã‚xþ:äðŽZ6ò‘÷ÒuRhGÇGöçÁÈÒ$nœ5'  â.Så¼#ã¥Î0¿øèsÚJ ÝÓ”Tƒ9 Ç饙8nÉÎ^W‚4³Qnž©¨òGho¶„¾ÉeTê˳÷%êKRÙ6åòôÕ·:{-)¦¦fŽZù’G†ŸÔýÁ+¬2L÷0S…8: g(þTosZ~b¦u};ÝVË…òß6ÖÝñKn5zÔÚ&§–ƒÒuŽoÄÄ{šJW9r1PÅKÕ:xƼ–[¯NþJÕzLi¦Aˆ:j†¥¡`‘ŸyÔ°&UsLToÃ|ØûêAVL„l©•¾šO”pF‰¯ '´B°-4ÌXqýu'ixPUfáZv8ÉÁÇÚ²×èJ¥Q‚ß¶àL Ç3Za …F­ŒÚqÃŒ’GÓ½F¬X¬Úô¦Ëg6|Qz=¬<«ÔêrXõðö‚¶‡jnSL#ì˜ùøÑm\öYÏ£ qtÞ“µ8ñLGo‚Úg™¡ÇU¯›ÆöÕK+z…›ëŸ:®úÛ+Tè’V¥o)ZF›9÷÷Õ*€µpôcPµƒtÇ&d<ë9õ$­Zt• ~É“(Ú®ê 㪋­;|ã´ÿ�}V}[+Œ¤yq¤·œh3ÞB”Ô&¶‘rÄ‚9ãCX섨•eå‡nGå}ÔjÏnfÏìØ}1z‘­Ârµtþ{’Û+ï7;NÎÚUOw3!¥ ¨ôDÄM -N;RÉu$àFk­¦Þì„_K”èûVÝiÚòÔ\6¬4ÏqÚÐË7ûVæ»ê«1m†8]©Dîq*aªÛ´ªÄ˫ӡõ÷×½i˦m>çöM»—~Añ©Ý´ÕïÝåÖ¦Xw>䤊g¹Ðˆšž9íSzð)Tˆ?Ï$ eÑG,fA¯Œ©eÊtúwB¢kÞ²ª8EEEUHŠ™ ‹Õ‘ŸÓŒB.O ’N$ã“© òˆIÙóÕ1ÏFÅ›‚pþƒK¶‹÷£u/²¬÷Pºôù(äŽ Ãws ŽjH"=¬qÎÁãž5WVh86Vpnر“~]|òé´ã [5ößqþõ¡¯¡‰ªšG‹ñ\¤ZŸ^4Çi‘ó:±Pe㸫káµ]Kpï�ÌDÁi‚Ø'ý?¤£ªèø1SýC ‡3$ïýS¼¦v¡%™BöžîuÕ ³G½W-Ù\ûÝÔMòü§ýæúýtæ°‰Þ§D›uBšcò#'Ÿá¦mQ%¥aï’ø”ó/¢G¨¬sãBkDÊF‚”ì}Û¼úu{‡qtÿ�tî}‡¸¤†jW¸XîÛêž ?~34 ŽQ½×8'Ÿ 3êfçïÅ8Ãè5 Õ²õÆöô½;²ï]ƒÒ¾¥í ªü%žébJ8ë%•X«êí¦–¶¶U.§ºyÜ0ƒ(Æ“êå°6nƒoϪŒàÁ;÷rÙ)cŸá¿rŰ­›ÃkuW§U“_j¦Ý‚Å[Mx‰mîXÅOl²N)Dm©îš¶BÁ<€q§mw‹Lë<üž·Aý1?§¤¬›}%WIºiw±|í.¶me¼Õîsf¡Û·û}U¶² k»¹×\3m…r,‘$îêDŽb䏯 µ·2E‡¾ôçñqpº_з߅þ¤Zì½[ݶÚ}¥½z²®”Ö‹îçÛÛ‚Šáiõê¤G0‘d¬†B­Û41º�¤±QΦn)²n-ïxQ£\ W¼:O¿6ö·ko“¼vN概ª‹mæÕQCWO±¬‘K$"ȑɑº»�®®¬ “µÛßš@ƒuY$i2“H§µÕb„{9‘ÁÓšâ`£c$Y-’xðZUö8Ò5Â3F5BÉdŸ´7¤Ê¾ÇÆtB¨˜K°´¤RÒò¸URÎON›·�I)Û†'dTûVá»RÌ!òÏúý5Xc™9fê˰ŒÑdÆöÒ{—äï�22±#óÏ?mLq ý3u[úGDņ™»Bý½µ;ªˆ•h’a:¥sjoŵ1Ž2”þ¾þÚ¢8¥âÀé#mÇz¾xM\¡å¿/=¼›§»·wËw‡jí}ʦ·ÑOs¯[u µF†Š Õ3ÔúpFKÊØDwu&#ˆÒ¢ÜÏpAC‡Ô¨Ki¶aD§ 07k=õm•¦êJ%¶IÃmycTbÞØÉÀý4nªrŠ"ã SííÑ® ôÞåGgêÌÝ;óSEMr§£»Û壞zIãAP©*«dGWI�íu9RF³hñŠ5 Èf žþ—W«pŠ´ìñ>ÿ�ÇDžøèæ÷éå«a^·•š;M¿tYSqX˜VSÎÕÖ÷šXVr‘Hí^žUì˜$˜\öö²±Ÿ ÄÙQæ“uoßóçÌ®R‹ß¡ýÜ*¥è°Ä�O嫆 XŽ�[!¹€Ïh:¦ç–¡j6;{vò¤}tA2ä(Ä·8$:´ òlŽJJ~šnÔ%Ù˜º1h(þšÑ¤Q©@A&~úF°GÙîQ‰D~à{}ô"ºsK’>2@ñÿ�]1©¹DSœT$ãå‹´äSäÙ;ÓÐsÈÂùÉ8öþZÔæˆSVU¯hÓ[­ÔÛƒtÉ%¶Ó$r½$*ªnˆqÛeôáiÄ’»#§õ`J‚½ºž@ªy{÷ÉDLœ­÷ïö0™ºƒ¸.Vz­½–ÙlÐÃw§Ž "¨¤OK7i˜·jÉ3Eøej‰É:ö¼Œ{pjãÜMÌ©aióóëÉkÚæÜ‰ú¾Ê}²zv£Üvû>õž«röYV¯ «jk•¾ªHj8&%‹Ó*KUŠJ‘$½A’@ÃåÓÄ8;-@dHâבêÖdh¥­…�Ò0 ðÁml¶"ß´öÿ�Q^„Ù*i$½Íé,ÒÛhj=gü7¨õ6uî‘Ç«SXÆky—ÔZO’Šš0_WL<‚Ëø_Ëñäªå=¾›Ú|µó*û:éCnŠðô¢k+(?†Eš=°’¯"äFA©nßÚ7`Ë�mΈ²‡X#¨mÌŒFïÁÓ²­ÔfÔîÙI‚>Qçë©™R ‚£U“i¦ Ùû£S² …N¥"tVµ‚�ÓV[]T©‡$+žÁ\cí÷äxÕ¦U²£RW]‚ô°üß–®·£S®;&èôÝ;^epuYupÆðîÀwvz§Æ|éêW¼(Ù„qU½ës æLçUÝ_ezŽ ¦7 Õ%9Õ*•¤J¿JŒHß$ïïÁçßê£êÍ•út?x§ïïÎªš¶²ºÊDku·“Üݧí盛ºÚ«Êûc;7j“Fqu3hÊ «s¹5b[­Õ× ¥ž¾e§…¤h©¡ŒÉ,Ä($Fˆ¬ÌÞ ž4"¹6FiÅÓÅ·¤³\.tvç¬KÕk]­öùh¬%+å–*ŠvœÉ X? C,ñ¸²3Åø#Þ½tðçÜ–BlÞí¯§Õ2ÕZvÅÛzjãµ­·:¸!¨Šv¼Ïp¨{–[¿Ö(–š…wDÎ$ pÕrúu?­.þ÷¸·M:•oú'L»™÷ßœz&‹ÆðºWî[–ãÛ”WÝÜn5íÚ­®—Úˆj ˜*jä)BÆdùÄ ýÒÈÝÀzkwã�¼Äýís¿¢°Î˜øtça·ª¥.¶ŠØÌ¦¬Ìî"@ÝìI 0sžÓÛJŽ0•¼þÊÛðŽh%Âñ>©‘6ÝU}CAOHâFB�ñþ¾º†®=Œfg¥nÎ~P/)ºñ³nV”ŒWQOLÏ‘{ÐŽà‚F|Œ‚?M5+N¡9 Ü#­Ã_N3ˆ×dÝIµên_‡¦Œ¼ìcí 8þ::¸Üƒ3º§§€/9�¼m\> º÷MÔo‡¹ß£ÛÞåfùŽÏKLöÉ—ûÆäh–¶;r©šy`^åŒâŒÎFy\gÄ´q8J¬ ââAý76ÔˆÜ1®‹¦Âpáq,«Z�m̘פÚtS¾ü&ß: ´*n×ý»sÛÕ«ö†Ùâže¶UÑ ©—Ô‹å)8€cËE# 6¹†¾,­Œâ´©Ö³…24#‘ƒ°ÿ�‹sü=G Ãjº„š›yÈŸøËÊÒ ÕiÐφ^§üFu¥ý%Ú— á½*¥ŸðöúP¾´â(ä–NÐÄ)“à}p¡q.Ì=0@.$@�I6›sϹq8~îÌàÐÛ’Lx¹ïâ™®½¿Zv´»º¶¿mSÒÅz¤¶Š.1%ƤÔDÒ¬ÐÑçÔ–™De^eˆÏ“™Uðÿ�Òª@mÁùøûR®Öà¥üÿ�‘'eHPØÍ\¶¨#Ç|ò_¿îùÚÞ}ÕïšçY‡½–Îî/„ÝóFª·FãÙô¶Ù·i·L* ä¸CAo–¶I JÁÜÑÔBóR›ýâà e†¹¬7Ä‚³ªµ$°ÁçLipn× ªÿ�ÃPÖ˶¼‚7`±0êE;Eî“i±ï ¶þÞÜvýçdв® Å-<°Cv§I;R¦(¥HÒEíp’�ê×§Ž{¨šz0ykä±*`ØÚÁ3o¨¤-ø{øeÚÝUéŸP÷Å÷©›gg^¬öÆ®²Y+bÔn™¢¥Š™†Z1<nK 8>F7âø†bƃ3I®æ-�‰&` Z¼3…Ð8cZ©ˆ•¬L‘3"ÒdªÞËÓþ˜Rì«Ëv_wUôªzi6•-2ÑV?ã U)_3º¼b„´~š¹w`µOv¬â8…rø§ú`ë×X-Þ•>A”³¾ß1Àœìf-¤è¨škr*iÉh•î¾€noìÁÿ�=m‡»7‡Ýb·ÞÇ6Ù¿öÊÜ¿ˆŽt+`t·lî>ï+®éßpÉUnÞÜ-ñÓÿ�t]£«­„ÁE0%¦ˆ­0%Ã}8ç’à¼c‰¬Wô8Km´÷ÜD€A0E¤ôügƒáèSsi‹Ó0Lïs"H;¨¶ìê,»"ÏÕ=­°÷?Sl{¾K}u“y^Žê¨š›yÙ%®·ÇKE5#(+O) ’<©7©Ë¤mð·W¨X›8^ ÷å~§–N?†k*4,�‰ÜÛøƒËÏ`:wÕÓxêCÓzYúa½­[sbWÑÙ¨7Ó·ÕP-½m÷J •$1Š·&hæ¨2J¯éÿ�³@51®¬Êå›`n.@ñü¬^€¡Z±eiÊdœ¶?+I±ÚþŠÞë[|2n‹×E6ÏAú ¸­6û~ H¤¬ºVUî:Õ’–ÏÔrÍ|Â$¬šHýtœ`]²�ç¸elSQ2Yó^äØËde-0Cf"g~Ÿ‰`°î4ÞÓä³D: Þ&LOzŽu[}ü%ub_h6¿ÂÇQº-]¶¤›mÂÁº »Ç#Õ]ëg]MU×2SÓÉGÑ‘R&-!V w j¶·hj$H "Yù\ÆoÐdW£4²AŠD@uoH’ÒO|_U_mnœ|,]7ÎÈ·¢uãmØ"ÚKzÝRUìz;BÖÇmš¶g·Çr#R`Sb9ˆ•@Ÿæ„ÌøœuRߟ.„‡Aœ¶ DEŒªÑÁŠNœÒë H`‰ ˜ j ‰Ð­²ø­›á—®;¤[ßeu[¢"ÝBËÜKM7%¶‚æháÍ_5TPUŸÅ<­nB°Ìž¡!pŸ³…53Ñ%òV½‘ús‡ÌH$ˆ‰½…ØñªÍ¬Ö8¦ìÙI¹D¶ö‰ùw‹E¤Ô»³áÛ§»gwtbÉý£ÿ� sôëeÜn·Ë]%úÅ~³VUÝ%ô²¬ÌmŒ"/%,TÅÍLð!ƒÕPÙõ¹Kÿ�æoéê[g�Ò ŒÁ­hyt¸Øf€ É~{€ªÚF‰-–º›Æh’b-3`geª•Ÿ PQÙvmÞ×ñ5ð;º/·šš„ŽÉIÕÛE=Më5ÉXðBRwõÕQ¤OAŒŠÓ»{ÿ�RÈK©UÙ<‘`o–F‡b}c8U@û9¼ç0ÜÍæÜ>¤Òn ýÒ=¿ðÙwøÀþÏ ¿OºZ75v׺ÓuGoQŹ¿‚•i©\¼urOS7âŒF¹cT C¼C·»Ú#qYjD@šOÔIäÌv2áÜ@…¯âU‡k’®2Ø|»™Ë­‰’µ¥·™íK†é£øÄøWéõÚóIu²Þ¬ÔSˆWÁn_D¼U߀¤´õCÔU¥V‘œÓâH×¾>íjÜkU…J†E…7OŽ`hfzž™Øjuç‘UŒ3˜_CarHîè.¹õ·7•>ü¾¥¦Áb½Í!ê'žeXã¥FZI�,ÃŒ�X’ uØŒE<;3¿Iޤì½.¹¬0v&¦Zbm'ê¶®ÇÑÛd½0ؽO¦ê§M+ï7ýÇSc¢Ùô’UÕ_i"c2×WÒÅ ~ ùâXã—άÒÄ®‘ÈV®'H6à'ºÃI7ˆV0Øz¥ßå¶b/}y kewu¢»doî«ÔÙ÷ׯ u6Á·ö´fÓ¾¨vÍêÿ�窡ŽJz*zHð'JiÝb…$f"(»X#€±œŠiP{›êwˆÔAç¯ÑjÔezÌK ‘Äë0Þ’w&ú¨}Û¢¿6³ïïŠk·|^åycµÅÒ]ÁøZ:H^JzÅTÈ‘ÊkVXâaPˆ@ZetÌã•@§F\H‘Ú3åýFuÞHˆ2a#ÂØê‘R´0 ÇüÝ#MD˜¸åtá{ø[éVãþê«èWX:‡Ôk 4¢–ëvªéîµW4‘Äo¶¹ˆ¢hÌ‚¡– a"®G‰ø¹ôžYÙLñ))|Ç)�óU£ðá{s=ÄOÿ�¡óùbyÁ#‘UêQ¸í w.21á¿-j—ƒq¢¬Úd#’”©¡9ûyÒ.0ÝΉÁióǤ‡ëòÿ�£¦/\©;#(˜éXàvvãMÚY&ÓF%#œeCË@÷„B™”jÑ1ÿ�>úlñªA›#b·ŒÙœùñ ‰´£ì¯té°qˆŒiƒÓŠJe·6]ÓqW už‚Zʰž¤œ¬iOÆešW* ÷<²2Æ‹–fU†¥™ß¦ÿ�ný½ø¦t7_}ÊÖ²íZ)©«±ËhÜ·z>Ã=Ö¨¬ö9²¥H2®jª¥§nÖŒ¢´Sfž¶„ÀÍv³ô›öÄ_ˈºŒQ/±°å¹õ´òÞuɆ¾éMBVX¤¹W_ÉžùY”¨‹±¦”qäþcT§À™b1:Æé …gbÌÈ7翇/¯#NÊ�ôÛ÷ûòTÆå·ÔÁ[ÓøíÔ=O÷ìm E{cfZj†�€ËÆHž~ºƒ‰`¤ìÚ�=\<•œ-º«C ›Ç–Ý~ªSdÛ”t{¾™)«ÍÂV²¿ã“¶ATeF=ÊhR… �xNÅ vŽê¬Ä=¯-~£N£n¶¸¾¦û•f­�úb£4åÈÞch6>Šè¦´;OlÑäÎAîå«-ÄɾБÃY_{¨»Æ–¯ñõÓµöéÚÁ.2J•èÅe ƺ&IäíyÃâV~Æ$±¯i±Oˆºe×=uó×ÍU©í·¾^üÕ“EL÷ ×ÖÔ[¯nváŠ(¢ŠB¥K$Ji¢ô›*–è³øhG|“6�]Xº/»ŒrØwªÇ Q¶}õü«t"²¡*«6uúüm±I"úÐMlʯVv‡u!’ß< ý˜î-¢�“þ[ƒ½ŸçÒY9Ûßé¶íÛ³½5ûn_,’#27â©^5 ­ÚøŽÓ†r yzµÍ;¼Bf჌5>QZ*©¦0ɉ2œ22à©û éRÆ5À8 Ø2ÓUµEµw–‹rOf¹-‚iÚ–*ãz̪£Yí.¡•Šç 0$`OO‰4»(7÷**¼1Ṏžÿ� àéÞÅÞ;îJê}¡·n{†zJsWT´ÑdRÀ)’V8XÓ¹Õ{˜–<\w {÷ÔÛEIœ)õ&=ûä9%-÷É‚±�cŽ|þZÕÃâ§ô•‰Âå$͸w}¢Û_-Â÷hµÕ<bDŠª¶(^EÉÊ®À‘FG{i«âÚÓóAGç7åÇB£•wS2¬«"Ë «#Vf}CS`B¸c0£…i©kššío»GéG'«x\²)eê¶Q˜ÆÇîSÚXcH≕¢0¦•<²t‡mîTnúÍðÔ·Áp’݆ŠÓ5e|­ø9*!”�Q=7x½3†.‹Ý/cªàó˜ž)[·ìÃlzSÝë1x .§Âh¡7×QÎ:ŸHÚePVΗî[ýî†ÑOBªyè UfõY?ŒôîÉy=9v‡@vDn×uSv­^p'吏ǚ Úa¤‚4éË_|¯¢Ú¾ |4t^ÏÐ.šnátÞVΦWTÓM|·Ü ²:*&¨h'H’¥ÅCT!ì úN„‡àŒã°<R£«¸9ÆH€ɰ2Ip‰0 z®»ƒh¥Ø¬d΃Q��M…çKì´¦³bí;% òÌ’Ý«¢µÖ¼ÏoµVWwTÒ]DðÉ8¥Š5’&Š" N0NP<‡ÒÚ¯ÄAnVj`OSvø<ºÌdáxkóf©ún|æ æ­¯‹Æè½öÓ¶ú/²®U6ºU½K]>éª2¬ÕóRE=dôÖØDtjŒFbŒcÓˆ "§?Ãø»ž3¼ÃLÁ2çe"Àh¹äº Ü ��5Á�f‰d“;ɉ°ë¨{ qì«îýÙÉÕþ¢mŠÍŠ»¯fÃ|´¥öŠ’)m“ÓÈóÉéFézTÔþ™rÈdùΦ6¹|ÌÇÏ'”¾ñ ™F§ú@˜ik‚~»]uÿ�¨ÝÝÛ®ÿ�_ÓzΓì;5¢X¨ìÔŠš¡Ï¥\cˆ3ÚIêžÙ€B®êмk7…¾» Tî©€Zu1±±6°+_ˆS¦æˆÓ:\‡ ÜÀ¹Žio†~®|>lޱìÝ÷»7‡F7®Ä³îŸÅÝéî—$’×SkŠŸ/4òàÄÔæQé’N•—È#UøÈ®ê9CNYˆ7!ߦ6˜¼rÕXáͦÀ]œ7æg@Z>`n-´ï²?¯{›duBõU¹åêGCl¬Ö¸¬”4vZK“À•1È 4h´6·W•iÌd±î‘ò¬ìÅ‹kV¯‡`h£QÐA6l—S/ÚNº’L•wо…w˜ ix.™±�ëÒÂ`Y4ôovìnŒõ3aõ’—tÓîkNꦿRöìmÏWK=�12æÒæxÈTcó…ã»$jål]z¬ }�dÒÖm­B âlFÐw­þK û€¸›@|Á´uÐØ^e=uçxXúÉw¢ÞDêŽüˆÉMqІúS¸ÿ�OëÜ%©ü= – ŽzÂìˆUbi‰`¡»Žo~3Lµ´4jS@’ ’�©•Î#ˆÃÖ{ ž7Àtï L’&V¶-E6þE [ªÕuUvhZßWsÛ°ÚàšOÛÅF'¬ÃAû`Å£‘AîRæ"¥J”EZÀfŸÕš["E„ê`ˆÒ6º N©§HÓ$�DC¢ÇR9~š«÷«¿»æýñ­ð¿Ö^¬ž±m;µ³zÏrš¶zêňÙ)é©¿<ò3Ô!ô™FAôäƒrq áîÃ6¦,|ÕdA;†Ü›}"6EŽÄSxn Y„ÁbLf´\Ì›k*±°õZ»ªÿ�†›_Fº™wêƒ_ÿ�C»ou)Ci¤¶IèŒJÍ""ʵE›%Uê·¿$ë›ÄðªX>0Î.j5Œku0eÎt‚ G;œº­QS‰À?)9ÁĸµÀ éqaôžJMÒ}“ñ°:±K5³¥¿ù¸­¦Ün(·=~åš5=*E%JbëIož# c`J‚¹e s­Äü6¥æ¨GöÛ1 €na‘¦ÄÌÙGG€qQî¥EÍÎ�wŽNÒÚ•¹ôƒ­sõÝgê>ÞÙ;Õe¯–Y·féÝÖ:ºx ƒÓ-SºÂïLc ¨Åd›¼/|”øþRwôõ‹Çéÿ�1Ô^ Òdè Uw Å9ôaù Ì„ ß-­;*Wmt²ùcÞ±RnÝåÐ^˜mm¹=lÒnKåæzêÐ±ÇøwxèÜT˜Äª’=Sƒ Ý»‰ø•†œRkœ÷lä4܈ù“¹M†8FÔ@hˆ9šf:H?4Ds‹óBÏy¢³îê=§Ô~·ô `l}«Op¶G~kÜ5pš”šžYi(®_ˆÍ$Qq )fË€«ÔÄT4˰ôœçU‰lepèqd ¢÷"L ÕÖbÈnW¹°Á±™ù¸Ì.K@ä«M«}é>ñK&÷ë¿Jzo´l_{Žßl]Ã;CâšOÀ%ÙLí#$@úa•Uݲ�iV¯‰©E¹)¹ÄÞË©¼œ†í:¬f¶{Ü\4Aä .Þuƒ€œ¶S:S·7”;Cu|PíKJmsÝé÷bµ[®Wíµ-�GZYj3Ê^ i;QÇb‹r„,øçâÃsá©û›I™¹tÓ� –º‡ÌP�At¾ò@;k…¤ê—C©:ƒh³\þ"æªé ²ìj­õÔv«yºjgf0ÉM,/TâFoIœ$-B7w¢ZcÄà1-kªa˜{B"æÖ‚-Î'P²$<J…H£Uã $òÔ‰çë½áV=Cêÿ�Gèwd4ë6í¯Ø ]Mr´Ç[h£Š²)4µpÃHÐz¦N螢”€Ò£z‡_ ‡®Üí ïsèg¾½YUx ï–dXkråÕ2Ã×­³ºª*­{ˬÛãý™¹]¥¯¯§ôÜ'âdõ¤j¢`§RIid^Ìv)œ�SŠU8Q¤Z4åíl ì"×1üFªçøÿ�i,ÄT%Žt‘›Þéñ3 àëgÄgÃópS]:ø†Ú6ÊŠCO{MÃ}«»ÖÞ&Q¯t“E2ŠnÈMLq)ïY¡‘”jp*ðþЦÒÊÙ]Ä@î0/<€‘0fd üv›¯œ‚bu¸´Opä@ (Îÿ�ë×ûgg[úGdø‡Ú»ÞßDô7mÁ_º.7¼Óµ-JþšŸº$¡¤f—Ð’ù Ã:áó¦Y0Ü?[² °!¶,s’Û™Üì O‡“B@~i˜¾ñsn€în‹‡âᦗSm{_Iú×MÕøOâ)7ؾ]ç’ØàB=;}°×$T¬Ý¿-QyÃ+*±™Da8F1¸‡T©P¤7A:ü¢fÓ¦¤l¸q|fƒ©5ŒÄßæ™1qóº€N°zm׎Ší;ÖߺþoýH¾WRÏw{‚ÜÕ¬u=Á©êb¡†¾jæ£RëS…OP¡N%­Šà8—Õí)Ô h¦Ðv7Ë#S�H³I@Íã ã)'rAüÇy€íb%G:5×.“t¶ï&êÝß ûc¬×H:ŠNòµ~&Ð[·¹ÒoNhflÉ„\–‰"õ?dÌcÛâ¼¾ ±ŒªZÝËIÞ×HœÐZâb.Lx^-Aq¨ÉvÖ-Öúé ˆA´0_> ú?7P«w¥GAöÜVQ ˆP[é­¿…AQÁ%*—"8¢I–ONQ,糺o™Ü;¾Å; hv¦L|ÒâF³5ˆ ™ËrA"áOŽáÚð÷²bmh:D9’M€€‡ëÆVÀênà¶U[z °úS·mëOýÝiÚûu‘´pµL@ÓÈÒTKE ;Îeýªwžøñ>ð¶+ K-JÆ£·./“"óxý1#,¶·41?aÜHË hð6m£c&âÊSº´Búºfé]/ÿFö.ۆ╕uö[M %ÊéLÑ45TuÕéHÕ5QMQ§yu9§f‘eY}8ß ð[éKÝP½Çžiæ#æ°N[¶ò�pRâ>-£Z  fFï¡A·1lß«Œ(ïK¿´*ñÑÛd¶=­ÓîœÝᨷMASr½Z($Ô³ÓÍø7«¥ÑŽÙ¡ˆò©èüânÿ�”ñÿ�·PתâÀˆ ˜"n $Þÿ�)l^:ÐcE^c] Úäã˜2µGucqõ!–©®¦ ™iÍö®VkÍdkÄÕw±.ã'ň$œóÇI„áì¡`´ÒÀì=…ƒân®éŧ©ëÝ·šÛŽ“ìÈv·F×pÚ(e[­e®{\ä7sÉéJQ{†E Àc$‚ θž1‹51¡Ñ¤é>+µà˜vÓÁvŒ “ë ¹¿´ã |tÇkt‡qõ›tVôÒÕ]ÒÛaŠ xh)æÍ:ÇÛ*ð8“¿ÀûƒÓ†ptßfnN¤Üˆ'[Ï]W3SâLSþgŸš�,6¶Ã`¢]7ø¹ëOF·]£zì ËWb½QVÖ0æ8« +#ÁP±”3BêÅLy©R±a±* ·8"ó ‹´ÛPeO‡ø¢½Ò�·¯:øÍÒIøÅê_U·ŽãßÛÞ;uÿ�x\ê.sÜj#–z•š¥Õ„¢yæ–Y$Qc‚IšF�ÉvÅžðí: É™2]¹“bsKôÍÐYP­ñ%Bìá  @þÛ[’&ïñÕñ¸æ‚§rn†Ý3C‚™îƒñ E{8§ƒ¼ŸNi²!ò l.��,7Ã4h·%:˜ä×9²`\bbê`Jz¿bjÕ!Ǩy‹í{ —`é¶®Áêr<ôwO[pË"Ç RQÃI}™ÈE]£½)ng!TÔ[¦Jž{žšRp4ÅJ“sóÄg<³6r¿þfëÝ¥kº‹ÁãËß§P«ûÏF7…‘ë˜ZÞ÷KLïDÔÉ#Röã?‰¥tJªO#Š˜b÷ÆpN²«á*²rŒÀr›wˆ w£iÇoÛcàJ‡ÁašEľ¬yÀtù”ãèGðÖoõ-Ôh¬öGtãØ«“i؃Ççÿ�] Å´"nÅ:QmJÚ׊*Jw¬’Bbd2nй,-@qŒ‰Iý#ô…:ÚÞûÆælÛOinMÏ{óUš;mÕSþ$2I/¥³v"+;60ª �'Qÿ�^È–™÷ÒvòSRr‘­•ƒÒÿ�†~­u‚[Ä=5Ø[zµ¾™ëkZ†ÑÓ@€s#CÚ>b-€[ƒ§Z£›™‚[ÏnùÓtÒ‘cb=è¯~‘|nþ¡X®Û¢Ïh;º’Šž¢©Ö;Œ»tCNg’¡n5%MdƦY r°‰ ¡š Ô­N˜%÷·8úŒw4vÙ&`jT9[c1ÌÌ4×ûˆô*Y¶zOóÚº¶ÍpéýþÛ·-²]͆[ƒXí’ǃ$ðÄJÕݧA*©/$ÏW1‡‡Î²qŸSiìämô/î6ÒgRZx_‡ñŸNºŸý£¨ÌìZ6¯Bî½aÚ{Ïu_:‘´öÝ^ÛµK[m²VSË�®‰$ËÁn†šÃÀí%_rÆDBF–f‡'#ñ˜àÂ{µëa�‰ÖÄ÷ÒÂp!Q’íi“pci¹ÐËçL~ö¾òÚB»nmïS±ïö«eE}¢ŠK$ÕÞ¥‰Tµ3J¬ <ê Šº·lÅ;AjØ®>)TìÉÖ"Ò Ò$\t$Î-6°œµf`;ï ‰›ê:)U­› V ÖééÛî]ýÏJk…ÁRÍIOI¶®Õ‚HÄ’Æ­1š„@¨p ÊŒXä!)ñJt¢ÐçÀ&ÀÍF:˜‡IÁÚ /kk4Ô —[ƒ{ò‹z§­¯µúGq¸Zw÷¨ý1Ù°ÃpÊI·©ôÙÑ¥¬¤Fu3ÆcK+`äFî²ãâ1XzÃM®{K k²f2¸Ä ÛapgaÔ°Õ©š ;0–ÈÐ G~óýÒw y×#ÐFÿ�Ü÷N’nzJ–™={]5mÖ;¥}"E–­©±¼Œá%pªÄ2®J:œ&&©`{šD›omA° ø�v²çkѤ•®´ <.mn±¾«÷Pî{2¦XŸetç}m+vßµÚ­×ɎܾÌÒÜj4u5-6)æªi¢Ó€€§§Ø­ó9›‡Õª2ŠÑ.ë¨]yxyÞ>!F–´¯iÓžÂãÄ›“Ñúáp¹ÙlwµQÑ®ªÓÏh½%Ö±ö%T´×)ÖAMBòÌ#d-Ó¹¥<¡#„ôä슆4‰xù„‹�Žöo¡1!­A¹ CtŽóvê¶nõfíÒ.õ×o·Ãöû»ÕÎðm[Þ¿m[ÍE¦wr==“Õ‰#•âD4jB¤5*̦ªëÕ鹯0¶æ>I"ÖÜHÔÆ«C²k)¸»åèbN`‘c1dÌø’êÇA÷]ÿ�q—ÔZ:µu¦4•û²ÅèÕִ󘡎#]2U:Ë.Á;Ð$øì¤x©­M‚…R3‰i¶Y&DDÏêÖt7ŒÖp¶µÎuJV>2 uŽRWíéñ¥»7.ëÞ›“yìž‚nŠôh®’ß7žÚŽK‹K-ô©ä†ÒT¦¥F%‚¢ÍPYÏ©8Ö¶ˆ<1…÷&ߦæpå¸çÍgãxKZ]ÿ�ª@Þ7æt6·$¥ÛãêÒ›"ÍÓ­/ÁÅ«nEn}Ïûn°WÍT”’Áa¼¦&=?VãyH$3áñ1¨HÍÎ3x½¥Ð�”8Œ3\ÑL¼nÓ#Q/a³2M¤¨e·ã÷§¶fÛËO¼þv¥=ôTAu‚>£njØlôæ©SÑ«ô¢î˜‹!bï9ÙÙýHÑcÐ{ÍRsQ?,lu±&à tå¬FM6ö-†TŒÓ¸ÓKèF»–¤õÛãz~ m*zm¾zM´® }u¨ƒb^/Í_=á V©š´Ä^³8ÄRùaVRÈýo /5 _NLÍHÔG=9N«—âÔi´K7Ó~ó·¿ÌÍçÖ­ó|¼Ö­û~î»â™Qœ×\f©`C9á‹(ÌŽJ© IÉ€D¸ÊMÌLà¢À½ÍoÊuê·‹£?; bôë§–+·Ä_Q6íÖŠÌ•”Ô}?¡¼-=jËV#¡IjiöªºÉ-CË2ÔˆÃDŸ†C®KKÊ“‡¢ÜÀÝùzH´l ÌÙu˜:¸G1¿ÔU!Ñu›Íµ2FÖ•hmïNºÝ ÛÕ»6“â²÷µšJ:º:ÊžZ^Jh£Ê…ލQVR6šŸ×cŠ˜¦t•p>mjÔƒE?Ô:¬-?È™¹Y4ÞÇ8ghËÊ/çþÑÉXÐüsïý»-Ò×C±?´ŠÝh‚*zzz7¦¢ô%Â*Z:aÜ´ð±§…`ô�Wä”D0«añwiò‹s6"`¶ÛNašcôØ-Ü5|8icËù æö¶âäܨŽãø‹ø€ÞK%wG>6®ÍPªdKŸYk)`‚'›×+E®ƒì'¶>á3/«+¹wíàíʘ›À¿, ÈD«íâ3A¼[–Ò Èé3B¦®®¬Ý¤í¹ü3o)©c•Þ™*úÂááF”·§$,Pн¡RRÝ­ÞÁ`Dg„´U5XûZD9lu¸2{„ÅœiŠuLhA‚>£ÃN²dq¹õ6¯Ò®§ø2øb°n�k^7rúù^Ö-O,sÕï…cÄŒÒö—íe’– ¶¡=±p ê¡Àˆµ·ˆãAd{é˺  )©uòš¯‡ï‚Ý›fJe¨[rÚÚëGUYNÔј™ãF‘pd2¿¤f’lHÀk …¦$>«ºXyr$h6äl^:´ç¥I®v—qÒGM¶BZëúñj¦»Ì›{àö›ÒQÑ[*vÅewý›¸<ëQ©ÕÞžU5>©‘‰˜ZA¥íªàý,�Ÿ¨·˜ØÂUqÕÝP3²nCÔÚÑÐÜß<å1ÕîÏ‹‰¶ñÛ³õ¡ôCÓ Â“mU-ȼ¬¦Iž´:É$ÅSÒ9,‘ !*c0í0Òì³9de˜åð"êßõø™ÎHˆ›ÌwϾk*þ¢|bUî Ô~!6m‹qSTCU]£g­;#ÅŠ1ÙêûXáB€ÍŽâX֩ðnk‹@Ê%Ó[n< 6ãë´Ër‚Màtˆ;ШUu×âZ¬Ö5WÄ­M0©¢šÛV”›RŽ5«¥–C#Å0bDˆX÷v¶F@8ÈN0xi‘NLÌÉט÷)¿Ä1@tk~þ|”zS×+ÄŸEŠ*ª¨«ª!¦´ÐõQã²sõT ?{µsÚì¥Ý€ÂÌöbE¦N‡nîþýPÿ�[_\þƒð¢í`êLFŸˆž²B ÔÀ!’š%‚F$–Œ*á'%@ §UÄæ†Ïùbúõ÷ÕFÜE{óm4ü&²·4t¦…úß×I(Í3Q˜ÖüP4 �1œ.Jª;sŒ xÎd8z‡šm']ß„t±øŠveW EœEŽ¢Ü÷TGWhî»V÷².ƒxuwÝ_ñ‹Þ.ï[4CäÊÓ—Óf5ž3—nìgͬ>ŽBÆ04ZÀôîUkck—µÏyq�êgê V;~Û¼õ.kÚ/[æ»eOHñÄó]%ŽIˆ§bpËŒÆhû{p{_Ø[¨Él¸ Ðsë¿UI•¥ÙA1ÞUÙ/Fú~Á¿G¬î>µæ¥û†sƒósÏ?Ÿ>yÔ"«ˆAIØ4‰?TÜ:Òx¤3.Ë·¼ùÉvšRÙüû¼êvã*úŠŒa)r Hº;ÒêvÌ;oÆÞǵÉþ?ç¡8ª‘úŠ!…¦?µ1éÔ¢¶“·Áùíþ'CýCÌÜÝ¡OH¸öɇýÞÏÛ Gÿ�É!ÇñÐÎTB“9%¿Ù]¯îí¸£<ÿ�Øbÿ�ót³¸É’‹²hП÷ ’3òXìˆ~ÔPçÿ�‡Q¶¡‹%lÃn C”·Û£û-<cü´ly›¡- #IûÁ;QFŸ6é5£T›PHb>˜ãNÓ)¢Éªëv¢²ÐT].·XmvÈGt³Ï7b'ÐîO€IöR4fp ¹è‚£ÃFb`uZ{ÔλK§žÁ´} ¤º™+ÞGŠyû[#ÓU#ÓR@ÉcÜÃŒ/ná0!‡3Íþ‹ÇñLÿ�-==}úªkÅΤSu¹Nÿ�yTì™Õàѯ%’jnP­!Ëæ'‚Ãüþš<Ö²`ÐuY?¹MÛíòà±úé¶©œîZ"b¶\§² ¯›?ðBÍŸà4AñªŒ4¢[oÞ¡„ÔÔZ®ôá‚wK"– 2@…'mvóO’Kg¸3Æ /ÂüÃæð=¿1 f%“�§sª³6ÍžZdZ:äô*ä4gƒœ`ãßV®f]²¹†ˆ…íû)³3¡?Ÿ›Ç¬=Vê=^ߦ³ìjʘè)ÕW줩ww‘‰ÌAQ ‚x#Ƽv±n#‹Ö¥R»hä ¶n\d�1Ýc&D פÓÇ>ˆm2ðë³EÆ£œïeÀ^“|'ôc¯7ë­¢ÝÔ}•¸)è*.‚(íÔÔ‹m„B¥Å]u}59˜´Àz*Æ@JãæÕÎ=ñV+Iµ2æarIÌdèÖ“×E‚akU, â�ˆI‰VnûþÈý§µú½·zMø³ÚÔ›†«{ÜvõÒŠßµªîU;BÇME`¼W$L#¨i)æ’_BšVEX×öØbÂÿ� ÿ�h¥f.¸ÊÒÀçÙß,¸ˆùºÉ&ÄM—œ´œ[ø.¶;‡©Š4ÜÐÚt›š£sA!¢\âÀìÄ�e ÁÜrNÿ�Ó+ºå=êés§ÅA§X}hÕÙV@™%{€ ÚI+ÝŒœg]VÖ}0^À 0HœÖ~»êPmZÔË@–ÌÁ"`LkGnWJd«£†á4 ÀogøOˆø†7eyãöXœC⎆©ÙW¨½ô^ƒèºwЭ>­¤ë ¿vÕUÉ ÜÖ4ü%<¶õ`uü-O­:N³zåQš%xûC´L®¹ó¬Oq¡ÓȴƱ­çÊÛɲú¼‹@sž#¼[} tÞûD¶û+rtWaïÍÃ[SpëOUº5CFi6óÖRÖ¥ÂÚM3ɤéC<*±ÉêJŽ)!õ$&> Âß‹±tÜJN°X6ˆ2è¾À^ÀA*Ã8àæ¼A0GQ &ÂA:A7�Ë w_ºQÕ®Ûýcø_ÛÇz¼v“sÜ«?Ù»¤©%ŽY¿ŒUA§=’Äò+·Íć·X|Têµ+ÒeF˜�€àIÐAlÈ@:XÁ²ÏÄp66ZÂæë”±“0A�Ì[QhQî¤|Cü]¶öÊڒ쮕ô¾é·c¨Y®4=^²½UÅ$†’³Ó³Ï5LðÈÆ˜K#;©gbó@=Q1ôk‰n ”Üo.h˜’l- U0Õ)œ†«-Î,E¶qÔìE®‰ëGÄ÷Gï×]­×ÕßÖ«Îª ¡¼Zêîqú¦Ý<k,ÜÖJE–­Z®/ZxÞ>ð‚hÌÆR©Iã ÛŒ5Bââ>gµº�Då�Éï6&Ù–Wø×ì¨cK�ÐçjO9ð´(\_W]¹ÔwÞ‰j¾Õï+uŽŠŠÕx¢Ø[®Â(¨„4‹O]WX8}€«°Q%8E |‹ q4éÅL;A0Fg’á¸3óiòÿ�Ó!Nq˜z¯ÏN±"òCLÞÖ“7;’Jao;­-ÖŽ÷µö_\­·zVY)*-û"ÑM-$‹)ü=e沺¢É) ‚6‹¸ü’÷±ÕN ÜV$5Õ2ˆ¸ù‰G-$ å= ¸zSÙ—:u9@žºïaÏœ¨¿{Ò®¦¢ãþÀ|K^®ó£G5Âí¿¬4³ÊX;,Œ½ÊÆ&Œ…>aé¹â†'…:«æ³šc˜{®.$’7¼ë6¸W(qfÓik‡þ¬M ÷wuM]lêu5­Ci$µJìE ׬7v„ÄÑh¤üq³�ÅYY];Hc($ê˸U+Dˆ3’úÎç}íÔBñ‡a§/,ý#`ƒBêÍE°Yߦ½†•)â‚Çn=Ép4²,䨌<¡yŠF²”P’,j$êJ´憹Æ$ ­‹íl6Ôï)ZFl¬‚`~§m¿Ûœ¡ë/]]ºS[©i-? ›V.uG‚cMYOU,ÁU£ž ©Y%¦ÂqLÙ‰Kª§TΆ|î.6�Im£yg­ÎÚ¥©Ä«9†›ZÖÉÛ4ißüø®ý]µõ"†ãÒÂÒôzõsJº‹EŽ‚ÍÓÚWŒšY]–Nös\¸ia¨-—ôÀaÚw0èÅH.’]צ–ÜE¶Y8ìMW|­ä�hå¿1¼ÔÓ¥›ç«­¿h6fÃëÖï³ì+ÆÒ¸SÒ]-vÝEOq¢Žujš8)¥IàL³H®¥U^A í!I0âðt…,Õ.k´.q‚Dt¼mÊ š†>©¨Â�"Ä5½ûƒûw-¶[GX.“T6âøªøžÜPUCIK_¿SÓGp§§íA7§O–Š?N>Ä$„ì^Ð05KúvÄRhË kiÖ.´)º¨~ròg]ù¢h«èÝïq\ä®ÝÝ}ø£ÝI<Ë=tU[ÑÃWº«i%X»ò0’ `jÅU&4RhÚ`!Ë˪9ÎɈ“µÇ)þ5JÅðÑÓŠ©Z¢é?T¯• Ìï%fõºH]›=ä‘0å»›?\êFñ,�ô-4Tªáë¹ÄL“sϽKåøsèµÂšÝMpé庲::cI IWUê2z’ÈZWYAš^éäý´…¤+Úݨª.ÿ�ŒT @Öhò¿¦‹-ܬÍ×3úœ~þƒîT‚¿áó Wk¥Æñ?BzIK5L¦g‚–̰ÓFO´PìåPŸ©ÕšœZ¥GgqEGÁ©áé Mþ¢I=ä§Š†tB‹Ÿ£%…Ç¿û;HÇÿ�ÆC÷Ñ7èŒÇͰ­aX½…Ó‹~NºuD3ŸÙmê%ÿ�þZ´ÌS§õJ®ü0Ø)í Z$’Ç·é>‚+u:ð Ô½½¤•QÔ9Ïk™mÏÒJu*‘%šç'j¨�3UB3Àú éx!–»¿ì¹Î1L5À.J_kÌ—:ÂòçßSU?1(¨¶T5„('æÃý|xÕ:¦H²´Ø‰^ƒ¾.Eþz;ÝÂíšdÇ8ÃÈ?‡ÃÅV‡Ÿ{-l5•[•5Ž~Ѱ<`ãý VuVø«‚ˆQz¹Ï õ:¬ê’¥ÈTj±ËŽ>Ÿmr>ÎTZ¯$þñ-çí Ï&†ì¢õˆÇ8ägMž™6Qª¤b|’=އ´Ù8i¨ÕJœçŒsÉöÒ.K³æ£5@ŽáœaŸ:qtÙTv§¸s>úit×LUÏçÇ0 !ºfŸÏÎŒºæi¢a’qÿ�ÏD.5BªüA­DÛ‡¦tuF†®I*DSž=ii•dÿ�Ý$7é­<è'¯åRÅ7æhQŠ]³K²úﵬ4U3Û£§ÿ�³¬¤³AÁ?ÈO×¼Jß“ >rêEÅDT�¶r@qôýuHD«ä!}ø'<}ôäÈ„Ñ ƒ$c9ÐQFå'ØÇ�yóŸüt0‰}JW’DAé!c€euEg–b�rFœÔŸÕTùPòÓr¹SÚHá¡÷Ò/2šæ2 ‚??:Œ¼GD²!ڲ÷'é§lR„#Áîñ£/t¨W¦8ÏHÏÓNÊÃtîjÖŸˆ˜)Å¿k%H§$IVè²c öÂüÈÏÜëSUòKzLx¬^,ÆÐzý–¬TRÛ€¨dJEÂËŒ{|ãË:Ö¤ú¹„ôú,'Ó`ꬱCh²l-ï ú–ˆ_ë)„T1YjR@rr£-ít<ã9ã88 @¨êÌ-ý ßÞ÷Z|>•áªÑ$[œÙ@í­þ¾sRÐ~¶(™£f¼ ¨Ç’Ä÷Õ¶f4á×6Ÿ5’Ú@U-¢i{UköÔECp“+ séœÚ;qŸ×øhƒm”éoÝ@Ú&Žõº=<¸Øêên´·Êø-–VÛ—%’²ª&ÑJ) SÉ’¤†üJүˆíw�¶hU–Üs_Ä®EÕIkvi&m`?*5¢ßÔ*:[3n‹.Öiª ’:‹•5\Ðz¿<~4ÑJUÃ8ˆìòF§¥H‚½þË5íivW8 9ë{›ú+R»àëwÚkÄ»ëeRÒTÕ·á»-·VYÈ=àGŠQ‘ù¼j,=6³äÍ%½ýÊZØ&�& Ñß…llÿ�ìûë^ûe¼mÇnšJËœôÔRZö­âu¬©@ÒIM ¤ÈŠîT…¿Âu#êS ˘ uîŸ;w¢£‚lÚ y;ËEØÏ‡ëGö‹ô'¢û {o¥û†-¯>Û©³Í5FкÁ*SË‘H KÛŽC•_”ä�yëÍ8¿À˜,n0cxv``ib<v]®q†-DL:~Vìoìÿ�ø†ØT´»^ƒgõªá5=u¤\v¹ôË<0š¡¼è$›Ò¦Yiµccä`öxÞKS=JŸ-§.¦ ‹¯¤j±°<NŽ‘‡G1kÀ3½ànº)Ó¯‡ÿ�Ž=Ýñ‰qø³¡±ìí…Ô)ýs-ºënjKM:Ëa[38 s’| t2d™K•ùu‹Åÿ�Ù§ ÆðÓÂjº®W�34Ó›Tí7iˆÒ#ª­ƒøé˜<Sq¡Í.eÃrºòܶùÆ“:…Üù;=u°É¶w|Aôv×QxºZ¨mÒz§l¯r•Ds˜ž“ðéê3Hÿ�á¾;ˆçÐhð#Šn˜Ýí‹w3]7 Šÿ�¤ú…Žq.ý´ØšŸbÜžäÉßjvÅ+ß~=~,µá™M4u¨"ñŒÊ€“ç¼gãTq áª;1aþiûz.~µ<wŽuú†þOÕyÖ“¯•[ãvY†èê‡ÄÎ𦧨¨ü=so9)§¤22±0RSGk9Vc88AÛŸã›ÂiѦYN›ŲÈ1kÉ: L/|wí]ó=ıЗ’D¿×Xižé¾l7¸Ý¢ —«Íâ©»NøçcÚ q’�T>Êu éâš~VA¾½ûF¿U7o…pùŸ=ä­†³ü9tÍa§¯Û½=ée]$˜h*(íôӤòHCwyãž>Þ5B·ª$8‘æ?=Uæà)-h*N:{ úû%ŠÜå„¥†01ŸŠ=ühF*ñ3ã(]‡h´}ï`¹£<‹$é+ 3+XúŽN¬ –UE¢.#–w@ÿ�.A^ð|’=ŽˆâIÝc¼ ¿¹ä oKvóªãu'c=éÒ0;­L1²öþÍ‹w7$qÆ8ÆIÏ�s ±HÆB: ¼$í8br02pGòÇßóÐm¥Ký8”ÿ�E´‰.cœd É9úy?ÏTÝ‹;+#7RÊ]£TUsOXÙÉ'³ÆìxÕwã„« ÁÎË]þ$¶ÅÄî‡[=«rܪ«w%xj+L¾Â²¡´T¬q‰ž™W33R”k›YÒs ¾ý%Sâø5ô›’ãmôÛ®É×§siß>,zeÿ�›JjÄéµÓk_ÞÓ<#Ñ·×Oqþ*KtDJpÉ nH ÓÃPTe\µ|n2«8}C\üí-‘¸“iëyÿ�”‰Ù[Á`©¿ÁKô`ìL^:}åtÚ‹¥R£ôªŒœ[ƸGqè:®ÕœrŸ—¥Ìägñ{áqåã@Þ=2‰Ü Q‹ÓSB}bx<‘Ïòüõ;8ÙUêpf¥ÅHûà ÁÏ’|-^¥Æ/uB¿‰„ Ûf{–I]>Ãé«ôx™‹,Úœ0Ù=Š"YªPc³lú­jxÒUnmŽ–©©`rqôúc_Ó²Î~lŒJQŸ÷ÓýA(¿×—úØ ©Á\Æøû-÷é(Ÿ×–=»W+*§(¯ZB–Ç×ÓlÝ:íþÄRsºý—ñ-Ú­oO¹\œ¸‡zª™=99sÉR3«ï2O%Iƒå ½ÔÚß¿ãúj¹7VÚmuèŸáÜ.¿ ý ¬†Eœ …Ï?,‘ÕT#/èPƒùk‡âxðÜCš}Ø.χ`3Ðïú•϶¥ÃaU¸Ï’9þ¦8€*ÿ�øiL“mІnÓÉà|ÙþXÐp”¿ÃމгkN;³þG8Ü@O¾ôŽ�¨õFÕ²ª£)Øý÷RŒÊ9S´jгÅAˆð¿®£v<#þ„¨ÅfÒ©QÝÚÞ>˜à£¤8ŒRþˆ¨…~ب÷ ÿ�×R ZŒá ‰Uíº€XúmãNÌmáBìÔ(]Še$®§f1¤_U]ørn{4ç%T•ûs©Û‰iPöGtÇ=ž ý“ô‘©¨Û—’Œ°¨í4]-Þ¦ª¥’’±èÝf@¾¡Ž$Ny„ƒ´œgŽ5+ÞZDûû `Í=¡|LR¥>ëéä±Ô5*ÑTM2¦;Ú3Q`¾ÝÄ!ï­~ðXã¥þË;ÒÕ °[· 'Z¶l{©+—qÔÏ•~»{ÞU G°ŒB˜úƒú§Wc©».‘åî|Tmc…AšÆËqÞÑ/hàøÏ:ÊíÀ²Öì¥D·­ÎŸeíkîè­ˆÍ "E»·Õ‘ˆTBÞe 2OSá?ͪÚ`Äû=æx’)0¼‰…[Yú¿´¯µ*”T¡Š$­©˜…†™ÁA~ª÷ iªW¼®}>Ñ’u x]P&Û@oÓ5RŸ¦Hå¹ÛºU—·o{rºÇn­"¥»JG&à£)*àà‚sƒŒê†3 Z€Ìáncï¿¢µƒÄÒªr´ßÞŠAž‚ßIÏUGEsaª*Bó–?¼í’p ä“çT*âɰûè´)ÐhmÊÆýý϶¬ÑxªØè`5sB†rˆì$ùþ§À'QR¨úØ>bm·]ÔµšÆ0Õq°ñú'vÛL£»Ó>Ÿ³c�ý1¨N7ÍL0a &Ýr¾ÛLÜh”„²m~F¬4M2Géw,Á—±äË~Ï·=ÝݪH8í' œ5a˜á ß—¿¢Ø[Ü/ÒX ï@xÉÆ›ú±Í °«A¾,å6=ñµÅ F–k&df�‚«S(P2,IÉÏ s×ü:óR‹ŽÀý‚ã~"=fƤ}Ê£vk×Ü¿Ú[ýE45Öͽik´Ñz$¤Žg‚šg«‡?P;xîκÓ€#çÎçYÆIÐUmÛ~—Ö2W ê´Ìæ1—bIb<“úivL™(?¬x²7%âiQe¹cf^ç)ƒ?¼@àyÀý<hÍ&…Å<êU©O°7Õ` nºµÖÏ ô”²¿âSü.‰Øµ‡#*÷�‚k± :2z…g拾Êþè÷ÇU÷~륷nÊmÝbÚiÙÁ«(*(ã•%cÆîñdEžæ•Ô1H£“ü]€»ª;)"œ$ É¿+OƒaªWªŸ+4qÔ�u‘¾öý—Ak>þ¶OEz']°vÿ�ZwwÄ=^Û«¸nzzÝÅ$6ý«qY}¨`¢4}’‰Œë1¨,¸ÁR\zvi—œSÅKS‘.D_}ÿ�ƒÖm0æ°—^Ûkmù]n̦Ϳu¤è¶òÝýnø¿ÜŸ½Oþð«¥F³kÍ»æ†;zI~h$Ô´”ä³á��*‹Ãh¹“Kæ#KAžüÃkø §ˆÅÑtUlÖDw\*C§û˧»í7TúC|ê%7Q/\ípU[Ý$’$žœI 11û*8 Ùíe\ä66ø­hPnÎ{Ë̈ÿ�„Ä‘îeTøs¶­WqíkËsæó#FÆ5½—Cz?°î½kèüuê'Q·m±ëÚåµà“µ¿QvŠžže©–1KÜhœ¼Ýê¨ÎVËÐàŒá»‡%¬ÊOÈ$æÑ¢4ç3¡Kñ.)Sÿ�h¨üÂsYùµ¸:FºèT¨#éu’Ýlƒ¬›ÇvW[ì7*JX¨mÅÅ5dóÓV¸ñtT¨ÑK9¨³ì Ç=Š]Šo5(»Z«D¿;‚ ¹MÁç«dX®?‡à>&ìë n‘22eênkæÌ!®h$8ÚBØ=«³~ íƒrí}¹ÕNµì݉¸¶= ºRÝ)¦ZÑ‚Fd¡bð)‘–>ÅhøœŸ+Üx“Áª¥PV©•¹¦{þ˜Íþm4´M°püâã„©J®–y²æ†È’C "Ò@¹ÐAV¿Mz·ÑýÕzºíÏŠÿ�Õϵ­õ6A²¯ Sã­ªjv§–ŽE†(Å4Év:àùgnñ‹…u ÅÎsÈ‘i‰¸3¬c¿Åmã~ã¦ö°Â† À˜îúʾ*:½ð+¶¥4V=Û×m×BàJ'7ZŠ5„€ÒHÄcåÏ9î<ñ¯[„.ÿ�.«ØÝù˜t,|'Á¼]ŒŠ”©—ÂZÑÒÅ“¢ð£·:¯¸ö–ò©Ýöë6Æ3$”Õ6¹l0uÏNëéÉLh”*¤l¥”úe$^æ*êÇ:òìE՜֛ÚÚrîþW³ÐÅ>“¥·Úâg¿Ü§í¥¼z;m˜ïž–o‹\ubJÕÛÛ¯ð¯5>A1BµPJ#8îPÆF#+œö’ÑסUÀ–8´]¼•Šº2;Fè ­óøaøªøné=ž]W·º¡5ërÇPÕµŸ‚ŠŽÁK3zFI›ñ Ó×Ów’4½%¦_žâÜ&½QžD´µ´ògA§]—CÂøÆŸùa¦ Üw¾œÏ¢ëFԴܲ»nÕG¸lÕñÒÏn®¢J{”uïO $H’žá÷€p3•^:ƒŸ0‚9ê6ÓŸÞÁtõè%—ÅÇ>iá:7U%–ïp®±]©¿2RÇÝK"»Oœ´e1œ„ùÉ öñÿ�Þ­vÓªD``Úˆ7:DwXµj5 æ÷·¯K·Ö¡³]¯[;rÙ­W(¦šÝSSCQ5éä’‘�•Æ“*##1*´ØZ`‰Ðè5÷²ªÌm78Ói5¿¿{(½¯`ÜoÞÛµîuæŠWÔŽE1CêEc`¸ï™À' _÷U™h†=ù‹l/¼kŸNû+Î.Óë¼ é¶&ã·Y¥¼Tíy-Z*Z¹äªªEj˜êåu†XQY™Ê0ÂÂÂÌÁNuYØZÙ]P ÂÆb/y Á&Û)pøÚ.x`0Dɾ­Ôô‰]Wë·N7펢–õ¸6~á§ÛÕÔ“5 ´ñ2ÈðJcä Þß±eu`Ê„ö¸R{XŒšØ¶µÌ‘ú´Þt3y6t]çÒ{Eâ'¤I ‘c$n òW¾ÕørêeÖW§§ÙÎî•vêF0ÜhKÃ5Zz°FèäXùV*˜ÊS<U§O´€AÛ©6›øÝG‡¥O´,¨Hp¾ñSúžº+R«áC®V:G½\ús<6yjj!üdõ(‰â›ÐdÈ €9 ݰs†BËç>³q™û2�‘«v±‰w;gIW°ï×B &ÚI"tÌN¢ÿ�ƒe¡ Þ4ÿ�]émEL÷µ¡/7ËÊÞ®Ïi¥µÓKO鄚áOA£’¥ikéÕãFtoœú|J78%*¸jUN!¹Èh˜"D“ ¡"A1&%fq×ÒÏG+ÇË'qcÖÜMÅãKÂt¤v;oö•|:ôër.žØ§·Ümô±ìÝß5ûiÚ««é¥†8a­4‘EO*Ñ+ÅOJLŒÑ¼²®³8öþSïs@{á Á.‚$f‰§`U¾Š¢qLĵÖ5Ç#Iq¼4†bñ¹êW|÷ÏÂU×en¹¶õnýéÎÛºÃKýåUà¼F´†Ÿ±¦_NXÕÝ%p¥<a‹(ÌdwkÃ[‰¬ÊîÃâ X@™ÎØÒ@Ì '@Ýs oN¡Ä(Ö¤1Xç´œ¶a™˜& Yº“ DÄ&+7C£¿í+â—wôBÁ-ÍgzjZ­ñ©‰$Y£jpÈAŽB20{H— ú¯ìêvŒk*™ÐFXœí¹h7�ÞuOŠÄµ•*Rìª8°€r³03¦SšûHÔH²dÝ¿ ½]Ù{B-ÍpÙÛ"ìZ𮵣Mùf‹¶žÞFe)$ž£ ˆãLã8ËauÒa08±ób@ eµ)8‘fŠ“¥ù‘ Yµ8Æ«²Ð.™ôê&�¹e„Û”ò ’ÙðçÕû…ž;~Éé=•ålG Q³™ä@Œ°ÆBUîÁK.G›®§Qƒ´sÚMßLM¦ß=ú[¦«6¶?œ2'“Gtä‰ç{-MøŽéõû¢ÛyÝv‚Šª•kbÿ�øš†©g6‘Ö#Ž%1…Ë‚¢H² Ê£]WÂ×sMÀHîiÖHˆ'P&ðn-u‹ÄQsZsEÚᦺnºkÉW½(X:‘h”í»¾Ý¬¨†‹ûÄ~"¥!*ÈÏ0z¡}z™V71Ó i$íÀ^µ*fcƒ\@™Üm·GrÈY¤%£Ðù÷s: Õ/Dú–Ûrá¹ÆÛ–+T5_„q4ôðÌÓ~Ì„H]Ä¥˜L…@_˜#!®æR­Ù‘—þfϔϾõލÚu;'á?ˆ…Û»gvn9¿cÚÛ’ïRT J)òRG ÚªY‰X&`N#bÆ ¿´O®³ø>EKÞºÿ�5Ïÿ�n‚ïë["¶]våÎÓt£Û6¨Žx‚Í�™ª§Fuä¯z¸eîÁÇ·‘®ïáüC©áb É:À: Í>1c�<‡ÔýÖ‰Ö|$oSéN(fŽ9ÃP¿0È%±€<jgqøO‘XTªÖêmïš *z#»énÕ¶zË5Ú†à“MH šIZ¢1—‹ÓeîõrÊFTHoë·Óe©R›è¼Ó¬2¸Xƒb ô³ý? ýH½|lY¨v~ç½ÏM[x‰–8DP#]§XÌîÏÙÀ)8È$r8?ˆ©V©ˆ(‰ÌH´j]Î×�O5èß chHòó¾ü¹u> ~ößÁGY÷R˜v—á=Ic„ Ù’š6•éÚ !f`TúhOo ž0¹ÊÌÆËH¸E±11r6ºèÆ# ”¹ï‚/gXåÏÜ(Tß ½@7J]µG°ežï4QÌ÷Œ_‰e,}3(XÕ½hÀ(l*ã¿Z8\*¤3³‡:âH:‚l6¸×EN¾+ ?Ìù[c�™&ö±˜éb.B’M÷onÊM¯OhÛ· U=mÏV‘ÕÕRú¾€¨– $ÊÊå}W ˆûs"AÄÆâš[–œ D LMõ"âÊl%L<÷É ŒD嘈 æÆêÁŸQk®”véiPE2³¥L÷½/L+ý¡»ð¥_ v0=Ø:ç«ñW†Av’æ‰;ܺ-ýÓ¢Úþž“sIœºÃ\c–ƒ}¹ªÊãð‹Öé÷íq¶í$z•¦zªË¼4‘Bs&DóJcü:±‹åi3(ù@,aÑøž“ÛŸNŽ-b~iwË0`›E•áÃ�‡H‰°qŸù`^$HDóO“üü@Û+íö©6EÆjêë|·*9'«¤U¸B³zL`a7¦Íê%Êá•XeªÜAôËCÛÂD–Üêƒ~²v(Ì9ÁòZ`Àu‰'å#RD ÞV³õ áû­ÛV¸ÙªúºR½êš ŠÓO<’YjDhí(BÍ ¸ÉÉ•_ŸZÌÄTa¬·¡¶Ù¾€Ÿ5TN“ÚM2ã¼ ‡2/ÖtºÕ½ë·¯û"û·¶fø¶Ûvfè»4+L—ªÚ*FŒÊªñþÇÖî Êëƒ/f „x:×ÃTªóòt·;ðA#ªÎÄS¦ œ;íÌŽò,cÜ¡¸ºY¼cŽ»>تÜ6ù¤¡Š ªJÊ*˜æz¸Zzu2SÈȯ$H\ ÆxåÕÊ5Ÿ”9í"tÛi´žWè5U*aÚç× ýãaÏ×EPÐ쮣ߨžëfé¾ð¯´ÁQ%5EZÚf–8¥H„ÒFÑÆÓ²6䎓ï‹T1Aàg0<¯§w%C†ÿ�Þ§K˜¹=u=U;ž&Z[œ‰W¾ä`0$dŽÜ`öœŒƒÎ¥6&ð~û¬º…¹ uþ<gº7}‹bÁ¿÷næk +P~+ðŽáb—½#"$ä¡nàNHàsãZØl ê5oX'§‡ˆU*W`“:é÷²×?ˆ]³¹n]P±½&×¼P›-4Vy2QK =QL‰.TÄKÂF!$|¨ö&,?¥I¥„Ÿ“Ó§§‚±ÄøF&Lµ™•Àhâ½ô=/öPK½§Tõ’͹ÏÔmûiµÖ@òÞjípщ'¬=8ØÆª…&R䌘äÏoaÄÔqt[‡$˜l§ÆÚùÆ‹*°q® ´\‘~ë÷-§¬ºRRÅOt(R6Tä0$~ñ# Ÿ8íoe:Ïc …·Ÿ=çÜ­·¸Ü£ª¨!¨†X*íÏø'ý„ÑJU’\“”ÁÁ`@9øûê›qiúÙKÙ´•Ë}ß_¹~z™L– ê_lÐRÔ%ºåh)ÜNÒG؇Ó"¨0ÍÙ dwŠ.à{0=ÚXü7ùƒ7܈1<ĉp&!pÕñU¸~&i›m¬Ô[Q7#Cw óÝvøsêOG6ü{siWts­0{mßðeÞË{ hÚ9d€ÂµâXa¦ì‘½PªÎèð˜le'å{»Jz€ui™Í…ºMÓb8–­2rvu oé#C<ÌhbgxVÞÃë?­‡aìä·ôãqnÞ£[¯µw[­çý±Ž5†zzŠciôM¾H¢¨ôäLÖöÊÃöª‹éô²áNDÔ#Cb7ÛÎW5ƒ¢jÔÿ�0œ tŸ ›÷ÏruŸá¨Ý>ÞÔ[‚%éÇV¦ªj ÓEvþò†ŽÑ. QEjt§ªï…âÚW=§±bŒƒ/‰cë’h6û �o=û ÇBaopü ¿5\KÄAÄí{‹s kY ¶ú¿·{.çèGYk÷ŒiNÊõupÖÛݰc ³‰A,ÇÔ1€Aäk-¼�Øã°ã›ŴÐs]¾á\MföÜ7\:æYç1EÕl?­¼@’=Éã»þÑm›éÄ‚7÷üôAß ÖÕ¥ŸúÇТ«Á¾!¤bÎíûÁR=«uëLObû¦[Šßq[…0¨† ,½žŠÌX;Kt^ñ àžÞH8É©Ã0B¼ajKLÜ»§#ꥈ5ŸùŠG4·ÖD‹ªF£â:8á·Ç¹væÛs+Ó¼¢¢# ¥YЇ çØüÆ®7àz¤M­t‚5è²*üPbµ'6#QùZÍ×ÛÅW7Û½íFïJ{i¢–»ƒ‰=fqŒ."L r§8ã=ø{„¦êu¦mÝßÑsq:8ª}# ƒ=ê_Ð.”í»¶ÌêÅíßsm;…òÊÖµHè#¨JU§¼ÙæYŽfä&@".V0ì;ˆ nbé#@ö»Ë½gá©ÝÈ·_™ºs*°z¶·tèÕûªÿ�`|Ó5ep- !B;üäïi¿wýÛr"…^)•ÍhÈÖ}ô]FàÚØŽ‰â@€Ê¤‚aÇ1Ê2ˆ7žqiäœS¢1¢ŠºÍù]Uo¡½ÇffNÈÖ«¾ ‰{ã ;‚? ÙÛØÀ³Ÿ´w5Ÿê]Ù‡7èw‰ÔºøM—9G�×Ô,?,^ò#¦„χ¢îOYþ ~7ÇÃçÀ}fðêwUv…V×è󯦣ÛVk}Wã ¡ÞÛ€‰¥žJøÕÊÕ1¦13’BßÀ·> ¸€à.D™'[ 6'n‹:»Ï,kdxì;–Ä|0ggÃ% Ó«Ý-Ýnµ[lûs¨[†Õ^h-V¨Òå·îú6jª™ëk£––«ñjbFHO,ìJÆÖq<-¬"¥J¡Ò@ m§~p>Šÿ� âØš_åQn¦n/Ëu®6¿†ûfíê…Ûdt¶ùÖn¯ËYxŠÛµb’‹øa!‹�7£$Š˜Aýž‘Q»1è°».{“ ¿8ÒÆU€»ý>˧ÿ�ߨqÿ�˜–ô—vu­T×þ¡îáiÙ—]«Gnyi¶õ]t¯ÿ�éàÕ‚«0ñ‡Ã„8íf´æe!™®o#UˆÌcq!Õ*Q‚`÷x®˜Ù?òVz´ ¹_aø¾ê¢:SO#¥=† z"â6 , ƒÜPwö–$ŽæÁ3©‚µX-âlcäQ¥ÿ�;mÕsKá›à³mu3á÷¥t´ß”»H\®’¥l›%]u=Ó³<õrWþ6/šX_ýÐD†("çÓ'VÙðÃk0â^L[ñ§Ým;‰¿ ]N‰›ùr>|!ôÛª?Õ ¦Ý‚kWû1ý÷Cum‡U=]]hØ»¯#ZAòEûª[:»‹øLQ¬(š‘òæ$‘�I¸çÑCˆø‚»pý®ù¢tùí eñCðoÓÏ„ýÃ[·ªo=>êò¢¯µÀÔtòHijU&ËÌnR¦V)ENÇ€‚Ú ß µ˜#ŒkÉnÚI¼J©ƒãµ«â[‡|߯ìºßý•Û3­Ÿ —Þ§]é:/³ë*÷Mm­)àém†âÒEK8 å9g•»U‚Œ’�%˜ÔlÒ`ÊãyúŸÂÇø‡Eø€Ú­Ífùµ½§O­“ü&Z:³Õ¿‰(¦ªè­ž­¿«¶ŒRô¶ÏOýá==3­D‘EÙHTö( lHý0«óæ'M‡!ÑY£‡¥…¤Úmk›iŒÄA$“m½Å—ó²ÿ�l%2ú²mí¯;³KÒÈ{¾i“Ï:¢þ×jçyþÊÈâ®™-o—î‹yze{¶†Ë”{÷ÑÎ^&Ü0öŽó… âÄX±¾GòŽMòcçýˆØŸoûW£Cþ�ÿ�xï1ÿ�jq{ÿ�»o‘ÿ�¹[Û3â³­?¶QÙ¶6ã¬ÙÖx± £¶\.TðB*€A#Z¾Ô_S´ü ê¥_‡i9⫝̸s Ë=o—Í_ÃüS^‹rS —ÍÒÙ•ƒnø÷ø¦¶UP×RuS¨)YL%JyÚÝÂ^–# Š­ýã•4lï)*r4Ô8 i»=:¯i¶ÿ�íõÕ_Š«Ôa§Q-:š̃¼|jõÇrÇGã»×nOE�®Ü[ŽuB²¨{¡íV�Ît€@YàÑÝþŽJ6|BA/ìY6ÿ�_ýècñ‹Öé'©ª–û~’¢zhh¥ö£pú’ÓEG ÿ�ÞEÚ$GdX‹ Å@�‘¡?²?ÞºtþßûaN>(©§fØÿ�«þîhê/Œ>´Ó=<°ÝofxLJrߋ§ÏakìÏü¸Ï¿F߇C}:®D’ÑpË`6ô„U>(¨ööo¦ yK£Ë2>—ã¬È5vá—aSþÕ_×µ v+Å‚e”ãþwÿ�ˆæµO„¨8dsäZÃa¦­Úml®QøÆ»KwÙÏÿ�Õª°öÿ�Çw]ì•Ô÷[}fë‚éwAUöÜTó@ÝýùGZ¤óßõ-Îs¬Êá)Ti85ÀÈ!”æyÎY•}ÿ�bj0ÓªÜÍ6 ½ñËIÓè¥w¯íø—ܵÖ_wgQoUNjªú¹&-)\w’õ–Ç9ãTÅÿ�³lzîÄ× uGD¸Ó¦\cI9fÛ+O1XzB�ZÁ xÂaj5oTïRÜê.Tn =l²½C¼;²ñ#¼Ž%VròT³"ª¤'$O($‰7iKSÿ�Ý3ÜÑô ˜ÅñÎÑùœËÚåÎ:@€@l¬ oYªkfµ>äÛ7MÍøT–8…nó¾JÈæI@Y*]P;±w3ã’s¬®)Àê×nGÖ$r-a‰×oåhð~<ÊÌÊ`Žpû­“ øïêU‹nÒìû Fì³m˜!–žxß·¹iã‰äwxý7©BÒÈJž>cƸv³L1©Ú|¹Œ_³¦܇EØTÿ�hu 6ö3w¸ýO‚ŠKñ¡Ô÷Äw+‚HÎÍê~Ö±`xÁÉç>O¾´Ùþΰ“.�ÿ�ÒÝ|• Ÿí  ó)–O‹ VSSRVWÔICÂB.Uˆ8�9çóÔ”¿ÙîŽÌÆ�ãrr7ܨ*´,s€cžâ9f*Smø˜†I)êî»zý]Y̓Ż«RH¾^ßzg.W�ã8Ô'à%”ò€øm¿z¿¹÷yqÿ�­Ê_Ö]©QVïM²7$Cá¼Êä[Çþïé­š ¾˜æÇü OXØŸŠ˜á¡ž÷õ E·¾!Ežé-H³Szž©ŠzÖ@¾¡‰¢/ÛxéÉ$}Þ{$eÎ óx-f‚À×ûAÖÚ[kj‹8ý,ÂZyX‘ë%[õ³i_(eŠñ´ú­{õ™ ³~VÉå ïG¥=ØWuÎ0š áÜÐ*¶?ùcm?ºñ²Ñ©Æ°e¹œÇ“ÿ�Ì?ö­”éó´ÞÅ5¾ßfê,AÚh”o«Šˆf< b+DÅeÄ«¨î‘‚u0à8’é/a6ÿ�ôÇ~ÎTÇðÍÿ�þ§ÿ�ål.ÑþÌn¹uyî÷ž•õR£jIrxª'¨½ï}Í<Õ’"vÆóÔ 4j‰F±%G ŽºlKD> =ͺåx‡ÃUqp¦éæ_?ûQ—ÿ�ì%þÐv´×S·_úu¸lÓÃèši7FìUÆ8 Ù-# à�ðqíήòIU0øþÊ£jSA‘{ÛªÓ>«ÿ�gÅïI®ÍsÞÛðW×Q=EXª£Ü×ù^’‚Y#¨’”y#Ž8œ«w:"#eF«š$iôðæ¦­Å{gº­i.&I›“32dŸõLt먽^øiÝ {Û]GëFß¼z-M<–Íçq…%FVB®Œ®;^@�g�|Äœ «P~¡#¡û8w²Ýá\n#ò‚'¨û´­¦ÚÕj»|“\úÝñ¢ÕÙFÅ7Q»cîEÂ’œ€0>ƒ¯<­ðÞ ?(ã¹ÿ�ÿ�d/I¡ñ%ÌÄÔž…Ÿö*Žÿ�ñÇÕ½ígsu—âçpíXê¾³¨&TYr Fh]{ò3œgøêßþkOmÙÒ/'$˜åzšxÂÎÿ�Ä´ÍC•ÕsÀ>”Ô†åñ#Ú]ÿ�Û¿Œ*c‰eSÔÙcF® —ü#= “ÚŒk%ü.½Jƒµ§D–ˆNàræ’§E°Þ#N›Icê€n@©¿3þZ®*¾1÷nâ¿W\7WY~-/óʯ=]Vö‚ªªbЍ®òOnfgUHÐ19 ª8� k㾩ZjÕe78êKIŸÿ�éµì4Xø?NùTŸQn€<ž™#½/¸>' Q%Ê—¬Ÿ× ÏQßW½([¶Wýçÿ�êðK60Nrpð5™Cá—8–v4FÖc´Ò?Z¿_â&°6µSÿ�S|?·øU´ûªåW*Üz»ñ,ižšJ7ô÷%(/NàŒƒDT«� säçZ/ø/MÚ®gô˜'ÿ�W°²¨üa]ï—U©ÿ�¨Oÿ�ŠOpüLܪi¤£^´|]V@ÎÓ¨Þ”æ<„( D¤^âœ<€ädj:48žÂŒIù ç]]¯5&#∸­VçVjË×Z{›Õÿ�{õÃâÊŽ®¢'‚icÝô¹’7(]]Ú—¸££$wrQs«máœ{<-*MhØ1ÃXÙVÂc›úŽqÝÎiú°¬îûïbíF5»/¬ÿ�ôµÁ2E»-ñ7 ܹ0ÑŽîpÃ'ƒÏVÿ�ÅcbèQwüÌqÖÆ3<øóÑ[©ŠÁáþž­Q10ö 4`˜ÚT×§ŸÛº†ÁGÅÇN׆í ÓÜšß¾i–JÔbƒJð>bH`O¹Ô8ŽW PUÃáèDHcôÙútÑGK†Å0SÄUªZ €\Òæ%¶=EÕm¾º´ê¨ªg¬ëoÅmÍ2**#©¾[„3²>§ìiÐ3$¸, F9:ÐÂ"Hh¥D ¡®á›èªb)pÚm.¨Nò[«÷>¬îýõ·ì”Ï|o=ÏIb ––Ö•ÕsOýÙE%KUM d“ˆÚ¦¡æ*II;¾˜ô:”ò† i÷Ípø|C!Î&]ªÝ-ë·úRݸn{×T~+®]UŸhE%]%]þŽ¢Ó=RV:vÌFg¢Iк!rTáƒwüÚóü7â?âN!@;,óÌóÊúG@½Šà¨UÀ^*µGW-’RÒèbH‹sˆ¨¥z†áÓMÊÇ~uÞŸu½EÒ–Ž–’ÿ�éZ ,«S'kµEKJ;‡wªç˹n«‰Å7Ø ìàf™Í¼Åâ4‰qø|øw:\ Êe´DÚFúØýCêM¢zѶ÷¾ê´^+Ú‚7¨k“¯¨¯:MIêLÄ”A4qÉF ÌA¹‰Ã3W6Âú F¤ ξ*–U®ÈÃú£ÿ�»I<¹©?Zlý6¶o;%¿iî>£njaCK%UUé)R¢†aZ¢U„DBöˆÈ!›’Hfp N2¥>³Ø&f‹;ÌH cáð”ê†S{ ItH9®´ëâ¦×Åxm¶ûioN¤Kd¸lõt•6óO¾v3̤m!-"Dx`AÆ�­púµûY®ÑÒfE·é*Ÿ§@ÒËEÏ;^ ga:ÂÖëÎÿ�¸Ý/Wú‹¼×G¡zŠI$©ô’j´ž–#à¢`*º�Á8:Ñ®É�7IÝÇ’ÌÁ.ͬŸõHðæ‚®º×ÐE¸-5[vájв`jc¥­‘ ŠÌ]Qà 2;¬O:™´LÓ·/ãè«>«FfÄ›ÁôÞɾšÅk¿-sm{VïñSO_ -2ÅÙE*ÃqžÓ– y:TPè©–7‰ŸºbÌ;›šˆu¯±Þ/¡é¢WzMAi¨£6{M®®)ê’(ài e‡×lÜ0ÿ�Çžt°ì–C„ƒãè¬qG†Õ!®",–ç=èý»×κíßIvçYº£o‡)Çꥣ䣱ܨäqí¦©ÃpÆî¦<‚7Äöº®ñâ~’¯Ú‹OŠtÖíϽžç_¡4ô·uŽè¬"'Ž0à ópÙ8 ùÖháx7·;kèOv‡ðºñj/ÉZ¤‘‚š‹÷WODz½ÔÛSu ë¸/{’åQ_YQWÕÊö÷«è`Œ#Œù}2¥0ñq8ž…7å�  ÷Ï?ªèøÅV¦k=Åĸë§tr#E'ÞŸ'µîËÆÒê/Â~ËܲQL‘ê9iU僚“¸2ÿ�ÎugÁ«º˜«B»›=O>†=<Cã<:ÎÃâ°toòýÛ÷V?B[¤Ûƒ}×õnÛâéNѹ µZãšÝ-mšÃVËÔ$QIRñÈ;»ìlà‡+®Œp¼kðÙ*4Ô'žl¦çRÛ’æjc051¾ M0�–Ȱ“+ové¯{Ó_a¬´QÙê77[£ÓÜ» °½|RÔ4ÇRÂQé®M8fVÃF¹ KXg féK€Ø´n¨Íz¹Íp›ŸÕêgöCVŽ›\ë7MÞ¦+%ÒK…K÷ É""xÌQš™¥t§ùcÊ®A]NÌ=PÆŽÎü·ä€7ê£þ—]šdžn?Bâw²¾ÏOÅÅE :E57àšeù¢ì pÝç´ü \8ã 4M$@-ÓþýÂ_ÐaIÌ<ÃÜ>Žõת±ä¿Kr· ¥Ò;µ0œÔ,rº¾fÈ*ìÿ�¼åÊwéóÙÛ“}>AÐg ƒ£BM;x“õ'ÑNz×îª|?uùÔ¾ˆïkŸO÷ÊÇýÛýëCAOVéO$Q™£ÅM<È…ŠDI yÁ °i&nFQ–¥ÄŽ|•×Ö_‹¿‰Ïˆû4s®[ß]C°-À\Öšk}=­X‰âIÃQÓBæEIdU=ß'q+‚ Ê`Tí@ù¹ûöwJ– ÀZÝ îwO–_Œ¿‹j+m–½õ’*šë­Â«!½jÊŠââ©åœÓú²zŠì;]Ø/¤ILs tð÷â€à°å¹H·Š×5%NÛ—pÿ�³í]‰nÐÉMqŠžz裫ŠJÃȆ {P<8Š,¤†''7©ñ ìnF<oÏ.wÕ5\¸¹àOŠŸmmßÔ½—½mÝDÚ;¯|ZwÝ1¢‚êž¼ÕQÒšoÃ[ÔGWÒ ¡X ûÀ6›ÄkÕ©ÚTy.€$òÐrBî…u>Ä´ÌÅõæ—ßÝNêßQ·U û~oN o+¬ÍMKRÕ‚NJjto@K"£اk,¼|ØE[UÔ»2âZ4k?d°ü3 Eí}6€F÷?U~tƒâÏâ·¡[N}…Ñž²õ3§{6ZÙnP[(à1µS…W4´Ò>[ÓO Œ€2sEß5¤ÙK_‡áê;=A~ò£{ëÕ¶á¹o4Sêªç}¹Ï{»ÍÓF÷*ùHYjg Ÿï[ÓPN ¸�cNj–Ùº"w¢ø-m׌qC|¹_áãVd.ˆX&໲ý~CãQÈ›)afa<%‡ÜcJDÝ %BrÇOiœJ 9%U~ž<c4švHŽ©tô^áÏ`wD’ýžÝÝ­îp4äF¨ )Æ;ŸËFÈ›;¢b±@&’6þ:c¢w§DT|õ Ç?ôÐ9󲙫áŽÕ_l‚†¾ž÷²yªÅvVª7ó9Î¥iTßÔªÏEo—š‹Åº‰HàÈä«Wq4•{ ”êèS4ÛVyŽSymHØŒâJ”Œÿ�øì>š¤ÚÏ,*é¦ÂXDÿ�².ˆÉ¾63Üþù¤R>ÇöÇóÔ®gôŸ#øUÞÁpó•ú «N²DƒsmIAÀ-Ò õà9:•õ-¡ú(YLi!Név54Ô¬í»¶Ôˆ¼”ZøË)#ÀPãŸ×Uˆ!×i…}´AoêzMEQ3wWSvgŒ¬™úëFO¿Ÿã«F»€€>¿…M´ZMÌ{ïK™ 3+MÒŽ“ê=ííÎ `É‘ù®§c‰7U«5¡ÖÑ^{CdX«©â’Çr–ã(½å·ÇV$Ð ­ÝõÇŸ¶¢cêL8z§¨ÊD|¤“ܺ±ðwÓÑSz¤fºlÖ©LÍöR0°Ë¤ÀR»{ã‰dÄ,׎¿EìWás¡ûß´­›ÝÜ@ê4±Ó(*˜ã(„28ãWÛ]RËu¸·o‡-…}°VÔ[vu–²Wû ½ rÍ&ynCxúrE£’A«Î—öôƒ§{šzxm}7޾f0¬"ðÂ_Ýi»ÈUgì^p2FH¸BŒ™yEêfÛ ª¾Õ~žÛUNß2IMAW$g'»œŒyäs¨ê;䲿…ñÉFK(žÝºÓKMLã¶9%´É61ŸÚH@÷þ§å+cOhZÓ§]<‹¼£‡oeœä¡›w¦Û~§pÓÛêî›rŸ½ÔXú6>d0Ï9þ>5>>³ÛD¹·T°†°a1ö[Ǽ~ ¬ Óª]Íf–Ó]BÊÁ¤§šÞ}'”ÁŠ9}@‡ñíõט`~&{±F“µÁü½Gðõ6᳂<üJÒ‹ÃÜ×;á¶À+$©9ìŒSG#1Î�Àh×>=ÿ�ñô,g ¤÷^w‚Àv•‹~£øS£ü.ïÍ«A•›n¢lc¾+u<%N!¿jG¸ð~‡ßY\#RªòïR~ËS𪔙.m¹À÷äª]«ðï¾/õ2-«kÜkOi<˜£Ïçß*¯¿œëw‰ñZT[5…Îp¬êÔ†‰+=÷ðßÔ¨K¸¬”öêVæ5Ô­ÜÐG!úyÎ5K‡qª5ùf| »Å8mjCç<>ËæÆéu$uqIW$¡»³ ñe?æÃHÙüñ}GÆ8#(û«'1ú£z‘²(;™â¹VŒóJ`QÇž}óªüᮾ*Ï¢–Ÿ§ãeÓ~Ÿ^ª¥ƒpõ^͵œãöÕžŸ ¿÷ˆ,GŸaúè¸î6»jtË»µ÷â¢àX*.µG†÷¨ïÄ×Fö¶ÔÙ••W«½6Þ•u˜§Ž:½v<’=ÊéòC"ŽåÎÈ£–/†xµjÕ jÒsbæAîpút©‚Êtò#ßî´vƒh5UŽÍ·pn 5KÐ[©=9檚[<ÁØ­Ü®cLåñŒv5^“£MÉÚÄýã’ã¨S&Í»œ,xE­Üµ{yq¹ïòWT ƒTP³“ j}(é”üÃ,TL[ŽÔP˜ä&Їƒ”mx¾äûÕJìSÜÌ¥Æo"m´{Ù3­êX©$’*‰šoÄÍ1§SˆÂzB%l†ýâYò¸öÎO´¹o~ŸY*ÝÑ ïö€m™(V¹)®wJ¯^ÙLbÙE\Tå¥vg ;{8 ©#·QÔ` $_ÖÞªJ5py Ï…¡Mﻢ{ä6 ÍÍ ïtî:ŠX*ªîszÒT³TT½C!cêI‚à·r¨£‡¤Êd²”5 À"Â4Ûu³ŽªàÊf¨Ì÷�âLß1›Ò5Ý%º÷EÞß<V§¹X"F¢„ÓVNc‘Æ*dgîí#¸ö)$‚ðq©éSh§.ƒ­ü`,ÌF ößåËE­Ýr«ãx­¤±Ðj)Ìÿ�€¨Ì-cž¥]@ œÈ@î�c€Î®Œæw@© KÚÑ÷ÛBvïOWšûmþ¬Ü¦‚½R½Ù䣕^7Zz`¥RDáÏÏŒÞÉÒ¤èh>î}®ìî2wÔt ÷ø{ÛvZ‹½m^ö°íø¶œvÙ¡µÏ5Õx JËlÐÆü³(TóRÇb`€Ídëÿ�ƒ×S k‚`Ëó:¬å ßñöÐtvöéWIê®W}çq½õ&ÛK0^ZÏ=¢•²œEOQSK­Te‘ÌU¦x•OÙ…câÍ7ÔÉ>SËš«ŒÃP5 0/xïyQ6Z³O{ڹ랚Ù}ŽË5òžªš/R3,T‹U+údp¦NÉA/pn@ ê~ΧgÜXY³ÎQi·t«ã㻥‡¯U—.ˆîªû¾Ä­±Úª¢¦Pg¥˜Òžû•e‰»‘•Y€#޹ï„ébi`²bÛÖÛ[GC¨°î]ŘŒ=|yvÒÂÏü·ž¡Riu®†’õSk¹ßí4Ï-kÓÒC^Â8Uc€Ê·ÌÁ_$ƒÛçm–4ÓêVK*¼S%®#ScÐ~S’’ý¸.õEE|Üu¢JJª¤+b¡›žùks†1½ i€ß+,záõ>k—y•`U÷ ®Õ;ÿ�F§Q­µ(¦S]Ëx¥Y‘aЬsÀ[«ž¡Kx¿¤¬{f±™Gd¤S˔̎ß7ËÇ·Ÿ¦¥§Æª4]×å›™UÝÀØc§@Ÿ¨÷*ÓG7+4ZES…÷À £ÛŸ<ëj–:«©sñ‘ܪ;ƒ°:tß•µ;htf²›oÝo=Xèµ]eE4Ui©º-÷q”0‰e‘ãPÓ Pò*Kª‡$6¼ÿ�â>9Äk8671£vêO]p=åv|‚`(¶j¹¯'caIæ¥Ý(²ü<Þº;Q¼7~éèý»uÒ^^­õתXëj©‹"«­;ºÈcY ÏvN5›ñ?‹7G´ ÊßÒ�Åæ7;ýÖ‡áœ9ØcÛfÜÄÆÚ¦½õO-ÔñYvÕn–Àÿ�–‚¹nPˆ{ÝèÞúd`a–<å‚eŽäK€â|AØ`^犙¯®hÐ ¤ 3ÖÊ,^j–Ók2A"ùn6›*ëyG¶¶¾ÈÚ•´7m½_w­«¹RMøk¬3IKÚ±dp®Ìf\ú ãW…qŒs«Ví\øk¤ÉÒF¾«+Â0§O³ ’ã1Ê7·Ýé­´÷Hnwйî¥"–‘QýE@² rcSƒÏŸm;xÆ4Ô`eWMÆ÷ðæ¡<7âX3E_5² °Øz‹½Ršõ<T~Hi4ª¹þ ÉÙܾª‚FI }}µwŽñÜ]<CÛEî1€Ûe à˜wRohÐm¬›ú¯NŸÙCðKýŸdþÐ_nuÃat㨽-Ù{3mUm»}ÇuLôt× ³ÝPÔÒ%X2IÂ!>£zeŠŽÞìÜCâ E>Iîy%×°4u·Š­K†QbÐ I¶³¾ëWÿ�´Ûá¿á¢],¢ë7@úoA±ékw­‚Õm†×¸kªV²Šd­z€°ÏY2”"™q#»…ø¥GâZÚîÌ./ò:,þ!ÿòÏìîrvñ^þþÿ�²"ãý—}ß=AèÇÀ­û{ÕôJß{¾Þo”ÖY®’ܤ²úõTÕÈÆoĉ]ÉrÝ轃·ŠqW‹>›ßòÄLͤlîÃhS¦ËàúÂãçögÁ¿ÆÇÁÞðêwÄ~Ù½ï]÷lÞQX!«®ÞUÖÉR—ýœ²Vý(*£Vk…OÎÀ±Éñg‰üEVŽFÑt<¶{€ô¿TØ>IÙY&y»p ñ!èT©#ÐŒŒû6y×H ©ÊE–KO&;„¹÷-Œÿ�×BuRRâ'äv øÏœé' ºÉ#›¸Ü¶ÿ�'BM²úÑÌ„$T>xÆ™¥"Ã+5ˆpDÅF|<ÿ�=ôFˆí8^Ř€áÀþgH)óD¤°vcÁÊçïõÐfFhE-:"‚ñJ[ß /3dY2‰KĈøoÃJ£þd<i‹£{©&ä"?Ñ´"d~˃¡�^S¹î,§¿VÉû9éacÿ�,%Iþ1DGÊT®¸%)®2©ô TgÁí^à?Ž„´ttžN…~o]»žz‰;‹g”aüÀ:@I†§3ýÉ&gfL¬Ê�à‰Ž?Pq£nª‚U)³RmùYEÚªá@3Ïáèi¥'>yi”ŸË8ì'ÇöLÆncßz±h¬Ý0ý¨¼C( =†…ïwÈC~˜çUMJÀþ›w•s²¤D‡z)µ­:)ORïU{»É!2R!¤ÀúÊ£>ù ôñ¡p®D�<ÓÓìsðVUªãÐÙ§¤ü6ßÞPE�MþÑ# 1î™yÇŒÏð-Lb@2àPÕþ”‘òŸ2·?¦Ý&¾wMC]ÕúʘfNÊYªiç§�Ok"S÷c8ù•ý±œàéÆMòÇqŸ¯Ù OèH¶`}>…w_àâ—yRUÛëí;R’íM3…Œ\褧ôO—3S ’8ÂÇ•2øÁÆ{­K¦ãßšÌp§?_ÇÝz9èíEmFÞ¥˜¶ÝŽi´©mêõÆŠX‘NìÄQÆ™àö)økEªŠ{ß+Ô½ÙWAQÓ[EƒqÛÕ Ss^¯~ *©lÅøwŒ‚ùP½þ¦r[#¦"W#¾-~+¶àÜ·»½‚Àhc…{Þéo¡¹¼Òg»ÒIë#tHÃ0î,ÎNHUùAÐ8 º‹//½Sé¦ÐmÓV÷ Nâ‡Öíˆ&㆙»A=°ÁPñ¦;°«ÜF�Àz“7V©ou¯w½™ðùn–º_î~¥m‘OÚCºiÕ‹Ã�'õ;›ÆcÜgÁ­Sã,Žr|mù]v¸f ç4ﺀڭŸV«¡«ÜÛëªtvTÌ&[ ³²Œç·È™8áIÏSÅÔÆöyh´Oq±ú«xJX1W=R@ï×dÿ�ê§ÂÄ·mŠŽ°VÜYKKk¬ŠÜàÉã µšbr¼ï íŒc\í>Ä;|õ0̃¸?úßA ¥Àö9)âÜC~¹Õjmfö†Žñ%ÖÑÔJ×–CľžÇ< ïþþZêÝ…ÌÜ¥‚;‰ÿ�ܹFâƒ]™¯>qöû+MÑ}Ý<B¨> j÷í-[°SÀô"·$ãÔë.Ž´ß-Ñ„z—~VŽ&»êÓœ×õƒè>É«nYºYV”ö¾·í{]Ophãydüð{h¼~CWñõ(†—T¥#×ëªÈáâ©|1ð}ôGïK/X­4¡oýDé¶èL²«QIQ4ªsŸÞ1Âëàj‡Äaïòé¹¾ƒêV—¡‰ ÿ�2£Hóû&þŸõwì:ås6ͼTeGán–¯ˆó᛺S€rThxƕƄu¨¿š“ƒbŸ@ê–-Ÿ¦žKïTº‘¿.SG=]¯cZ„‹€mÖx£É>ݱÃòžÄs¨¸.‹3ˆæ.SñÜMWLáû":1ºúåþÐSÒm©j`®¨nå1íïÄ=»ìÜrF0WœùÑ|GC Ø—ÕñGáÕ1"¨e7IÄUgR+ šl«EžfŒÇ+ÿ�²‰LóýK–~ óò¨aªŸ » áÞOýR<•‰Ù]ÍŠÍúas¯nPE ÔoL²¨P%‚>�~ìÌ03ý5߇íÂóèxnX±ü«Šé²:%S±­ÂÉå¨Þ_„ VÒÃF´éPÀz‰LÌÀÝ ý†q®pcq­ÄœÁ½”ÛõLmý±>+ª?ü0-.í#þ“¨˜ð•PÓôòßýÝ$“ÐÖ ÃžÖ0ÄAàˆ¸cùvãï­·ã†hiÞ œ:ZIà™aØfNñ¾¦¦�ËÞVà ü¹Ä>¥}v’ï÷à«RÂÕ#åi#¹vÚtVêº_ÂË<+F —ŠhÈ9ù¼áÇ#è9ñ¦Ã¸T—8iÖ}…66™fQ>zŽõ†â´ZoÕ²ÔK]q=îÒ;K+IÝ#ïv$w݉œçÀÔÎ?HU^À÷I:ó”ÎÛ"І/ÃÝåªfb_Ó,…±ù±Î?×:Tê=ÇælE¶û&«E­»]'Çî·Á:Yðoøk¨º õÕ{ÏÄ=A0 p®–*j$à“Ø”̲#œ—÷�Œë8Õź´e¼ÈýÕ8"ÃE§;zóÛ¯xŽœRVGX#3»u-cJR3_ÛÃ&°•í$êó°qiå;žõ­†âOcOgté£e'»o}á»è*­Û«qnÚsRV(ýóª##󈻻ü 44ðm¦FFûóVkqgÖµ`ÛÃ–Š´}•biYQ/®2y þ¾yÕÐúº@žõ‘ÙÑŸÔ|U{R×fJ#w’b z^Åí>GwqþŸ]T&àGzg2ˆ+Œ÷&†·[mýŸŒ’zJ@ìTºc%“µ—éÈw5ãd�Óæ¶áBçÓm»¸7}Uë­7Ε–¤£Š™è«pvƒ+÷Å +ì~;{A-“ŒjX‘ó{‡Ôc*6¦_ºß‰7·N*Y$§øë¼ÀÃäÃî+cd{$Œ×0cFËpâN½°óL’QìIÚSMñ•C^ÅÒnÚŠë,ÝÌ®Ò+ÄCHÌ?ï~ZrÔ(?ïZtä¤ïâáGIo¦øÙ†*HÜLНf`ýü°”1ËrA>äxãA•±¢˜º¹� Í–COÔêÕø®ÙUÃÁÛÖ‰rà›D2‹_Ì£sñfâ£}¹Û=T+ ›¬}-º;F²þ×fP7zçáŽ5 Ä¡pñ(#->CÓÞ¤GS]VöØšPòCþÉÑŠBνޙ…È9¾V�º© ò ug\µÄÔ¥Ùbµ-iÃñ÷@×t{Ýká„SônI•¥mDÝÄ:¯h˜FçœqÈ'–þ¢«¿¼Çy*'á«i¶{‚ù´¾w~Õ žm½&½<’I0{–ØYýþÐâ"ôÄÆER<èé>£LŠ„(Y†¬ÁjM'ºTί¢q¼µoWѯ†ºÑ…#ÔÙôÙÏý”ü¸ó«­â8È\ù¹Fh8þžA2Ôô>9;Ѻð4A€‰_l@½„ã'9ñþZã1/�>¬Ö|¥(:ö ðòÛtÓ_*>¾·EUPÈ”SÚ"¤D1’Ì…£^æWàøÇÆ€c1 çeRШ¬L;Ò9{%#½ú7A¼¨.ÐÛþ~6ÅUPGWES –™›Ã®)£%†AýáùêCŒ®ãóºNò}ù¥ˆsmî…«Ø]%Ù´V >äø=è¥êâ("&²{¨žZ° !–F @fhØ• ñƼCmŸKj>ãE^Mlv?¿]W)âÚ7íh¹„<Ú)XLkœ8¶^ãÌ+#ãx>E8Á±êe—ÓZK„2/%^Ý9íSòp>úަ<͈ï•nŸ$ÇØþØØ51CêTSÍÎ�ktù?q•ƒüGfýB±þbÿ�B©:|µ~ŠÅNÒH|·T¹'íÚ?ù}µ N%éÞ>êZ|4:ßb®é]]$°EQl«‚iNPuTþÐq݃¡§Å› Û¼jš· ÊàÒ/Ü~éÚƒ¥ª²ç¶]*däöÅf¨gO¾;N¢©Å‰Ò<»C„ ÄøåoéU=l³GßÜ3²àlÕ2Hsàzh½ÜèjqW4˜y€™œ1®q¦ÝØ%èz{LÏ5Ú;–¼÷`öíjöŸ Áý9j¸ò ‡ÿ�SPÒÀ°ü¹Ið)Ο¥2Ô±ü&ÁÝ-!88´O'?@¼äþœj¿ø¶]^<œpÐtaò)Àt¶:XÏ÷¶Ó½ÛKV[^1ÇË¥sq2ãò>G|ý³†îdx(´û=I!·-ZŒ( ̰*®;Ióÿ�ެÓâÖ}ùl v�Oé÷à«ê­µ’þÆÕU. Ùü>l nS­òôX8Š1²°6I«/RÈÅ{‡rö™dL"³1?¦³ø‡m=MýîUîÃËö•lU|>ÒGIG4±îಡe"•OØfÇŽAçì5K8¸én¥jÕà­�¦‰ú·¢ž?Fçyî`O¥$ð1#è )þzÒ£Åã<%gâ8SF¥ C«¤ŠJÚ ;…} “³öy8Ïk|„+qÈÏÓS7ˆ·49Rw1 HS{OÃÍÚá@’ÿ�°{’ >^a]N„®3ŽÇ*F0yíªŽâôÚ`<y²½O…8³ô0šjºp¡–Qýѹ¦\‚Íu3“à~×·?–Hú]§ÄšEˆHU]È:ï½aH®tŒ ©µß¿Þ�ªaî'Øe‰çÏßViâ�´ˆU„-³¦Uá²ú?u£© Š¢ºõ@½ÌÏ U4Þ¼ªpŒ° 2€qÉ!¸j|AŽvYôC_†¹­˜÷â»iðCÑ'Ýwm²·mÛt¨²OQ-,ëH†¸ÑTÄâ`Ë«v4ÉàîøÐe@tXu¨–’ºõEÐ…»On· ½ú’®ÌÊ«uUfh%lFÀÌ{å¾CßÚã�œÚ ©­£º|+Ø®6Ú˜F×ÚDéû8LÂZ|€pJˆÐ©í\1Îy?R„‹W)þ1~mÅs·Ü­0[j"ˆË/ááôãx¼!îïÎCH½ ÷e²>­¨Ëey9ø°éP)^²¶«Gp¶ËÝ5,wºÈ*R q©Òª p�ÎX/ qEQà ÅY¢ÂHh\ç­éÏSåŒIK½¯¶…ì,ñTnúºis–¼ß0ÁýåÛœcâ1Ôƒ Þ:YtønðÜß§Ç젖Ιõ"¦²zë=þ²¦¹x˜ny}b%˜äãÛPVÆQk~anåc ‚¨] 7ï÷öSÛ­—âtÙÌ:µ¿§°¸õ„n Þ‘ûy’\)#ìOÓÛX,Äà;RáLs>‹ vÙäíIoyŽvÛð©­‘¾ç™ñtUªbuUÀ|ÜþöJö‘Çœë_úºQò š,3„ª].>ª'ré¶îŽ£W qV1»°çIùr#žqöÕŠ|B“¶ºŽ·ª7·¾Š=S±¯•.ªntSHIå\0n9ÁÆ~Ÿ]XÆOúGb›ŽÄÜqÔ´?¢ F m,¿<:cN1´ÎÞüÓ?öêG¿ ¶mûj¨’ªÅt»EX«ÛÝMS b?áÀ#9Çùj–#AÃ-@#¨ î \ÔÉž‰:†ß½•wëØtÏì¤Ãp|ÓüôôÙD] xD'­Ú‘ó:{ѶÉ:£K,u=ùèj¡#RVÉD1œV¾3ä`ûA‰8h" cU&¸€e„ƒÑ3nKÿ�Vn,Ë|ßC¸ùµ7êÙòlÌ@çœ-I„ÂáóScGƒƒSë=Î>$ýÔfeöEŽgÑíª¯ŸµÉòqê`êZµ)Ìî9øOB…B�u'ò†®Û±CÄô;m©Ï¥Y*ƒôÁg9?a¨èâÉu‰òùS?�ˆo™ü¦!Go*Sýœ·zÇ÷dK„£ÿ� ×SšŽ×? ýЦ)¶?@¿üGïdáo¨½Ú‹E%ooi®e&w† -EY´ª\:{*J.©NÀâ˜ow+ÕkT5EÉe ÚJú¡³Ž \ãmO„¡M­¡AŒÄTq%Çß”ú¨-R–cÝ$Yú÷’u¬Á¬,J•9¬aIÃvÅÚÑ’?¦¥•YÄ•!’¾®zJ‹`¯¼t+ßÿ�ùã'Ç騭3'Í•ž+€È3ö‘œa±©föFDJ-Wq>´ç³ÿ�-na)$Ù,±•qU1øïΜ> EÑHK|ο4õeüç/Æ…Õ¦à$)N©¢ªÃR“$ÎÀ’ÙÏnØ({�´ÓÛmÕ•Û­µ2Á ’7ìípÁ²;Š‘ÝÏ×è4@J‘ ‹„V½¨óCPöhÜÉ%\Ì’œòwGäF¤s¢À¦šs$z”E×oÚ/÷;ⶪ»ñÕS4ó?®,Ç'ãè=€Bt ê5®%Çt ôûnœÜª#ƒÛ'ø®ˆ¼ÝEØ3Á=Qt–Ã\1úHó¡©#'?OP¿E””ð­vŠWEðåIr bÜñÀÄ‘ûKf@ǾGç¨F8h¦4DÊžX¾ w–àž}»ºì3³¯p&7‰U~¬pp>úµN£p:´˜ÛfW*ÿ�f/Ä44¦¢ á±É=‘Üj #<²©À÷äãSöOåê ÎÁ¹L§à+âºÛÞ´7ø/�S|tÐí!H*£Šd©øaøÁ²U~îMßA+ar»Š­8àdI÷Ó}6«¿Õ _eøPþÐk£÷mÛ—Uk¼Žèw5`PsÞ2`é tô@ü[†µ=J¸6ŸÀ¯ö¸î ßîý¡CÖ›wûÎÈw¡ Žy%ç�<“ò3±-*ñL·5O™[7fþÈü¡ª«j^ì}øŠš…PJ ïÛRÈçÒ’´?·ÓÛt#Ù"’‹üe»T>«[÷ÁöÓí»Ô»wvt³â†½ú-×Á'¤2[´2HTŒ±<}CSH›Ô#^M_Uy¾ò”Zö UžµLŠN•x ÊBp02psô×3þeM·Ò2½Øâÿ�ÊaÃËÁN¶GRd ê%ÆD(pðUÖ¹ô‡oÎþ¤Åä Œø¹óΪãxp8@Mï°¿u­ç~jî—[ÓCõÜùY!E¿—fî Økªæ¨Š­˜=4ñˆÀ Š#í�œç'p5(ÁbZȱæ=™åoÆ÷ÌÁ:Gñ÷舿_ž}¹±jÂ5mDЧªa‘r2 ²å>^F|cC‡Ã´U¨Ý„õ÷ëk$úäÒ¦E‰„oX.5”÷¡-—xUÖÀÁe0wHÓ§ø}.¤žÜyàüÀyçRðf´áêvŒƒéìªÜUΖdt_Ï»'퇹è.{í¬uÖÛ„tÕ4«S5Ml=¥$�ªz‹ØÎ›·¹íýibpÎfUiƒ:ïm­çªØÁâñ“ƒy>çÍ)±o¶ø7æì¥¥½Ûèìó i”Alj{-ò‘íŒscŠSs°ôÜám‡în{¯;ª8n)íiï:"v ¡jk÷KE¾7e¾  <u°Žù‘Ô|y%T`äò4|N¦Zl–‡âmЈü¨¸hÍQÿ�1¼kÜe³n÷J;ÒÝ.ƃ·¸;Ësk{1ÉϦ§°G²FAÁơư7(™é?“çÞ¦Á<·6cÖæÌ¬¼_mu†Çvzjy&xû&«üBT(ç°ÇQ[�qœHñ kÆq$r´m«HÑKÃj<°¹†/¿à‚n™­Û:°SUÛî[Vš•ds0F• ¦D}®Ð•9ʰÇ?3xÐÔÆ�s6IòßY½½Â±G #)€<ü"×÷+M7¥ CßçKWã¢f=#D´ŸaÞßÈ€uèX ÀR†[ßrà8"jü×÷ÞUÇÑ‹%’*ð³­¹`‘‚»,Ëp0À`•#ü§>OŒã ŽÔylV¿kW¦ñµY­ôˆ¶+ß©©o‘¤¥¢£`ÎÎÈ\‚¾{p9Ϊ÷.1ïÅlñ@ ïš„\n÷Yh©iÍ]mSa€Š–²9žìwv(ìú|}5¹…`Ì]ö…ŒyŒ¼ºÊŽ½á ’Hࡎ»?4¥û›'•L)É_?MjR¤ òþV3ÞéË"ÜÔïfØã£«µOD [#’‹SQ[øˆ‡nq+Ú3ç�àŸ Õ<KÁtÛ×w Ø¢ç¾Êmv±Zê\UIvg¼Á1îZ{zHÅÉïšB±BŒæ<ûó£kœÐ%¶z_ð£%³™¦ãÞª0m–[¤‘TÉx©´WC)IcdŠgFÆg¦ê‡ë?_ÐÃv¤Ì~*¾(4º\í ·v…«`z¶Š[¦îž’,Ä•_ÞfE‡,rOd‘¸RT~ú³öã$É™¨±çûwçï·ÑETÓ�vÒ 3Ý·šígözm€×ïªJ˜ Ø;pz±Õ!­¯˜µ=<k,acJÊ#BÎLÊZ)ûSŒycÐ`û<ð#Nsö…ÌcÅ`ɼO >óè½pô~’ŠŸnT=-¦‚i*çic¡P‘¯Îs’ª«! Ü=Le±ž9Q²Vµ½0ИÄÒ¿{‹ï㤒æ?Æ5÷hÿ�²w¹' Ÿp2ÙãÏ¥%NÅŸ@È2[)§y=¤eÍŽOŠÞ¤Ýw¾ò«­¤ªµ^öD”–È©o&ã ¸³’tŽ•ÔERUgv|g=ÅGu S€i¼BÒáì—€DÂÔÛu²µeºÔÔl[ŒÈ{‡÷Ä}­Lý̧€Bð9 …ñ®YÁùø| ýÚu^‹†¢2f1ã·ÂhÛó[èb¨¶m¢ËzsÓÎñ˜Ô=@ýª¬€rF5’ÉõŸ‹+øŒà8é?·¤«qîŸZ‚eµm õ3DÑ-ÐSƒÜÀv¯l¡B*ùùH#¸gœ‘®i”�|—ÁOÛ_z.–­oòÀ ž¾Ê‹´‹…UIÙ¬H=SŠW©Gˆ²òK§µ¹$Óç[µœØ†¸÷ïuI®.š‚<ŠÎûv©’5npO®±´4ÝÈ?áŠÒž0[î~ø #!óæoçnè cçˆËö‘å>2¨wj\+Ý+h-¬„÷ ‰éáìSîÌ„xÆ|ó­÷ötùý×9O!0áôû&›èÚ�mâÑwIQèH³„çíVùFO‚?!¨pý¬É0|¾ÊÕwS”ëïÅ<íX­v×"+MËÕ’Áidj&P9Dš wxòiÉ<jcȇμ|þX 4§Ô‰ðA^WjzX"™®òa^ŽºHå™0 ti”>q‘ì3>TŒÂ㘰ŸB–3 9]grÔýÓMŽÓµªÁ[ý £o˜Ô2É4i‡^9ý˜Ï‘•9ÉçXºõ…é}ó⃠N™ÿ�x½ècétÙ¼¬Tõ4Qµªãg¯rîݱCPÇçæŒ$kŒyà,b×ðDs½ÏDøÚaÍùN¾öþŸNvUªá4¿ß\©Š©§R¡ÕDŽªÞGŸãçUx¾-Ì$¾ß`¯p\#\ïó�w¾ŠÔÞ»"Þ–Ôš¦×=U2D¡¿vÀc,½Øç¯Ør¸\k³ÄÁ=Oà.»„4;¾²Vn8v…¾½ée±ßà*s x{9Ç ±ªö~|ûûëºÁœCÚ:^} ?…ÂãEº LûéûÛ) T ££ŠW±…b8B9à»LAóîñÐâ{reܶþáD`çü¤7ÞΊUW£é¥*ÜÓSTLOù”Œýóî8—…cH5YèaCÅp@¶YJÝ%QgoÁø¤§šÙUN…¼³Æøú~^?ñ×`1',‚¸waFxpRË?L)ëÊN³STÂx1öˆØôT'#ÇÍ÷Õ Ü`·åÝY§Â2®JŽ‘[)ìÑÉ•ªîUÅ\Œ9À œ}ˆàê˜â.7›w„ЉËûªÎ»¥Á%E{ÙïÓ«P„`Ü““œ}<êOñŒÃ&`:ÏÜ-Z| ©ž[zû•§ÙSE$ñÔm«…-DN@Yã¾<ã°§n@#ßßhÔÇ <ç÷•B¼9„áé¾E±î•f6¶æ—ÏÍöcë’Üÿ�-Dì{CC³SSáä»(að„áÿ�šJÙá5T–ûÐ@Œ@je¼¯8S'qç骮âí,‰÷γÁDå>û¥An{ûE1޲žñÁàD û~cùkN†9l´…‹ˆÀTkï>J s¶OJÌ­OPdÿ�Ÿ¸‘¶@Õ¤ðë…—Z“‚`Ž–^쪺'¹ ãWbo  N1QOܽñ4Ã8ý›�OÛFæ”Á·º!ã— 4S'ÐgX Çš|¢Š~äùf‰G‚¬£·êH÷ã:ôš™+g¶ ‹¶auµH;ðûûòŸnA¬<UšBÛ‘.®í…%΢ýOJjë«jLФ5(‹°sûŸ8Æ<÷�Çì5w ¡AZ›gæ^‘>�úm´º€Íl¥´ÍSºi*"šžžº‰&J XÜæ®#Ü{°ÀwñÚØ=¬§‘㕱 -¿Å—cÁðxg6Ày/Cµ¿[.õÓ™§¬§²Zé–”v¢ÓwöÆ>`HQÀú.OŽäk«³æ3ÞVéÑ.ìœÑä¼Êÿ�hCº7Ó ¥ú]­¹7méDjËrQæíI^ÍÚ²E41¿­LŠÀ�Ðü2ºè¸ž mB EŒÚ<y²Æâÿ�àÅ2ð ô“+L~·µÊ{ß«_¹*h©äc†<S¼g?0GóÚ 6FHÏ×£Óq�]yF?Ù z.Žô?­·kR¬¹«ì¶úF—¶u ©XïIˆh‰,gqÏo W:£¦r̬^F`½kt¾‰®Ý4¡¸5}в¢x‘éj­4µ Ú~WU=Ààä’rKr5ŸE²ÜË0°I+ž}NÙ{¶}í|z 55ÆÞ&aÍLÒ:äó# …³ÜpàŽrHV K¤JMx"¢ž*Ú ¦ ¦ßtÝ•œ¸_ñà¿qò2Ã</Ž8JŸ+êLô6æ¦ÃceX[ozSµ»†én¶YnU‚ƒÓ¦¤(Êñ¸ùUÑÕ´%$TÈ$¤ÐÄáH Úo$ ¹Û»_POÕjaq-v!ÏhŇ_]:<ÔZ´µ{J¼ÖRÑG?âZZ–ììŽ,g)RdxÎOxä÷»0+‚."ß’Eý4U{SØ»5ŒßöÊOt¼Yžß°êi+¢[Lrˆ˜ArŠ …?µ‡ŽóŽ8'UèÐ~z–—wOï#»U5JÍ ¦F{GŠ_«õþ¥ïmÏI¹-7§Ú@ý¤tÑ—ÇtØe�œ’C18“àtÿ3N¾&ÛsòPqšÎþ©ŽüG_å8l‰®ô;Ê»»YGøcøÊÉ$oNU+Þ21E$’Fr57#°à\°ôЉûs þ�¸W:D\Ÿ´î“ÚWÄï[Œ—QeÛ¶©V¦¸ÜÄÕ‘( ‘ˆdDü]ªAö#V¸0Ü8ìåÆÒ˜$úʧ€¬N%Åã(Ø™#ȯ8º°ºg{ªÛ§rQÕ×íë©yœ$«ê;ä¨îA h•†r#`qŒ·$š<a¨Ö–ͽó¹ïòW8uCMÎ fÿ�Kx&ݶ-÷Oö¢çQ¹žÏGK#ÔÉGOlŠXçç9ÅDR2;Ç9:l~%ÍÊÐ$›LÁô"=Ù6 Ó&#_0T÷d\ïÖË]úžßt¦·ÒÔ÷¡’zx}j´,~`…eƒ€;BHÏŠN•'9²'/R�>šøÀ•…Ô¨Àò>Yó#Ö#ÁT”tKxKÅášœ9t4pK<ݪĂ̓*G÷¸ÁdgZ/©9K‡¨“Þ<lªÑ`³+Ÿ0<Vºïš*÷ íApY!ïÊbOOêäûN»Pš 8z.GŠÒh© …gô™k⪒®i*ZÞ„ LrÄŠqÎå•;N3Çpîã鬎4Z[–Óãö_àÙ¦vïê¶|EItšQd¡žZSÙ~Nð8ýÀÕ¸òAÉ0¸[²ÜɃ­ÇØ~WCÄÛ˜Àm?kþW¹öô4–ª-MžI¬òË"©Îpc¤còãês‘­ì+5Cï¬Ï’ç±”"”Û×øCÐMw¢x½¨­°´@(¦†:8ÈÉ#'Ó/Çœ÷{yÕÚnwÊÓ=o÷ú¬ò/"<¾ÊîÚWK¡·-FæÚ8,fIc£J‡,qóH^Py>åH÷レêä‚ó²ÔÃْãûºV+•M–°Êw=²“é £¡1•Ý¡Eg+ã€pNX¨Àà~O8þ¥Aðà §»ñ­”F¿ý²ª¾G ÝW9»‘QÐÚ*)r¹ä1pÒóδðÝ›Y9G˜?K*8ÁP¾ >Dxs…}[í{†Ýh§“ñur׫DU±Š–xýûJá—8Á?/æªÓ«LÔS¬\ûñWªRp§2gnEt àׯBÚ»‡kØ­v¾šYíÖ‰§y67÷ÔÈ[ä?4Ò=T†"IDÏË!ÆsÒЩ”LwŸ`™> ”ÄR.´'”zô<W¹>Ýj¯›FÕu¨Žhh£ŽKWàžð”I"¶�ò®ÃÁ#ïƒ+œVuVcPÆ32žJŸcŒãí§IyòþÓ‹Ÿ…íƒýëµn»»mõ|U†‰väòÜ¢5�"#ÕÁMU=‘�>YJ߆hÕtù¢å ¹/ »æáQWºÍ}¦¶çY·evô-ôµ°RÄ’0A+`Ì{Bã×e_Þ“$ë+æå‚}÷h¶8p9¢/Õ[T;}w^¬SSTÄ¥RIãC(>1ßIêöŽI"A<ëÍñ˜Ìµ9ƒÎm¯'G¢õ ¹‚ß0åwlúª-Ÿ}ºn™)mrÜ®4©*ÄêŠ$CÇ( WTÈ$²Ÿ}YĽ¬¤ àO3K÷Xª8gÔul¬“áûÇªÚ ×Ó-±Iµ)ÖífºÅ¸%3UÍUWÙŽ�YguÓÆ#ãæp´1§·9-ðÎÀO‰=cP»ìF v ¾f'Î�î0´âÓMýÛxJJ*Ì}þ”M-L/£ e„#’yÊ…>2<믬ZY$ëÏóÖd.:‹Ú€@·½Œô°(¾¤íÝ¿%ª¸ÓXãæ5§2÷DÄ’¤hÉ÷88ÆGAÂj¼»(>ôµÈ÷`¦âìhd»_}'ðµú– –Xíu´µŒhJSÈ1ŒãÕ\Ë$mâK‡êw‹zÏá×lãTÕy–ñ¡i/wê2‡²8ëe‡¼�84‰{WÎ;A>úŠŽ]À#¤ýI>öV+¹ÚÜ·ô÷RŽšÐÍ,“‚öz©Y¹I’7•ÉÏÌ Æp~_±9ñïª]äCDøL}­ï¢¿Á©Ì“�|¬cÝ”¨h(ë⥦y¬ìYä-øÉ£vry?Ý>ï÷\>©so}–ŽôüJ˜k­o1VÅÛû¾¯ÑŠ…÷ez°†›Ñ§> fÀúwyÇætKL~¨Æÿ�+"á”ï•’GH²ýÔ-©}¶5Dð½:0v“×2È$÷æN÷Üg>ÿ�C¨øf-Ž/Óíb¤â˜w7Ùú¦“m–D·Ei¾_,Lî¿ pDi� ÚÅQ†@�á[Æ‹ŠÕÔ–‡wƒžvº—„�#®ÿ�ÆêÖÜ[Róo¶Î•»‚†åQ*ãÖø„‰‘ʼÌ<ýpuÉ3ǼC`wŸ¤y׿Q´ÿ�Viè>¿[¨»šÝWc¸Uzû¾¸’0P4 :žÂGp}ˆë®ÁVkÛf_Ã^|þ«ŽÇ4ÒyÌóïe0Ú;_wVÓ… _«#s‡4wÙwœÌy öÏœUÆâ)5Ù]½£í²Ÿ‡ªá™»òqŸ[¨¾ð°ÖÀÕrÑU^çœw0åÉÉýòàsàä}�ÕÞ‰i�:=òwT8•' ŤÈ×÷Uu4sEPó\)j¤ín † 2ÿ�í>oÓßøc[Õ\ܰÃûx.v“]˜—ÿ�>7RÔg­’œA=ý‚ʪHŒŒG$DãØc#?®³é�.à=ø«ÕŒ‹O¿kR5ÄÚVíFÒŠf¨ôÕ±ã#¹Oo'†ú•În³§¾ýVM0EB`ÁðNRlùo´3A=-²Ùïz­w™ŸIó!ö÷ÇŽ}³’üY¦ìÀ“áõÑuØ|>ve"|~UM¸¶ÌTS~+åÁf6HnRJs‚ 3ù{Ÿa­&*LåJž/ 3y’Ò¶ôºÖ¨àG ÅýGêÊy8ÀÎyÔ˜œFaòÛ¬(0ÔÃzŽ…|Üv«cH•ÕÔÔVêVVÏ9žxÕJòÜ$ŽyN8: Qó•„“ÝÞ§Å5€K€¾ê«¹ÓÓ §Zj‡–(—´™âC†?b1œkv…G\\õ\î! ¼ÆÊ¨º¬ÈõqUä“MTõöÖå>dBÁªcªI(ŽiLD‚Ny>~ükV›„ Yn²M* Ǽ?süµ-·HÒëXÙcê«`@Ñ·T%Ò•Šç*ì9ÎB÷å 0ˆ?’ŸØ7uÂÜ««¦x¤û% ÏTëPk„•v†%Ͱ[QÒ[Åö[©–…]ã2#4aÈ2{”’û·ß@Ú «³‰/@_]ë}zX)­ßÝt‰A1¸G;×~®«*áÖ”Ó",ûÒpÈøîb´7ŽR¦ÖælΞÏzî¸%JŽù]½jÚºãºi¶ERPnzj•y.ÓE$®{r½Í*…TÁ€ÎpFNx×b\BÞìÛ›7åy þÔ-ïº+ú¸gƒ¥öéï•ùžk…45‹…£p„¯á* ˜Êðò"”fù;OwwUð˜—]ÐÒÛ÷é~Z÷,ˆœæ²Ùžÿ�·Ýs‡¤žíMÔ7~—\·E­@Y=9c€9 4RdH€Ž�ÀúëÓ\dÈ^GŠ:“²Ù^n|}SÚ“²“tú8®QÇG'ª¦(0ýÆYönÜ3w&Xdx"¶)ðÃVn)ã!´¯fÝéü=áC-E¾5Šš¸Œò…_Üj¨P‰QT¨ ò¶|k •[I\ãš¾$ý<¹¨™nvûa1jÇt•q಻‚2û@f¨©´EŒ/ç¿5¦È”Ó==*L¡û¦FG$“ت¼wy$yç:ãÜ@ªù¹îåº÷†^“�0½·V–ÿ�[nÝÕPÔÜîL”n#Xåez~;ƒÈcqr£Çœð5B¾? '¥ú]ë_ ˆÉ‰‚fÜÿ�=ÉŽk¥DtW¸joõSHXµ4ÿ�‡bÇꊭڨ0GxçŒØÚ\ÒÖ÷‹yJØ‚àç_cöeÂõ ]ƒmE-ÊëKSI(‘§‘¿idgç|¹vvƒŸ}’£ä 윿5& 27÷öOÛûsRî&°ÔÛ÷47(â+’F¹·¼È@!‰ýÜy8è¸U>Ék›6ý€úéºn*ñZ«^Ó ýµü';5Î7Ls[w]Ö¥a„%-,m"ÅPI%ƒØc‚[Ï’G'UêSq£ó4u6‘è=ù+øG´×³É¯œþPv;Žò£Ü7[¦ß{mEÉfé-$Ýò°bB»Æéœs–î dz‹M±Òl~Äñê«aÅQ]ÏlLéô…–Õ»núÙ.4O¸é㦕˜É÷[¼fQÿ�«“Õ˜°}ðG§Æµ­ùg•ÿ�mO% ¼½Ã7¤ùßöS{ªáMs¤¸×íŽèûŒb5Lw6�É`Àà‚{Aî�ËÄÓ ÊX ùÏíç-L%RKšè·¾·ún¤;½ÇJ×*{¥¥,'öu0ÒÓ7©ìÁ» ž|áŽ98Á ê+Hå¤uÚOà#á5à–¼iÝö%1Û7jÒO}†Ýº§˜T,Œ°Á�h£Œ2/jŽlñΤÄÐÌ\Ûs}!¸Á®×–ße¬;¾ó¹ªo­~Ù¤îl÷RPÄ®ÿ�sÚO>5ØðêTò‰·2}μÕ7�}•¡ÑKäÑîŠ J™$¼K+aàJ0ëžïÞ!X|r?<j‡¥4Iy*ÿ�­€&zBÝ­ý_j†®Ýk†Ó©ePŒ¥Í»Kó«{ööªòÙ#pü-®s\ç:cÜmÔ\ö]¯{æ´6'b}Õ/¹+h-ôºî ÁP;²¬f"€q•"5Ùà“1ô×Gi5!Íüf?•ÏãÈk Ï¿+¾KýÝG=7÷¥T½ b:¥vTÇs(LcÝéã6¨Ñ¤{úϪËÄV=ž¿~!]ûJº½¬’¹Á@ lDŒË!�öSŒägãë3j¢šþýë[óQ ›{ä¢pWþ*®¶­j$B®ZºSqÿ�¬tídÿ�Þ?Ç[U©–€MýÙaájµÎ!¶õ$(­Q{]š–¢M³yƒ¸¹ì»T°À†v.Ä {yûßÃË™i¤(< ãùVe…é)(ÍD1ìZJÕ�Åø‹¥V&lùŽ3(£Ÿ™”·Гɍ'À{…;©·²$ ÷ý¾ú-Òø^½nÚÝÕjš×xÚ‰©ª Ÿ2ÔмU¯Œ˜•Þ!Ñ”.  ëCúWvƒ#²Ç3öºÌþ­¦‘í>ùþëÚ÷ÀUÃsÿ�æðÚ÷%dY¦Š8ˆŽÍéá8ÏkY¢p8¤†QØ  uÔæ/ªâž.·ær mÜ„Œ;{³ú:4+Êçö¼ïã¶÷L—ûvüÜÖ(ꢊ)wÎÕøCP•ÑIMCH®€ó~$€v᳦&‘6+Èæâß—'¼Èïe‹oÑ7Ì‘Ã-[åyùƒJÅ”pr •\ìuC¥ià_ó(¤êêÚ-ÆÞÛV<ÿ�Ú&¬šDhÀð�.A#ãøë‡Å`³»3ŸO)^…Dz'ÄûúµuJÓ.棞ŽÅo©Uy%|9îà–±8ÿ�‹ÛÎе�(‘˜Ìsþ=ÙE‡­š»KZ ž]}ße±»×âlÔíÙ¨­ûqÜkÞµ S4ŒÈ,{‡#ÛóçpøNTUÎ]�;ú]wؾ-G³ÊÐdûß½i}£~\iw fÓº«-²HàF %Ic¸ÏÛÈÆ8×q‰¤×ÓÉ"Fë„Á¸Ó«šätþ×õ,WZP7MÖÙJ€ØRM4ÑûüÏ“Ÿo>-dð¬W~©>Cß’ÓâøÒæIlU¯¶î¢=4çÒ²ÝR2{U壄?ò€ËÈüõ½‰Ãgn£ÍsX<Wf~Qè‘ÜÛêjÒ=H§xôÃJAǹË6>À}õ[ „�[ì¼f'1çæžöOQ7q ÊÊ_ÙéÎ9Æä/À?ŸÞŸÁÓ-— òW¸n1ä€,;ŠO~õ6{­BGuŒÜëTfW–”?çË\ó“ü4¸V %†'Ã쟋bóÀx’5Ý5mõ@W–É*æ8=Â0¿e €9úcíÆŸ‰31ùŒz á-oÈ'ßE–ê¾Ö@$kœ4öÒà3%8vþì­ƒúvùÏ4Ø*AĆ^70¤âÑ5-çîÈ}›¸ÝSÙLý·y`L?óÜI�þ^ÙÔJ˜{cß–ýªÏ ¬æ;å:ô÷îêð¯Þ3XÜTÏp¥ã´ÄÓB˜} *À�}†1ïç\ômí-=?uÚ a4àŸQû-PÜ—HMÐ=;F›°úµaËñã*Xã킺ì"C¾‡ö\†>¤¼ïê§6 ÉsŽ)ÒX$€œGèÞˆDÆ3UN8ûxöΨâpÌ.“ÿ�ãû•6àØúÁ¾7½}RFµRÑÜ$D1ÄÔÒ¹8óÈ%†}¿¹Ã0blÞà~¡Äøƒœ>kòÝT´бjV¦ž˜·ïåÄŒ9òsäë¤ÄáÚ@ŸÂæðØ—f±Tö*îS¹¦¤¾+ÝÃ*$çóóÏÉúê›XÐeÌWQÄ¥4ñï5ü?fë¬fÊD®¿l¨9•ΤYf@º£O>x%JmæøbšK…ê¦Ycå;Þ0IóòL–üá¬l@düƒëù]VÔ‰q>Ÿ…Ü­-RKøú›ùbÝ­‰qôà{ÿ�=^Á2è©b‰7$¨…žÛO-IXÎá%HǦ9ÙÉ÷óü¾º¿Š« ÙQÂѯ)ßrX$« d¤ÿ�h*dAê*×úqáû¹ÈîÏŒžsôñªØ<^J’ð�<¯o%6?ž”0™ÚUup¥¹,³ˆ`æGîF<ûr Èþ:ÛeV9ÒÝ=óXUi=­‡*Îé&¤M ˜öP߯µiƒ,šÑ0¡-4¦fb'ë­›YåÚ•Š–µHSŽp¸Î¦†9, Ê �£Nˆ5Bã{¥âhÎ{dÃ}çøé‰Oe"¶³$ˆq£ŽÇùê7@*j\ÖÂtòd4ý¶êˆ»!â,Çúÿ�–€@ÑN$ì»Íð¹á·nZ=ã‡2*K=4Aâ/Ê’Hj£`“cç¹Nq¹Å¶29~˽øhKÕu£ª·Ù6M¶žËb¨²GLÝôµvò®þrÉ1r¢0(Ç <k†5ÌFË­ìÄÁ+Í'ö“u†Ër¿KGi¢£¶^ úr==BTO ú€]@B‡æ ÆHP2puÖ|+LæÌE½ùôX_U fY¿ªæŸJ.Q[çW’†éJé î(Þ‹ aÕ;Y±ŒcõÚô×ë켎¸¹[-ÓmÂÓõÃýÑx¢4‹Wø¡E2ÌÐ2|ÂCŸ› ÄA^2™YX¡ %z¼è×Uç}jt©üuØR$_ŠjãL  M¡îãí‚×sM©–ÅsåÛª>¯\«9*ëvÄ’ªvw=Ôäò^d î~Þþø�êælUŠl‘uüí–²FŠ™å]VPA.N>¸³ƒõãYý˜Ì`j°I'~iúß¹¤‚ª¢Yš†D?8deôŸþ0¨8úü¸ýuJ®å³ï¿î¯ÒÄÁ%ÑýÙ(nUäÈÑÑF’–VqÎÀŸg8$ž=óç9Óµ‚D“m¶òý’5l7÷îVp½A‚‚–uD‘¹pf߸KxÇ8ËF÷�\âu÷¶É©ƒ”….½ÜëoD4¾„0†>”tÝý¤ðNNœ�ÿ�]VÁÍ'ÌMµSâÝÚ€H˜èšèï÷+|óJ•m+¨Plq)n1“‚p¸/<œdá�ª‘•Ü]ÉGîW;Èyæ’KdFFÌ-[ª¿9ù•£�{{{~Z¶à ¿ú̪u\ö’M§[”ç·oU”3ÌcªÛÕAÀ¥ü[³¨0=ü|}µ.#B<?? QÍ2 ø©Eìžjû ³ŒžÕgíÇžHóòòØÉÕWaäzûÒ=Ý[n-ÐtñJ[·dMUQG ¢›ªÆ þe‹ß>' ó�ëÛê Š 6G—ï)ÒϸîðRÉ÷¨žvùJzoPÀGxUSϼ}G†½&‡pü«zÏ‹øŸÇ¢¦w•|Ò\¥©ª«îv`~`¨?'ô?}t¼6ŸÉ.cмö™‰O»ý¾óG;VÔG"°a Æ çÝÔðå¨8“êDmËððºÑT:~Þ«e·§Q¥­þíjš«µK÷ ‡×Öâ“´(p°Oãàk”á¸Ë€ÓM¢êøŸ&3w›žíµèªû§PLTkÜØ�É´í¦qìC’y÷ gï­ü61u…‰Ç|‘›ÐÛ×ê£ðuybz:»Íôð]ŠŸÈû}¼êóp#4´ 0ãœæå$Bm΢Çg¥(·*D{›ÿ�E¤ýòF2Ì c÷§‰Á¾Kg¿ø*îN3Gw±÷IËÔx)ªgin2ÎîAõfyç' ÞÅAÇÛóÕÃ…Ì�~AQf##õëû¡†á¦©'©½ËM 7{Ç Èì@÷$?—ÓV¨Ò-°ÁAZ°&I…jX·‚¡a¶ÐÕ_®5“Q Öv9cãê 9'(ûq¢ì*f·IõFüM>ÎæG~ž  ¶KûöÇO¹mÖ: ç¸GM ÷±SMøYU;Õ‚¤´ª~nÅfyi‘ß¡JƒÉÚ{ä²*biÁ×Â>ÿ�²÷/ðG¸hvÞÕ’†²÷c¸OS3´‰JmñÅ Ê‘øH#ïʪ|Ä/Ôç÷޳L®ƒ^/ÒÛš¢hjÖ6F=ѼJ@ñÞËœŒŸÈÆ2óEý§<¶ïKFè®]ËGAbX¥é¦·­aΡ¦eïOQû@B¤€� d¸À9Fu^5ú—v·l­Ï{´RQ§”ÄÍ=5:ÉÚ̧ö0I<qÊùU™Ôa°Ä5OÀá_ÁÔÊs“¨·JºvŽ®Õnù+1g˜àœàÈö8_Ë^j¾f$9uøliɱõQú}ÑYK_ô¶;bKœ²~"X_£v)žHa}V­NZA6÷×Ò;•¬=H|/·ÝYõÛºÿ�]n>•.̤°ñRÒJ§žù_>Oœc:曆k]b}躓ˆqm¢6Z÷$»ž²ã3¤öéUeá©WÆ0}¼ùÖûšÀѬûÙsyž_=õJVÝ®ôд,‰HçÍX™äÎxîwà¬ê*Mn£éo¢*Õ”ƒUZ×î‹4Ìȵ%@ÇsIP>˜ù³úë[°i Vx6LU[’ëU:Ì*kjj²¦C±þc}‡M¤�ù´Iï'©R+fèÝtKéÓT=¦?�Š¿LŽr˜ÆOŒÿ�–uGB™2í{¥hajÔ Ê-ïß_~éÝm+ ëÁ­“¹ë’[ìÅ@xúãóÒ§A†íl{ꊽgÿ�y”­³qßbŽC ZGùcÂçî”ç僧tZë>úÏÛ¹>£Æ‡ß¢g»^ëê¯Q ÷?”žeb `«?ÏÇ¶Ž…‹ y%Z¹77AZ.æÖ}k†*˜‡i�ÈÈWôîóàãùj<U>Óåc”ØGd»Ûï¹Mgê E1§¦Û·'$1‰"qõÈd°ýñ¬Wp×w_eoŽ" 4ú*Úãv«zWlÎŒy4ª}Ïá÷Ö­,gÉcâ+™Ï¢s¡šëPѺíSQTAíq LOß?AöãóÕw¶˜o÷ 69îþËùý’7¹·;¥ \L�È'GÙOùcS`™GZd(qN«£Â‡I5<’•š’)P”KÚF~à`ãòÖ»Z豃ÑdÈs®,Ÿè«©Sç‚Ón€�²KéÏ÷‰ý9Õ'Qq¹q÷à´Q�ú)µ&ꮆ˜Fµ0¤yífä}¹ qÀÿ�ÃOR€‰…R™{_bŠ;æ¤Ã-$qÌIcÝßQÜ\{çÀýxñïªoÀIv[´1¤ °¢u÷Ë•Zº8Hà Œ%Dƒ'ô8'òÕ¬=´Ìý? õ\DE”mn•À‡Žà)πߋ|Ðþš´)Ç U)Ô#Tî²Õ=&±d‘xq#Œs‘çòñª†æ!Z[s*1r§ª@í%Áã'Ï©!çòëW FÁdbØ[©UÞÊÔ-Cx,Ïæ5½F¦ë¬/¨ÕÚNڼ؋ê¨ÊÀTªŸÝœþîœÊ|Ö_šd9ùê1¢l›¤G5š3ä0 =Ï×NJWNôrÍ”Eï)Ÿn?ÏQ8T»6Z\ DÀ¬Ì;[ƒƒŸ§þ: Ž‹…=fu¾ÚëU¸-KIo¨«-ò$4R §˜4]§ÉíÉðx#…ø„ýüüazÃÄ ÓÆØ£ÜvÍ¢²Ýíµ·%¦s5=] · Ä�¡äR;qŽI>r8Bº×™=WŸÏzÊ:]͸(*]ªV”òWµ JÁRž±8%x8ùI5Õü<ÒH"ñÒ Ž;å#Aß ’–‹U;TL´ïF!öpŽqœŸñ„Æ<{có×§Sy1ø…ä˜Á?™[#Ñ'‚—}Y꣢·½TÞž2¬qÉfhˆ÷ã?¦£Åd,<mš½)ô÷ ÛoQSEaÛÏú¯ø;ÔKó91ÔOº’¼ƹ² ®iï½–ÑÐÓKOLš+6îžù aõ=¬ëãŒc@Z °×ÀЯçtµ°áÍL˜÷õ?ÔŒêMÇo~K×ö¶ò—Š÷k%„2zH"7q†þ1ôï ©IÛ«LIJlR5×(b•êèQé‡q%RoÙøú3xý?ŽŠ•FW÷ꆭi2Ô,{Ø  Ys3ÅÈXQ£ùsç)$û1£8)Ð}eǨÛßB°ºï»]B¢+Ü$(;Jö£1Ž‘õãRPÀ¹²=ýÔU¸ƒN¦Boƒ|€F?‚ÙÓ+ûcŒ·ØhÝÃÆÿ�_Ù>%yûOÕ%àT“/uL†;š #ìÄqúiéÓ"ÇФꠙû" ¬©ž?ÃÓÖNðÿ�4Lœ{äxþ¸ÔD — £k‰)¥mò÷3=ºif I÷œŸ¾<jQ^,Š1J÷ñRŠ+*˜ÃUÑÖ¥US£ŒàþYñªu17±žá÷Whámqê›jìñ©pŸ‹¦$ã1M1Ïü¼ãùœjÃ+MõðAW6·‰PšË:1.óO!oûA'õÁ:Ó£Zß²ÅÄaÄÊ~²X,Õ²™gB©uçèI9û ýµ[ˆ|ÄYYÂa˜íî¬z¿oJô4U’I'Ôu.Ã?¦5™Gâeþ‹^®¡¿/ÝFVÅZZAÝq¤𤑯ñ,ÙÇçãZ ©hû,ÇáÉ×êÝ·o ª…®ñ+Võ Gé§u|·#ÑD(¡ŸaRmh)¢¢áKéã9,9ð�Áþ9ä}1ªG\àÐæaÃ[™Å'ý×h¨ùWÕ–Cd2ÆŒø^Gß<ñƬ²«òª¯¤ÉN¶]«m3ÄôÕ¸“�ºµàùÞ~¿õÔ¯ªè‚!(ŒÒ ¾v=’ßf®·=Æë#Q3+Ûé!õ0N픃ÛÜ0“Ü:zx†›Ÿ©ü'©… GØ~Ws¾¶·Hhßoîšî­lÛ⣕(êM|µtò vj¤‘m‘Á[‘%D…²ºÔ¢Zt:w¬\K]×Ñ{ør¬Ýɶ館ÝÛÂóH²zhÕôær�8ò4²òrT‚H6ƒ4T–ÆUÏ}¬Šhk)ḨGj—„¢ãŸÝ »+€ÁÏñÒ+Žÿ�÷ª}©MYu’8ª =Ò=1FÊö’M$ÐÇ�b\c¹™ûUX ÕëÉçÆ… Ü\î6:)è®—dFIbšzŠ·§ïí*‘ÐÆBÈG¤²ú™ÀîÉ`ÕŸ¤)¨˜ˆ\•ÜrîËeÆ¢Šªš½–6»Ãûý>uÉÏg€îcb©°>$O-×K©S!0cÑF½k׫Ü-ù¦7bSÇ/Íõg%TñôÖUP ™Ÿ?¢Õ ç~˜·HOrn È¡¦Ü·BBçôðd ynÂãè~¼}2{&æ2ïµÚ»/éü¨÷•ÜL¦[l±HN;QjŽ9,xãþº½�ˆºÌ/ É·šo­J©„¾…º»óêwvßsÚÜ'œ“õñ©é< £¬ M‰lžd&D¥A 1ù€ÁmOQíÇNåL4»T²Û‘iÒ;gx/deŽ~ä{pyÕnÖ×VNJœY­UÑ H)¤Ôcö1¯rsÏùþŸmgb&çU¥†¦rÄy&û¥»öèk"ˆJí’j;Â÷ç“ÆëÆŠ‹ˆý'É=V‰ù½SAGOø‘I-#¸íÉüÁ¤qôOsˆù‰÷꣦À?L{ôQ{„—×WcZ‹Gç(áGÛ éŸmOLSÒ/Ü¢v}A²ÂÍ`ÜU¤Ž®i#qÈJ¢†AœŽ¿åçQâkÒoÊEû‚±ƒ¢ópuê§5-¿zµ d3z}ý°Ty>A |ßÃÛXŸâԃܷªDɽWW-žôÉøª¤�“,£·¸à€§œŸçƵ(cÃÀÈ=N#”œÊ[·¶Ö½} fï*¡™Ò¥Õ<`ddùöóüµK‹kL»ì¤Ãáû7ßÝ}«Ú÷(Ý£†¢Zèí|UÊägY°<MKu"<�û!«…#ôßÕD”þ5ÅŽ11i°s“ãúyÖ«\rÚz 2>kj¤Knž0¤ÓV§†“ çå>sÈÎ=°~š¦×‚~Sïš´öÚê[Ih¢©§§üTM,„0LŸÅGœÿ�–¤Ìÿ�í›xªYÙš øü¥)6 ýä¶›l¥™ F;˜yæ*@ú}r5^¶%× “ï¹ká¨>hL÷½‘kÿ�Ò¶ÈYÌÇÏž{°÷_<ãn]=”XŒu1ïÞŠ%G´íòTSG$“ÒLGpïd�¯¹U'¸pTóÆWëbž`$xÿ� ;KƒO¿}S¥NňJ ë=4$0Ž HÍê88„nG>F†ª7F—éëÉZ~»ÎeU÷Ë= 3OsB…H °füÁ'ž-n`ë¸2¹Üu‰*êémS¹cð÷ ÿ�¦¶è½`Wf§E–%O›ç�iÍ¡PÐ~š`|§}?E Hö‘íÏúú;B¢"8næÇüÜéæf”÷B#WSþïÇï�s yÜ)iòWŽÊŠÝQ=1ªQéx*±d·Û9Ôfêv: ÂíÁÏcÕnkVزíǨ¹N�Š+ŒÝ®rq(ï¹*�nÐH>3®'ažÖæqôÛö]ïÃØ¦;åëÕVÖÛ==¶tþÓuÜÛüìù dŽŽ’j‡¯h8nÕvÈoÝ çéÇZÝ&ë¯í]?(^`ÿ�´ ­—÷%æÌ»]+¬rTJRâÒè®á#ÔÌÎÄŒ‰[°|¹ swß pênùƒŽn[ý>’¹/ˆ±µÜ¥¶æ´3cU[î #ŸÇ•»•'BÌêFr{0?¦»ç^e^¦c+cöò±míÝ`ŠãCMF†tF•¢3±Ï JNÿ�äHÔXš2ÈY˜šY‚ôÓÒ;þ̧ٶ‰ †“rÖ”Rh¤Y%©o—=±’ì¿—h8ƹ†Fh\µmn··b5 ómÑ\¡Ùj$“žÙ)LŽÜ 3öò¬A+aô±—˜ Íܺ}×ó ŽÇkrY¥–EðJÉòÿ�×ôÓº«†ËÑÆ›¢ã³Y8$ãÏíHcüôϪýO¿E;p´õ_ZÑl`Õ—ËJÅ¿†.˜(އO©Y-Ž’D( ÅÈ{ˆí§í]®¡7ôÍ_eÚÁcS Ô†?~Ä8þ$ŸÈè[‰¾‰‚�@NtÛzxÕJUS@¸ðËüÿ�{PTÅ7q>úª¹ObfrmÞãÛ5⊷÷AþaÎIþ:1ˆq'+S$êï~hø6ë4qúÕ.¨x!¢$7æ'Ÿ¸Æ¢~&æˆéàí'D,–*ˆYbk‘=û!| ûÕÇé¤Úó Á>ú¦8x0=÷¤l×HË$Uw>ÌžßÙ8ý‰>M1-èœa ¸'Õ9½šçN;f¹U—%Ê~¤–ÏòÐS­M×hR¿öjLûê£ÓÑ»LV¢åTWØ~þGä8ÕÖ8ÇÊß~«2«`ÝÖS]·j‰¤S T¨Ã�<“G “õ=ÄãÇÓ9Ƴñu}ÜýŽø ©±Ê”âZÛêLÌ"† «÷lÇ$ãõÖu™ÊÆú{õZU)�>wzûôL²SÅ"+Gww‡s‘÷< hÑwö,Úå Îdão†±d”N=‹ ãõÇ·¹úèÜѺ®]}l§–Û$utÈjêíóÓ‘•X£!XûüÄðOù{j›Éi¾¾ú«Ôéæ'mÙÉô#³žÜ–oÌœŽ=¾Ú¶×˜h«vM&Y¾×µš…Y!’x òTŒ8Ï·“Î¥§P‘aUjm Õ¿dŽ+U¾ýjŠt2*£Â€)cû¨C8P8n{sǾ™µ$ÃEýú)_L5¹œl» ð=žçþ‹|l뮂åKsTWÜŒoøF–bÕ…‡b‰½1Ú¡€™ìÕ Ù¹!câb+ÜÂä•·=­zRï ‹|Œ’SV é4˜Â;*ˇeeíí$ ëI§šÌ+h÷5ÿ�Üòµ-¶ž^ ;Ãê2ÇÈ�[œ†ýtI—¾4ªm›bÕ_¹wfÈ¿ßÞoþ¦ºWß¡¡J¥(cy ´x‘{°Hä60ÍØ JJò ñ»ºEvÝ÷}³µ,RÚé™-öûÕÂhUhâ•뿱¡F`ޚÞþÕØ‹ª˜—åi'Eo O3€ ×=« q,4µXiV(¥”Ut~Ð彊p>b@#'Œë—­]…ß.¿_E×Ñ¢ð /m½ú&jM³jŽH¤’ñm¤‡9ïžh»@Àùs�àçÏ œjj®Ò =ý£ÞŠõ*L$Ì›Õ[ìµ7 z{œq(…ÛÒŸ{E ÝÇ ’ ñŽq¬@ÚpvSç?ªèCé–ÆhðåQ·y$–ë$vÕqß,‰éá²@'€|Ž2Añç[ q©÷Xh¾cnãø@Ü6’5â…êÚ2~ÉäÑC ÔdcœiéV2Dºiçï’Uè‚ÌÀ§ÙE`Û’fŸûÎGä´ª†Lûß8ã<êÓë ‹,út3™a%Ú¥0‘& pTHç‘û¹úŸËAóžŠL´Ä)^Ùk]T¤-²�Ìò~(I"Ƹà’Xd cÏõΨã)8jï/ÂÐÁ¹®0ž‰Òÿ�WEM)élòñÞ°Fžš g$~XÏ$s¨0¬q7žžãO%cQ |±éü$výòÙ$¦?îºZjF)ÞìŒ)8 NX~^üã,NÃ]oïEÒm`cñ¥ý7N·iàŠ¦†£û¾~×h±Ydžå+=°A#ž~º†#¡¾ÑöñSÕ­:ýí?EÛÓÚ½B\d–àYf_N`ÑñƒÞï„ÎHÇrœŒcœ ñÔ[òÛÌ_Ýù&áØ†f—õß·š·/4òOIÙA5Šž6Qß$qÀ�„3ˆ”|`±Uù:åá›çÓï¸y]uî'É òî÷u¯—44ÕŠô¦*”õI(HHÇŽH~¤p5ÐP9„y®sÈ’/}l§jðíò\Ù®N®$ÙM•9Yp[ŽAàÈÀSÄÒkf¼}vSàë8ºþÚóAÕE5MÖãl©Y ïÓÒúŽ£æÁb{{x_ð¶56 †°DÖ^~¿{AŒkÈ/y‘Êñéé*¥z÷‹ˆe Ñ«³wž�=½ÀÌ{k¡«%Ÿ,ÍS?=ÁufGmèR³ñQÔSD㕦' ÷3—?áò|ë¶|ë×—ªÚ©H†É:tõåÑGxš–– ¸¢˜²ƒ lI€ràà€ÐóƵ2´€"{¾÷¸:wê°Z@%ÀÄóß»ØS+MÃjAMøZºŠËn{âq#HW• Ê‹•ÆpŽ®²1 ¨N`'^Ÿs~dX®£ úmnG[ïâ"Ý ­÷U¦‚fž¶'¨—%DeÛÓ…€ª½ç»óñŸ®x¹ªZCû÷éè«q �ŒÍü±$TuÂz¦‚óÌͶÿ�å%ÛÁÉårÛέc-µ½|›ªX6Òn®í}³¥$”4PÇB#&œ÷ÄÊF;‚w°CÚ?|Œç8ÇfS¢òs:ß_ òŸÊÔ­Yl7ºß^úª²÷#±—ÒªYâÌŠLŽêx÷ œ`{c[˜B56ŸO~kŸÆµÆÃAï½TWêzªpìÐÔÁñ‘Œ§òã:èð•@~ù®gAÌ0DJ­jðî|ÌÞy9:Ói:›Þ›ZB0s%&m’M'¶ 8úèÔed’öòP1ÏñÓ^:¥Ð' yÈå\¯éãM©’ŒJÙÙµ¾…]+K$’S_H†~ÌòBž3ÏÔ[XÜ)Xà ×VþzƒÒú­Ó¥¬V×z¨£Š9ªîµô©M#÷÷,ô´¸ŒEP¸ÁFwå‡ €uËqš»2ìÒÙäIÓBcNK¶à˜Šã,ñ[/OÛ+©•¶Þ‘RS­âé[4”ÑC(I#jx @Сúµ {P2*3�¸l½ºóLjyY÷ûÊîÚÙW><él—]ÁSY¶éìWÔòKæ}µ­¨ sW &"1•ôÒ0Ãç ½Å³Û|0÷Ikæ;ͶÐ~{¬¹OˆØ–D÷}ÏásnÅÝh¹Ô~Ú¯oÔ÷äŽX’5 @ìÇËœó€r| kФºó@!ÄyôÖãW6ðµMAIOIPzV%K$òŒAe<Œ`¯9ãœj@$AYx¨ ºï?@ŽèºÛ¡¦ƒoÓn‹eJ¤f˜[%ÂýÀ:¿cÆ\€Àüýʼ0ÉùuθFšûô\Õr3-ñ°\:µKnTµSlk]v1*âA*ƒÚ‰@RB€T‚ŸÞ&_ÀQ6© ùøÇT©!ÇY±ö9ÑJõfº 'Sò3'z)í*xü»xús¨ÚËÁV›µ~BU¥™X1 ž,Ÿ¯¾7½8tØ•˜­§eµ_'?1¤~žþ8Æ–Wo_$‹Û¥þˆÅ4ë”M'`>™#+’}¿fs¤Ð;ýóNâLïÑ ÿ�ge%¢…{¾VÍ0Îå8ÀjG8Ʊâkwˆå’;`V2Ÿ)ìçž[#ßÀöýu‹‰&-¯¿eH2Ø%㬙’Y"ž .{²¯’Gœ~š‰ÌhG¿…Cx)Ö ÖNé$w©GsÚÉϾIùûÿ�ž…Ôÿ�´@>jvT‹ºãÅ;Á-eg­ÛM]P{K¸XØ g’qÎŒ9Ujån¤z+L/&@%#5K-2GUKOO’£º@ŽÀ�óÎrqç9ÔÍ`ÎKLÏ| 5nWˆaCêjb’D@C1àáˆÄŽ?Ž´èXtXÕ]1•L, %4ј¨©¤eáÝä\¡þ ¿óÕcšD’{‚¿‚iiˆºWMNµ9žž´©Pªxù0�îò�Ï#ùë?NXÿ�u¡X34¸[ß-~ÈpR^Ê#$DBΤvý;sãíŸ:½Ù´Úcß=UÐLHESÜkÁTŽ*z|]áqü†ƒ²lÉ$úyj“^@€#½N­·›ÊÁ"-ÕàvŒÙîŽ>¯ž}¹÷Õz”˜M†žö_U+q/Œ³´¹\Kú pwʳÈ^ ¹¼ò¡È#\ãSµŒ7p¸=>°¡}W�Û}T‚ŠóP°­ )ËŽc¡T9–ã>Ç<êÝ15üÕzŽ1–Þ^ϪœÓWRȵ3ÅMå?vDõ ç$÷ƒ’óõ&“lßc»]ÂgÀ=oßïm—J~ >·gP·–Ü}·Iö°ÑÏwžÛJoó<‘Ó²³GU«*ÎÂ!nåF å—³Z”)s ¾‹6½@Ñ{êJöoýŸ°Qm}œ»b³dレ\)Ú)*?½î7 u¦©f1~¤c,@?s2J™Œ×¹fA¡e7]8¿îÙ¬ÖùåŠá¶éÝ£ˆD÷ — VÊvôŠä¿hîÁÄä˜DÂeçóûB«6îûµÑî«—ÄOP(ìtHטöžë³¤5r¢z‘ÇTð[ÌÇވ߼¬¬{×»ºEñmR¶ëÈY¾º—¶ïÛã|Ag¸QìªYªª«®×}Çm©õ&P$©ª5²ú÷´ÐöANÕ•’"ä<¦(¤´HW0mÂ}«×ÖÜ#ê*R¢HÁOE'õ•±ä°Æ@ñžyçÛ\¡í=!vˆs¤ßI»·©Ž* ÁÓÕ•ìP`‘f�¥‹€å(\xãAW D¶^tžvô>¿DT1Õs|¢Æ4¬ˆÃÁM®ûŠÑOe¬‚éjJK²KNÍ#\d!wwFcý˜±�†�ä€0FN°iQq©!ÒÓ6òÓ_§Ñt•ñ ì‹^Øp"ÿ�Q´ßϹk´õ‚iäZ„¬¸¸QL ‚#>W»»ÜÇ'κ  7åâ=ùx.q¯’I%Þ$®ëN¶øêi£ª¦BÏtOM /qÁvÂ63ÈRH#‘ãQaÜK²Ÿ-ÿ�>|ú¢ÄS†Kmôüy(…ES1‘Þy#õUÁíúsŒûàœjþ"Â"<–f›I‘²ç]R„N,¿+€p¨ÃþQïŽ~Ã:‚" ÅãÞªjµA)îÕqŽ=ëZ…aYc”)œdsŒ·8 e±’yÏq4äæy~kG SûAÓ˜MòÝcý¥W­h­d?*Kww?¼AbI ð3ì4B”œ·ß§zÔ&¿¯‚"Ér™ZœË%ˆ»vSé @ÁËÂÄáx@áN3Œh±4Ø_¿Aæ–÷—@ O’*é¹}ZS5(¦ª‰Ü*ñ'©Œ÷Uª¿Dð5 3³CíÝé®ýT•ñ#.f߾㮂<6H*[ò­JQ ¦³÷«¤uT¡¡æPÀV@vàÁ>xÓÖ c´7‰ŸÅúÝ´; ùO1ný'ì§7kÍ¢*÷„n TQ'iª«ª¢A ±îl’ß!úžpyÖRyšÒ'aºƒV›NRàcrL}oܪ{µê–ãS~ÓµIb̰©U„ŽâsÜN>\þ/ÏZØl;ØÒK#¿í‹ù,lF%•úïÑeÝÛl}ïQMY„fWÏ`§lpHPYŽ õtÕðÕ·Ôþß²ªm¦~Þ_Dÿ�xÓªwPMGYÜÁÖ8‰•{pGÍó`FppsãR`°N'ç{÷×’ƒ‹h þê¤y$žŽ±#§ ¤9œsܧÿ�,~|ë€!Úø¬·I9·‚qkÂ:XjMÁ$$À¼lêy²Œ€2sŸ¡uCKÈu“Ô'&m@ë?Âx¥Ü÷´©F¥U+àa¤Œgž¨P3ãÏ·9ѾƒKyåSew‡ˆV6ã”ÓÍUq·Ú®×&@œW"ȱ©>SÓ=À€~`F0sÜ|â>€$ÜwñþWMB¼Œî‚{ùx~긯”’VNñÙ&¢¨¸zÅ_´HGibp0Ày9´°Xge#§¿~Š–33ü­ƒ×íû¦ JêZš¸fw¸v¬`«2;—n û }½µaôi=ÑT§U¤É'ê§U7ðÔ±KoX)X¢‡ùÀàÞò¼p0G?BAÖK0òï›ÞšóZïÄZYb«+\SÎëpü©aÚª£*À9>}ò?ž|maå­†iæ°k5…ÑP_ÉFšŠÛr¥ž9¦¦›&'@~Fúº~_L{yó«nªúd뺂 UZZë§î©ûå4Ô2ÀÒ£�Ç´ÆA>GÞÃÕkÛ!s˜ª&›²•.sÏòð5i¢Š¬’8/ãé§#d°FT1ã‚tÎó$É:'JJ”Vª¯ØiàB&º °lxa–f2Ç?1 |F[¸D ºè'Ãnð±Qß!¨»]:„è+Q¥4È줕Hý7Ycn3òƒÆIÈàó|e, Gó¿%Öð°:äúmê½ôÓ­6Çk§Š²÷`¶Û$ATQ—‚á\Œ{L…$ô3RÞ˜W-ÜäuçØŒ3‹ˆ$Ÿ¥ºßíê½– +ŽŸ½MÛ;Îê¶Zsr°E”ôÕ³Û lÏÀ‘¤9 ÉBOÞìç#vü‹²f2 ëô÷ê¸Ïˆ1 ˜zÂÑ EÊzúpƦÝT@ÁX;"ôÀÇÌclpqöíC¹^wUò¶g¤´Ûûæ–垺y’ZßÂG鑚TŒ’¥Ô¨ {I¬¸ÕLM„mæ²±E eý×júC{6š+]]æ¾ã32Ã4-ø’® È‰F 2XÈÀd¾æ9{÷ªç«fA6†çÚòÙc¬‹tÚ6ªÔI%A¤§¶Fª 1%¸^Ö$äå>_×:®÷Á¸žò¡2z/1Õ÷cÁÆ OÍüun¶ÝËÔñ§Ýfî$.‡!iÉþý}4ÐÊÏ·T°–¡U–IýáhN}þšwA‹HL\Féhæ¸ –ë& € v9 ùqçóÓ?!7 Fº ùAú¬?^2߉ªBöœ<qƒ¢hfœ”sP£#¨•õe¹9<…b¤=«ýuºGª–™#YG­|ê‘gI†b¸œ’Iãùê1LIÝKÛ:jˆZ’ÑK%}C±ÈæT=Ã>2FqçBê ‚Ð>©ÛUÛŸ¢"š¥VC+Nèݤ€[<ý {–€‰Ú|ÔŒ±”j\bŽQ+ŠfBÞ†îüóÏ?¯×UË „8¨ÐsuxÈÑnÑÃÅC“dž*ª?_ó:š2Ñ¿Ÿä”«¿3ŽXð€=õQ™åÁQè@«ýÆÎu}—ßïöYO¶ÞüÓõ L°G/¢˜ ŽÙ;Ч¿ ûžN©Öp.!Z¢Ènhú©:ÕW$@äÁ…âU A\ñË ñíùj»ZÐ~ß°k:'ó÷A´õJÑ—žz‡ä,ž~þ5n‘nR4ñTêµä‚ã)Ö†áY½œ+7�úc>‡ÇNö‡3ƒ ©}ú¢)B'¬®ßýц#€r~¿q÷󪎧¹W[R4±>¡w¬°^xû¹ï«Ê`ø��óŒ:˜´Ú>…DL‹§ú=Á\ŠBŒ~wUaAÎ{[ƒƒç·ÛFÊR&~¿ºT5òRÊóÓ(’¸ËKJ£µ¦2+€üÁ°r@O'ÕÐ-:žéUŽi˜õ•Ô_߉N“tߨ{nÑÔz{eŽªd©¤‚ÝYq¹Å|¯5ã)á†EhEó ”ú`0õ ê%¢ÃEB©r÷eðÓ¼z[tÚ{~ë±`¨Û‚¢©¥£þñ·Ö[–¥AŸöfs8.½²©š5WB ¼­à¨J½w ¦ï¶V®ï[l½S×Ã,¨±XÚišEréafY 2„””gp¸`�mIpOûAº×ÓëÎ×Ü=/·³tãwZZ Ë ]΂áWSLê³™ä°Üiª!WuÊF•ëëLI+ ­ ¸”¢W«ÓW^÷E×pÛ¶Ù±QQÔÌe†¼ÔfIØ ‰cŠ–”QÅ,Ò¼jÂ( ša ‰H\Œìi ^JÒÀ‹Å¾ª¥žªÙW5TtÒÆˆtSÉR>n@Çïx ’8ÿ�„ó}½A¥¼¿.¡Ô˜D~Bg¦»í—1¤¶º0U»ã†9UòÃ# >~Ÿ¸<žÞ•aR>G{÷ºTLA>^þŠÂ¸\­µ–øî&(¨ ‰½C<R'¢#DCûX±Çwkò•»O×XÌü³1Eï:WÁ¾‹¡¨ììÎDk¬Ú-µ½5U%»oG,抵Q»‚–b¢Sÿ� ¹<ÆNs€Fµ©¹æ3!ìÝê°Þ')ó?}矠Mv§ÔGqª‰Ʊ¤˜U� “Œrp<‘ÿ�1:pÃáëïIQ=ÒH™ñ÷û¨±­Y;?i ”Œ†üÜñúý5v¤LïºÏ¤ 3sÞ‡šåK;¯â#D„ ‰;Y¹Ç}yÀñÏØ2ßÒTý qù…»î³¢®¤¤Z‰bø¾Qڡæ �œžR=Žªâh¹Ä Ÿ}=Øzh1o}}Ýrº½_{¬ÔËÁ(Š#aŽ1¨ärNy>Bð‹;]”˜Š£b#ÞÐßÒzšŠZV¦y”zMI‘Žö`¤Êœgœý|èËÄå7ßOßÈ i f´wG¿uÎ9©åŒ"ÒVw˜æe™¤ï#óÎ|ñ§ç¨ÙD‡ÌÈòRU¬ HhƒºÊž¦áÍU3Dq»T³Ÿ”áTO­€¿‚³iê‡çö•kjE͹ûöÜ·+SéQZž iZ8dŠä3>Ulj ¥DâH¿OÆ«Aµk6C@ÓÐøè¡Ó××QvK<’Ë(g9¢ti�¨v-ܾ}†:»J7|¬Û®žÕ µÁ™à_§ÞWÃx†ZyŸÔ¢Ž_PÌÖL+•Ê’G8 Î<cHÐ!Â'ÎÞH{`Z]¿_gßDÎn0ÇÌ’×à>Oýœ|ÄýÉžFOéãV›L’4žÿ�dªÂ°kIÞþ颢r& Õ-F0B•QŒøÀÇý?–­ëÉg¼‰¶¾ Eº´ž”5o+D š¿‘ÆOóÓ6óªj$e%¿…ª(PÛ–@{ŽS÷³žK` ?×râÙ™PÓa$§4z8½•!p>` ê ±çÁã‘þ®Xã~~ûôZAÀ|§ñ¢ý[v®ôD1þ‚�Ý \€Áldàr|ùÉÓQcsf¹<ÊzÕDÓùõAÃw¬i‡¨Ð³*€D]¡H8G<ý³“©M±#u*¿7á9Iv¢gALƽ¤ä‘óò|j©aœÑ}ÕÎØDM½ø¨]tÍ!ªXó”$Žãõ$cßýq­65fU©˜ËÒ s§ôZ&îE<¡¸ñÇç€yÜ~zw0æ²zu„ºˆ*•ÃÍ)o™“!› 猟\¢KD…R»ÄJ€ÎÞ“2–}j5ò±ÞȲ×¹‘Ž<àùÓè2–Y²qÇŸxÊp¥¨l‚;ýNt²Á½ÑfSû±™?c<c´ãÒ=¤ž9#éÿ�]D÷ TÌeÖìôbÇOtºZDÉ4Š–d8ƒ,�îeí`�bá˜9�0ØG�åüûûkÝÔðª2DŸ~K¥tuV«Ü4Ô7ûR´’ 6 w2ʧ<„eD°ãdÂ8×**—8ZÃß/|×\Ù›ûä¹áñþÐ^·Êõ{ÜÉRµ¤¢7¨È ¬c/+3*® F 9×SÂ*€ÐÖ¶ÃEÊqªOq$»UªÒ#A:Ån¥ˆc´T°î#‚ËŒc?©:éXðnåÇV¦¢Ún‡î»Í†¶–² Tµ0D‹ì{¿8' øÉ@9Æs^¹Í ¬¼e0GUÕþ“uÛ[ýÙMw¡ÜÑдìÎ$¹Éù…Ì(…_Öà��PFeIØßßUƒU°`-µOejAèIS !»W²V”¸��XÄÈ{`¯pÇ$ðu]ï‚ +ÇbÌT«4x¼n?®­ê½1Ü##¯îÃNïcœŸçãQ¸){I e­”ãRGžáœ}xѶ’‹pô€¤�qÇ'ùóäieŸ²qV5X5XÉeËØ~d“¤',3ºü• É*;AòGñÑ8sNÇÊÌÔ#€;»åþ?rt F‰Z"©p¥æ‘Ç€ ¨¯Ÿ§¶£ `¤5#õ#£¨pLp €sóTã@9“§DAܽú#Þ¨•‚yF;HfÉþ>üj2 ê§.¶žûÐÓTÔº€±„ y�ŽÓ<hé4 ¨ê9æá4É"/&ˆ'‘ÝŒÿ�=Yk­#ELŽ‰ÎŽ®dR:—…qÈV8þ¿Ÿ8Ô5#’ž“Ìe˜÷ÞŸcnñ–feíÈnð8û9Õ|×6VD¸DŸ4¢“D©|Œà³ùçúhšëŸed JV ïH€{CŸ8`{¿/¡Ô„^E›s¯½ž UU4=Ѧ\‘€Aÿ�˜•×Ρm1Ï÷VûKh‰7Ûx ÒQÆU|Ä3|¿@):f}?*Th:'J[Ý ±Ål]£ä1Ï>Hû€A÷þ›³'H>J!T Í#ÍlÏCí=EÝ·'´tÆ=«eÜÜÊnÕ·5¢¨X²Òi*eX—»³Žà /pPÜbÅdicàUªƒ®ž~‹ÑÇönlÝÓh°mšiúaÔÙ7 ªY*ó=ÆKU¶é~ÙÞhæHeĬM*Æê’» l‹´Í®³ê8O˺ôWÒíÛÔû}--]Ö Ã²÷%ÐCM]ÆJêÅfMd·xٙєÇ$DúŸ<r±ÊÙªêëݽVÜ?ìÅbÙo[«s4u,Ðíª)ÒXò‹¥1«ï>¢zœ”ïÆ5îÓ“²Ep›ã2«§›º}×A»àêŽþµCh´mÚ*ØV¦ár,I¡Š[„UÖɤ”džz\7¡ ú‡Ê&7HAÑy—ëE£ª}KÞv­ÁIm¹ô–‚Õû-·oÞ;˜CÛ†º¤ ~Ê9=:H î^ö!‰v­Zöþ õå¼}W=î·ºúŪšZš{•3Êóú²ÔÉó—$÷—,Yƒv–�¶‘眀É0nFö÷äµéÔòˆóARÝ"8ÿ� c«ç'-ABO#äøûQ¨Ëënï¿ÛE{V"ÿ�;B¶íI's=‹±‹†q5LÒ!o'Êp¾Ùýt gÍ ù�=7ú+©i-õ'øQêö¢U/Ml†š0% ŠF#»+–Æ�Æ3ÈùF<êv9ÄÃ=t÷Ë¿B«83a¾ÿ� ¦ZõX Z‹}® ¿?s¬Ž{OøU íU÷Ïaï¢-’r“§O®¾ 'Ö€@çìhš$«ïŒ*ÐÐÇ#(A`o¿-ç‘Æ?¦M‚Óþ©ï÷uS6¿(ð²ª$f (¥,¬0r¼à4æ�$YIýKäòF<qQHQT*ƒïž@ÉýxÔo¦×H¦eBÐ ±ºKSK+ªQÓ{”‰æÏ`*œ{Ž<ó¨Å2/é÷÷ÉHê¹›”ØóöH £8ŽZ?ÂË QóóŽÜÙÎO:7�ZKM¼ü}©‡ }ÏTeÙ^6‡i �É#÷ç‚3žsùê:tü¼Õ¬"²”¡ÜPÅ,KSUè¨cê##ÈöÈ�ä`‘ùòul.íïÑYÃbî$Âvêi–Z†§£­¨NC-¿º2<g H÷¼j‰Â¹¢fßó_ßšÔf(:æ ðÛ¿ßì¢uÕùšF©¦ý³|í*îO¹°ÓŽxóÇ©² ¶·¿²ÏÄU¼8}VFïcÕ¦xÁLaXr>Œ Ï×ï§ÈMšf=Ûd⸋ÚPKyì!¤P‡{òJöýy?MJÖÔòªÕ¨Ãî=ø¦y+iŸ±@y9ùI ãý×[coÍS5‰JÚl¡2$rÃ�98ñœcbǧ¿²|í™D-Zƒ–‰ýì° <xöΚ\èˆ7(Y3<Èù=3…8ÇÆ3ôçùh2;E6vø å©iØAéŽì¯rŒcëŸo§é£`�Ý ß'D(–XÙ™HLç!AãúŒÓNÂ4@×LYc$î{°¬äœp1Æh™G˜ÅÂnfÔŠ6öàu$€Fë#QNU‡“Á––Y F¸rMsÆË V`ѱŸ±ÔÍ :¡ÕÊ¡‰ “àsÿ�]]¤bÁfÕ`‰M^¢å‡h\~š²�UÔ/¡ÓØíãÆ„Á6Fާš2ØÄl>„yðN!Ml’¯âcqN­ÈÈcÚíÏ·ë¦|D©)Hu–Ùtªã%<â®’ÙGøR;LѳÊÀùíò?/ðç>uƒÊ~Ry+¦á¯3˜ºÜûmöh? !Z°Šcôêœðx`W ¾~^0|óÏ‚ëëàºV»å…«]B¼¼Õw ­šŽ9Éh+GSœ–dÇŸ� ‘Á{ï`X?¾W=Äjk–Š×YR³öÔË_WÉdeeÀ$d6Œs®Œ@²äkÉ&Uѱj¤¨–Ó‚œÄ¾sAÚøÈÿ�&sþ‡8Ô/7…ˆl·ßát£¥µQ-*K¬´’�±&+ øòeŒ㌆ò99γÈ&Þýû+®¶[ o̱ æiÊ U’gG¿Üc÷Õy üª½†¦™1È`\§?oòÕœã_ßA”Hw²gÎUŸÏ:hû£.ºP° ©W<û‘¢m€„DIØÅ‚¬˜eWÇêtžy§ªYpTw#õïü´Œ/  í-Ø<—'>ÞÚgÔäšépñ†Ï©Jp?ÀŒ þ#HuNHÑ-W °œçåÀ6a2y(ˆæ—ŽöY<Ž;Û:€¸L4G’‘®‹’³5�à™ˆä„$ÓO¡‘õD*uôCM3|¢0ß›ô÷u+\/uÞmd©Ç*”¹Æ?s“üôö*èä‚iY”NÙò*ãò?Ç .mÞ¦cçdrÔ:¨,B¸$eÿ�_á¨ÚÙ1R—À¹ºü*dv>šÈÄÿ�Ì3ÇØyÑ´�/e ß&ÒœéjåL¯¢Ì ò }°F~ùÐ:é5älžEh‘R?¨$÷w+`õöдA™Óß¹F^4„å |„1BÝà÷.eîí?–pQ¶‚âéØÙ:Û*^yf–¦b’D¢R­?¤®™=ÈH!»€wr05 `‘¯wáFjªÛî‘îiåÜVÛ€´År¸5L‹Smi«!ù *Ã"†3ÆG$k‚±€ä«@6déïÝåVy1zøbܫĖKë¦[W§”±ÄæK´ö^û˨bÒ£%aQWÞʪӴq’_÷ŒqÆ«`‚U'BôСÖZ¶®åY¶©+èc êÛT$ªªB’»Aû;Q{ ‰;z’ÄÛhYE;« ¨p¤»Xç§–Û]y jj–¥žšÍ%LRÍÛÙÝ$ý±À£¸äwžÖUb¿9á‰M+‰5V£ÍaÜ›ÐX«f¤“oTÉ=ðÙi¨UlT¬QCV_ ¾‰ŠA”1¬`H&¯?]Mø{Þ×*;ݚݿ—©{ÞËSG¹²[lTÓúé"˜ÔÂ! ,ž³ ˆŸ$*i¢ž›ÃL•ÌÚȬToMÂx꥖¡„²C<Áà"_>‹Ã̈ Ø!Õ¿ÃØÔjÒ"ekQªÂmªŽ~2™môóæ)N{ ,$ìÎ[»)ñé€2IÏh»9ì5Ž‹FHh&=Rµ3ÑM šh½1H¢ZÔ-�$“‘ÜX€!X ±Æ ÊOàž›+M9›?u©zdšsÔL¬Xº¨gï0`@úqÏ€ #]"2ûõPÔh9]÷M[)… Xä“È+ØÝ¸÷\ýÿ�¨Õ‹kïøPœ±M-UVщV"Ü:(`I €?nxÑC]·Óߢ®áad¨)k$ˆÙl0RpÃéË}?ÏNA&}ýf`³ªDp•Ô 2„ƒä¨ãlhfm*V´ ]î³ÈJ³HѰùó&IÁû?Lý¼ê1M­·/|ÿ� NԞ⇎ã%+×îVïUÆUÈÇ‘àþ¼hÞµ÷AeBÍ¿ºBZšÉÈËé`ÎÞ?1“Ëí  '%ÄL$!º×ÓN®µ•œep{sÏßh\Æ<hŽ•W´Ì¢–÷SEVTÈŽ{ê'PiwÍo5~ž-Á¶?DÇ-{·piÖAOÓèS `hgâlâ‡jÎÞñ!‰àvû4š*R&RoYŸ”%AOåÆCƈ ¹Õ©²oyIgU‚Uç» @Χk@UK­ ê0{QŒD8ÿ�_ÃNN¨ä#›»æ…cœžqý4€ßTY§¢_ñ,£³ÓÊùÆt0fT¡Æ4I=lkò±Œ§¶é¤uIÕBLÕS¨$úEÈ8Ç<þcNÙ0 ]#øˆ9R]—óÎOätNRÌ4XbÜWNqŸÐúèZ@ЦÍ:$ZdØ@팘‚4qÍ rnšI;” �P=µ#H¼Õ Ò~©ûƒ… <¦.«?K&Ys“ó(çë‚5m¤›ê©½&¹`,ûÂQQ3}ƒõ?†™¤´Ýê]j'Ôe0K0íµp–4ª:ÊJZ­„Ø—*Š3À”2ÇÞ#•»V`?Áê( [ŸóŒ‚5‘‰d‹ÏOÏ-=æ�‘îÓ‹…SÚR¢‚Ûqõ¤£­ÌHÁ@¡»N;¼ü¥€Þu† »¦öõ] M¬µÓ~\)êk¸ZªiûN]Š)d�ブ<6I ã?mnaÇ÷ôXç‚n7Mp§¡1@îe.NO±üå­ Õsu#`¯~£ÓÃ\ÔQ§[ü*œqsÇß?A¨*J£\Cl·ßd_Í:Ã5Ñ<¯¢$PÏ�«•îÀ'ݾ˜Ï ÐË7}ÂĨݸ©º5X§¸QFäí§˜Bª1À*%Lž<öŒýüê'Óééû^H°«ÏÜs&8[õþº2o%w²‹Iˆb'<dž?= ÄÍÑQ + ŽègÇhà~‡OÐ$I 2Ã%‰Œcê3¡1âžl¾£EŒ˜Ã¶8È'øhåÆÌ Kš‚zhãïÏýF˜”AÖJ •�|³x8Nâ?Žt!‡ßáp uªWµ`ŠQàã¨ÃLÜÝx,Öµ�*”ðàã$(çøyÒ,qÝh4}jÌÿ�€ãÏŽå¦m$ý¤ Þ­øU‘¼û.§§N%F÷ig`Kóàø:•¢!ADdRHŒp1Æ]Fñ2‹<# TŒ>i�á‡?¨Ð6˜Ô)»c¢ûêK!É‘å+Æ[þ£Î´ÚHQ¹äï(˜ý^äf>™>qäj êœi‰ì(¤œ€ÜŒÿ� P‚Žy©]¾%h %J’Ç8<NsŒ`€1œiÛmªïØW:Ëem5ÎÊoÆÍ$ŠñTR¯íÔ’Y£o”3pW€?0'ÛRSj† Ùtw£«}Lª{Åš!B#-4TñÈò÷ÒóF}P2S ½à7zª1=Æh´*Ï]oèíUºåMQf§†ËSG-:KêÅ)(ÎZaO $ˆ`ö»®G'54 ˆ]#é«P^¡7îño�©º¢þPVh@NØÎrá«209³DÝY»5òªÖ ¥º-Þ x¦R¥õ’¦nÐAQB9¸üÝä¾@]#Ñ4• =p°%®)ÿ�ôòíÌ~1‘%§‚†–FcÚ"\“$’ª³ä`žÕhDZ4 ƒ}~†š´î´²Þl51Hõ¡©ÑV*¹T 2´…˜œ C¯�`|ʼn @ˆR²W,7ÎkítÕ÷{•þçX™š®­¥íääa‚'^8#ΩÔtÜ- & ‚UÍ4²‹ZÎÙsê¾s÷#ßÁþCT^�»–…96 ¾g˜¨\îùín?ñý4" œŽA6Ë5s ±®NN?Èù{éÚÖÌý”Ns´ šY&qûGvùxàþCV€€«_B›e¬‘ªÀ²òÍÿ�A¢æSÈ! miÀ1$@û„?ž=´ù wD$¤¿V;j)#´¿>2:Eƒp‰®!g²6KÔÕ<žA�dþ¾tAÚÈú¥§™dÁÆ@ÈÏ?Qï¤æÚi¶«)_™³€9Ïïùžu!ÒÐ¥Í ¨¸d9ääÓQöGmT­«±Kúð‚Ž±.O¹ÏñÇ:®‚U€æ›¤Zd ÅþVÆxSÇçöÓ6žè\îiRÃ*Œý󢎪,Àè¾<‘üÅgr9Çý4`P9ÃD©8\0Ç9àjV´¨3�Rð„uÉù|þº|¤ÞSµ ÛT¸h�^ظÇÿ�ÇB[(ZéR6ÂÇ0ùùGú:§R„úPîç+Œç÷pOóñÆœ8{)Œ¤e<vããå¤�:¥à°g”¬ÍŸ |~Z"É7K9s:¨9foaÉÇ饠Ì!ó’ .ùçŸ74!‚šw8-+6=¸Çôó©rò)œâ£³ãÃäûó©š$@U\9 ¹8ãýgS囨 Š—úçë£Ë:&"cg<ã@Ñ:'O”3ún2î¸#Ÿ9öçëütÙd#k «ãd_ãŽh#õ颗B’g·åðrOžïlýs«Ð±‘ï¯rØÀV¸ `-Zé­u5&ñèQ½:pGRGaí+Ú3ÊöOÊ{ùÊG¿5»H™…MîÙ õZz©!’VíHÖDíyf ç<×>5©„’`}´YØÜýUiB¶©‚N˜ãÀ?é­fØ]sõÙ]]?H„ËU¶X‘‡{° ± þó<yÔNl³ñ–%nžÒ¯¢¶SÒTËFà•im¬ÌÄAEläsÜqž�àpuMÂfuY5‹+&}ΊQi¢¨¨„.I, x$0Ëã8?\ûêSÝ¯Š„Ñˈç“”'ê1©$ÚŽ©oU²rëöäèHóDR©0ç–í϶yý4íd¤›%–d�v¬ÇǼi‹J) ?ÄaqéwãÏs0¤u)g/ŸŠ<Ž?ÈcßóÒÊSæ…ôVËÏȪ~£¦/2”üLÌIîDûq¤Ö„³«¨óøŒ}F@ÆŸ(‰E˜ÊÈK3ßR¾ØÓ@6)¯ çt rį„7Õf²:?ñÿ�ÃMÜ•ÒË&I˜óã³ÎžRD,±üÀ*ŸÓL2ˆ¸l–Yrʹü€Îœis"£™ûp[#Ø}>‰GÇ<¸P ì?óiˆ”òàªô°È±Ì¨C‚ÞÃ#Û‘ÿ�Ïí§a½Ó¥–ûåah„íëÊe^Àð((í'ÿ�)ÏÉ89È9‚�7[cÒÍý¸æ¨µZh?º»½&‰ãü$R0*[ÔJÏÚ2¤4h$*Ç$‘©P‘i]¥è7V.›2;bÜ·nÏŸmþ+Ô¨¦Š÷wáÔ÷"ÁTb.±óßꀨŒÁ;F HNê&˲}¹ å%¥lÖ*uï¨²ÔÆÔ/O4™s$Êg7j/q žì™"¤lcE-•ݵÓP$ƒ`î;­xVh%Žjzuic ,α�¾¤lO~I‘ò‰H”=˘ŸÝC±TÓ]èå³Y-h¤žº²ºí ý’X•ª»¥9Up^ù£b™ Bmb“BàS/ªƒuÁ¶oòÝ ¤x‘ntqµ3í_F–@î«M•™K²öÈBùϧ¦Цiæ¹­»¯ö¨*å§´Ål«­ïh^v§,dÏø”<qŒ€€TøcäÔ{A2®Ò&s[Y#“¯Ó š·=‡nËœƒû¼}01ª¯¶¿·¾õ¡NnBm–Yû‹é°?»ÏëŒj"áqxSÉd ¿Š$«÷/<.ËÇéçH4L”/ÍS{zàwNìr¤dœ{ãS"UwNé¶Ydv¨Tÿ�¯ËôÔ YDI(q3³)iˆËœýñ¦´¤$¥¦<Žñòø?_éÉûiœÒ¤Ì4…ôTw •gÏØéœÞIH•‹J¡ÙÇ+m'A‚œfD*§=¯ž<ýñÒkŒÛD³á2wHÌÀp{›ö ž¢ÙHÑi+ñ› œÆF»òÒhè¥Òƒ.ÄŒ@äœ7å¤è„åb ‘ƒ4ާ™8:@†å 'jŒ÷FÞáHÁÇä5#]òʉð‡Y0ÒÏÞ'õûê@ÓºˆoÉ*“ŽGž2F?,øÓ¨B2:†P;½"˜ã´äþXÒ"D YzÊ_ ë!ÀÈΙ͘!p”3ͨf Î~ø?õÓ´”2<VbiõUN<x-E’÷X˜$‡ä9ûéà%šn“’ Žàþg#ë¤î‚óSPqÊ÷gød~z-¥0&eER•ÁQãœhÀd/rf–@Á¼§ÔjV¸sUÜ !Y”Hcùñ© Ñ&$4aÆMÒð¶ínÞMÕ<©-¬.<½Üpéí§s†ª@ «³k )L!¤Á‘Ì.ˆp|°$Œ|ûûŒÌIßßáladØ>{ÅvÑ4uuðÆê˜jLvû½½™\døã~š¨ÖÔ,}¯š±`«íÚÕRÝ´éR"I©'µ3Àì ƒú ÏiRc‚ÊÄUi²…ˆ–I!JÃ÷÷Éú}2Gå«¢MÖKýâÃp¸ÒUS}»9ùK!šÂÎ4/mîWÄ-›Û}Cª¤·Óú•ÖØ%?¼@‡q“† …n@ÉÁÀÇ’5›Óß¿åfÕ¥2~á^u;þ¹¢én²^+^4-$®“åOÚ�Ç·ÆHö�`hJÖU›D‘òÌ.K+ó•Sôñª“.·TªòGGå¨Éº0–%G©ÛŸ¸çõ:E×JaãÀùóÏ>ú7D¤y%?c• ÜOØñ¡€ˆ%¯TçÈÁÔ•*l”Â]db1Ú?!àj,Ð $JÌIà2)÷:POêNL´r®¢D¼~ºL"dJ)YþÕÌAú:0bé+ça-ÇðîðtÍ6ñJ/uû·´€ ϸΖyî@X+ç•®td%°À·Ê«‘ŒŒçI¦B`X‡óóóõÇ ‰—F$¼Îsä“çNatYQ°º(ÌÙýß—<ýüqüôó)¡+ëSüÌÊùÎAUΜILën©%¶´rú³WÓUE€ƒÞ3 dð$cÔ“¸N5•wì‹õ±ëíÔuöJk­¼È²L\v²Ä‹ÝWöŽ¥A¹»{NBa�Ñ‚ùP8 —`>v¶Ü¢ºC]GvØv²fxàê!y©Ù7&c/©›Ó‘b é—¢ÅPKšÖUÊî¯Oj7ÆÝh¦»\l–š×Š „U¢¦xŠ!»EÚJSF0¯>D!q1`=š•¦`(Ϋ`«òãoŽå[¼¶õ’Í%,f²ª²u­ª^Ær½‹‰e"4‡�³pòidE£f²tÞª¯xÍ&âÚûÒÝ+«ÿ�ôBK$õ3‰aW­ž¨Ó5CU%=2 ¿¨\ Bh›. |EîËpV-VØÿ�Î~÷ T„×_ç–’„¢Ô4°f^÷c"ŽÔ.Ä£JØ,¬DdìT¬+šRÞvìõ“Ô׆ŽÅ,®ÂH³Ý;d<Žª£X¯ÎÄ��^ìj¥F ûú-Uw=e:–P)»AÂö)‡<žH÷Ç’@ûê³ØMÎÊÓ w ¼WåUB¿ãóú{cQ:Š”VMrU.cÆ|–ð>£œþ¼jPÛÞÊ#R.²UFCw€Ù$8éõÑcåQ—_T•Ü$'Û œ;LíïðκE¦ŒÀ.3íõýtä^éH²É&î9ùØ‘€9àþ\hvѯÂfPÿ�³ìˆ‘ÇoÛB[Q‘”²v¨$ã.pt%ÃBœ»uœ|ãè2/ã¢{ ÈöR×_£~T~Ñ_Ž£s¤D©@7Y�ã<ýðÿ�ÃHÄYM}P­é‘‘/g¹‡~ZkÆŠ3u‚,m‚0|r<ý?ùhœ\tBÐî_³R¨P<�?ž˜DBP"nrC«/wÔÁ×ÑWy›¯½Î£8ùsÇ éËD@L4ºüf é#¸LïM™+êÆ·Ðàžá§=èÂú^!’8ýþG×B)’R0,GƒœcÀÒ¸Bb.±Gí$³ à€9ûûèÜ$÷$‘‹ww$yãÆ™–² Ý~RÇ!‡*r×DAL@AÈ ©c)cï<jV;’LÊÙ8ì$$}øÑ°FŠ'4ì•€àôÔ¥Ò%W:!Ôóï2˜´oÚ~nŒçO$”£šPI4lä4j§8ù“Ø9òÔNº•† °ìÓ@• èS¹C)2DBç>ääãü_7ñÆ«Ô2ÛÙ^¤á2Un–É#JI©¯4¬§'½KžWŒ~è }üäôæ§fI1úûóZ%íÑÓ*/p±Êa Tªƒ¶1݆<~÷pl yル {µn¾öüªu²› ª2ä¼pȱƒæsÉ'ïùêÕ:€kªÎ¬Ó²“Økéi¦bZ®Ö(g*ÙeàáHÀÎGç©MÈT* h¶+bnTª…j+èå­—¾@Õq²ú¸åùÈÁðUqà1ÔNo¿ºÍÄ0ûº²(ö™. 4°¹ùYbŠ�r];˜ûdû¡sˆ1ùP»(7÷õ\ãñʆúãTrŽ‹«l½òœad#9óãHÀÕ8™YbSË {ûiœè6H)@H?¼‡?AãNãÙ(Ýf§r0>ÚNqÕ<J%^ £çNtÄòHit—<)AúgøçB@&QtKSÏ«Çä?¦™×ƒN{×Ñ:‚A™r~çü´›3™!¬½¥¹÷ÆõÓÛR˜¼è¾z½ÌK#cé¢.„ÂÚ¯Âl±ݘã:a§˜ O[‚ó9'>xý4YI ’‹! á_´ó‚ré¢�^”¡i HUúãNÐ�)D”·r‚Kz¬??òÓ˜Ô¦ J¤±©P$�ž¶–·OÐ#Vx'ñÌ0@–úçéöãE½NT’Û<%d•Z…ã‚&qJîD„deF>làw``œœ£ QƒÍl7N·íM¢J’Y6Üu0ÏY<VñYZ‘ÂŽýÊ„žI$ ªŸ•€Á˜ŽJê/A÷­E.ß·m©¡ôMeKÍEM €fvbY#B’œp=E’pý¬Ž: `rO5 lÝu7¥ÝU³¤vKõe=Žé<i Ô4Õÿ�sM !=9' UT4q‡ü:@¦EϬÌ JF¨!m³_k†Ð½EG¸ïÔ¡$ªôÖ¦ë5öøåÄa¡Žâ]ÝÊB²/Ús—hHΟˆªî´ßm·5¼ïýѸçX}$­½Ó‘4}Ñ„§¡gK<QvõRQÚ˜ ¡d,}PƒÍq?ªÛ~õ¸ép§ªŸ}À¿‡ô¯"”UÆf.B,••¬¯+'Ì£ÒPŠB˜ˆæ#•LÃu¥Û’IèûÚã{[µÍçzŠ¢þ¡OU?7Êu�®¬ü•εG‰îWª¸–YÛÉMM#¿- {ü<ýru¢Bµm6C¹N#dŠEÆA^ òqîûþ}^ARL›„ “BCe¢`<p8úqõÑ™BH„ “FQ‚HÝÞT(þ|„¨ý‚ €Ã|¸aÏ åï£7²†`¤‹²œ½ÇéÇ:c·$…ÊÀ2/qnòO¹Ï®€rD9¬D±’{U€áxñ§)ó/¦`¿¸„qᘎtŒjžcEƒKÚHXÛæäžìç¡Èu)ó€¿5L„’]=Éÿ�_ôЋy—ß[¤,y>On�}'4溔 'ŸÔ|«œ |¿_ÏG–Èðl2vä‚¡x?};@²cÍ`ó8ùˆaž1ç:1K‰Õ Ü\ào'ÇQëªû¿àöÆr2F„ó%02ˆõ$ RAÇý~Zc§~5R/8ÏØòž‘h‰EY`jÝS*½‡>Íå¦ìÂEå"ÕE›ýÑÆ2yÏ?A¢ Á1}×ÏÇ7ÌZ5 x$gNhS6¢M«2¶x'$Ÿ·až‰œëICµA ŽÙ÷#œ~¿¦‹(I¯óHy\¹#:’¥17MÕ·{.Q¹öç?}mûÔqÑdŽà1ÀjVÌIQ,;€÷Àò=ôa¢جՂ‘ó/?M4ìRÓ3¡Æ* °9÷ú{süôŽ’¢E5³WÓA$2UÓ:Ç‘Ù?i|ðœ~_O¾«Ôlˆj·IÂn¤õt©$N¯ r®êYâÆ2B¨ü°0|5˜ä¥cÚ êœîSÛZ =u",Ÿ¾¤7q‘†Ç¿ü'Æ|jy‰µúJƒ=4.`LÇÃæóà|qùêóOŸ’Ì|‰R U Àö´F©S8! OäsŸçü5$õUàÕ©oš×o5RÃ]"L‰Ø±z$¯scá°žr<ŒñãA}Õ{À Y>Ñ^6ÝJK5|¶XªYùPž>Qç Œýx—¹c˜èŠGÜ-Z‡ÿ�WùdRýÁmŸÖ-à~gR?d~ýVMþ/Ëüõ36Q¿E”^òÔ5?AñF‡÷—ôþš1¯—Õ8_÷†¢©£{Ó”¿º¿¯ôÓsHj”oÜ_ûÚ’®JGþïõ…ú©‰?¹¥CW!zGÿ�Vÿ�û_óÑQßÞÈÊZOÞ‡ôþšgþ‘ÜPJÁ?xjjš55=JZ?Üó?×Q·ô{¤5Y7ïOÿ�t–¥§þì÷¤ä)ó'ëý4Õ?Btéÿ�«—ý{hêjŸdä¿î?Aýu#?Z&l¬)?ô;ÿ�ƒÏÿ�ìt{ª»cIÿ�ÖÖ?ý´¿üqè›§á5µ[Sÿ�±{‡ÿ�×7ÿ�%›BýüSUýCÁzeø~ÿ�ë½ÿ�ööÿ�&m\oèPÔ×Çÿ�¶Ô¿ÿ�]]ÿ�g£nž 'íàµû~ÿ�úºÿ�ú‚§ÿ�Ê ÒÄh;Ô‹—Û·ÿ�ÑgþÞ§ÿ�Š·P?ôcõ®9Üÿ�ôZ¿ý›ûUÔu4÷Õ[vŠtÿ�zÿ�û?ÿ�wBuð gèP³~ôÿ�÷ô»÷Í ¿Rù'îîÿ�ÓLÍOz•û&™?zoÈQ¦­¢ºÿ�sõLÑDVÿ�¸ûßå«t[,éü7úÿ�¢~¥!ªÊ_?¨ÒfÊÉMÏå1¦¥FÍÏñû£NíŠA"ß¼ßëÜiö_ºü?Ý?ëýN€j;Ô¬ý)I?x¯m -<R)?dÿ�¿ÿ�M#¯š©I7î·ýïóÑÓý!'èR‹â_ûÇüõ5=P7uñ¼ÇùŸé¡f¾ù§þåøþïè?¡ÓSýÞê1ºÀÿ�‡ÿ�f?®–ã½ü¡ÛÁüÿ�ë¤  ãÿ�t]#©GºüŸº?ï驪h„ì¿MçôÕS¡ïRqÿ�»ýÌêr˜¦ÏñKÿ�tÿ�]LJŒèƒó:’—ë*úþ?ÏAE —Ááÿ�3«NÕG²Q</éþz"“t)xõ—ùhö¥SB¥ÔîŸõÿ�á:®5 å?ÒT–_÷íÿ�´_꺯‚²tÿ�d•Ûýãí%ÿ�-FÝÒ­©ïM×ýƒó?þ¾JM–‹÷©$þº´t*…E,¿ÿ�õ%_ÿ�‚ÿ�ž„êP?Pª×ÿ�§Òø$?ü:…Êp¿ÿÙ����������������������������������Plack-1.0030/share/face.jpg�������������������������������������������������������������������������000644 �000765 �000024 �00000011211 12244057435 016275� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿØÿà�JFIF��H�H��ÿÛ�C�   %# , #&')*)-0-(0%()(ÿÛ�C   (((((((((((((((((((((((((((((((((((((((((((((((((((ÿÀ��z�–"�ÿÄ������������� ÿÄ�>�����!1AQ"aq‘2B¡Á#ð3Rr‚±$Cb¢ÂáñÿÄ��������������ÿÄ�(��������!1A"Q2#aÑÿÚ� ��?�¾w¤Öרl*IH¤\EEqŸ—I–Í>St™F*#d£‡]•M.*K1\q~”¥$’{T¤”q6¼s‡¾(ßÑÞ¨œ¸êA8èÿ�• u«&‰²Væ}>¢è7ÍCé«zfÜ[KÉ HÁ"šK|¸Ê9Q@çVÿ� ·.-‚·'–Ô»æãÑoOÓYZfÔÌ[{im�7ÀçOæZëd”æŸA—o¶G Ê”Àÿ�U/üvزPԆמƲ=¼òj)óÀ5ÔzLId©õŽt'»Á§g$)Gð·­6óÌ©%I�¤Ðë^o0¶Õ9v=éôY*Þ2µ«=ƒ(sa\À*HCéÜœàçµ(¶aÔ½Edz€Iõ$÷ª¥ÆÌHR˜B’y¸¯±/Òbåú‡sÌ|ëYKr2¥NxIâºÝ-Z5;À¨�–e¸p{p¯¿ÇŸ|ó£ª2 õùð›ëKZV¢°°sžµ©| ñ"5þ vi². # äæ$tã·Æ»�Iy •ÕðŠûPÕÕÕÕÇå'4‹ˆôz´Ò*EE'`i%&pzFi%§$Žœëqb<ûêám´)j'°Þ³6³¼9s’·Iâ^qžC ù 6x»9Q´ÒØi@)õ�¿ò Ï×aYÆSÞcÀ«¹¨äó!Ü7œòÅ<'·«økk*á+ÜPfAÊP‘Ìš(Û/rm6FY‚Ú×%i H@åïUµ-íIt±M¶Â]ÛN åNÊ\ÊT¡LáÛR€mìoC+½Sθ­%oÉm|*K¥e Ü Œö9Tð÷GÝØyá"J·ðúxq)'¸WíU' /ì[®y}K¿ï̧ÔSÌÐ÷S5Å=ÆŠHPÎGlQ£I[Ñ9ޝVõ â.”jIUÁ†Ç’PðOPFÊ•²[w¢UÉY±™vÿ�¶´ë̱rJy¸zmÞªÒÃo†–¦”Éy<h ä¡íZÏ ­¡*û£kB1øGaPú‡Ãx¯F ˆÐBÛHÅZ†¢¸¥s¦r|³?ÈR£9Œ“R6kœ›}Â<ë{«*:Òãn à¡C‘þ¹ÔîªÒs EYu•z6*• G‹„Ç O:½\ÔÖQBÊÜúá–¤þÖh›eÝA)yä¼”òK‰%*QúÕª‚eeÝ:!Rˆb_Aè‘ûƒFú&!öuuuuA�éb‘Xåb‘X£` ±Ì{ÒKM8PõI¸žêž8NŠã#ññ¡£ì*?úÐ+u/~Cj(øý)HÔâ(#„¥.‘þ?j Ò£Øì(Ç¢KNÀrí¨bCi%EKåŠÕúKÃä1§dy€mA³}¹¹ÚÑÙ ÃyNzkbDZCc íT/ùÏkð^©¸W¹y+±ôÌvpJUÔâ¥Ym˜mÕ%&JÑ� ¥Þï Š—uD!;$ÄT}…t(•’P©eƒ•‹ä+qÔÌÙŸL7^m/½¸IPûõSÔž'E²Æã¸ÈB+�ïÅš¥ßá[¯WátºCC®62“)å(§|úP‚¹$ÒŽ^,’O›d?Aâ Sp翨VåŽj%¾Iº <rì(˜CÐÝK‘ÞHRprzTé†ÂÈX 'P†.¯‡  ˜Q[m)Ø%(JÐ yÄŽº<•2€6-:ÁCqô4/ñ›×’%ܼ]Ec2 í©¤)*#ëê{:¬Ú’á y.q£Ý'qZ¥Ís î€Ü8¢¯8Q +F:î9|èã:b¹¨!]mÏ6üiM©²´¸’sƒØàÕ% Ôi'þXá}ø+ê”r>ÈïæÝ|dò i@ç¸VØùV†–þÊ÷Y¼Ü"%?È} VäSÿ�\V¤ÆgK³íuuu@%iÞ’Z}©âÓMÖñLbƪOª¼)§ AØû×’ŸjŒ¥ãëªwÄÚôØm#·SûÐÝJáGh«ãå¼ÅÕ¿x¿œž.#Ôÿ�X¡”hÅõì ÉÆh é³i O¸8yå)ý+PF•ü¡YKìþ÷ ¸DVÅHC€bAÿ�qZZ#À`ÈVV¡¸ÜÍJ0ëCcûœu0Ԁ̅þn"ßèC¨5÷…-±ÉYçñ§~&^R/o4 ‚ñ‘× ¡ÝÅÕ#!^’{í^÷Óª«G§Ž?³Y`Y?y:ø ¬qœ¹Ôxœ¥uçRþèÁ«dÊvdÂÌHê !¿ÄµŸ ÛëRZ·A½§c^·…p8Uºš= =R{ãn´™z¢•ÞÓ`ûrqÜWÓ%X²$(,àÓ~64’ÕÃÌÖš›B^Iè÷Ž qœ÷ÏZBïp‡pŒ¨NÅgîË ”«¢ˆþ¶ªÛòJTFi¸’¢sJ·T¤œ ³Cx Y0ÐÃ:âK©ãFÄ€ÚO¨tþ¹ÑüVOû>] öåHCóp’+X óZºã]˜L­5†}®®®ª ÷H-4õIÊA¤T‘šhE§ÒkÉFiÊÓµ$9T`€Oã~—7È×¼¾ÊŽàsPa:bUœ˜’š!×P‡Û$`FGéš×O²‡R´‚qTýe£yòdEp32:BQ‘”© ç…¬r22ðÌסnbÏâ„GxZ}e•㲆߮+Pµ,Ìk%ølvË«¥²xšq§’zŽ Š5imX‹ÅŽ<Ĩy¼<.¤±Ì~ÿ�YšêÞTѧ¤’kiIñ.B¥ê© °•8âÝáJ�É'µìw ³Ä·jÔ3 ç0Ôy¥-º•( ¾ @V;óïšVÕ£äÿ�Ä[-ÚlOùI!o¤œc)Oæ9‚*×ãm™©úab(Ql‡ýäS¾vøU¿Vœ5p® øX~WðŠM䂱5;Lê$·=ˆZe,pÌ…,¥.òO˜”ò$m¾Ü·¢5Ùq$B1œB^K©)RHÎAý«8X¯—T`˜ò ðÖœþb8Hä3¸úÑGëú.ðTÛç2ã•gªz먥jtÍ47]†¾×Ÿ÷×$Áž¸Óïé‹¡d¤˜’¨ÎÝP}Çê>uO’ùI=«UÞìñ/öw`ÜY4±×šHäAèGzj]j·Þ·›Â­ï㉴Ïo-:TºŸ}ˆ#jÔÒúÒPUÝœ¯8È›*ÏÉùÎnÙlž�ëHÊjTW’‰L-•)!I ˆwq»èË•ˆC“%ÈŽÄqж\*ÉÁ òå·:µ]¬ Õº}Ÿ!ÖšŸ(+lžDcúRåêµNÄë–brÓ9Vååþ2©^ XÛ (J$y¤Ï…*8ùÖÉO!Y“ìå§§D×s>›0Pp«!jÀN\ŒàŽ•¦Å7U59,<ðgYÞµÕÕÕTYYǧáI©4á#eÆ“R{Ó°,j¡I„ó즑)õª Á¯ ©Â“L®o°}C)id|rYxGc&^ûC@QÕk•û­¡)�s"«^YusVôvÔÌ% º—ÊÇB”ó$Q^÷3øüöå„>ŸJoŸ*ñk¾ ¤Š¤>ëÅgÊuJPá@;dmÐâ¶?‡ÙS²î^:_ôѦ­­naÅå¶? å‚;õHÇéÎ¥ZòdÃ,8¤¨b˜B γ–—¹q84ÊÐùgŠ3êËÌOQÐý+ÇJYye§ðüXÒh뙓µ*Í%yB†á•ŸÈ{ÓéT›.­rÅz9€¥ ’ŸÌƒÏç×åZ¯T=o¸[—jÃà BÀ"€>&ÙnÖ6~÷d-®Ö”2 Ú÷÷OûV„}VÉÕúÍ'ž2ÞpÇÌ'o#¦ÜÛ‘ÀqJOOBª¦¢Šiiu Ok.GQ8º¤û_CC]p¸¼ÃŸCŠŒNZyCõ !X祷ПÂIéY“ŒèžSå"ã8õØ“öáøna>è\†d'Õ”¶sž{l~¦¡´í¼2ûJ<+Á«½Òsõ$åž{»Ã…ÃqÂp÷á?µPtÝ’çmXtŒë+kÓ…£�|ù^ž—‰Ùd“ryÇX"q–ÅѪ¼'•÷»TÅ­(FJF Û?­^T´¡9Q�w&ƒ~=64'S>ëÄYòýxïÆ@ÆüÁ‰5¿,-r Ê·½œ=à•¶£þ$¨7öù޵«¦y­Z¨âÖ)7UX º[•uˆÛƒšK€‘òÕ ¨Q”r¶šW²’>µÔþ Ä`ÍXù×Å Q[=ñÊSÅ”šMiÂÅ9"¼8“€{P´pÝI¨ÍAn7K<¸It²§›)Kƒòž„ûwö©æ£8öÈNÝÏ*ñqvßgŽ§ç¾Ø Ü—”üs]ÓXìž¹2µÁ¹Vɲ"I© ,¡iÏ/íûT<•àdó=kBø™¤£jË;«sÌ3qàÃ|^„>މ$ò=‰ï†x¹°üINF˜ÓŒ>Ñá[n' I÷é´úèê!†þK²äl܃χ”MÓÉ’âJÙ>C¸Û HØŸˆÁ¨]K¨W&äãöåð©#gñ{PïE]¶J˜ÐR~ã!±æ‚ •ô$þU¶D6„IˆCº8†v=Aë^/ÕtrÓÜøø¾Q¡DÓY}íÝvJÖ$9œú£¸ùU–Õ·Øòž!IPÁªsã!ç8ѳƒ¶ÄW7ä4BÉZ¬½™\r-'OÆÂÉámGðŽŸ ~À»zÒ®2¤¤ìªb¸4⸠ç_oÂÑÁæà²*2úd<’p'%¶›R•¾0~5+!qæ!fBÓ¸Z*•lPW˜‡UÆNàž†ž3(¥|G#j„žpt¾Ë&Ÿ¿ V¤ƒ;ðÑ*jË ™DàgrBF ;cùÑp3vZ8]\ÔÔbøq,¨5E“nm µÚ¤J¹¸K±ÄF¸ÖIܲ�@Æ3“¶Í8Ñq®º†Üë×Û½ò Á—”Û›q¦ƒ ~Ò“Å‘¾IÞ¶é¯ehȺíÓx-J°¸áË—›ÁöL„ â‘]H#I°®×õ«$ÝßäÔ[Pì¾ÇKüiÍ)ÃI;Éæ§h¬$PIØRñã%EJw„¥;c=}ëÜoï~U¨ kIêu6J S±ÊçK›ÇEq’&óx¿ÝÜ ic1<*¹ÉÝ'Ý´ Ô=ÎƽY4 %o’½OJ¸Ã“Ä”ûP=#éA ^q«Þ–-8´Þqð¨1!]DZ­3iÝ¢zÑ5¶<œ³ÝÉôĈ¥†|õŸJÅIÚ„~([dk ,ç£éGY¹[›*rL‡RÒý#*m¾ù£‰ÂwØÕÞ3®;âT–œZ–ÓP›BŽB 8$†­KH%À@!IÁs ‹u½Ë±™f(N˜»Ì·ËŸe"d8îyn–F\o (q z€Á÷Üí%«²;÷ §­å\ˆõG'™ŸqÓ˜¢ß„µâ5ý¶ÉCj³ÃqINÀ¨l ñ¶jõ¬ívùvIKƒ÷BNë)R¾¤UÉÙû0pµdl/”Y@™kndÊŽR° ‡Q¾Gî*ÈImIp†Ö;õ¯:áÒ÷6‹‹-51ä6Œì„‚6 ¤ïäù<ëÌY ³qú5«žå–FÊx6Tq9Ï:I¹iu¹8óz,u’s’MxWJˆ%/ŠÜ¤•u«5¾~bTrAÁªSÜ…´Îí5ö™üyA¥Ãj“16Ø-Ãi—¼¬!ä­e*àÆÅ;sÎÖª·¤ê«á>¶¦Õ->T‡¢ÉJÝ~‚œœ³ñ«–0H©=P³<HŒí[K‘Y155¨Éàóü Ò êíÙÔ׆º¥`’b2IÉàOûWSDíGÿÙ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/script/plackup�������������������������������������������������������������������������000755 �000765 �000024 �00000015532 12244057435 016476� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!perl use strict; use Plack::Runner; my $runner = Plack::Runner->new; $runner->parse_options(@ARGV); $runner->run; __END__ =head1 NAME plackup - Run PSGI application with Plack servers =head1 SYNOPSIS # read your app from app.psgi file plackup # choose .psgi file from ARGV[0] (or with -a option) plackup hello.psgi # switch server implementation with --server (or -s) plackup --server HTTP::Server::Simple --port 9090 --host 127.0.0.1 test.psgi # use UNIX socket to run FCGI daemon plackup -s FCGI --listen /tmp/fcgi.sock myapp.psgi # launch FCGI external server on port 9090 plackup -s FCGI --port 9090 =head1 DESCRIPTION plackup is a command line utility to run PSGI applications from the command line. plackup automatically figures out the environment it is run in, and runs your application in that environment. FastCGI, CGI, AnyEvent and others can all be detected. See L<Plack::Loader> for the authoritative list. C<plackup> assumes you have an C<app.psgi> script in your current directory. The last statement of C<app.psgi> should be a code reference that is a PSGI application: #!/usr/bin/perl use MyApp; my $application = MyApp->new; my $app = sub { $application->run_psgi(@_) }; =head1 ARGUMENTS =over 4 =item .psgi plackup --host 127.0.0.1 --port 9090 /path/to/app.psgi The first non-option argument is used as a C<.psgi> file path. You can also set this path with C<-a> or C<--app>. If omitted, the default file path is C<app.psgi> in the current directory. =back =head1 OPTIONS =over 4 =item -a, --app Specifies the full path to a C<.psgi> script. You may alternately provide this path as the first argument to C<plackup>. =item -e Evaluates the given perl code as a PSGI app, much like perl's C<-e> option: plackup -e 'sub { my $env = shift; return [ ... ] }' It is also handy when you want to run a custom application like Plack::App::*. plackup -MPlack::App::File -e 'Plack::App::File->new(...)->to_app' You can also specify C<-e> option with C<.psgi> file path to wrap the application with middleware configuration from the command line. You can also use L<Plack::Builder> DSL syntax inside C<-e> code. For example: plackup -e 'enable "Auth::Basic", authenticator => ...;' myapp.psgi is equivalent to the PSGI application: use Plack::Builder; use Plack::Util; builder { enable "Auth::Basic", authenticator => ...; Plack::Util::load_psgi("myapp.psgi"); }; Note that when you use C<-e> option to enable middleware, plackup doesn't assume the implicit C<app.psgi> path. You must either pass the path to your C<.psgi> file in the command line arguments or load the application inside C<-e> after the C<enable>. plackup # Runs app.psgi plackup -e 'enable "Foo"' # Doesn't work! plackup -e 'enable "Foo"' app.psgi # Works plackup -e 'enable "Foo"; sub { ... }' # Works =item -o, --host Binds to a TCP interface. Defaults to undef, which lets most server backends bind to the any (*) interface. This option is only valid for servers which support TCP sockets. =item -p, --port Binds to a TCP port. Defaults to 5000. This option is only valid for servers which support TCP sockets. =item -s, --server, the C<PLACK_SERVER> environment variable Selects a specific server implementation to run on. When provided, the C<-s> or C<--server> flag will be preferred over the environment variable. If no option is given, plackup will try to detect the I<best> server implementation based on the environment variables as well as modules loaded by your application in C<%INC>. See L<Plack::Loader> for details. =item -S, --socket Listens on a UNIX domain socket path. Defaults to undef. This option is only valid for servers which support UNIX sockets. =item -l, --listen Listens on one or more addresses, whether "HOST:PORT", ":PORT", or "PATH" (without colons). You may use this option multiple times to listen on multiple addresses, but the server will decide whether it supports multiple interfaces. =item -D, --daemonize Makes the process run in the background. It's up to the backend server/handler implementation whether this option is respected or not. =item -I Specifies Perl library include paths, like C<perl>'s -I option. You may add multiple paths by using this option multiple times. =item -M Loads the named modules before loading the app's code. You may load multiple modules by using this option multiple times. =item -E, --env, the C<PLACK_ENV> environment variable. Specifies the environment option. Setting this value with C<-E> or C<--env> also writes to the C<PLACK_ENV> environment variable. This allows applications or frameworks to tell which environment setting the application is running on. # These two are the same plackup -E deployment env PLACK_ENV=deployment plackup Common values are C<development>, C<deployment>, and C<test>. The default value is C<development>, which causes C<plackup> to load the middleware components: I<AccessLog>, I<StackTrace>, and I<Lint> unless C<--no-default-middleware> is set. =item --no-default-middleware This prevents loading the default middleware stack even when Plack environment (i.e. C<-E> or C<PLACK_ENV>) is set to C<development>. =item -r, --reload Makes plackup restart the server whenever a file in your development directory changes. This option by default watches the C<lib> directory and the base directory where I<.psgi> file is located. Use C<-R> to watch other directories. Reloading will delay the compilation of your application. Automatic server detection (see C<-s> above) may not behave as you expect, if plackup needs to scan your application for the modules it uses. Avoid problems by specifying C<-s> explicitly when using C<-r> or C<-R>. =item -R, --Reload Makes plackup restart the server whenever a file in any of the given directories changes. C<-R> and C<--Reload> take a comma-separated list of paths: plackup -R /path/to/project/lib,/path/to/project/templates =item -L, --loader Specifies the server loading subclass that implements how to run the server. Available options are I<Plack::Loader> (default), I<Restarter> (automatically set when C<-r> or C<-R> is used), I<Delayed>, and I<Shotgun>. See L<Plack::Loader::Delayed> and L<Plack::Loader::Shotgun> for more details. =item --access-log Specifies the pathname of a file where the access log should be written. By default, in the development environment access logs will go to STDERR. =item --path Specify the root path of your app (C<SCRIPT_NAME> in PSGI env) to run. The following two commands are roughly the same. plackup --path /foo app.psgi plackup -e 'mount "/foo" => Plack::Util::load_psgi("app.psgi")' =back Other options that starts with C<--> are passed through to the backend server. See each Plack::Handler backend's documentation for more details on their available options. =head1 SEE ALSO L<Plack::Runner> L<Plack::Loader> =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/HTTP/������������������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 015124� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/�����������������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 015377� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack.pm���������������������������������������������������������������������������000644 �000765 �000024 �00000014427 12244057435 015745� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack; use strict; use warnings; use 5.008_001; our $VERSION = '1.0030'; 1; __END__ =head1 NAME Plack - Perl Superglue for Web frameworks and Web Servers (PSGI toolkit) =head1 DESCRIPTION Plack is a set of tools for using the PSGI stack. It contains middleware components, a reference server and utilities for Web application frameworks. Plack is like Ruby's Rack or Python's Paste for WSGI. See L<PSGI> for the PSGI specification and L<PSGI::FAQ> to know what PSGI and Plack are and why we need them. =head1 MODULES AND UTILITIES =head2 Plack::Handler L<Plack::Handler> and its subclasses contains adapters for web servers. We have adapters for the built-in standalone web server L<HTTP::Server::PSGI>, L<CGI|Plack::Handler::CGI>, L<FCGI|Plack::Handler::FCGI>, L<Apache1|Plack::Handler::Apache1>, L<Apache2|Plack::Handler::Apache2> and L<HTTP::Server::Simple|Plack::Handler::HTTP::Server::Simple> included in the core Plack distribution. There are also many HTTP server implementations on CPAN that have Plack handlers. See L<Plack::Handler> when writing your own adapters. =head2 Plack::Loader L<Plack::Loader> is a loader to load one L<Plack::Handler> adapter and run a PSGI application code reference with it. =head2 Plack::Util L<Plack::Util> contains a lot of utility functions for server implementors as well as middleware authors. =head2 .psgi files A PSGI application is a code reference but it's not easy to pass code reference via the command line or configuration files, so Plack uses a convention that you need a file named C<app.psgi> or similar, which would be loaded (via perl's core function C<do>) to return the PSGI application code reference. # Hello.psgi my $app = sub { my $env = shift; # ... return [ $status, $headers, $body ]; }; If you use a web framework, chances are that they provide a helper utility to automatically generate these C<.psgi> files for you, such as: # MyApp.psgi use MyApp; my $app = sub { MyApp->run_psgi(@_) }; It's important that the return value of C<.psgi> file is the code reference. See C<eg/dot-psgi> directory for more examples of C<.psgi> files. =head2 plackup, Plack::Runner L<plackup> is a command line launcher to run PSGI applications from command line using L<Plack::Loader> to load PSGI backends. It can be used to run standalone servers and FastCGI daemon processes. Other server backends like Apache2 needs a separate configuration but C<.psgi> application file can still be the same. If you want to write your own frontend that replaces, or adds functionalities to L<plackup>, take a look at the L<Plack::Runner> module. =head2 Plack::Middleware PSGI middleware is a PSGI application that wraps an existing PSGI application and plays both side of application and servers. From the servers the wrapped code reference still looks like and behaves exactly the same as PSGI applications. L<Plack::Middleware> gives you an easy way to wrap PSGI applications with a clean API, and compatibility with L<Plack::Builder> DSL. =head2 Plack::Builder L<Plack::Builder> gives you a DSL that you can enable Middleware in C<.psgi> files to wrap existent PSGI applications. =head2 Plack::Request, Plack::Response L<Plack::Request> gives you a nice wrapper API around PSGI C<$env> hash to get headers, cookies and query parameters much like L<Apache::Request> in mod_perl. L<Plack::Response> does the same to construct the response array reference. =head2 Plack::Test L<Plack::Test> is a unified interface to test your PSGI application using standard L<HTTP::Request> and L<HTTP::Response> pair with simple callbacks. =head2 Plack::Test::Suite L<Plack::Test::Suite> is a test suite to test a new PSGI server backend. =head1 CONTRIBUTING =head2 Patches and Bug Fixes Small patches and bug fixes can be either submitted via nopaste on IRC L<irc://irc.perl.org/#plack> or L<the github issue tracker|http://github.com/plack/Plack/issues>. Forking on L<github|http://github.com/plack/Plack> is another good way if you intend to make larger fixes. See also L<http://contributing.appspot.com/plack> when you think this document is terribly outdated. =head2 Module Namespaces Modules added to the Plack:: sub-namespaces should be reasonably generic components which are useful as building blocks and not just simply using Plack. Middleware authors are free to use the Plack::Middleware:: namespace for their middleware components. Middleware must be written in the pipeline style such that they can chained together with other middleware components. The Plack::Middleware:: modules in the core distribution are good examples of such modules. It is recommended that you inherit from L<Plack::Middleware> for these types of modules. Not all middleware components are wrappers, but instead are more like endpoints in a middleware chain. These types of components should use the Plack::App:: namespace. Again, look in the core modules to see excellent examples of these (L<Plack::App::File>, L<Plack::App::Directory>, etc.). It is recommended that you inherit from L<Plack::Component> for these types of modules. B<DO NOT USE> Plack:: namespace to build a new web application or a framework. It's like naming your application under CGI:: namespace if it's supposed to run on CGI and that is a really bad choice and would confuse people badly. =head1 AUTHOR Tatsuhiko Miyagawa =head1 COPYRIGHT The following copyright notice applies to all the files provided in this distribution, including binary files, unless explicitly noted otherwise. Copyright 2009-2013 Tatsuhiko Miyagawa =head1 CORE DEVELOPERS Tatsuhiko Miyagawa (miyagawa) Tokuhiro Matsuno (tokuhirom) Jesse Luehrs (doy) Tomas Doran (bobtfish) Graham Knop (haarg) =head1 CONTRIBUTORS Yuval Kogman (nothingmuch) Kazuhiro Osawa (Yappo) Kazuho Oku Florian Ragwitz (rafl) Chia-liang Kao (clkao) Masahiro Honma (hiratara) Daisuke Murase (typester) John Beppu Matt S Trout (mst) Shawn M Moore (Sartak) Stevan Little Hans Dieter Pearcey (confound) mala Mark Stosberg Aaron Trevena =head1 SEE ALSO The L<PSGI> specification upon which Plack is based. L<http://plackperl.org/> The Plack wiki: L<https://github.com/plack/Plack/wiki> The Plack FAQ: L<https://github.com/plack/Plack/wiki/Faq> =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/App/�������������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 016117� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Builder.pm�������������������������������������������������������������������000644 �000765 �000024 �00000020526 12244057435 017330� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Builder; use strict; use parent qw( Exporter ); our @EXPORT = qw( builder add enable enable_if mount ); use Carp (); use Plack::App::URLMap; use Plack::Middleware::Conditional; # TODO delayed load? use Scalar::Util (); sub new { my $class = shift; bless { middlewares => [ ] }, $class; } sub add_middleware { my($self, $mw, @args) = @_; if (ref $mw ne 'CODE') { my $mw_class = Plack::Util::load_class($mw, 'Plack::Middleware'); $mw = sub { $mw_class->wrap($_[0], @args) }; } push @{$self->{middlewares}}, $mw; } sub add_middleware_if { my($self, $cond, $mw, @args) = @_; if (ref $mw ne 'CODE') { my $mw_class = Plack::Util::load_class($mw, 'Plack::Middleware'); $mw = sub { $mw_class->wrap($_[0], @args) }; } push @{$self->{middlewares}}, sub { Plack::Middleware::Conditional->wrap($_[0], condition => $cond, builder => $mw); }; } # do you want remove_middleware() etc.? sub _mount { my ($self, $location, $app) = @_; if (!$self->{_urlmap}) { $self->{_urlmap} = Plack::App::URLMap->new; } $self->{_urlmap}->map($location => $app); $self->{_urlmap}; # for backward compat. } sub to_app { my($self, $app) = @_; if ($app) { $self->wrap($app); } elsif ($self->{_urlmap}) { $self->{_urlmap} = $self->{_urlmap}->to_app if Scalar::Util::blessed($self->{_urlmap}); $self->wrap($self->{_urlmap}); } else { Carp::croak("to_app() is called without mount(). No application to build."); } } sub wrap { my($self, $app) = @_; if ($self->{_urlmap} && $app ne $self->{_urlmap}) { Carp::carp("WARNING: wrap() and mount() can't be used altogether in Plack::Builder.\n" . "WARNING: This causes all previous mount() mappings to be ignored."); } for my $mw (reverse @{$self->{middlewares}}) { $app = $mw->($app); } $app; } # DSL goes here our $_add = our $_add_if = our $_mount = sub { Carp::croak("enable/mount should be called inside builder {} block"); }; sub enable { $_add->(@_) } sub enable_if(&$@) { $_add_if->(@_) } sub mount { my $self = shift; if (Scalar::Util::blessed($self)) { $self->_mount(@_); }else{ $_mount->($self, @_); } } sub builder(&) { my $block = shift; my $self = __PACKAGE__->new; my $mount_is_called; my $urlmap = Plack::App::URLMap->new; local $_mount = sub { $mount_is_called++; $urlmap->map(@_); $urlmap; }; local $_add = sub { $self->add_middleware(@_); }; local $_add_if = sub { $self->add_middleware_if(@_); }; my $app = $block->(); if ($mount_is_called) { if ($app ne $urlmap) { Carp::carp("WARNING: You used mount() in a builder block, but the last line (app) isn't using mount().\n" . "WARNING: This causes all mount() mappings to be ignored.\n"); } else { $app = $app->to_app; } } $app = $app->to_app if $app and Scalar::Util::blessed($app) and $app->can('to_app'); $self->to_app($app); } 1; __END__ =head1 NAME Plack::Builder - OO and DSL to enable Plack Middlewares =head1 SYNOPSIS # in .psgi use Plack::Builder; my $app = sub { ... }; builder { enable "Deflater"; enable "Session", store => "File"; enable "Debug", panels => [ qw(DBITrace Memory Timer) ]; enable "+My::Plack::Middleware"; $app; }; # use URLMap builder { mount "/foo" => builder { enable "Foo"; $app; }; mount "/bar" => $app2; mount "http://example.com/" => builder { $app3 }; }; # using OO interface my $builder = Plack::Builder->new; $builder->add_middleware('Foo', opt => 1); $builder->add_middleware('Bar'); $builder->wrap($app); =head1 DESCRIPTION Plack::Builder gives you a quick domain specific language (DSL) to wrap your application with L<Plack::Middleware> subclasses. The middleware you're trying to use should use L<Plack::Middleware> as a base class to use this DSL, inspired by Rack::Builder. Whenever you call C<enable> on any middleware, the middleware app is pushed to the stack inside the builder, and then reversed when it actually creates a wrapped application handler. C<"Plack::Middleware::"> is added as a prefix by default. So: builder { enable "Foo"; enable "Bar", opt => "val"; $app; }; is syntactically equal to: $app = Plack::Middleware::Bar->wrap($app, opt => "val"); $app = Plack::Middleware::Foo->wrap($app); In other words, you're supposed to C<enable> middleware from outer to inner. =head1 INLINE MIDDLEWARE Plack::Builder allows you to code middleware inline using a nested code reference. If the first argument to C<enable> is a code reference, it will be passed an C<$app> and should return another code reference which is a PSGI application that consumes C<$env> at runtime. So: builder { enable sub { my $app = shift; sub { my $env = shift; # do preprocessing my $res = $app->($env); # do postprocessing return $res; }; }; $app; }; is equal to: my $mw = sub { my $app = shift; sub { my $env = shift; $app->($env) }; }; $app = $mw->($app); =head1 URLMap support Plack::Builder has a native support for L<Plack::App::URLMap> via the C<mount> method. use Plack::Builder; my $app = builder { mount "/foo" => $app1; mount "/bar" => builder { enable "Foo"; $app2; }; }; See L<Plack::App::URLMap>'s C<map> method to see what they mean. With C<builder> you can't use C<map> as a DSL, for the obvious reason :) B<NOTE>: Once you use C<mount> in your builder code, you have to use C<mount> for all the paths, including the root path (C</>). You can't have the default app in the last line of C<builder> like: my $app = sub { my $env = shift; ... }; builder { mount "/foo" => sub { ... }; $app; # THIS DOESN'T WORK }; You'll get warnings saying that your mount configuration will be ignored. Instead you should use C<< mount "/" => ... >> in the last line to set the default fallback app. builder { mount "/foo" => sub { ... }; mount "/" => $app; } Note that the C<builder> DSL returns a whole new PSGI application, which means =over 4 =item * C<builder { ... }> should normally the last statement of a C<.psgi> file, because the return value of C<builder> is the application that is actually executed. =item * You can nest your C<builder> blocks, mixed with C<mount> statements (see L</"URLMap support"> above): builder { mount "/foo" => builder { mount "/bar" => $app; } } will locate the C<$app> under C</foo/bar>, since the inner C<builder> block puts it under C</bar> and it results in a new PSGI application which is located under C</foo> because of the outer C<builder> block. =back =head1 CONDITIONAL MIDDLEWARE SUPPORT You can use C<enable_if> to conditionally enable middleware based on the runtime environment. builder { enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' } 'StackTrace', force => 1; $app; }; See L<Plack::Middleware::Conditional> for details. =head1 OBJECT ORIENTED INTERFACE Object oriented interface supports the same functionality with the DSL version in a clearer interface, probably with more typing required. # With mount my $builder = Plack::Builder->new; $builder->add_middleware('Foo', opt => 1); $builder->mount('/foo' => $foo_app); $builder->mount('/' => $root_app); $builder->to_app; # Nested builders. Equivalent to: # builder { # mount '/foo' => builder { # enable 'Foo'; # $app; # }; # mount '/' => $app2; # }; my $builder_out = Plack::Builder->new; my $builder_in = Plack::Builder->new; $builder_in->add_middleware('Foo'); $builder_out->mount("/foo" => $builder_in->wrap($app)); $builder_out->mount("/" => $app2); $builder_out->to_app; # conditional. You can also directly use Plack::Middleware::Conditional my $builder = Plack::Builder->new; $builder->add_middleware_if(sub { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' }, 'StackTrace'); $builder->wrap($app); =head1 SEE ALSO L<Plack::Middleware> L<Plack::App::URLMap> L<Plack::Middleware::Conditional> =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Component.pm�����������������������������������������������������������������000644 �000765 �000024 �00000010226 12244057435 017700� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Component; use strict; use warnings; use Carp (); use Plack::Util; use overload '&{}' => \&to_app_auto, fallback => 1; sub new { my $proto = shift; my $class = ref $proto || $proto; my $self; if (@_ == 1 && ref $_[0] eq 'HASH') { $self = bless {%{$_[0]}}, $class; } else { $self = bless {@_}, $class; } $self; } sub to_app_auto { my $self = shift; if (($ENV{PLACK_ENV} || '') eq 'development') { my $class = ref($self); warn "WARNING: Automatically converting $class instance to a PSGI code reference. " . "If you see this warning for each request, you probably need to explicitly call " . "to_app() i.e. $class->new(...)->to_app in your PSGI file.\n"; } $self->to_app(@_); } # NOTE: # this is for back-compat only, # future modules should use # Plack::Util::Accessor directly # or their own favorite accessor # generator. # - SL sub mk_accessors { my $self = shift; Plack::Util::Accessor::mk_accessors( ref( $self ) || $self, @_ ) } sub prepare_app { return } sub to_app { my $self = shift; $self->prepare_app; return sub { $self->call(@_) }; } sub response_cb { my($self, $res, $cb) = @_; Plack::Util::response_cb($res, $cb); } 1; __END__ =head1 NAME Plack::Component - Base class for PSGI endpoints =head1 SYNOPSIS package Plack::App::Foo; use parent qw( Plack::Component ); sub call { my($self, $env) = @_; # Do something with $env my $res = ...; # create a response ... # return the response return $res; } =head1 DESCRIPTION Plack::Component is the base class shared between L<Plack::Middleware> and C<Plack::App::*> modules. If you are writing middleware, you should inherit from L<Plack::Middleware>, but if you are writing a Plack::App::* you should inherit from this directly. =head1 REQUIRED METHOD =over 4 =item call ($env) You are expected to implement a C<call> method in your component. This is where all the work gets done. It receives the PSGI C<$env> hash-ref as an argument and is expected to return a proper PSGI response value. =back =head1 METHODS =over 4 =item new (%opts | \%opts) The constructor accepts either a hash or a hashref and uses that to create the instance. It will call no other methods and simply return the instance that is created. =item prepare_app This method is called by C<to_app> and is meant as a hook to be used to prepare your component before it is packaged as a PSGI C<$app>. =item to_app This is the method used in several parts of the Plack infrastructure to convert your component into a PSGI C<$app>. You should not ever need to override this method; it is recommended to use C<prepare_app> and C<call> instead. =item response_cb This is a wrapper for C<response_cb> in L<Plack::Util>. See L<Plack::Middleware/RESPONSE CALLBACK> for details. =back =head1 OBJECT LIFECYCLE Objects for the derived classes (Plack::App::* or Plack::Middleware::*) are created at the PSGI application compile phase using C<new>, C<prepare_app> and C<to_app>, and the created object persists during the web server lifecycle, unless it is running on the non-persistent environment like CGI. C<call> is invoked against the same object whenever a new request comes in. You can check if it is running in a persistent environment by checking C<psgi.run_once> key in the C<$env> being true (non-persistent) or false (persistent), but it is best for you to write your middleware safely for a persistent environment. To accomplish that, you should avoid saving per-request data like C<$env> in your object. =head1 BACKWARDS COMPATIBILITY The L<Plack::Middleware> module used to inherit from L<Class::Accessor::Fast>, which has been removed in favor of the L<Plack::Util::Accessor> module. When developing new components it is recommended to use L<Plack::Util::Accessor> like so: use Plack::Util::Accessor qw( foo bar baz ); However, in order to keep backwards compatibility this module provides a C<mk_accessors> method similar to L<Class::Accessor::Fast>. New code should not use this and use L<Plack::Util::Accessor> instead. =head1 SEE ALSO L<Plack> L<Plack::Builder> L<Plack::Middleware> =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/���������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 016754� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler.pm�������������������������������������������������������������������000644 �000765 �000024 �00000003656 12244057435 017324� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler; use strict; 1; __END__ =head1 NAME Plack::Handler - Connects PSGI applications and Web servers =head1 SYNOPSIS package Plack::Handler::AwesomeWebServer; sub new { my($class, %opt) = @_; ... return $self; } sub run { my($self, $app) = @_; # launch the AwesomeWebServer and run $app in the loop } # then from command line plackup -s AwesomeWebServer -a app.psgi =head1 DESCRIPTION Plack::Handler defines an adapter (connector) interface to adapt L<plackup> and L<Plack::Runner> to various PSGI web servers, such as Apache2 for mod_perl and Standalone for L<HTTP::Server::PSGI>. It is an empty class, and as long as they implement the methods defined as an Server adapter interface, they do not need to inherit Plack::Handler. If you write a new handler for existing web servers, I recommend you to include the full name of the server module after I<Plack::Handler> prefix, like L<Plack::Handler::Net::Server::Coro> if you write a handler for L<Net::Server::Coro>. That way you'll be using plackup command line option like: plackup -s Net::Server::Coro that makes it easy to figure out which web server you're going to use. =head1 METHODS =over 4 =item new $server = FooBarServer->new(%args); Creates a new adapter object. I<%args> can take arbitrary parameters to configure server environments but common parameters are: =over 8 =item port Port number the server listens to. =item host Address the server listens to. Set to undef to listen any interface. =back =item run $server->run($app); Starts the server process and when a request comes in, run the PSGI application passed in C<$app> in the loop. =item register_service $server->register_service($app); Optional interface if your server should run in parallel with other event loop, particularly L<AnyEvent>. This is the same as C<run> but doesn't run the main loop. =back =head1 SEE ALSO rackup =cut ����������������������������������������������������������������������������������Plack-1.0030/lib/Plack/HTTPParser/������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 017333� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/HTTPParser.pm����������������������������������������������������������������000644 �000765 �000024 �00000002015 12244057435 017667� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::HTTPParser; use strict; use parent qw(Exporter); our @EXPORT = qw( parse_http_request ); use Try::Tiny; { if (!$ENV{PLACK_HTTP_PARSER_PP} && try { require HTTP::Parser::XS; 1 }) { *parse_http_request = \&HTTP::Parser::XS::parse_http_request; } else { require Plack::HTTPParser::PP; *parse_http_request = \&Plack::HTTPParser::PP::parse_http_request; } } 1; __END__ =head1 NAME Plack::HTTPParser - Parse HTTP headers =head1 SYNOPSIS use Plack::HTTPParser qw(parse_http_request); my $ret = parse_http_request($header_str, \%env); # see HTTP::Parser::XS docs =head1 DESCRIPTION Plack::HTTPParser is a wrapper class to dispatch C<parse_http_request> to Kazuho Oku's XS based HTTP::Parser::XS or pure perl fallback based on David Robins HTTP::Parser. If you want to force the use of the slower pure perl version even if the fast XS version is available, set the environment variable C<PLACK_HTTP_PARSER_PP> to 1. =head1 SEE ALSO L<HTTP::Parser::XS> L<HTTP::Parser> =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Loader/����������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 016605� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Loader.pm��������������������������������������������������������������������000644 �000765 �000024 �00000005755 12244057435 017157� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Loader; use strict; use Carp (); use Plack::Util; use Try::Tiny; sub new { my $class = shift; bless {}, $class; } sub watch { # do nothing. Override in subclass } sub auto { my($class, @args) = @_; my $backend = $class->guess or Carp::croak("Couldn't auto-guess server server implementation. Set it with PLACK_SERVER"); my $server = try { $class->load($backend, @args); } catch { if (($ENV{PLACK_ENV}||'') eq 'development' or !/^Can't locate /) { warn "Autoloading '$backend' backend failed. Falling back to the Standalone. ", "(You might need to install Plack::Handler::$backend from CPAN. Caught error was: $_)\n" if $ENV{PLACK_ENV} && $ENV{PLACK_ENV} eq 'development'; } $class->load('Standalone' => @args); }; return $server; } sub load { my($class, $server, @args) = @_; my($server_class, $error); try { $server_class = Plack::Util::load_class($server, 'Plack::Handler'); } catch { $error ||= $_; }; if ($server_class) { $server_class->new(@args); } else { die $error; } } sub preload_app { my($self, $builder) = @_; $self->{app} = $builder->(); } sub guess { my $class = shift; my $env = $class->env; return $env->{PLACK_SERVER} if $env->{PLACK_SERVER}; if ($env->{PHP_FCGI_CHILDREN} || $env->{FCGI_ROLE} || $env->{FCGI_SOCKET_PATH}) { return "FCGI"; } elsif ($env->{GATEWAY_INTERFACE}) { return "CGI"; } elsif (exists $INC{"Coro.pm"}) { return "Corona"; } elsif (exists $INC{"AnyEvent.pm"}) { return "Twiggy"; } elsif (exists $INC{"POE.pm"}) { return "POE"; } else { return "Standalone"; } } sub env { \%ENV } sub run { my($self, $server, $builder) = @_; $server->run($self->{app}); } 1; __END__ =head1 NAME Plack::Loader - (auto)load Plack Servers =head1 SYNOPSIS # auto-select server backends based on env vars use Plack::Loader; Plack::Loader->auto(%args)->run($app); # specify the implementation with a name Plack::Loader->load('FCGI', %args)->run($app); =head1 DESCRIPTION Plack::Loader is a factory class to load one of Plack::Handler subclasses based on the environment. =head1 AUTOLOADING C<< Plack::Loader->auto(%args) >> will autoload the most correct server implementation by guessing from environment variables and Perl INC hashes. =over 4 =item PLACK_SERVER env PLACK_SERVER=AnyEvent ... Plack users can specify the specific implementation they want to load using the C<PLACK_SERVER> environment variable. =item PHP_FCGI_CHILDREN, GATEWAY_INTERFACE If there's one of FastCGI or CGI specific environment variables set, use the corresponding server implementation. =item %INC If one of L<AnyEvent>, L<Coro> or L<POE> is loaded, the relevant server implementation such as L<Twiggy>, L<Corona> or L<POE::Component::Server::PSGI> will be loaded, if they're available. =back =cut �������������������Plack-1.0030/lib/Plack/LWPish.pm��������������������������������������������������������������������000644 �000765 �000024 �00000003210 12244057435 017077� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::LWPish; use strict; use warnings; use HTTP::Tiny; use HTTP::Response; use Hash::MultiValue; sub new { my $class = shift; my $self = bless {}, $class; $self->{http} = @_ == 1 ? $_[0] : HTTP::Tiny->new(@_); $self; } sub request { my($self, $req) = @_; my @headers; $req->headers->scan(sub { push @headers, @_ }); my $options = { headers => Hash::MultiValue->new(@headers)->mixed, }; $options->{content} = $req->content if defined $req->content && length($req->content); my $response = $self->{http}->request($req->method, $req->url, $options); my $res = HTTP::Response->new( $response->{status}, $response->{reason}, [ Hash::MultiValue->from_mixed($response->{headers})->flatten ], $response->{content}, ); $res->request($req); return $res; } 1; __END__ =head1 NAME Plack::LWPish - HTTP::Request/Response compatible interface with HTTP::Tiny backend =head1 SYNOPSIS use Plack::LWPish; my $request = HTTP::Request->new(GET => 'http://perl.com/'); my $ua = Plack::LWPish->new; my $res = $ua->request($request); # returns HTTP::Response =head1 DESCRIPTION This module is an adapter object that implements one method, C<request> that acts like L<LWP::UserAgent>'s request method i.e. takes HTTP::Request object and returns HTTP::Response object. This module is used solely inside L<Plack::Test::Suite> and L<Plack::Test::Server>, and you are recommended to take a look at L<HTTP::Thin> if you would like to use this outside Plack. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<HTTP::Thin> L<HTTP::Tiny> L<LWP::UserAgent> =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 017454� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware.pm����������������������������������������������������������������000644 �000765 �000024 �00000012155 12244057435 020016� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware; use strict; use warnings; use Carp (); use parent qw(Plack::Component); use Plack::Util; use Plack::Util::Accessor qw( app ); sub wrap { my($self, $app, @args) = @_; if (ref $self) { $self->{app} = $app; } else { $self = $self->new({ app => $app, @args }); } return $self->to_app; } 1; __END__ =head1 NAME Plack::Middleware - Base class for easy-to-use PSGI middleware =head1 SYNOPSIS package Plack::Middleware::Foo; use parent qw( Plack::Middleware ); sub call { my($self, $env) = @_; # Do something with $env # $self->app is the original app my $res = $self->app->($env); # Do something with $res return $res; } # then in app.psgi use Plack::Builder; my $app = sub { ... } # as usual builder { enable "Plack::Middleware::Foo"; enable "Plack::Middleware::Bar", %options; $app; }; =head1 DESCRIPTION Plack::Middleware is a utility base class to write PSGI middleware. All you have to do is to inherit from Plack::Middleware and then implement the callback C<call> method (or the C<to_app> method that would return the PSGI code reference) to do the actual work. You can use C<< $self->app >> to call the original (wrapped) application. Your middleware object is created at the PSGI application compile time and is persistent during the web server life cycle (unless it is a non-persistent environment such as CGI), so you should never set or cache per-request data like C<$env> in your middleware object. See also L<Plack::Component/"OBJECT LIFECYCLE">. See L<Plack::Builder> how to actually enable middleware in your I<.psgi> application file using the DSL. If you do not like our builder DSL, you can also use the C<wrap> method to wrap your application with a middleware: use Plack::Middleware::Foo; my $app = sub { ... }; $app = Plack::Middleware::Foo->wrap($app, %options); $app = Plack::Middleware::Bar->wrap($app, %options); =head1 RESPONSE CALLBACK The typical middleware is written like this: package Plack::Middleware::Something; use parent qw(Plack::Middleware); sub call { my($self, $env) = @_; # pre-processing $env my $res = $self->app->($env); # post-processing $res return $res; } The tricky thing about post-processing the response is that it could either be an immediate 3 element array ref, or a code reference that implements the delayed (streaming) interface. Dealing with these two types of response in each piece of middleware is pointless, so you're recommended to use the C<response_cb> wrapper function in L<Plack::Util> when implementing a post processing middleware. my $res = $app->($env); Plack::Util::response_cb($res, sub { my $res = shift; # do something with $res; }); The callback function gets a PSGI response as a 3 element array reference, and you can update the reference to implement the post-processing. package Plack::Middleware::Always500; use parent qw(Plack::Middleware); use Plack::Util; sub call { my($self, $env) = @_; my $res = $self->app->($env); Plack::Util::response_cb($res, sub { my $res = shift; $res->[0] = 500; return; }); } In this example, the callback gets the C<$res> and updates its first element (status code) to 500. Using C<response_cb> makes sure that this works with the delayed response too. You're not required (and not recommended either) to return a new array reference - they will be simply ignored. You're suggested to explicitly return, unless you fiddle with the content filter callback (see below). Similarly, note that you have to keep the C<$res> reference when you swap the entire response. Plack::Util::response_cb($res, sub { my $res = shift; $res = [ $new_status, $new_headers, $new_body ]; # THIS DOES NOT WORK return; }); This does not work, since assigning a new anonymous array to C<$res> doesn't update the original PSGI response value. You should instead do: Plack::Util::response_cb($res, sub { my $res = shift; @$res = ($new_status, $new_headers, $new_body); # THIS WORKS return; }); The third element of the PSGI response array ref is a body, and it could be either an arrayref or L<IO::Handle>-ish object. The application could also make use of the C<$writer> object if C<psgi.streaming> is in effect. Dealing with these variants is again really painful, and C<response_cb> can take care of that too, by allowing you to return a content filter as a code reference. # replace all "Foo" in content body with "Bar" Plack::Util::response_cb($res, sub { my $res = shift; return sub { my $chunk = shift; return unless defined $chunk; $chunk =~ s/Foo/Bar/g; return $chunk; } }); The callback takes one argument C<$chunk> and your callback is expected to return the updated chunk. If the given C<$chunk> is undef, it means the stream has reached the end, so your callback should also return undef, or return the final chunk and return undef when called next time. =head1 SEE ALSO L<Plack> L<Plack::Builder> L<Plack::Component> =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/MIME.pm����������������������������������������������������������������������000644 �000765 �000024 �00000016656 12244057435 016502� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::MIME; use strict; # stolen from rack.mime.rb our $MIME_TYPES = { ".3gp" => "video/3gpp", ".a" => "application/octet-stream", ".ai" => "application/postscript", ".aif" => "audio/x-aiff", ".aiff" => "audio/x-aiff", ".asc" => "application/pgp-signature", ".asf" => "video/x-ms-asf", ".asm" => "text/x-asm", ".asx" => "video/x-ms-asf", ".atom" => "application/atom+xml", ".au" => "audio/basic", ".avi" => "video/x-msvideo", ".bat" => "application/x-msdownload", ".bin" => "application/octet-stream", ".bmp" => "image/bmp", ".bz2" => "application/x-bzip2", ".c" => "text/x-c", ".cab" => "application/vnd.ms-cab-compressed", ".cc" => "text/x-c", ".chm" => "application/vnd.ms-htmlhelp", ".class" => "application/octet-stream", ".com" => "application/x-msdownload", ".conf" => "text/plain", ".cpp" => "text/x-c", ".crt" => "application/x-x509-ca-cert", ".css" => "text/css", ".csv" => "text/csv", ".cxx" => "text/x-c", ".deb" => "application/x-debian-package", ".der" => "application/x-x509-ca-cert", ".diff" => "text/x-diff", ".djv" => "image/vnd.djvu", ".djvu" => "image/vnd.djvu", ".dll" => "application/x-msdownload", ".dmg" => "application/octet-stream", ".doc" => "application/msword", ".dot" => "application/msword", ".dtd" => "application/xml-dtd", ".dvi" => "application/x-dvi", ".ear" => "application/java-archive", ".eml" => "message/rfc822", ".eps" => "application/postscript", ".exe" => "application/x-msdownload", ".f" => "text/x-fortran", ".f77" => "text/x-fortran", ".f90" => "text/x-fortran", ".flv" => "video/x-flv", ".for" => "text/x-fortran", ".gem" => "application/octet-stream", ".gemspec" => "text/x-script.ruby", ".gif" => "image/gif", ".gz" => "application/x-gzip", ".h" => "text/x-c", ".hh" => "text/x-c", ".htm" => "text/html", ".html" => "text/html", ".ico" => "image/vnd.microsoft.icon", ".ics" => "text/calendar", ".ifb" => "text/calendar", ".iso" => "application/octet-stream", ".jar" => "application/java-archive", ".java" => "text/x-java-source", ".jnlp" => "application/x-java-jnlp-file", ".jpeg" => "image/jpeg", ".jpg" => "image/jpeg", ".js" => "application/javascript", ".json" => "application/json", ".log" => "text/plain", ".m3u" => "audio/x-mpegurl", ".m4v" => "video/mp4", ".man" => "text/troff", ".manifest"=> "text/cache-manifest", ".mathml" => "application/mathml+xml", ".mbox" => "application/mbox", ".mdoc" => "text/troff", ".me" => "text/troff", ".mid" => "audio/midi", ".midi" => "audio/midi", ".mime" => "message/rfc822", ".mml" => "application/mathml+xml", ".mng" => "video/x-mng", ".mov" => "video/quicktime", ".mp3" => "audio/mpeg", ".mp4" => "video/mp4", ".mp4v" => "video/mp4", ".mpeg" => "video/mpeg", ".mpg" => "video/mpeg", ".ms" => "text/troff", ".msi" => "application/x-msdownload", ".odp" => "application/vnd.oasis.opendocument.presentation", ".ods" => "application/vnd.oasis.opendocument.spreadsheet", ".odt" => "application/vnd.oasis.opendocument.text", ".ogg" => "application/ogg", ".ogv" => "video/ogg", ".p" => "text/x-pascal", ".pas" => "text/x-pascal", ".pbm" => "image/x-portable-bitmap", ".pdf" => "application/pdf", ".pem" => "application/x-x509-ca-cert", ".pgm" => "image/x-portable-graymap", ".pgp" => "application/pgp-encrypted", ".pkg" => "application/octet-stream", ".pl" => "text/x-script.perl", ".pm" => "text/x-script.perl-module", ".png" => "image/png", ".pnm" => "image/x-portable-anymap", ".ppm" => "image/x-portable-pixmap", ".pps" => "application/vnd.ms-powerpoint", ".ppt" => "application/vnd.ms-powerpoint", ".ps" => "application/postscript", ".psd" => "image/vnd.adobe.photoshop", ".py" => "text/x-script.python", ".qt" => "video/quicktime", ".ra" => "audio/x-pn-realaudio", ".rake" => "text/x-script.ruby", ".ram" => "audio/x-pn-realaudio", ".rar" => "application/x-rar-compressed", ".rb" => "text/x-script.ruby", ".rdf" => "application/rdf+xml", ".roff" => "text/troff", ".rpm" => "application/x-redhat-package-manager", ".rss" => "application/rss+xml", ".rtf" => "application/rtf", ".ru" => "text/x-script.ruby", ".s" => "text/x-asm", ".sgm" => "text/sgml", ".sgml" => "text/sgml", ".sh" => "application/x-sh", ".sig" => "application/pgp-signature", ".snd" => "audio/basic", ".so" => "application/octet-stream", ".svg" => "image/svg+xml", ".svgz" => "image/svg+xml", ".swf" => "application/x-shockwave-flash", ".t" => "text/troff", ".tar" => "application/x-tar", ".tbz" => "application/x-bzip-compressed-tar", ".tcl" => "application/x-tcl", ".tex" => "application/x-tex", ".texi" => "application/x-texinfo", ".texinfo" => "application/x-texinfo", ".text" => "text/plain", ".tif" => "image/tiff", ".tiff" => "image/tiff", ".torrent" => "application/x-bittorrent", ".tr" => "text/troff", ".txt" => "text/plain", ".vcf" => "text/x-vcard", ".vcs" => "text/x-vcalendar", ".vrml" => "model/vrml", ".war" => "application/java-archive", ".wav" => "audio/x-wav", ".wma" => "audio/x-ms-wma", ".wmv" => "video/x-ms-wmv", ".wmx" => "video/x-ms-wmx", ".woff" => "application/font-woff", ".wrl" => "model/vrml", ".wsdl" => "application/wsdl+xml", ".xbm" => "image/x-xbitmap", ".xhtml" => "application/xhtml+xml", ".xls" => "application/vnd.ms-excel", ".xml" => "application/xml", ".xpm" => "image/x-xpixmap", ".xsl" => "application/xml", ".xslt" => "application/xslt+xml", ".yaml" => "text/yaml", ".yml" => "text/yaml", ".zip" => "application/zip", }; my $fallback = sub { }; sub mime_type { my($class, $file) = @_; $file =~ /(\.[a-zA-Z0-9]+)$/ or return; $MIME_TYPES->{lc $1} || $fallback->(lc $1); } sub add_type { my $class = shift; while (my($ext, $type) = splice @_, 0, 2) { $MIME_TYPES->{lc $ext} = $type; } } sub set_fallback { my($class, $cb) = @_; $fallback = $cb; } 1; __END__ =head1 NAME Plack::MIME - MIME type registry =head1 SYNOPSIS use Plack::MIME; my $mime = Plack::MIME->mime_type(".png"); # image/png # register new type(s) Plack::MIME->add_type(".foo" => "application/x-foo"); # Use MIME::Types as a fallback use MIME::Types 'by_suffix'; Plack::MIME->set_fallback(sub { (by_suffix $_[0])[0] }); =head1 DESCRIPTION Plack::MIME is a simple MIME type registry for Plack applications. The selection of MIME types is based on Rack's Rack::Mime module. =head1 SEE ALSO Rack::Mime L<MIME::Types> =cut ����������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Request/���������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 017027� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Request.pm�������������������������������������������������������������������000644 �000765 �000024 �00000046166 12244057435 017402� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Request; use strict; use warnings; use 5.008_001; our $VERSION = '1.0030'; use HTTP::Headers; use Carp (); use Hash::MultiValue; use HTTP::Body; use Plack::Request::Upload; use Stream::Buffered; use URI; use URI::Escape (); sub new { my($class, $env) = @_; Carp::croak(q{$env is required}) unless defined $env && ref($env) eq 'HASH'; bless { env => $env }, $class; } sub env { $_[0]->{env} } sub address { $_[0]->env->{REMOTE_ADDR} } sub remote_host { $_[0]->env->{REMOTE_HOST} } sub protocol { $_[0]->env->{SERVER_PROTOCOL} } sub method { $_[0]->env->{REQUEST_METHOD} } sub port { $_[0]->env->{SERVER_PORT} } sub user { $_[0]->env->{REMOTE_USER} } sub request_uri { $_[0]->env->{REQUEST_URI} } sub path_info { $_[0]->env->{PATH_INFO} } sub path { $_[0]->env->{PATH_INFO} || '/' } sub script_name { $_[0]->env->{SCRIPT_NAME} } sub scheme { $_[0]->env->{'psgi.url_scheme'} } sub secure { $_[0]->scheme eq 'https' } sub body { $_[0]->env->{'psgi.input'} } sub input { $_[0]->env->{'psgi.input'} } sub content_length { $_[0]->env->{CONTENT_LENGTH} } sub content_type { $_[0]->env->{CONTENT_TYPE} } sub session { $_[0]->env->{'psgix.session'} } sub session_options { $_[0]->env->{'psgix.session.options'} } sub logger { $_[0]->env->{'psgix.logger'} } sub cookies { my $self = shift; return {} unless $self->env->{HTTP_COOKIE}; # HTTP_COOKIE hasn't changed: reuse the parsed cookie if ( $self->env->{'plack.cookie.parsed'} && $self->env->{'plack.cookie.string'} eq $self->env->{HTTP_COOKIE}) { return $self->env->{'plack.cookie.parsed'}; } $self->env->{'plack.cookie.string'} = $self->env->{HTTP_COOKIE}; my %results; my @pairs = grep m/=/, split "[;,] ?", $self->env->{'plack.cookie.string'}; for my $pair ( @pairs ) { # trim leading trailing whitespace $pair =~ s/^\s+//; $pair =~ s/\s+$//; my ($key, $value) = map URI::Escape::uri_unescape($_), split( "=", $pair, 2 ); # Take the first one like CGI.pm or rack do $results{$key} = $value unless exists $results{$key}; } $self->env->{'plack.cookie.parsed'} = \%results; } sub query_parameters { my $self = shift; $self->env->{'plack.request.query'} ||= $self->_parse_query; } sub _parse_query { my $self = shift; my @query; my $query_string = $self->env->{QUERY_STRING}; if (defined $query_string) { if ($query_string =~ /=/) { # Handle ?foo=bar&bar=foo type of query @query = map { s/\+/ /g; URI::Escape::uri_unescape($_) } map { /=/ ? split(/=/, $_, 2) : ($_ => '')} split(/[&;]/, $query_string); } else { # Handle ...?dog+bones type of query @query = map { (URI::Escape::uri_unescape($_), '') } split(/\+/, $query_string, -1); } } Hash::MultiValue->new(@query); } sub content { my $self = shift; unless ($self->env->{'psgix.input.buffered'}) { $self->_parse_request_body; } my $fh = $self->input or return ''; my $cl = $self->env->{CONTENT_LENGTH} or return ''; $fh->seek(0, 0); # just in case middleware/apps read it without seeking back $fh->read(my($content), $cl, 0); $fh->seek(0, 0); return $content; } sub raw_body { $_[0]->content } # XXX you can mutate headers with ->headers but it's not written through to the env sub headers { my $self = shift; if (!defined $self->{headers}) { my $env = $self->env; $self->{headers} = HTTP::Headers->new( map { (my $field = $_) =~ s/^HTTPS?_//; ( $field => $env->{$_} ); } grep { /^(?:HTTP|CONTENT)/i } keys %$env ); } $self->{headers}; } sub content_encoding { shift->headers->content_encoding(@_) } sub header { shift->headers->header(@_) } sub referer { shift->headers->referer(@_) } sub user_agent { shift->headers->user_agent(@_) } sub body_parameters { my $self = shift; unless ($self->env->{'plack.request.body'}) { $self->_parse_request_body; } return $self->env->{'plack.request.body'}; } # contains body + query sub parameters { my $self = shift; $self->env->{'plack.request.merged'} ||= do { my $query = $self->query_parameters; my $body = $self->body_parameters; Hash::MultiValue->new($query->flatten, $body->flatten); }; } sub uploads { my $self = shift; if ($self->env->{'plack.request.upload'}) { return $self->env->{'plack.request.upload'}; } $self->_parse_request_body; return $self->env->{'plack.request.upload'}; } sub param { my $self = shift; return keys %{ $self->parameters } if @_ == 0; my $key = shift; return $self->parameters->{$key} unless wantarray; return $self->parameters->get_all($key); } sub upload { my $self = shift; return keys %{ $self->uploads } if @_ == 0; my $key = shift; return $self->uploads->{$key} unless wantarray; return $self->uploads->get_all($key); } sub uri { my $self = shift; my $base = $self->_uri_base; # We have to escape back PATH_INFO in case they include stuff like # ? or # so that the URI parser won't be tricked. However we should # preserve '/' since encoding them into %2f doesn't make sense. # This means when a request like /foo%2fbar comes in, we recognize # it as /foo/bar which is not ideal, but that's how the PSGI PATH_INFO # spec goes and we can't do anything about it. See PSGI::FAQ for details. # See RFC 3986 before modifying. my $path_escape_class = q{^/;:@&=A-Za-z0-9\$_.+!*'(),-}; my $path = URI::Escape::uri_escape($self->env->{PATH_INFO} || '', $path_escape_class); $path .= '?' . $self->env->{QUERY_STRING} if defined $self->env->{QUERY_STRING} && $self->env->{QUERY_STRING} ne ''; $base =~ s!/$!! if $path =~ m!^/!; return URI->new($base . $path)->canonical; } sub base { my $self = shift; URI->new($self->_uri_base)->canonical; } sub _uri_base { my $self = shift; my $env = $self->env; my $uri = ($env->{'psgi.url_scheme'} || "http") . "://" . ($env->{HTTP_HOST} || (($env->{SERVER_NAME} || "") . ":" . ($env->{SERVER_PORT} || 80))) . ($env->{SCRIPT_NAME} || '/'); return $uri; } sub new_response { my $self = shift; require Plack::Response; Plack::Response->new(@_); } sub _parse_request_body { my $self = shift; my $ct = $self->env->{CONTENT_TYPE}; my $cl = $self->env->{CONTENT_LENGTH}; if (!$ct && !$cl) { # No Content-Type nor Content-Length -> GET/HEAD $self->env->{'plack.request.body'} = Hash::MultiValue->new; $self->env->{'plack.request.upload'} = Hash::MultiValue->new; return; } my $body = HTTP::Body->new($ct, $cl); # HTTP::Body will create temporary files in case there was an # upload. Those temporary files can be cleaned up by telling # HTTP::Body to do so. It will run the cleanup when the request # env is destroyed. That the object will not go out of scope by # the end of this sub we will store a reference here. $self->env->{'plack.request.http.body'} = $body; $body->cleanup(1); my $input = $self->input; my $buffer; if ($self->env->{'psgix.input.buffered'}) { # Just in case if input is read by middleware/apps beforehand $input->seek(0, 0); } else { $buffer = Stream::Buffered->new($cl); } my $spin = 0; while ($cl) { $input->read(my $chunk, $cl < 8192 ? $cl : 8192); my $read = length $chunk; $cl -= $read; $body->add($chunk); $buffer->print($chunk) if $buffer; if ($read == 0 && $spin++ > 2000) { Carp::croak "Bad Content-Length: maybe client disconnect? ($cl bytes remaining)"; } } if ($buffer) { $self->env->{'psgix.input.buffered'} = 1; $self->env->{'psgi.input'} = $buffer->rewind; } else { $input->seek(0, 0); } $self->env->{'plack.request.body'} = Hash::MultiValue->from_mixed($body->param); my @uploads = Hash::MultiValue->from_mixed($body->upload)->flatten; my @obj; while (my($k, $v) = splice @uploads, 0, 2) { push @obj, $k, $self->_make_upload($v); } $self->env->{'plack.request.upload'} = Hash::MultiValue->new(@obj); 1; } sub _make_upload { my($self, $upload) = @_; my %copy = %$upload; $copy{headers} = HTTP::Headers->new(%{$upload->{headers}}); Plack::Request::Upload->new(%copy); } 1; __END__ =head1 NAME Plack::Request - Portable HTTP request object from PSGI env hash =head1 SYNOPSIS use Plack::Request; my $app_or_middleware = sub { my $env = shift; # PSGI env my $req = Plack::Request->new($env); my $path_info = $req->path_info; my $query = $req->param('query'); my $res = $req->new_response(200); # new Plack::Response $res->finalize; }; =head1 DESCRIPTION L<Plack::Request> provides a consistent API for request objects across web server environments. =head1 CAVEAT Note that this module is intended to be used by Plack middleware developers and web application framework developers rather than application developers (end users). Writing your web application directly using Plack::Request is certainly possible but not recommended: it's like doing so with mod_perl's Apache::Request: yet too low level. If you're writing a web application, not a framework, then you're encouraged to use one of the web application frameworks that support PSGI (L<http://plackperl.org/#frameworks>), or see modules like L<HTTP::Engine> to provide higher level Request and Response API on top of PSGI. =head1 METHODS Some of the methods defined in the earlier versions are deprecated in version 0.99. Take a look at L</"INCOMPATIBILITIES">. Unless otherwise noted, all methods and attributes are B<read-only>, and passing values to the method like an accessor doesn't work like you expect it to. =head2 new Plack::Request->new( $env ); Creates a new request object. =head1 ATTRIBUTES =over 4 =item env Returns the shared PSGI environment hash reference. This is a reference, so writing to this environment passes through during the whole PSGI request/response cycle. =item address Returns the IP address of the client (C<REMOTE_ADDR>). =item remote_host Returns the remote host (C<REMOTE_HOST>) of the client. It may be empty, in which case you have to get the IP address using C<address> method and resolve on your own. =item method Contains the request method (C<GET>, C<POST>, C<HEAD>, etc). =item protocol Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request. =item request_uri Returns the raw, undecoded request URI path. You probably do B<NOT> want to use this to dispatch requests. =item path_info Returns B<PATH_INFO> in the environment. Use this to get the local path for the requests. =item path Similar to C<path_info> but returns C</> in case it is empty. In other words, it returns the virtual path of the request URI after C<< $req->base >>. See L</"DISPATCHING"> for details. =item script_name Returns B<SCRIPT_NAME> in the environment. This is the absolute path where your application is hosted. =item scheme Returns the scheme (C<http> or C<https>) of the request. =item secure Returns true or false, indicating whether the connection is secure (https). =item body, input Returns C<psgi.input> handle. =item session Returns (optional) C<psgix.session> hash. When it exists, you can retrieve and store per-session data from and to this hash. =item session_options Returns (optional) C<psgix.session.options> hash. =item logger Returns (optional) C<psgix.logger> code reference. When it exists, your application is supposed to send the log message to this logger, using: $req->logger->({ level => 'debug', message => "This is a debug message" }); =item cookies Returns a reference to a hash containing the cookies. Values are strings that are sent by clients and are URI decoded. If there are multiple cookies with the same name in the request, this method will ignore the duplicates and return only the first value. If that causes issues for you, you may have to use modules like CGI::Simple::Cookie to parse C<$request->header('Cookies')> by yourself. =item query_parameters Returns a reference to a hash containing query string (GET) parameters. This hash reference is L<Hash::MultiValue> object. =item body_parameters Returns a reference to a hash containing posted parameters in the request body (POST). As with C<query_parameters>, the hash reference is a L<Hash::MultiValue> object. =item parameters Returns a L<Hash::MultiValue> hash reference containing (merged) GET and POST parameters. =item content, raw_body Returns the request content in an undecoded byte string for POST requests. =item uri Returns an URI object for the current request. The URI is constructed using various environment values such as C<SCRIPT_NAME>, C<PATH_INFO>, C<QUERY_STRING>, C<HTTP_HOST>, C<SERVER_NAME> and C<SERVER_PORT>. Every time this method is called it returns a new, cloned URI object. =item base Returns an URI object for the base path of current request. This is like C<uri> but only contains up to C<SCRIPT_NAME> where your application is hosted at. Every time this method is called it returns a new, cloned URI object. =item user Returns C<REMOTE_USER> if it's set. =item headers Returns an L<HTTP::Headers> object containing the headers for the current request. =item uploads Returns a reference to a hash containing uploads. The hash reference is a L<Hash::MultiValue> object and values are L<Plack::Request::Upload> objects. =item content_encoding Shortcut to $req->headers->content_encoding. =item content_length Shortcut to $req->headers->content_length. =item content_type Shortcut to $req->headers->content_type. =item header Shortcut to $req->headers->header. =item referer Shortcut to $req->headers->referer. =item user_agent Shortcut to $req->headers->user_agent. =item param Returns GET and POST parameters with a CGI.pm-compatible param method. This is an alternative method for accessing parameters in $req->parameters. Unlike CGI.pm, it does I<not> allow setting or modifying query parameters. $value = $req->param( 'foo' ); @values = $req->param( 'foo' ); @params = $req->param; =item upload A convenient method to access $req->uploads. $upload = $req->upload('field'); @uploads = $req->upload('field'); @fields = $req->upload; for my $upload ( $req->upload('field') ) { print $upload->filename; } =item new_response my $res = $req->new_response; Creates a new L<Plack::Response> object. Handy to remove dependency on L<Plack::Response> in your code for easy subclassing and duck typing in web application frameworks, as well as overriding Response generation in middlewares. =back =head2 Hash::MultiValue parameters Parameters that can take one or multiple values (i.e. C<parameters>, C<query_parameters>, C<body_parameters> and C<uploads>) store the hash reference as a L<Hash::MultiValue> object. This means you can use the hash reference as a plain hash where values are B<always> scalars (B<NOT> array references), so you don't need to code ugly and unsafe C<< ref ... eq 'ARRAY' >> anymore. And if you explicitly want to get multiple values of the same key, you can call the C<get_all> method on it, such as: my @foo = $req->query_parameters->get_all('foo'); You can also call C<get_one> to always get one parameter independent of the context (unlike C<param>), and even call C<mixed> (with Hash::MultiValue 0.05 or later) to get the I<traditional> hash reference, my $params = $req->parameters->mixed; where values are either a scalar or an array reference depending on input, so it might be useful if you already have the code to deal with that ugliness. =head2 PARSING POST BODY and MULTIPLE OBJECTS The methods to parse request body (C<content>, C<body_parameters> and C<uploads>) are carefully coded to save the parsed body in the environment hash as well as in the temporary buffer, so you can call them multiple times and create Plack::Request objects multiple times in a request and they should work safely, and won't parse request body more than twice for the efficiency. =head1 DISPATCHING If your application or framework wants to dispatch (or route) actions based on request paths, be sure to use C<< $req->path_info >> not C<< $req->uri->path >>. This is because C<path_info> gives you the virtual path of the request, regardless of how your application is mounted. If your application is hosted with mod_perl or CGI scripts, or even multiplexed with tools like L<Plack::App::URLMap>, request's C<path_info> always gives you the action path. Note that C<path_info> might give you an empty string, in which case you should assume that the path is C</>. You will also want to use C<< $req->base >> as a base prefix when building URLs in your templates or in redirections. It's a good idea for you to subclass Plack::Request and define methods such as: sub uri_for { my($self, $path, $args) = @_; my $uri = $self->base; $uri->path($uri->path . $path); $uri->query_form(@$args) if $args; $uri; } So you can say: my $link = $req->uri_for('/logout', [ signoff => 1 ]); and if C<< $req->base >> is C</app> you'll get the full URI for C</app/logout?signoff=1>. =head1 INCOMPATIBILITIES In version 0.99, many utility methods are removed or deprecated, and most methods are made read-only. These methods were deleted in version 1.0001. All parameter-related methods such as C<parameters>, C<body_parameters>, C<query_parameters> and C<uploads> now contains L<Hash::MultiValue> objects, rather than I<scalar or an array reference depending on the user input> which is insecure. See L<Hash::MultiValue> for more about this change. C<< $req->path >> method had a bug, where the code and the document was mismatching. The document was suggesting it returns the sub request path after C<< $req->base >> but the code was always returning the absolute URI path. The code is now updated to be an alias of C<< $req->path_info >> but returns C</> in case it's empty. If you need the older behavior, just call C<< $req->uri->path >> instead. Cookie handling is simplified, and doesn't use L<CGI::Simple::Cookie> anymore, which means you B<CAN NOT> set array reference or hash reference as a cookie value and expect it be serialized. You're always required to set string value, and encoding or decoding them is totally up to your application or framework. Also, C<cookies> hash reference now returns I<strings> for the cookies rather than CGI::Simple::Cookie objects, which means you no longer have to write a wacky code such as: $v = $req->cookie->{foo} ? $req->cookie->{foo}->value : undef; and instead, simply do: $v = $req->cookie->{foo}; =head1 AUTHORS Tatsuhiko Miyagawa Kazuhiro Osawa Tokuhiro Matsuno =head1 SEE ALSO L<Plack::Response> L<HTTP::Request>, L<Catalyst::Request> =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Response.pm������������������������������������������������������������������000644 �000765 �000024 �00000016417 12244057435 017544� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Response; use strict; use warnings; our $VERSION = '1.0030'; use Plack::Util::Accessor qw(body status); use Carp (); use Scalar::Util (); use HTTP::Headers; use URI::Escape (); sub code { shift->status(@_) } sub content { shift->body(@_) } sub new { my($class, $rc, $headers, $content) = @_; my $self = bless {}, $class; $self->status($rc) if defined $rc; $self->headers($headers) if defined $headers; $self->body($content) if defined $content; $self; } sub headers { my $self = shift; if (@_) { my $headers = shift; if (ref $headers eq 'ARRAY') { Carp::carp("Odd number of headers") if @$headers % 2 != 0; $headers = HTTP::Headers->new(@$headers); } elsif (ref $headers eq 'HASH') { $headers = HTTP::Headers->new(%$headers); } return $self->{headers} = $headers; } else { return $self->{headers} ||= HTTP::Headers->new(); } } sub cookies { my $self = shift; if (@_) { $self->{cookies} = shift; } else { return $self->{cookies} ||= +{ }; } } sub header { shift->headers->header(@_) } # shortcut sub content_length { shift->headers->content_length(@_); } sub content_type { shift->headers->content_type(@_); } sub content_encoding { shift->headers->content_encoding(@_); } sub location { my $self = shift; return $self->headers->header('Location' => @_); } sub redirect { my $self = shift; if (@_) { my $url = shift; my $status = shift || 302; $self->location($url); $self->status($status); } return $self->location; } sub finalize { my $self = shift; Carp::croak "missing status" unless $self->status(); my $headers = $self->headers; my @headers; $headers->scan(sub{ my ($k,$v) = @_; $v =~ s/\015\012[\040|\011]+/chr(32)/ge; # replace LWS with a single SP $v =~ s/\015|\012//g; # remove CR and LF since the char is invalid here push @headers, $k, $v; }); $self->_finalize_cookies(\@headers); return [ $self->status, \@headers, $self->_body, ]; } sub to_app { my $self = shift; return sub { $self->finalize }; } sub _body { my $self = shift; my $body = $self->body; $body = [] unless defined $body; if (!ref $body or Scalar::Util::blessed($body) && overload::Method($body, q("")) && !$body->can('getline')) { return [ $body ]; } else { return $body; } } sub _finalize_cookies { my($self, $headers) = @_; while (my($name, $val) = each %{$self->cookies}) { my $cookie = $self->_bake_cookie($name, $val); push @$headers, 'Set-Cookie' => $cookie; } } sub _bake_cookie { my($self, $name, $val) = @_; return '' unless defined $val; $val = { value => $val } unless ref $val eq 'HASH'; my @cookie = ( URI::Escape::uri_escape($name) . "=" . URI::Escape::uri_escape($val->{value}) ); push @cookie, "domain=" . $val->{domain} if $val->{domain}; push @cookie, "path=" . $val->{path} if $val->{path}; push @cookie, "expires=" . $self->_date($val->{expires}) if $val->{expires}; push @cookie, "max-age=" . $val->{"max-age"} if $val->{"max-age"}; push @cookie, "secure" if $val->{secure}; push @cookie, "HttpOnly" if $val->{httponly}; return join "; ", @cookie; } my @MON = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); my @WDAY = qw( Sun Mon Tue Wed Thu Fri Sat ); sub _date { my($self, $expires) = @_; if ($expires =~ /^\d+$/) { # all numbers -> epoch date # (cookies use '-' as date separator, HTTP uses ' ') my($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($expires); $year += 1900; return sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT", $WDAY[$wday], $mday, $MON[$mon], $year, $hour, $min, $sec); } return $expires; } 1; __END__ =head1 NAME Plack::Response - Portable HTTP Response object for PSGI response =head1 SYNOPSIS use Plack::Response; sub psgi_handler { my $env = shift; my $res = Plack::Response->new(200); $res->content_type('text/html'); $res->body("Hello World"); return $res->finalize; } =head1 DESCRIPTION Plack::Response allows you a way to create PSGI response array ref through a simple API. =head1 METHODS =over 4 =item new $res = Plack::Response->new; $res = Plack::Response->new($status); $res = Plack::Response->new($status, $headers); $res = Plack::Response->new($status, $headers, $body); Creates a new Plack::Response object. =item status $res->status(200); $status = $res->status; Sets and gets HTTP status code. C<code> is an alias. =item headers $headers = $res->headers; $res->headers([ 'Content-Type' => 'text/html' ]); $res->headers({ 'Content-Type' => 'text/html' }); $res->headers( HTTP::Headers->new ); Sets and gets HTTP headers of the response. Setter can take either an array ref, a hash ref or L<HTTP::Headers> object containing a list of headers. =item body $res->body($body_str); $res->body([ "Hello", "World" ]); $res->body($io); Gets and sets HTTP response body. Setter can take either a string, an array ref, or an IO::Handle-like object. C<content> is an alias. Note that this method doesn't automatically set I<Content-Length> for the response. You have to set it manually if you want, with the C<content_length> method (see below). =item header $res->header('X-Foo' => 'bar'); my $val = $res->header('X-Foo'); Shortcut for C<< $res->headers->header >>. =item content_type, content_length, content_encoding $res->content_type('text/plain'); $res->content_length(123); $res->content_encoding('gzip'); Shortcut for the equivalent get/set methods in C<< $res->headers >>. =item redirect $res->redirect($url); $res->redirect($url, 301); Sets redirect URL with an optional status code, which defaults to 302. Note that this method doesn't normalize the given URI string. Users of this module have to be responsible about properly encoding URI paths and parameters. =item location Gets and sets C<Location> header. Note that this method doesn't normalize the given URI string in the setter. See above in C<redirect> for details. =item cookies $res->cookies->{foo} = 123; $res->cookies->{foo} = { value => '123' }; Returns a hash reference containing cookies to be set in the response. The keys of the hash are the cookies' names, and their corresponding values are a plain string (for C<value> with everything else defaults) or a hash reference that can contain keys such as C<value>, C<domain>, C<expires>, C<path>, C<httponly>, C<secure>, C<max-age>. C<expires> can take a string or an integer (as an epoch time) and B<does not> convert string formats such as C<+3M>. $res->cookies->{foo} = { value => 'test', path => "/", domain => '.example.com', expires => time + 24 * 60 * 60, }; =item finalize $res->finalize; Returns the status code, headers, and body of this response as a PSGI response array reference. =item to_app $app = $res->to_app; A helper shortcut for C<< sub { $res->finalize } >>. =back =head1 AUTHOR Tokuhiro Matsuno Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::Request> =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Runner.pm��������������������������������������������������������������������000644 �000765 �000024 �00000020731 12244057435 017211� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Runner; use strict; use warnings; use Carp (); use Plack::Util; use Try::Tiny; sub new { my $class = shift; bless { env => $ENV{PLACK_ENV}, loader => 'Plack::Loader', includes => [], modules => [], default_middleware => 1, @_, }, $class; } # delay the build process for reloader sub build(&;$) { my $block = shift; my $app = shift || sub { }; return sub { $block->($app->()) }; } sub parse_options { my $self = shift; local @ARGV = @_; # From 'prove': Allow cuddling the paths with -I, -M and -e @ARGV = map { /^(-[IMe])(.+)/ ? ($1,$2) : $_ } @ARGV; my($host, $port, $socket, @listen); require Getopt::Long; my $parser = Getopt::Long::Parser->new( config => [ "no_auto_abbrev", "no_ignore_case", "pass_through" ], ); $parser->getoptions( "a|app=s" => \$self->{app}, "o|host=s" => \$host, "p|port=i" => \$port, "s|server=s" => \$self->{server}, "S|socket=s" => \$socket, 'l|listen=s@' => \@listen, 'D|daemonize' => \$self->{daemonize}, "E|env=s" => \$self->{env}, "e=s" => \$self->{eval}, 'I=s@' => $self->{includes}, 'M=s@' => $self->{modules}, 'r|reload' => sub { $self->{loader} = "Restarter" }, 'R|Reload=s' => sub { $self->{loader} = "Restarter"; $self->loader->watch(split ",", $_[1]) }, 'L|loader=s' => \$self->{loader}, "access-log=s" => \$self->{access_log}, "path=s" => \$self->{path}, "h|help" => \$self->{help}, "v|version" => \$self->{version}, "default-middleware!" => \$self->{default_middleware}, ); my(@options, @argv); while (defined(my $arg = shift @ARGV)) { if ($arg =~ s/^--?//) { my @v = split '=', $arg, 2; $v[0] =~ tr/-/_/; if (@v == 2) { push @options, @v; } elsif ($v[0] =~ s/^(disable|enable)_//) { push @options, $v[0], $1 eq 'enable'; } else { push @options, $v[0], shift @ARGV; } } else { push @argv, $arg; } } push @options, $self->mangle_host_port_socket($host, $port, $socket, @listen); push @options, daemonize => 1 if $self->{daemonize}; $self->{options} = \@options; $self->{argv} = \@argv; } sub set_options { my $self = shift; push @{$self->{options}}, @_; } sub mangle_host_port_socket { my($self, $host, $port, $socket, @listen) = @_; for my $listen (reverse @listen) { if ($listen =~ /:\d+$/) { ($host, $port) = split /:/, $listen, 2; $host = undef if $host eq ''; } else { $socket ||= $listen; } } unless (@listen) { if ($socket) { @listen = ($socket); } else { $port ||= 5000; @listen = ($host ? "$host:$port" : ":$port"); } } return host => $host, port => $port, listen => \@listen, socket => $socket; } sub version_cb { my $self = shift; $self->{version_cb} || sub { require Plack; print "Plack $Plack::VERSION\n"; }; } sub setup { my $self = shift; if ($self->{help}) { require Pod::Usage; Pod::Usage::pod2usage(0); } if ($self->{version}) { $self->version_cb->(); exit; } if (@{$self->{includes}}) { require lib; lib->import(@{$self->{includes}}); } if ($self->{eval}) { push @{$self->{modules}}, 'Plack::Builder'; } for (@{$self->{modules}}) { my($module, @import) = split /[=,]/; eval "require $module" or die $@; $module->import(@import); } } sub locate_app { my($self, @args) = @_; my $psgi = $self->{app} || $args[0]; if (ref $psgi eq 'CODE') { return sub { $psgi }; } if ($self->{eval}) { $self->loader->watch("lib"); return build { no strict; no warnings; my $eval = "builder { $self->{eval};"; $eval .= "Plack::Util::load_psgi(\$psgi);" if $psgi; $eval .= "}"; eval $eval or die $@; }; } $psgi ||= "app.psgi"; require File::Basename; $self->loader->watch( File::Basename::dirname($psgi) . "/lib", $psgi ); build { Plack::Util::load_psgi $psgi }; } sub watch { my($self, @dir) = @_; push @{$self->{watch}}, @dir if $self->{loader} eq 'Restarter'; } sub apply_middleware { my($self, $app, $class, @args) = @_; my $mw_class = Plack::Util::load_class($class, 'Plack::Middleware'); build { $mw_class->wrap($_[0], @args) } $app; } sub prepare_devel { my($self, $app) = @_; if ($self->{default_middleware}) { $app = $self->apply_middleware($app, 'Lint'); $app = $self->apply_middleware($app, 'StackTrace'); if (!$ENV{GATEWAY_INTERFACE} and !$self->{access_log}) { $app = $self->apply_middleware($app, 'AccessLog'); } } push @{$self->{options}}, server_ready => sub { my($args) = @_; my $name = $args->{server_software} || ref($args); # $args is $server my $host = $args->{host} || 0; my $proto = $args->{proto} || 'http'; print STDERR "$name: Accepting connections at $proto://$host:$args->{port}/\n"; }; $app; } sub loader { my $self = shift; $self->{_loader} ||= Plack::Util::load_class($self->{loader}, 'Plack::Loader')->new; } sub load_server { my($self, $loader) = @_; if ($self->{server}) { return $loader->load($self->{server}, @{$self->{options}}); } else { return $loader->auto(@{$self->{options}}); } } sub run { my $self = shift; unless (ref $self) { $self = $self->new; $self->parse_options(@_); return $self->run; } unless ($self->{options}) { $self->parse_options(); } my @args = @_ ? @_ : @{$self->{argv}}; $self->setup; my $app = $self->locate_app(@args); if ($self->{path}) { require Plack::App::URLMap; $app = build { my $urlmap = Plack::App::URLMap->new; $urlmap->mount($self->{path} => $_[0]); $urlmap->to_app; } $app; } $ENV{PLACK_ENV} ||= $self->{env} || 'development'; if ($ENV{PLACK_ENV} eq 'development') { $app = $self->prepare_devel($app); } if ($self->{access_log}) { open my $logfh, ">>", $self->{access_log} or die "open($self->{access_log}): $!"; $logfh->autoflush(1); $app = $self->apply_middleware($app, 'AccessLog', logger => sub { $logfh->print( @_ ) }); } my $loader = $self->loader; $loader->preload_app($app); my $server = $self->load_server($loader); $loader->run($server); } 1; __END__ =head1 NAME Plack::Runner - plackup core =head1 SYNOPSIS # Your bootstrap script use Plack::Runner; my $app = sub { ... }; my $runner = Plack::Runner->new; $runner->parse_options(@ARGV); $runner->run($app); =head1 DESCRIPTION Plack::Runner is the core of L<plackup> runner script. You can create your own frontend to run your application or framework, munge command line options and pass that to C<run> method of this class. C<run> method does exactly the same thing as the L<plackup> script does, but one notable addition is that you can pass a PSGI application code reference directly to the method, rather than via C<.psgi> file path or with C<-e> switch. This would be useful if you want to make an installable PSGI application. Also, when C<-h> or C<--help> switch is passed, the usage text is automatically extracted from your own script using L<Pod::Usage>. =head1 NOTES Do not directly call this module from your C<.psgi>, since that makes your PSGI application unnecessarily depend on L<plackup> and won't run other backends like L<Plack::Handler::Apache2> or mod_psgi. If you I<really> want to make your C<.psgi> runnable as a standalone script, you can do this: my $app = sub { ... }; unless (caller) { require Plack::Runner; my $runner = Plack::Runner->new; $runner->parse_options(@ARGV); return $runner->run($app); } return $app; B<WARNING>: this section used to recommend C<if (__FILE__ eq $0)> but it's known to be broken since Plack 0.9971, since C<$0> is now I<always> set to the .psgi file path even when you run it from plackup. =head1 SEE ALSO L<plackup> =cut ���������������������������������������Plack-1.0030/lib/Plack/TempBuffer.pm����������������������������������������������������������������000644 �000765 �000024 �00000000735 12244057435 020001� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::TempBuffer; use strict; use warnings; use parent 'Stream::Buffered'; sub new { my $class = shift; if (defined $Plack::TempBuffer::MaxMemoryBufferSize) { warn "Setting \$Plack::TempBuffer::MaxMemoryBufferSize is deprecated. " . "You should set \$Stream::Buffered::MaxMemoryBufferSize instead."; $Stream::Buffered::MaxMemoryBufferSize = $Plack::TempBuffer::MaxMemoryBufferSize; } return $class->SUPER::new(@_); } 1; �����������������������������������Plack-1.0030/lib/Plack/Test/������������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 016316� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Test.pm����������������������������������������������������������������������000644 �000765 �000024 �00000011300 12244057435 016647� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Test; use strict; use warnings; use Carp; use parent qw(Exporter); our @EXPORT = qw(test_psgi); our $Impl; $Impl ||= $ENV{PLACK_TEST_IMPL} || "MockHTTP"; sub create { my($class, $app, @args) = @_; my $subclass = "Plack::Test::$Impl"; eval "require $subclass"; die $@ if $@; no strict 'refs'; if (defined &{"Plack::Test::$Impl\::test_psgi"}) { return \&{"Plack::Test::$Impl\::test_psgi"}; } $subclass->new($app, @args); } sub test_psgi { if (ref $_[0] && @_ == 2) { @_ = (app => $_[0], client => $_[1]); } my %args = @_; my $app = delete $args{app}; # Backward compat: some implementations don't need app my $client = delete $args{client} or Carp::croak "client test code needed"; my $tester = Plack::Test->create($app, %args); return $tester->(@_) if ref $tester eq 'CODE'; # compatibility $client->(sub { $tester->request(@_) }); } 1; __END__ =head1 NAME Plack::Test - Test PSGI applications with various backends =head1 SYNOPSIS use Plack::Test; use HTTP::Request::Common; # Simple OO interface my $app = sub { return [ 200, [], [ "Hello "] ] }; my $test = Plack::Test->create($app); my $res = $test->request(GET "/"); is $res->content, "Hello"; # traditional - named params test_psgi app => sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello World" ] ], }, client => sub { my $cb = shift; my $req = HTTP::Request->new(GET => "http://localhost/hello"); my $res = $cb->($req); like $res->content, qr/Hello World/; }; # positional params (app, client) my $app = sub { return [ 200, [], [ "Hello "] ] }; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, "Hello"; }; =head1 DESCRIPTION Plack::Test is a unified interface to test PSGI applications using L<HTTP::Request> and L<HTTP::Response> objects. It also allows you to run PSGI applications in various ways. The default backend is C<Plack::Test::MockHTTP>, but you may also use any L<Plack::Handler> implementation to run live HTTP requests against a web server. =head1 METHODS =over 4 =item create $test = Plack::Test->create($app, %options); creates an instance of Plack::Test implementation class. C<$app> has to be a valid PSGI application code reference. =item request $res = $test->request($request); takes an HTTP::Request object, runs it through the PSGI application to test and returns an HTTP::Response object. =back =head1 FUNCTIONS Plack::Test also provides a functional interface that takes two callbacks, each of which represents PSGI application and HTTP client code that tests the application. =over 4 =item test_psgi test_psgi $app, $client; test_psgi app => $app, client => $client; Runs the client test code C<$client> against a PSGI application C<$app>. The client callback gets one argument C<$cb>, a callback that accepts an C<HTTP::Request> object and returns an C<HTTP::Response> object. Use L<HTTP::Request::Common> to import shortcuts for creating requests for C<GET>, C<POST>, C<DELETE>, and C<PUT> operations. For your convenience, the C<HTTP::Request> given to the callback automatically uses the HTTP protocol and the localhost (I<127.0.0.1> by default), so the following code just works: use HTTP::Request::Common; test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "/hello"); }; Note that however, it is not a good idea to pass an arbitrary (i.e. user-input) string to C<GET> or even C<< HTTP::Request->new >> by assuming that it always represents a path, because: my $req = GET "//foo/bar"; would represent a request for a URL that has no scheme, has a hostname I<foo> and a path I</bar>, instead of a path I<//foo/bar> which you might actually want. =back =head1 OPTIONS Specify the L<Plack::Test> backend using the environment variable C<PLACK_TEST_IMPL> or C<$Plack::Test::Impl> package variable. The available values for the backend are: =over 4 =item MockHTTP (Default) Creates a PSGI env hash out of HTTP::Request object, runs the PSGI application in-process and returns HTTP::Response. =item Server Runs one of Plack::Handler backends (C<Standalone> by default) and sends live HTTP requests to test. =item ExternalServer Runs tests against an external server specified in the C<PLACK_TEST_EXTERNALSERVER_URI> environment variable instead of spawning the application in a server locally. =back For instance, test your application with the C<HTTP::Server::ServerSimple> server backend with: > env PLACK_TEST_IMPL=Server PLACK_SERVER=HTTP::Server::ServerSimple \ prove -l t/test.t =head1 AUTHOR Tatsuhiko Miyagawa =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Util/������������������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 016314� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Util.pm����������������������������������������������������������������������000644 �000765 �000024 �00000034561 12244057435 016663� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Util; use strict; use Carp (); use Scalar::Util; use IO::Handle; use overload (); use File::Spec (); sub TRUE() { 1==1 } sub FALSE() { !TRUE } sub load_class { my($class, $prefix) = @_; if ($prefix) { unless ($class =~ s/^\+// || $class =~ /^$prefix/) { $class = "$prefix\::$class"; } } my $file = $class; $file =~ s!::!/!g; require "$file.pm"; ## no critic return $class; } sub is_real_fh ($) { my $fh = shift; { no warnings 'uninitialized'; return FALSE if -p $fh or -c _ or -b _; } my $reftype = Scalar::Util::reftype($fh) or return; if ( $reftype eq 'IO' or $reftype eq 'GLOB' && *{$fh}{IO} ) { # if it's a blessed glob make sure to not break encapsulation with # fileno($fh) (e.g. if you are filtering output then file descriptor # based operations might no longer be valid). # then ensure that the fileno *opcode* agrees too, that there is a # valid IO object inside $fh either directly or indirectly and that it # corresponds to a real file descriptor. my $m_fileno = $fh->fileno; return FALSE unless defined $m_fileno; return FALSE unless $m_fileno >= 0; my $f_fileno = fileno($fh); return FALSE unless defined $f_fileno; return FALSE unless $f_fileno >= 0; return TRUE; } else { # anything else, including GLOBS without IO (even if they are blessed) # and non GLOB objects that look like filehandle objects cannot have a # valid file descriptor in fileno($fh) context so may break. return FALSE; } } sub set_io_path { my($fh, $path) = @_; bless $fh, 'Plack::Util::IOWithPath'; $fh->path($path); } sub content_length { my $body = shift; return unless defined $body; if (ref $body eq 'ARRAY') { my $cl = 0; for my $chunk (@$body) { $cl += length $chunk; } return $cl; } elsif ( is_real_fh($body) ) { return (-s $body) - tell($body); } return; } sub foreach { my($body, $cb) = @_; if (ref $body eq 'ARRAY') { for my $line (@$body) { $cb->($line) if length $line; } } else { local $/ = \65536 unless ref $/; while (defined(my $line = $body->getline)) { $cb->($line) if length $line; } $body->close; } } sub class_to_file { my $class = shift; $class =~ s!::!/!g; $class . ".pm"; } sub _load_sandbox { my $_file = shift; my $_package = $_file; $_package =~ s/([^A-Za-z0-9_])/sprintf("_%2x", unpack("C", $1))/eg; local $0 = $_file; # so FindBin etc. works local @ARGV = (); # Some frameworks might try to parse @ARGV return eval sprintf <<'END_EVAL', $_package; package Plack::Sandbox::%s; { my $app = do $_file; if ( !$app && ( my $error = $@ || $! )) { die $error; } $app; } END_EVAL } sub load_psgi { my $stuff = shift; local $ENV{PLACK_ENV} = $ENV{PLACK_ENV} || 'development'; my $file = $stuff =~ /^[a-zA-Z0-9\_\:]+$/ ? class_to_file($stuff) : File::Spec->rel2abs($stuff); my $app = _load_sandbox($file); die "Error while loading $file: $@" if $@; return $app; } sub run_app($$) { my($app, $env) = @_; return eval { $app->($env) } || do { my $body = "Internal Server Error"; $env->{'psgi.errors'}->print($@); [ 500, [ 'Content-Type' => 'text/plain', 'Content-Length' => length($body) ], [ $body ] ]; }; } sub headers { my $headers = shift; inline_object( iter => sub { header_iter($headers, @_) }, get => sub { header_get($headers, @_) }, set => sub { header_set($headers, @_) }, push => sub { header_push($headers, @_) }, exists => sub { header_exists($headers, @_) }, remove => sub { header_remove($headers, @_) }, headers => sub { $headers }, ); } sub header_iter { my($headers, $code) = @_; my @headers = @$headers; # copy while (my($key, $val) = splice @headers, 0, 2) { $code->($key, $val); } } sub header_get { my($headers, $key) = (shift, lc shift); my @val; header_iter $headers, sub { push @val, $_[1] if lc $_[0] eq $key; }; return wantarray ? @val : $val[0]; } sub header_set { my($headers, $key, $val) = @_; my($set, @new_headers); header_iter $headers, sub { if (lc $key eq lc $_[0]) { return if $set; $_[1] = $val; $set++; } push @new_headers, $_[0], $_[1]; }; push @new_headers, $key, $val unless $set; @$headers = @new_headers; } sub header_push { my($headers, $key, $val) = @_; push @$headers, $key, $val; } sub header_exists { my($headers, $key) = (shift, lc shift); my $exists; header_iter $headers, sub { $exists = 1 if lc $_[0] eq $key; }; return $exists; } sub header_remove { my($headers, $key) = (shift, lc shift); my @new_headers; header_iter $headers, sub { push @new_headers, $_[0], $_[1] unless lc $_[0] eq $key; }; @$headers = @new_headers; } sub status_with_no_entity_body { my $status = shift; return $status < 200 || $status == 204 || $status == 304; } sub encode_html { my $str = shift; $str =~ s/&/&/g; $str =~ s/>/>/g; $str =~ s/</</g; $str =~ s/"/"/g; $str =~ s/'/'/g; return $str; } sub inline_object { my %args = @_; bless \%args, 'Plack::Util::Prototype'; } sub response_cb { my($res, $cb) = @_; my $body_filter = sub { my($cb, $res) = @_; my $filter_cb = $cb->($res); # If response_cb returns a callback, treat it as a $body filter if (defined $filter_cb && ref $filter_cb eq 'CODE') { Plack::Util::header_remove($res->[1], 'Content-Length'); if (defined $res->[2]) { if (ref $res->[2] eq 'ARRAY') { for my $line (@{$res->[2]}) { $line = $filter_cb->($line); } # Send EOF. my $eof = $filter_cb->( undef ); push @{ $res->[2] }, $eof if defined $eof; } else { my $body = $res->[2]; my $getline = sub { $body->getline }; $res->[2] = Plack::Util::inline_object getline => sub { $filter_cb->($getline->()) }, close => sub { $body->close }; } } else { return $filter_cb; } } }; if (ref $res eq 'ARRAY') { $body_filter->($cb, $res); return $res; } elsif (ref $res eq 'CODE') { return sub { my $respond = shift; my $cb = $cb; # To avoid the nested closure leak for 5.8.x $res->(sub { my $res = shift; my $filter_cb = $body_filter->($cb, $res); if ($filter_cb) { my $writer = $respond->($res); if ($writer) { return Plack::Util::inline_object write => sub { $writer->write($filter_cb->(@_)) }, close => sub { my $chunk = $filter_cb->(undef); $writer->write($chunk) if defined $chunk; $writer->close; }; } } else { return $respond->($res); } }); }; } return $res; } package Plack::Util::Prototype; our $AUTOLOAD; sub can { $_[0]->{$_[1]}; } sub AUTOLOAD { my $self = shift; my $attr = $AUTOLOAD; $attr =~ s/.*://; if (ref($self->{$attr}) eq 'CODE') { $self->{$attr}->(@_); } else { Carp::croak(qq/Can't locate object method "$attr" via package "Plack::Util::Prototype"/); } } sub DESTROY { } package Plack::Util::IOWithPath; use parent qw(IO::Handle); sub path { my $self = shift; if (@_) { ${*$self}{+__PACKAGE__} = shift; } ${*$self}{+__PACKAGE__}; } package Plack::Util; 1; __END__ =head1 NAME Plack::Util - Utility subroutines for Plack server and framework developers =head1 FUNCTIONS =over 4 =item TRUE, FALSE my $true = Plack::Util::TRUE; my $false = Plack::Util::FALSE; Utility constants to include when you specify boolean variables in C<$env> hash (e.g. C<psgi.multithread>). =item load_class my $class = Plack::Util::load_class($class [, $prefix ]); Constructs a class name and C<require> the class. Throws an exception if the .pm file for the class is not found, just with the built-in C<require>. If C<$prefix> is set, the class name is prepended to the C<$class> unless C<$class> begins with C<+> sign, which means the class name is already fully qualified. my $class = Plack::Util::load_class("Foo"); # Foo my $class = Plack::Util::load_class("Baz", "Foo::Bar"); # Foo::Bar::Baz my $class = Plack::Util::load_class("+XYZ::ZZZ", "Foo::Bar"); # XYZ::ZZZ Note that this function doesn't validate (or "sanitize") the passed string, hence if you pass a user input to this function (which is an insecure thing to do in the first place) it might lead to unexpected behavior of loading files outside your C<@INC> path. If you want a generic module loading function, you should check out CPAN modules such as L<Module::Runtime>. =item is_real_fh if ( Plack::Util::is_real_fh($fh) ) { } returns true if a given C<$fh> is a real file handle that has a file descriptor. It returns false if C<$fh> is PerlIO handle that is not really related to the underlying file etc. =item content_length my $cl = Plack::Util::content_length($body); Returns the length of content from body if it can be calculated. If C<$body> is an array ref it's a sum of length of each chunk, if C<$body> is a real filehandle it's a remaining size of the filehandle, otherwise returns undef. =item set_io_path Plack::Util::set_io_path($fh, "/path/to/foobar.txt"); Sets the (absolute) file path to C<$fh> filehandle object, so you can call C<< $fh->path >> on it. As a side effect C<$fh> is blessed to an internal package but it can still be treated as a normal file handle. This module doesn't normalize or absolutize the given path, and is intended to be used from Server or Middleware implementations. See also L<IO::File::WithPath>. =item foreach Plack::Util::foreach($body, $cb); Iterate through I<$body> which is an array reference or IO::Handle-like object and pass each line (which is NOT really guaranteed to be a I<line>) to the callback function. It internally sets the buffer length C<$/> to 65536 in case it reads the binary file, unless otherwise set in the caller's code. =item load_psgi my $app = Plack::Util::load_psgi $psgi_file_or_class; Load C<app.psgi> file or a class name (like C<MyApp::PSGI>) and require the file to get PSGI application handler. If the file can't be loaded (e.g. file doesn't exist or has a perl syntax error), it will throw an exception. Since version 1.0006, this function would not load PSGI files from include paths (C<@INC>) unless it looks like a class name that only consists of C<[A-Za-z0-9_:]>. For example: Plack::Util::load_psgi("app.psgi"); # ./app.psgi Plack::Util::load_psgi("/path/to/app.psgi"); # /path/to/app.psgi Plack::Util::load_psgi("MyApp::PSGI"); # MyApp/PSGI.pm from @INC B<Security>: If you give this function a class name or module name that is loadable from your system, it will load the module. This could lead to a security hole: my $psgi = ...; # user-input: consider "Moose" $app = Plack::Util::load_psgi($psgi); # this would lead to 'require "Moose.pm"'! Generally speaking, passing an external input to this function is considered very insecure. If you really want to do that, validate that a given file name contains dots (like C<foo.psgi>) and also turn it into a full path in your caller's code. =item run_app my $res = Plack::Util::run_app $app, $env; Runs the I<$app> by wrapping errors with I<eval> and if an error is found, logs it to C<< $env->{'psgi.errors'} >> and returns the template 500 Error response. =item header_get, header_exists, header_set, header_push, header_remove my $hdrs = [ 'Content-Type' => 'text/plain' ]; my $v = Plack::Util::header_get($hdrs, $key); # First found only my @v = Plack::Util::header_get($hdrs, $key); my $bool = Plack::Util::header_exists($hdrs, $key); Plack::Util::header_set($hdrs, $key, $val); # overwrites existent header Plack::Util::header_push($hdrs, $key, $val); Plack::Util::header_remove($hdrs, $key); Utility functions to manipulate PSGI response headers array reference. The methods that read existent header value handles header name as case insensitive. my $hdrs = [ 'Content-Type' => 'text/plain' ]; my $v = Plack::Util::header_get($hdrs, 'content-type'); # 'text/plain' =item headers my $headers = [ 'Content-Type' => 'text/plain' ]; my $h = Plack::Util::headers($headers); $h->get($key); if ($h->exists($key)) { ... } $h->set($key => $val); $h->push($key => $val); $h->remove($key); $h->headers; # same reference as $headers Given a header array reference, returns a convenient object that has an instance methods to access C<header_*> functions with an OO interface. The object holds a reference to the original given C<$headers> argument and updates the reference accordingly when called write methods like C<set>, C<push> or C<remove>. It also has C<headers> method that would return the same reference. =item status_with_no_entity_body if (status_with_no_entity_body($res->[0])) { } Returns true if the given status code doesn't have any Entity body in HTTP response, i.e. it's 100, 101, 204 or 304. =item inline_object my $o = Plack::Util::inline_object( write => sub { $h->push_write(@_) }, close => sub { $h->push_shutdown }, ); $o->write(@stuff); $o->close; Creates an instant object that can react to methods passed in the constructor. Handy to create when you need to create an IO stream object for input or errors. =item encode_html my $encoded_string = Plack::Util::encode( $string ); Entity encodes C<<>, C<< > >>, C<&>, C<"> and C<'> in the input string and returns it. =item response_cb See L<Plack::Middleware/RESPONSE CALLBACK> for details. =back =cut �����������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Util/Accessor.pm�������������������������������������������������������������000644 �000765 �000024 �00000001654 12244057435 020422� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Util::Accessor; use strict; use warnings; sub import { shift; return unless @_; my $package = caller(); mk_accessors( $package, @_ ); } sub mk_accessors { my $package = shift; no strict 'refs'; foreach my $field ( @_ ) { *{ $package . '::' . $field } = sub { return $_[0]->{ $field } if scalar( @_ ) == 1; return $_[0]->{ $field } = scalar( @_ ) == 2 ? $_[1] : [ @_[1..$#_] ]; }; } } 1; __END__ =head1 NAME Plack::Util::Accessor - Accessor generation utility for Plack =head1 DESCRIPTION This module is just a simple accessor generator for Plack to replace the Class::Accessor::Fast usage and so our classes don't have to inherit from their accessor generator. =head1 SEE ALSO L<PSGI> L<http://plackperl.org/> =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut ������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Test/MockHTTP.pm�������������������������������������������������������������000644 �000765 �000024 �00000002032 12244057435 020242� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Test::MockHTTP; use strict; use warnings; use Carp; use HTTP::Request; use HTTP::Response; use HTTP::Message::PSGI; use Try::Tiny; sub new { my($class, $app) = @_; bless { app => $app }, $class; } sub request { my($self, $req) = @_; $req->uri->scheme('http') unless defined $req->uri->scheme; $req->uri->host('localhost') unless defined $req->uri->host; my $env = $req->to_psgi; my $res = try { HTTP::Response->from_psgi($self->{app}->($env)); } catch { HTTP::Response->from_psgi([ 500, [ 'Content-Type' => 'text/plain' ], [ $_ ] ]); }; $res->request($req); return $res; } 1; __END__ =head1 NAME Plack::Test::MockHTTP - Run mocked HTTP tests through PSGI applications =head1 DESCRIPTION Plack::Test::MockHTTP is a utility to run PSGI application given HTTP::Request objects and return HTTP::Response object out of PSGI application response. See L<Plack::Test> how to use this module. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::Test> =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Test/Server.pm���������������������������������������������������������������000644 �000765 �000024 �00000002371 12244057435 020125� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Test::Server; use strict; use warnings; use Carp; use HTTP::Request; use HTTP::Response; use Test::TCP; use Plack::Loader; use Plack::LWPish; sub new { my($class, $app, %args) = @_; my $server = Test::TCP->new( code => sub { my $port = shift; my $server = Plack::Loader->auto(port => $port, host => ($args{host} || '127.0.0.1')); $server->run($app); exit; }, ); bless { server => $server, %args }, $class; } sub port { my $self = shift; $self->{server}->port; } sub request { my($self, $req) = @_; my $ua = $self->{ua} || Plack::LWPish->new( no_proxy => [qw/127.0.0.1/] ); $req->uri->scheme('http'); $req->uri->host($self->{host} || '127.0.0.1'); $req->uri->port($self->port); return $ua->request($req); } 1; __END__ =head1 NAME Plack::Test::Server - Run HTTP tests through live Plack servers =head1 DESCRIPTION Plack::Test::Server is a utility to run PSGI application with Plack server implementations, and run the live HTTP tests with the server using a callback. See L<Plack::Test> how to use this module. =head1 AUTHOR Tatsuhiko Miyagawa Tokuhiro Matsuno =head1 SEE ALSO L<Plack::Loader> L<Test::TCP> L<Plack::Test> =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Test/Suite.pm����������������������������������������������������������������000644 �000765 �000024 �00000057475 12244057435 017767� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Test::Suite; use strict; use warnings; use Digest::MD5; use File::ShareDir; use HTTP::Request; use HTTP::Request::Common; use Test::More; use Test::TCP; use Plack::Loader; use Plack::Middleware::Lint; use Plack::Util; use Plack::Request; use Try::Tiny; use Plack::LWPish; my $share_dir = try { File::ShareDir::dist_dir('Plack') } || 'share'; $ENV{PLACK_TEST_SCRIPT_NAME} = ''; # 0: test name # 1: request generator coderef. # 2: request handler # 3: test case for response our @TEST = ( [ 'SCRIPT_NAME', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); is $res->content, "script_name=$ENV{PLACK_TEST_SCRIPT_NAME}"; }, sub { my $env = shift; return [ 200, ["Content-Type", "text/plain"], [ "script_name=$env->{SCRIPT_NAME}" ] ]; }, ], [ 'GET', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/?name=miyagawa"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, 'Hello, name=miyagawa'; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [ 'Hello, ' . $env->{QUERY_STRING} ], ]; }, ], [ 'POST', sub { my $cb = shift; my $res = $cb->(POST "http://127.0.0.1/", [name => 'tatsuhiko']); is $res->code, 200; is $res->message, 'OK'; is $res->header('Client-Content-Length'), 14; is $res->header('Client-Content-Type'), 'application/x-www-form-urlencoded'; is $res->header('content_type'), 'text/plain'; is $res->content, 'Hello, name=tatsuhiko'; }, sub { my $env = shift; my $body; $env->{'psgi.input'}->read($body, $env->{CONTENT_LENGTH}); return [ 200, [ 'Content-Type' => 'text/plain', 'Client-Content-Length' => $env->{CONTENT_LENGTH}, 'Client-Content-Type' => $env->{CONTENT_TYPE}, ], [ 'Hello, ' . $body ], ]; }, ], [ 'big POST', sub { my $cb = shift; my $chunk = "abcdefgh" x 12000; my $req = HTTP::Request->new(POST => "http://127.0.0.1/"); $req->content_length(length $chunk); $req->content_type('application/octet-stream'); $req->content($chunk); my $res = $cb->($req); is $res->code, 200; is $res->message, 'OK'; is $res->header('Client-Content-Length'), length $chunk; is length $res->content, length $chunk; is Digest::MD5::md5_hex($res->content), Digest::MD5::md5_hex($chunk); }, sub { my $env = shift; my $len = $env->{CONTENT_LENGTH}; my $body = ''; my $spin; while ($len > 0) { my $rc = $env->{'psgi.input'}->read($body, $env->{CONTENT_LENGTH}, length $body); $len -= $rc; last if $spin++ > 2000; } return [ 200, [ 'Content-Type' => 'text/plain', 'Client-Content-Length' => $env->{CONTENT_LENGTH}, 'Client-Content-Type' => $env->{CONTENT_TYPE}, ], [ $body ], ]; }, ], [ 'psgi.url_scheme', sub { my $cb = shift; my $res = $cb->(POST "http://127.0.0.1/"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, 'http'; }, sub { my $env = $_[0]; return [ 200, [ 'Content-Type' => 'text/plain', ], [ $env->{'psgi.url_scheme'} ], ]; }, ], [ 'return glob', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; like $res->content, qr/^package /; like $res->content, qr/END_MARK_FOR_TESTING$/; }, sub { my $env = shift; open my $fh, '<', __FILE__ or die $!; return [ 200, [ 'Content-Type' => 'text/plain', ], $fh, ]; }, ], [ 'filehandle', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo.jpg"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'image/jpeg'; is length $res->content, 4745; }, sub { my $env = shift; open my $fh, '<', "$share_dir/face.jpg"; return [ 200, [ 'Content-Type' => 'image/jpeg', 'Content-Length' => -s $fh ], $fh ]; }, ], [ 'bigger file', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/baybridge.jpg"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'image/jpeg'; is length $res->content, 79838; is Digest::MD5::md5_hex($res->content), '983726ae0e4ce5081bef5fb2b7216950'; }, sub { my $env = shift; open my $fh, '<', "$share_dir/baybridge.jpg"; binmode $fh; return [ 200, [ 'Content-Type' => 'image/jpeg', 'Content-Length' => -s $fh ], $fh ]; }, ], [ 'handle HTTP-Header', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/?dankogai=kogaidan", Foo => "Bar"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, 'Bar'; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [$env->{HTTP_FOO}], ]; }, ], [ 'handle HTTP-Cookie', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/?dankogai=kogaidan", Cookie => "foo"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, 'foo'; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [$env->{HTTP_COOKIE}], ]; }, ], [ 'validate env', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/?dankogai=kogaidan"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, join("\n", 'REQUEST_METHOD:GET', "SCRIPT_NAME:$ENV{PLACK_TEST_SCRIPT_NAME}", 'PATH_INFO:/foo/', 'QUERY_STRING:dankogai=kogaidan', 'SERVER_NAME:127.0.0.1', "SERVER_PORT:" . $res->request->uri->port, )."\n"; }, sub { my $env = shift; my $body; $body .= $_ . ':' . $env->{$_} . "\n" for qw/REQUEST_METHOD SCRIPT_NAME PATH_INFO QUERY_STRING SERVER_NAME SERVER_PORT/; return [ 200, [ 'Content-Type' => 'text/plain', ], [$body], ]; }, ], [ '% encoding in PATH_INFO', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/bar%2cbaz"); is $res->content, "/foo/bar,baz", "PATH_INFO should be decoded per RFC 3875"; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [ $env->{PATH_INFO} ], ]; }, ], [ '% double encoding in PATH_INFO', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/bar%252cbaz"); is $res->content, "/foo/bar%2cbaz", "PATH_INFO should be decoded only once, per RFC 3875"; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [ $env->{PATH_INFO} ], ]; }, ], [ '% encoding in PATH_INFO (outside of URI characters)', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo%E3%81%82"); is $res->content, "/foo\x{e3}\x{81}\x{82}"; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [ $env->{PATH_INFO} ], ]; }, ], [ 'SERVER_PROTOCOL is required', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/?dankogai=kogaidan"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; like $res->content, qr{^HTTP/1\.[01]$}; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [$env->{SERVER_PROTOCOL}], ]; }, ], [ 'SCRIPT_NAME should not be undef', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/?dankogai=kogaidan"); is $res->content, 1; }, sub { my $env = shift; my $cont = defined $env->{'SCRIPT_NAME'}; return [ 200, [ 'Content-Type' => 'text/plain', ], [$cont], ]; }, ], [ 'call close after read IO::Handle-like', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/call_close"); is($res->content, '1234'); }, sub { my $env = shift; { our $closed = -1; sub CalledClose::new { $closed = 0; my $i=0; bless \$i, 'CalledClose' } sub CalledClose::getline { my $self = shift; return $$self++ < 4 ? $$self : undef; } sub CalledClose::close { ::ok(1, 'closed') if defined &::ok } } return [ 200, [ 'Content-Type' => 'text/plain', ], CalledClose->new(), ]; }, ], [ 'has errors', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/has_errors"); is $res->content, 1; }, sub { my $env = shift; my $err = $env->{'psgi.errors'}; my $has_errors = defined $err; return [ 200, [ 'Content-Type' => 'text/plain', ], [$has_errors] ]; }, ], [ 'status line', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/?dankogai=kogaidan"); is($res->status_line, '200 OK'); }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [1] ]; }, ], [ 'Do not crash when the app dies', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); is $res->code, 500; is $res->message, 'Internal Server Error'; }, sub { my $env = shift; open my $io, '>', \my $error; $env->{'psgi.errors'} = $io; die "Throwing an exception from app handler. Server shouldn't crash."; }, ], [ 'multi headers (request)', sub { my $cb = shift; my $req = HTTP::Request->new( GET => "http://127.0.0.1/", ); $req->push_header(Foo => "bar"); $req->push_header(Foo => "baz"); my $res = $cb->($req); like($res->content, qr/^bar,\s*baz$/); }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [ $env->{HTTP_FOO} ] ]; }, ], [ 'multi headers (response)', sub { my $cb = shift; my $res = $cb->(HTTP::Request->new(GET => "http://127.0.0.1/")); my $foo = $res->header('X-Foo'); like $foo, qr/foo,\s*bar,\s*baz/; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', 'X-Foo', 'foo', 'X-Foo', 'bar, baz' ], [ 'hi' ] ]; }, ], [ 'Do not set $env->{COOKIE}', sub { my $cb = shift; my $req = HTTP::Request->new( GET => "http://127.0.0.1/", ); $req->push_header(Cookie => "foo=bar"); my $res = $cb->($req); is($res->header('X-Cookie'), 0); is $res->content, 'foo=bar'; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', 'X-Cookie' => $env->{COOKIE} ? 1 : 0 ], [ $env->{HTTP_COOKIE} ] ]; }, ], [ 'no entity headers on 304', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); is $res->code, 304; is $res->message, 'Not Modified'; is $res->content, ''; ok ! defined $res->header('content_type'), "No Content-Type"; ok ! defined $res->header('content_length'), "No Content-Length"; ok ! defined $res->header('transfer_encoding'), "No Transfer-Encoding"; }, sub { my $env = shift; return [ 304, [], [] ]; }, ], [ 'REQUEST_URI is set', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo/bar%20baz%73?x=a"); is $res->content, $ENV{PLACK_TEST_SCRIPT_NAME} . "/foo/bar%20baz%73?x=a"; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [ $env->{REQUEST_URI} ] ]; }, ], [ 'filehandle with path()', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/foo.jpg"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'image/jpeg'; is length $res->content, 4745; }, sub { my $env = shift; open my $fh, '<', "$share_dir/face.jpg"; Plack::Util::set_io_path($fh, "$share_dir/face.jpg"); return [ 200, [ 'Content-Type' => 'image/jpeg', 'Content-Length' => -s $fh ], $fh ]; }, ], [ 'a big header value > 128 bytes', sub { my $cb = shift; my $req = GET "http://127.0.0.1/"; my $v = ("abcdefgh" x 16); $req->header('X-Foo' => $v); my $res = $cb->($req); is $res->code, 200; is $res->message, 'OK'; is $res->content, $v; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [ $env->{HTTP_X_FOO} ], ]; }, ], [ 'coderef res', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/?name=miyagawa"); return if $res->code == 501; is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, 'Hello, name=miyagawa'; }, sub { my $env = shift; $env->{'psgi.streaming'} or return [ 501, ['Content-Type','text/plain'], [] ]; return sub { my $respond = shift; $respond->([ 200, [ 'Content-Type' => 'text/plain', ], [ 'Hello, ' . $env->{QUERY_STRING} ], ]); } }, ], [ 'coderef streaming', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/?name=miyagawa"); return if $res->code == 501; is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, 'Hello, name=miyagawa'; }, sub { my $env = shift; $env->{'psgi.streaming'} or return [ 501, ['Content-Type','text/plain'], [] ]; return sub { my $respond = shift; my $writer = $respond->([ 200, [ 'Content-Type' => 'text/plain', ], ]); $writer->write("Hello, "); $writer->write($env->{QUERY_STRING}); $writer->close(); } }, ], [ 'CRLF output and FCGI parse bug', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); is $res->header("Foo"), undef; is $res->content, "Foo: Bar\r\n\r\nHello World"; }, sub { return [ 200, [ "Content-Type", "text/plain" ], [ "Foo: Bar\r\n\r\nHello World" ] ]; }, ], [ 'newlines', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); is length($res->content), 7; }, sub { return [ 200, [ "Content-Type", "text/plain" ], [ "Bar\nBaz" ] ]; }, ], [ 'test 404', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); is $res->code, 404; is $res->message, 'Not Found'; is $res->content, 'Not Found'; }, sub { return [ 404, [ "Content-Type", "text/plain" ], [ "Not Found" ] ]; }, ], [ 'request->input seekable', sub { my $cb = shift; my $req = HTTP::Request->new(POST => "http://127.0.0.1/"); $req->content("body"); $req->content_type('text/plain'); $req->content_length(4); my $res = $cb->($req); is $res->content, 'body'; }, sub { my $req = Plack::Request->new(shift); return [ 200, [ "Content-Type", "text/plain" ], [ $req->content ] ]; }, ], [ 'request->content on GET', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1/"); ok $res->is_success; }, sub { my $req = Plack::Request->new(shift); $req->content; return [ 200, [ "Content-Type", "text/plain" ], [ "OK" ] ]; }, ], [ 'handle Authorization header', sub { my $cb = shift; SKIP: { skip "Authorization header is unsupported under CGI", 4 if ($ENV{PLACK_TEST_HANDLER} || "") eq "CGI"; { my $req = HTTP::Request->new( GET => "http://127.0.0.1/", ); $req->push_header(Authorization => 'Basic XXXX'); my $res = $cb->($req); is $res->header('X-AUTHORIZATION'), 1; is $res->content, 'Basic XXXX'; }; { my $req = HTTP::Request->new( GET => "http://127.0.0.1/", ); my $res = $cb->($req); is $res->header('X-AUTHORIZATION'), 0; is $res->content, 'no_auth'; }; }; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', 'X-AUTHORIZATION' => exists($env->{HTTP_AUTHORIZATION}) ? 1 : 0 ], [ $env->{HTTP_AUTHORIZATION} || 'no_auth' ], ]; }, ], [ 'repeated slashes', sub { my $cb = shift; my $res = $cb->(GET "http://127.0.0.1//foo///bar/baz"); is $res->code, 200; is $res->message, 'OK'; is $res->header('content_type'), 'text/plain'; is $res->content, '//foo///bar/baz'; }, sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain', ], [ $env->{PATH_INFO} ], ]; }, ], ); sub runtests { my($class, $runner) = @_; for my $test (@TEST) { $runner->(@$test); } } sub run_server_tests { my($class, $server, $server_port, $http_port, %args) = @_; if (ref $server ne 'CODE') { my $server_class = $server; $server = sub { my($port, $app) = @_; my $server = Plack::Loader->load($server_class, port => $port, host => "127.0.0.1", %args); $app = Plack::Middleware::Lint->wrap($app); $server->run($app); } } test_tcp( client => sub { my $port = shift; my $ua = Plack::LWPish->new( no_proxy => [qw/127.0.0.1/] ); for my $i (0..$#TEST) { my $test = $TEST[$i]; note $test->[0]; my $cb = sub { my $req = shift; $req->uri->port($http_port || $port); $req->uri->path(($ENV{PLACK_TEST_SCRIPT_NAME}||"") . $req->uri->path); $req->header('X-Plack-Test' => $i); return $ua->request($req); }; $test->[1]->($cb); } }, server => sub { my $port = shift; my $app = $class->test_app_handler; $server->($port, $app); exit(0); # for Test::TCP }, port => $server_port, ); } sub test_app_handler { return sub { my $env = shift; $TEST[$env->{HTTP_X_PLACK_TEST}][2]->($env); }; } 1; __END__ =head1 NAME Plack::Test::Suite - Test suite for Plack handlers =head1 SYNOPSIS use Test::More; use Plack::Test::Suite; Plack::Test::Suite->run_server_tests('Your::Handler'); done_testing; =head1 DESCRIPTION Plack::Test::Suite is a test suite to test a new PSGI server implementation. It automatically loads a new handler environment and uses LWP to send HTTP requests to the local server to make sure your handler implements the PSGI specification correctly. Note that the handler name doesn't include the C<Plack::Handler::> prefix, i.e. if you have a new Plack handler Plack::Handler::Foo, your test script would look like: Plack::Test::Suite->run_server_tests('Foo'); =head1 AUTHOR Tokuhiro Matsuno Tatsuhiko Miyagawa Kazuho Oku =cut END_MARK_FOR_TESTING ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Request/Upload.pm������������������������������������������������������������000644 �000765 �000024 �00000003160 12244057435 020611� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Request::Upload; use strict; use warnings; use Carp (); sub new { my($class, %args) = @_; bless { headers => $args{headers}, tempname => $args{tempname}, size => $args{size}, filename => $args{filename}, }, $class; } sub filename { $_[0]->{filename} } sub headers { $_[0]->{headers} } sub size { $_[0]->{size} } sub tempname { $_[0]->{tempname} } sub path { $_[0]->{tempname} } sub content_type { my $self = shift; $self->{headers}->content_type(@_); } sub type { shift->content_type(@_) } sub basename { my $self = shift; unless (defined $self->{basename}) { require File::Spec::Unix; my $basename = $self->{filename}; $basename =~ s|\\|/|g; $basename = ( File::Spec::Unix->splitpath($basename) )[2]; $basename =~ s|[^\w\.-]+|_|g; $self->{basename} = $basename; } $self->{basename}; } 1; __END__ =head1 NAME Plack::Request::Upload - handles file upload requests =head1 SYNOPSIS # $req is Plack::Request my $upload = $req->uploads->{field}; $upload->size; $upload->path; $upload->content_type; $upload->basename; =head1 METHODS =over 4 =item size Returns the size of Uploaded file. =item path Returns the path to the temporary file where uploaded file is saved. =item content_type Returns the content type of the uploaded file. =item filename Returns the original filename in the client. =item basename Returns basename for "filename". =back =head1 AUTHORS Kazuhiro Osawa Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::Request>, L<Catalyst::Request::Upload> =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/AccessLog/��������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 021317� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/AccessLog.pm������������������������������������������������������000644 �000765 �000024 �00000011472 12244057435 021662� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::AccessLog; use strict; use warnings; use parent qw( Plack::Middleware ); use Plack::Util::Accessor qw( logger format compiled_format); use Apache::LogFormat::Compiler; my %formats = ( common => '%h %l %u %t "%r" %>s %b', combined => '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"', ); sub prepare_app { my $self = shift; my $fmt = $self->format || "combined"; $fmt = $formats{$fmt} if exists $formats{$fmt}; $self->compiled_format(Apache::LogFormat::Compiler->new($fmt)); } sub call { my $self = shift; my($env) = @_; my $res = $self->app->($env); if ( ref($res) && ref($res) eq 'ARRAY' ) { my $content_length = Plack::Util::content_length($res->[2]); my $log_line = $self->log_line($res->[0], $res->[1], $env, { content_length => $content_length }); if ( my $logger = $self->logger ) { $logger->($log_line); } else { $env->{'psgi.errors'}->print($log_line); } return $res; } return $self->response_cb($res, sub { my $res = shift; my $content_length = Plack::Util::content_length($res->[2]); my $log_line = $self->log_line($res->[0], $res->[1], $env, { content_length => $content_length }); if ( my $logger = $self->logger ) { $logger->($log_line); } else { $env->{'psgi.errors'}->print($log_line); } }); } sub log_line { my($self, $status, $headers, $env, $opts) = @_; $self->compiled_format->log_line( $env, [$status,$headers], $opts->{content_length}, $opts->{time} ); } 1; __END__ =for stopwords LogFormat =head1 NAME Plack::Middleware::AccessLog - Logs requests like Apache's log format =head1 SYNOPSIS # in app.psgi use Plack::Builder; builder { enable "Plack::Middleware::AccessLog", format => "combined"; $app; }; =head1 DESCRIPTION Plack::Middleware::AccessLog forwards the request to the given app and logs request and response details to the logger callback. The format can be specified using Apache-like format strings (or C<combined> or C<common> for the default formats). If none is specified C<combined> is used. This middleware uses calculable Content-Length by checking body type, and cannot log the time taken to serve requests. It also logs the request B<before> the response is actually sent to the client. Use L<Plack::Middleware::AccessLog::Timed> if you want to log details B<after> the response is transmitted (more like a real web server) to the client. This middleware is enabled by default when you run L<plackup> as a default C<development> environment. =head1 CONFIGURATION =over 4 =item format enable "Plack::Middleware::AccessLog", format => '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"'; Takes a format string (or a preset template C<combined> or C<custom>) to specify the log format. This middleware uses L<Apache::LogFormat::Compiler> to generate access_log lines. See more details on perldoc L<Apache::LogFormat::Compiler> %% a percent sign %h REMOTE_ADDR from the PSGI environment, or - %l remote logname not implemented (currently always -) %u REMOTE_USER from the PSGI environment, or - %t [local timestamp, in default format] %r REQUEST_METHOD, REQUEST_URI and SERVER_PROTOCOL from the PSGI environment %s the HTTP status code of the response %b content length of the response %T custom field for handling times in subclasses %D custom field for handling sub-second times in subclasses %v SERVER_NAME from the PSGI environment, or - %V HTTP_HOST or SERVER_NAME from the PSGI environment, or - %p SERVER_PORT from the PSGI environment %P the worker's process id %m REQUEST_METHOD from the PSGI environment %U PATH_INFO from the PSGI environment %q QUERY_STRING from the PSGI environment %H SERVER_PROTOCOL from the PSGI environment Some of these format fields are only supported by middleware that subclasses C<AccessLog>. In addition, custom values can be referenced, using C<%{name}>, with one of the mandatory modifier flags C<i>, C<o> or C<t>: %{variable-name}i HTTP_VARIABLE_NAME value from the PSGI environment %{header-name}o header-name header in the response %{time-format]t localtime in the specified strftime format =item logger my $logger = Log::Dispatch->new(...); enable "Plack::Middleware::AccessLog", logger => sub { $logger->log(level => 'debug', message => @_) }; Sets a callback to print log message to. It prints to the C<psgi.errors> output stream by default. =back =head1 AUTHORS Tatsuhiko Miyagawa Masahiro Nagano =head1 SEE ALSO L<Apache::LogFormat::Compiler>, L<http://httpd.apache.org/docs/2.2/mod/mod_log_config.html> Rack::CustomLogger =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Auth/�������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 020355� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/BufferedStreaming.pm����������������������������������������������000644 �000765 �000024 �00000003340 12244057435 023406� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::BufferedStreaming; use strict; no warnings; use Carp; use Plack::Util; use Plack::Util::Accessor qw(force); use Scalar::Util qw(weaken); use parent qw(Plack::Middleware); sub call { my ( $self, $env ) = @_; my $caller_supports_streaming = $env->{'psgi.streaming'}; $env->{'psgi.streaming'} = Plack::Util::TRUE; my $res = $self->app->($env); return $res if $caller_supports_streaming && !$self->force; if ( ref($res) eq 'CODE' ) { my $ret; $res->(sub { my $write = shift; if ( @$write == 2 ) { my @body; $ret = [ @$write, \@body ]; return Plack::Util::inline_object( write => sub { push @body, $_[0] }, close => sub { }, ); } else { $ret = $write; return; } }); return $ret; } else { return $res; } } 1; __END__ =head1 NAME Plack::Middleware::BufferedStreaming - Enable buffering for non-streaming aware servers =head1 SYNOPSIS enable "BufferedStreaming"; =head1 DESCRIPTION Plack::Middleware::BufferedStreaming is a PSGI middleware component that wraps the application that uses C<psgi.streaming> interface to run on the servers that do not support the interface, by buffering the writer output to a temporary buffer. This middleware doesn't do anything and bypass the application if the server supports C<psgi.streaming> interface, unless you set C<force> option (see below). =head1 OPTIONS =over 4 =item force Force enable this middleware only if the container supports C<psgi.streaming>. =back =head1 AUTHOR Yuval Kogman Tatsuhiko Miyagawa =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Chunked.pm��������������������������������������������������������000644 �000765 �000024 �00000002704 12244057435 021376� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Chunked; use strict; use parent qw(Plack::Middleware); use Plack::Util; sub call { my($self, $env) = @_; my $res = $self->app->($env); $self->response_cb($res, sub { my $res = shift; my $h = Plack::Util::headers($res->[1]); if ($env->{'SERVER_PROTOCOL'} ne 'HTTP/1.0' and ! Plack::Util::status_with_no_entity_body($res->[0]) and ! $h->exists('Content-Length') and ! $h->exists('Transfer-Encoding') ) { $h->set('Transfer-Encoding' => 'chunked'); my $done; return sub { my $chunk = shift; return if $done; unless (defined $chunk) { $done = 1; return "0\015\012\015\012"; } return '' unless length $chunk; return sprintf('%x', length $chunk) . "\015\012$chunk\015\012"; }; } }); } 1; __END__ =head1 NAME Plack::Middleware::Chunked - Applies chunked encoding to the response body =head1 SYNOPSIS # Mostly from server implemenations $app = Plack::Middeware::Chunked->wrap($app); =head1 DESCRIPTION Plack::Middeware::Chunked is a middleware, or rather a library for PSGI server to automatically add chunked encoding to the response body when Content-Length is not set in the response header. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO Rack::Chunked =cut ������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Conditional.pm����������������������������������������������������000644 �000765 �000024 �00000004146 12244057435 022262� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Conditional; use strict; use parent qw(Plack::Middleware); use Plack::Util::Accessor qw( condition middleware builder ); sub prepare_app { my $self = shift; $self->middleware( $self->builder->($self->app) ); } sub call { my($self, $env) = @_; my $app = $self->condition->($env) ? $self->middleware : $self->app; return $app->($env); } 1; __END__ =head1 NAME Plack::Middleware::Conditional - Conditional wrapper for Plack middleware =head1 SYNOPSIS use Plack::Builder; builder { enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' } 'StackTrace', force => 1; $app; }; # or using the OO interface: $app = Plack::Middleware::Conditional->wrap( $app, condition => sub { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' }, builder => sub { Plack::Middleware::StackTrace->wrap($_[0], force => 1) }, ); =head1 DESCRIPTION Plack::Middleware::Conditional is a piece of meta-middleware, to run a specific middleware component under runtime conditions. The goal of this middleware is to avoid baking runtime configuration options in individual middleware components, and rather share them as another middleware component. =head1 EXAMPLES Note that some of the middleware component names are just made up for the explanation and might not exist. # Minify JavaScript if the browser is Firefox enable_if { $_[0]->{HTTP_USER_AGENT} =~ /Firefox/ } 'JavaScriptMinifier'; # Enable Stacktrace when being accessed from the local network enable_if { $_[0]->{REMOTE_ADDR} =~ /^10\.0\.1\.*/ } 'StackTrace'; # Work with other conditional setter middleware: # Transcode Jpeg on the fly for mobile clients builder { enable 'MobileDetector'; enable_if { $_[0]->{'plack.mobile_detected'} } 'TranscodeJpeg', max_size => 30_000; $app; }; Note that in the last example I<MobileDetector> should come first because the conditional check runs in I<pre-run> conditions, which is from outer to inner: that is, from the top to the bottom in the Builder DSL code. =head1 AUTHOR Tatsuhiko Miyagawa Steve Cook =head1 SEE ALSO L<Plack::Builder> =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/ConditionalGET.pm�������������������������������������������������000644 �000765 �000024 �00000004104 12244057435 022614� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::ConditionalGET; use strict; use parent qw( Plack::Middleware ); use Plack::Util; sub call { my $self = shift; my $env = shift; my $res = $self->app->($env); return $res unless $env->{REQUEST_METHOD} =~ /^(GET|HEAD)$/; $self->response_cb($res, sub { my $res = shift; my $h = Plack::Util::headers($res->[1]); if ( $self->etag_matches($h, $env) || $self->not_modified_since($h, $env) ) { $res->[0] = 304; $h->remove($_) for qw( Content-Type Content-Length Content-Disposition ); if ($res->[2]) { $res->[2] = []; } else { return sub { return defined $_[0] ? '' : undef; }; } } }); } no warnings 'uninitialized'; # RFC 2616 14.25 says it's OK and expected to use 'eq' :) # > Note: When handling an If-Modified-Since header field, some # > servers will use an exact date comparison function, rather than a # > less-than function, for deciding whether to send a 304 ... sub etag_matches { my($self, $h, $env) = @_; $h->exists('ETag') && $h->get('ETag') eq _value($env->{HTTP_IF_NONE_MATCH}); } sub not_modified_since { my($self, $h, $env) = @_; $h->exists('Last-Modified') && $h->get('Last-Modified') eq _value($env->{HTTP_IF_MODIFIED_SINCE}); } sub _value { my $str = shift; # IE sends wrong formatted value(i.e. "Thu, 03 Dec 2009 01:46:32 GMT; length=17936") $str =~ s/;.*$//; return $str; } 1; __END__ =head1 NAME Plack::Middleware::ConditionalGET - Middleware to enable conditional GET =head1 SYNOPSIS builder { enable "ConditionalGET"; .... }; =head1 DESCRIPTION This middleware enables conditional GET and HEAD using C<If-None-Match> and C<If-Modified-Since> header. The application should set either or both of C<Last-Modified> or C<ETag> response headers per RFC 2616. When either of the conditions is met, the response body is set to be zero length and the status is set to 304 Not Modified. =head1 SEE ALSO Rack::ConditionalGet =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/ContentLength.pm��������������������������������������������������000644 �000765 �000024 �00000002571 12244057435 022573� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::ContentLength; use strict; use warnings; use parent qw( Plack::Middleware ); use Plack::Util; sub call { my $self = shift; my $res = $self->app->(@_); return $self->response_cb($res, sub { my $res = shift; my $h = Plack::Util::headers($res->[1]); if (!Plack::Util::status_with_no_entity_body($res->[0]) && !$h->exists('Content-Length') && !$h->exists('Transfer-Encoding') && defined(my $content_length = Plack::Util::content_length($res->[2]))) { $h->push('Content-Length' => $content_length); } }); } 1; __END__ =head1 NAME Plack::Middleware::ContentLength - Adds Content-Length header automatically =head1 SYNOPSIS # in app.psgi builder { enable "Plack::Middleware::ContentLength"; $app; } # Or in Plack::Handler::* $app = Plack::Middleware::ContentLength->wrap($app); =head1 DESCRIPTION Plack::Middleware::ContentLength is a middleware that automatically adds C<Content-Length> header when it's appropriate i.e. the response has a content body with calculable size (array of chunks or a real filehandle). This middleware can also be used as a library from PSGI server implementations to automatically set C<Content-Length> rather than in the end user level. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO Rack::ContentLength =cut ���������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/ContentMD5.pm�����������������������������������������������������000644 �000765 �000024 �00000002204 12244057435 021730� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::ContentMD5; use strict; use warnings; use parent qw( Plack::Middleware ); use Plack::Util; use Digest::MD5 qw/md5_hex/; sub call { my $self = shift; my $res = $self->app->(@_); $self->response_cb($res, sub { my $res = shift; return unless defined $res->[2]; return if (Plack::Util::status_with_no_entity_body($res->[0])); my $h = Plack::Util::headers($res->[1]); return if ( $h->exists('Content-MD5') ); my $body = $res->[2]; if (ref $body eq 'ARRAY') { $h->set('Content-MD5', md5_hex(@$body)); } # Do we need support $fh? return; }); } 1; __END__ =head1 NAME Plack::Middleware::ContentMD5 - Automatically sets the Content-MD5 header on all String bodies =head1 SYNOPSIS use Plack::Builder; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable "Plack::Middleware::ContentMD5"; $app; }; =head1 DESCRIPTION Automatically sets the Content-MD5 header on all String bodies =head1 AUTHOR Fayland Lam =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/ErrorDocument.pm��������������������������������������������������000644 �000765 �000024 �00000007421 12244057435 022606� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::ErrorDocument; use strict; use warnings; use parent qw(Plack::Middleware); use Plack::MIME; use Plack::Util; use Plack::Util::Accessor qw( subrequest ); use HTTP::Status qw(is_error); sub call { my $self = shift; my $env = shift; my $r = $self->app->($env); $self->response_cb($r, sub { my $r = shift; unless (is_error($r->[0]) && exists $self->{$r->[0]}) { return; } my $path = $self->{$r->[0]}; if ($self->subrequest) { for my $key (keys %$env) { unless ($key =~ /^psgi/) { $env->{'psgix.errordocument.' . $key} = $env->{$key}; } } # TODO: What if SCRIPT_NAME is not empty? $env->{REQUEST_METHOD} = 'GET'; $env->{REQUEST_URI} = $path; $env->{PATH_INFO} = $path; $env->{QUERY_STRING} = ''; delete $env->{CONTENT_LENGTH}; my $sub_r = $self->app->($env); if ($sub_r->[0] == 200) { $r->[1] = $sub_r->[1]; if (@$r == 3) { $r->[2] = $sub_r->[2]; } else { my $full_sub_response = ''; Plack::Util::foreach($sub_r->[2], sub { $full_sub_response .= $_[0]; }); my $returned; return sub { if ($returned) { return defined($_[0]) ? '' : undef; } $returned = 1; return $full_sub_response; } } } # TODO: allow 302 here? } else { my $h = Plack::Util::headers($r->[1]); $h->remove('Content-Length'); $h->remove('Content-Encoding'); $h->remove('Transfer-Encoding'); $h->set('Content-Type', Plack::MIME->mime_type($path)); open my $fh, "<", $path or die "$path: $!"; if ($r->[2]) { $r->[2] = $fh; } else { my $done; return sub { unless ($done) { return join '', <$fh>; } $done = 1; return defined $_[0] ? '' : undef; }; }; } }); } 1; __END__ =head1 NAME Plack::Middleware::ErrorDocument - Set Error Document based on HTTP status code =head1 SYNOPSIS # in app.psgi use Plack::Builder; builder { enable "Plack::Middleware::ErrorDocument", 500 => '/uri/errors/500.html', 404 => '/uri/errors/404.html', subrequest => 1; $app; }; =head1 DESCRIPTION Plack::Middleware::ErrorDocument allows you to customize error screen by setting paths (file system path or URI path) of error pages per status code. =head1 CONFIGURATIONS =over 4 =item subrequest A boolean flag to serve error pages using a new GET sub request. Defaults to false, which means it serves error pages using file system path. builder { enable "Plack::Middleware::ErrorDocument", 502 => '/home/www/htdocs/errors/maint.html'; enable "Plack::Middleware::ErrorDocument", 404 => '/static/404.html', 403 => '/static/403.html', subrequest => 1; $app; }; This configuration serves 502 error pages from file system directly assuming that's when you probably maintain database etc. but serves 404 and 403 pages using a sub request so your application can do some logic there like logging or doing suggestions. When using a subrequest, the subrequest should return a regular '200' response. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Head.pm�����������������������������������������������������������000644 �000765 �000024 �00000001351 12244057435 020653� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Head; use strict; use warnings; use parent qw(Plack::Middleware); sub call { my($self, $env) = @_; return $self->app->($env) unless $env->{REQUEST_METHOD} eq 'HEAD'; $self->response_cb($self->app->($env), sub { my $res = shift; if ($res->[2]) { $res->[2] = []; } else { return sub { return defined $_[0] ? '': undef; }; } }); } 1; __END__ =head1 NAME Plack::Middleware::Head - auto delete response body in HEAD requests =head1 SYNOPSIS enable "Head"; =head1 DESCRIPTION This middleware deletes response body in HEAD requests. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO Rack::Head =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/HTTPExceptions.pm�������������������������������������������������000644 �000765 �000024 �00000007642 12244057435 022644� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::HTTPExceptions; use strict; use parent qw(Plack::Middleware); use Plack::Util::Accessor qw(rethrow); use Carp (); use Try::Tiny; use Scalar::Util 'blessed'; use HTTP::Status (); sub prepare_app { my $self = shift; $self->rethrow(1) if ($ENV{PLACK_ENV} || '') eq 'development'; } sub call { my($self, $env) = @_; my $res = try { $self->app->($env); } catch { $self->transform_error($_, $env); }; return $res if ref $res eq 'ARRAY'; return sub { my $respond = shift; my $writer; try { $res->(sub { return $writer = $respond->(@_) }); } catch { if ($writer) { Carp::cluck $_; $writer->close; } else { my $res = $self->transform_error($_, $env); $respond->($res); } }; }; } sub transform_error { my($self, $e, $env) = @_; my($code, $message); if (blessed $e && $e->can('as_psgi')) { return $e->as_psgi; } if (blessed $e && $e->can('code')) { $code = $e->code; $message = $e->can('as_string') ? $e->as_string : overload::Method($e, '""') ? "$e" : undef; } else { if ($self->rethrow) { die $e; } else { $code = 500; $env->{'psgi.errors'}->print($e); } } if ($code !~ /^[3-5]\d\d$/) { die $e; # rethrow } $message ||= HTTP::Status::status_message($code); my @headers = ( 'Content-Type' => 'text/plain', 'Content-Length' => length($message), ); if ($code =~ /^3/ && (my $loc = eval { $e->location })) { push(@headers, Location => $loc); } return [ $code, \@headers, [ $message ] ]; } 1; __END__ =head1 NAME Plack::Middleware::HTTPExceptions - Catch HTTP exceptions =head1 SYNOPSIS use HTTP::Exception; my $app = sub { # ... HTTP::Exception::500->throw; }; builder { enable "HTTPExceptions", rethrow => 1; $app; }; =head1 DESCRIPTION Plack::Middleware::HTTPExceptions is a PSGI middleware component to catch exceptions from applications that can be translated into HTTP status codes. Your application is supposed to throw an object that implements a C<code> method which returns the HTTP status code, such as 501 or 404. This middleware catches them and creates a valid response out of the code. If the C<code> method returns a code that is not an HTTP redirect or error code (3xx, 4xx, or 5xx), the exception will be rethrown. The exception object may also implement C<as_string> or overload stringification to represent the text of the error. The text defaults to the status message of the error code, such as I<Service Unavailable> for C<503>. Finally, the exception object may implement C<as_psgi>, and the result of this will be returned directly as the PSGI response. If the code is in the 3xx range and the exception implements the 'location' method (HTTP::Exception::3xx does), the Location header will be set in the response, so you can do redirects this way. There are CPAN modules L<HTTP::Exception> and L<HTTP::Throwable>, and they are perfect to throw from your application to let this middleware catch and display, but you can also implement your own exception class to throw. If the thrown exception is not an object that implements either a C<code> or an C<as_psgi> method, a 500 error will be returned, and the exception is printed to the psgi.errors stream. Alternatively, you can pass a true value for the C<rethrow> parameter for this middleware, and the exception will instead be rethrown. This is enabled by default when C<PLACK_ENV> is set to C<development>, so that the L<StackTrace|Plack::Middleware::StackTrace> middleware can catch it instead. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO paste.httpexceptions L<HTTP::Exception> L<HTTP::Throwable> =cut ����������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/IIS6ScriptNameFix.pm����������������������������������������������000644 �000765 �000024 �00000002271 12244057435 023163� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::IIS6ScriptNameFix; use strict; use parent 'Plack::Middleware'; sub call { my($self, $env) = @_; if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ /IIS\/[6-9]\.[0-9]/) { my @script_name = split(m!/!, $env->{PATH_INFO}); my @path_translated = split(m!/|\\\\?!, $env->{PATH_TRANSLATED}); my @path_info; while ($script_name[$#script_name] eq $path_translated[$#path_translated]) { pop(@path_translated); unshift(@path_info, pop(@script_name)); } unshift(@path_info, '', ''); $env->{PATH_INFO} = join('/', @path_info); $env->{SCRIPT_NAME} = join('/', @script_name); } return $self->app->($env); } 1; __END__ =head1 NAME Plack::Middleware::IIS6ScriptNameFix - fixes wrong SCRIPT_NAME and PATH_INFO that IIS6 sets =head1 SYNOPSIS # in your app.psgi use Plack::Builder; builder { enable "IIS6ScriptNameFix"; $app; }; # Or from the command line plackup -s FCGI -e 'enable "IIS6ScriptNameFix"' /path/to/app.psgi =head1 DESCRIPTION This middleware fixes wrong C<SCRIPT_NAME> and C<PATH_INFO> set by IIS6. =head1 AUTHORS Florian Ragwitz =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/IIS7KeepAliveFix.pm�����������������������������������������������000644 �000765 �000024 �00000002203 12244057435 022757� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::IIS7KeepAliveFix; use strict; use parent 'Plack::Middleware'; use Plack::Util; sub call { my($self, $env) = @_; # Fixes buffer being cut off on redirect when keep-alive is active my $res = $self->app->($env); Plack::Util::response_cb($res, sub { my $res = shift; if ($res->[0] =~ m!^30[123]$! ) { Plack::Util::header_remove($res->[1], 'Content-Length'); Plack::Util::header_remove($res->[1], 'Content-Type'); return sub{ my $chunk; return unless defined $chunk; return ''; }; } return; }); } 1; __END__ =head1 NAME Plack::Middleware::IIS7KeepAliveFix - fixes buffer being cut off on redirect when keep-alive is active on IIS. =head1 SYNOPSIS # in your app.psgi use Plack::Builder; builder { enable "IIS7KeepAliveFix"; $app; }; # Or from the command line plackup -s FCGI -e 'enable "IIS7KeepAliveFix"' /path/to/app.psgi =head1 DESCRIPTION This middleware fixes buffer being cut off on redirect when keep-alive is active on IIS7. =head1 AUTHORS KnowZeroX =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/JSONP.pm����������������������������������������������������������000644 �000765 �000024 �00000003443 12244057435 020707� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::JSONP; use strict; use parent qw(Plack::Middleware); use Plack::Util; use URI::Escape (); use Plack::Util::Accessor qw/callback_key/; sub prepare_app { my $self = shift; unless (defined $self->callback_key) { $self->callback_key('callback'); } } sub call { my($self, $env) = @_; my $res = $self->app->($env); $self->response_cb($res, sub { my $res = shift; if (defined $res->[2]) { my $h = Plack::Util::headers($res->[1]); my $callback_key = $self->callback_key; if ($h->get('Content-Type') =~ m!/(?:json|javascript)! && $env->{QUERY_STRING} =~ /(?:^|&)$callback_key=([^&]+)/) { my $cb = URI::Escape::uri_unescape($1); if ($cb =~ /^[\w\.\[\]]+$/) { my $body; Plack::Util::foreach($res->[2], sub { $body .= $_[0] }); my $jsonp = "$cb($body)"; $res->[2] = [ $jsonp ]; $h->set('Content-Length', length $jsonp); $h->set('Content-Type', 'text/javascript'); } } } }); } 1; __END__ =head1 NAME Plack::Middleware::JSONP - Wraps JSON response in JSONP if callback parameter is specified =head1 SYNOPSIS enable "JSONP", callback_key => 'jsonp'; =head1 DESCRIPTION Plack::Middleware::JSONP wraps JSON response, which has Content-Type value either C<text/javascript> or C<application/json> as a JSONP response which is specified with the C<callback> query parameter. The name of the parameter can be set while enabling the middleware. This middleware only works with a non-streaming response, and doesn't touch the response otherwise. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack> =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/LighttpdScriptNameFix.pm������������������������������������������000644 �000765 �000024 �00000004370 12244057435 024232� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::LighttpdScriptNameFix; use strict; use parent qw/Plack::Middleware/; use Plack::Util::Accessor qw(script_name); sub prepare_app { my $self = shift; my $script_name = $self->script_name; $script_name = '' unless defined($script_name); $script_name =~ s!/$!!; $self->script_name($script_name); } sub call { my($self, $env) = @_; if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ /lighttpd/) { $env->{PATH_INFO} = $env->{SCRIPT_NAME} . $env->{PATH_INFO}; $env->{SCRIPT_NAME} = $self->script_name; $env->{PATH_INFO} =~ s/^\Q$env->{SCRIPT_NAME}\E//; } return $self->app->($env); } 1; __END__ =head1 NAME Plack::Middleware::LighttpdScriptNameFix - fixes wrong SCRIPT_NAME and PATH_INFO that lighttpd sets =head1 SYNOPSIS # in your app.psgi use Plack::Builder; builder { enable "LighttpdScriptNameFix"; $app; }; # Or from the command line plackup -s FCGI -e 'enable "LighttpdScriptNameFix"' /path/to/app.psgi =head1 DESCRIPTION This middleware fixes wrong C<SCRIPT_NAME> and C<PATH_INFO> set by lighttpd when you mount your app under the root path ("/"). If you use lighttpd 1.4.23 or later you can instead enable C<fix-root-scriptname> flag inside C<fastcgi.server> instead of using this middleware. =head1 CONFIGURATION =over 4 =item script_name Even with C<fix-root-scriptname>, lighttpd I<still> sets weird C<SCRIPT_NAME> and C<PATH_INFO> if you mount your application at C<""> or something that ends with C</>. Setting C<script_name> option tells the middleware how to reconstruct the new correct C<SCRIPT_NAME> and C<PATH_INFO>. If you mount the app under C</something/>, you should set: enable "LighttpdScriptNameFix", script_name => "/something"; and when a request for C</something/a/b?param=1> comes, C<SCRIPT_NAME> becomes C</something> and C<PATH_INFO> becomes C</a/b>. C<script_name> option is set to empty by default, which means all the request path is set to C<PATH_INFO> and it behaves like your fastcgi application is mounted in the root path. =back =head1 AUTHORS Yury Zavarin Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::Handler::FCGI> L<http://github.com/plack/Plack/issues#issue/68> L<https://redmine.lighttpd.net/issues/729> =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Lint.pm�����������������������������������������������������������000644 �000765 �000024 �00000016014 12244057435 020722� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Lint; use strict; no warnings; use Carp (); use parent qw(Plack::Middleware); use Scalar::Util qw(blessed); use Plack::Util; sub wrap { my($self, $app) = @_; unless (ref $app eq 'CODE' or overload::Method($app, '&{}')) { die("PSGI app should be a code reference: ", (defined $app ? $app : "undef")); } $self->SUPER::wrap($app); } sub call { my $self = shift; my $env = shift; $self->validate_env($env); my $res = $self->app->($env); return $self->validate_res($res); } sub validate_env { my ($self, $env) = @_; unless ($env->{REQUEST_METHOD}) { die('Missing env param: REQUEST_METHOD'); } unless ($env->{REQUEST_METHOD} =~ /^[A-Z]+$/) { die("Invalid env param: REQUEST_METHOD($env->{REQUEST_METHOD})"); } unless (defined($env->{SCRIPT_NAME})) { # allows empty string die('Missing mandatory env param: SCRIPT_NAME'); } if ($env->{SCRIPT_NAME} eq '/') { die('SCRIPT_NAME must not be /'); } unless (defined($env->{PATH_INFO})) { # allows empty string die('Missing mandatory env param: PATH_INFO'); } if ($env->{PATH_INFO} ne '' && $env->{PATH_INFO} !~ m!^/!) { die('PATH_INFO must begin with / ($env->{PATH_INFO})'); } unless (defined($env->{SERVER_NAME})) { die('Missing mandatory env param: SERVER_NAME'); } if ($env->{SERVER_NAME} eq '') { die('SERVER_NAME must not be empty string'); } unless (defined($env->{SERVER_PORT})) { die('Missing mandatory env param: SERVER_PORT'); } if ($env->{SERVER_PORT} eq '') { die('SERVER_PORT must not be empty string'); } if (defined($env->{SERVER_PROTOCOL}) and $env->{SERVER_PROTOCOL} !~ m{^HTTP/1.\d$}) { die("Invalid SERVER_PROTOCOL: $env->{SEREVR_PROTOCOL}"); } for my $param (qw/version url_scheme input errors multithread multiprocess/) { unless (exists $env->{"psgi.$param"}) { die("Missing psgi.$param"); } } unless (ref($env->{'psgi.version'}) eq 'ARRAY') { die("psgi.version should be ArrayRef: $env->{'psgi.version'}"); } unless (scalar(@{$env->{'psgi.version'}}) == 2) { die('psgi.version should contain 2 elements, not ', scalar(@{$env->{'psgi.version'}})); } unless ($env->{'psgi.url_scheme'} =~ /^https?$/) { die("psgi.url_scheme should be 'http' or 'https': ", $env->{'psgi.url_scheme'}); } if ($env->{"psgi.version"}->[1] == 1) { # 1.1 for my $param (qw(streaming nonblocking run_once)) { unless (exists $env->{"psgi.$param"}) { die("Missing psgi.$param"); } } } if ($env->{HTTP_CONTENT_TYPE}) { die('HTTP_CONTENT_TYPE should not exist'); } if ($env->{HTTP_CONTENT_LENGTH}) { die('HTTP_CONTENT_LENGTH should not exist'); } } sub is_possibly_fh { my $fh = shift; ref $fh eq 'GLOB' && *{$fh}{IO} && *{$fh}{IO}->can('getline'); } sub validate_res { my ($self, $res, $streaming) = @_; unless (ref($res) eq 'ARRAY' or ref($res) eq 'CODE') { die("Response should be array ref or code ref: $res"); } if (ref $res eq 'CODE') { return $self->response_cb($res, sub { $self->validate_res(@_, 1) }); } unless (@$res == 3 || ($streaming && @$res == 2)) { die('Response needs to be 3 element array, or 2 element in streaming'); } unless ($res->[0] =~ /^\d+$/ && $res->[0] >= 100) { die("Status code needs to be an integer greater than or equal to 100: $res->[0]"); } unless (ref $res->[1] eq 'ARRAY') { die("Headers needs to be an array ref: $res->[1]"); } my @copy = @{$res->[1]}; unless (@copy % 2 == 0) { die('The number of response headers needs to be even, not odd(', scalar(@copy), ')'); } while(my($key, $val) = splice(@copy, 0, 2)) { if (lc $key eq 'status') { die('Response headers MUST NOT contain a key named Status'); } if ($key =~ /[:\r\n]|[-_]$/) { die("Response headers MUST NOT contain a key with : or newlines, or that end in - or _: $key"); } unless ($key =~ /^[a-zA-Z][0-9a-zA-Z\-_]*$/) { die("Response headers MUST consist only of letters, digits, _ or - and MUST start with a letter: $key"); } if ($val =~ /[\000-\037]/) { die("Response headers MUST NOT contain characters below octal \037: $val"); } if (!defined $val) { die("Response headers MUST be a defined string"); } } # @$res == 2 is only right in psgi.streaming, and it's already checked. unless (@$res == 2 || ref $res->[2] eq 'ARRAY' || Plack::Util::is_real_fh($res->[2]) || is_possibly_fh($res->[2]) || (blessed($res->[2]) && $res->[2]->can('getline'))) { die("Body should be an array ref or filehandle: $res->[2]"); } if (ref $res->[2] eq 'ARRAY' && grep _has_wide_char($_), @{$res->[2]}) { die("Body must be bytes and should not contain wide characters (UTF-8 strings)"); } return $res; } # NOTE: Some modules like HTML:: or XML:: could possibly generate # ASCII/Latin-1 strings with utf8 flags on. They're actually safe to # print, so there's no need to give warnings about it. sub _has_wide_char { my $str = shift; utf8::is_utf8($str) && $str =~ /[^\x00-\xff]/; } 1; __END__ =head1 NAME Plack::Middleware::Lint - Validate request and response =head1 SYNOPSIS use Plack::Middleware::Lint; my $app = sub { ... }; # your app or middleware $app = Plack::Middleware::Lint->wrap($app); # Or from plackup plackup -e 'enable "Lint"' myapp.psgi =head1 DESCRIPTION Plack::Middleware::Lint is a middleware component to validate request and response environment formats. You are strongly suggested to use this middleware when you develop a new framework adapter or a new PSGI web server that implements the PSGI interface. This middleware is enabled by default when you run plackup or other launcher tools with the default environment I<development> value. =head1 DEBUGGING Because of how this middleware works, it may not be easy to debug Lint errors when you encounter one, unless you're writing a PSGI web server or a framework. For example, when you're an application developer (user of some framework) and see errors like: Body should be an array ref or filehandle at lib/Plack/Middleware/Lint.pm line XXXX there's no clue about which line of I<your application> produces that error. We're aware of the issue, and have a plan to spit out more helpful errors to diagnose the issue. But until then, currently there are some workarounds to make this easier. For now, the easiest one would be to enable L<Plack::Middleware::REPL> outside of the Lint middleware, like: plackup -e 'enable "REPL"; enable "Lint"' app.psgi so that the Lint errors are caught by the REPL shell, where you can inspect all the variables in the response. =head1 AUTHOR Tatsuhiko Miyagawa Tokuhiro Matsuno =head1 SEE ALSO L<Plack> =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Log4perl.pm�������������������������������������������������������000644 �000765 �000024 �00000003515 12244057435 021506� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Log4perl; use strict; use parent qw(Plack::Middleware); use Plack::Util::Accessor qw(category logger conf); use Carp (); sub prepare_app { my $self = shift; if ($self->conf) { require Log::Log4perl; Log::Log4perl::init($self->conf); } $self->logger( Log::Log4perl->get_logger($self->category || '') ); } sub call { my($self, $env) = @_; $env->{'psgix.logger'} = sub { my $args = shift; my $level = $args->{level}; local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; $self->logger->$level($args->{message}); }; $self->app->($env); } 1; __END__ =head1 NAME Plack::Middleware::Log4perl - Uses Log::Log4perl to configure logger =head1 SYNOPSIS use Log::Log4perl; Log::Log4perl::init('/path/to/log4perl.conf'); builder { enable "Log4perl", category => "plack"; $app; } # in log4perl.conf log4perl.logger.plack = INFO, Logfile log4perl.appender.Logfile = Log::Log4perl::Appender::File log4perl.appender.Logfile.filename = /path/to/logfile.log log4perl.appender.Logfile.layout = Log::Log4perl::Layout::SimpleLayout # Or let middleware to configure log4perl enable "Log4perl", category => "plack", conf => '/path/to/log.conf'; =head1 DESCRIPTION Log4perl is a L<Plack::Middleware> component that allows you to use L<Log::Log4perl> to configure the logging object, C<psgix.logger>. =head1 CONFIGURATION =over 4 =item category The C<log4perl> category to send logs to. Defaults to C<''> which means it send to the root logger. =item conf The configuration file path (or a scalar ref containing the config string) for L<Log::Log4perl> to automatically configure. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Log::Log4perl> L<Plack::Middleware::LogDispatch> =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/LogDispatch.pm����������������������������������������������������000644 �000765 �000024 �00000003044 12244057435 022214� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::LogDispatch; use strict; use parent qw(Plack::Middleware); use Plack::Util::Accessor qw(logger); use Carp (); sub prepare_app { my $self = shift; unless ($self->logger) { Carp::croak "logger is not defined"; } } sub call { my($self, $env) = @_; $env->{'psgix.logger'} = sub { my $args = shift; $args->{level} = 'critical' if $args->{level} eq 'fatal'; if ( ref $args->{message} && ref $args->{message} ne 'CODE' ) { $args->{message} .= q{}; } $self->logger->log(%$args); }; $self->app->($env); } 1; __END__ =head1 NAME Plack::Middleware::LogDispatch - Uses Log::Dispatch to configure logger =head1 SYNOPSIS use Log::Dispatch; my $logger = Log::Dispatch->new; $logger->add( Log::Dispatch::File->new(...) ); $logger->add( Log::Dispatch::DesktopNotification->new(...) ); builder { enable "LogDispatch", logger => $logger; $app; } # use with Log::Dispatch::Config use Log::Dispatch::Config; Log::Dispatch::Config->configure('/path/to/log.conf'); builder { enable "LogDispatch", logger => Log::Dispatch::Config->instance; ... } =head1 DESCRIPTION LogDispatch is a L<Plack::Middleware> component that allows you to use L<Log::Dispatch> to configure the logging object, C<psgix.logger>. =head1 CONFIGURATION =over 4 =item logger L<Log::Dispatch> object to send logs to. Required. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Log::Dispatch> L<Plack::Middleware::Log4perl> =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/NullLogger.pm�����������������������������������������������������000644 �000765 �000024 �00000001027 12244057435 022064� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::NullLogger; use strict; use parent qw/Plack::Middleware/; sub call { my($self, $env) = @_; $env->{'psgix.logger'} = sub { }; $self->app->($env); } 1; __END__ =head1 NAME Plack::Middleware::NullLogger - Send logs to /dev/null =head1 SYNOPSIS enable "NullLogger"; =head1 DESCRIPTION NullLogger is a middleware component that receives logs and does nothing but discarding them. Might be useful to shut up all the logs from frameworks in one shot. =head1 AUTHOR Tatsuhiko Miyagawa =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/RearrangeHeaders.pm�����������������������������������������������000644 �000765 �000024 �00000002617 12244057435 023222� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::RearrangeHeaders; use strict; use warnings; use parent qw( Plack::Middleware ); use HTTP::Headers; sub call { my $self = shift; my $res = $self->app->(@_); $self->response_cb($res, sub { my $res = shift; my $h = HTTP::Headers->new(@{$res->[1]}); my @new_headers; $h->scan(sub { push @new_headers, @_ }); $res->[1] = \@new_headers; }); } 1; __END__ =head1 NAME Plack::Middleware::RearrangeHeaders - Reorder HTTP headers for buggy clients =head1 SYNOPSIS use Plack::Builder; my $app = sub { return [ 200, [ 'Last-Modified' => 'Wed, 23 Sep 2009 13:36:33 GMT', 'Content-Type' => 'text/plain', 'ETag' => 'foo bar', ], [ 'Hello Foo' ] ]; }; builder { enable "Plack::Middleware::RearrangeHeaders"; $app; }; =head1 DESCRIPTION Plack::Middleware::RearrangeHeaders sorts HTTP headers based on "Good Practice" i.e.: # "Good Practice" order of HTTP message headers: # - Response-Headers # - Entity-Headers to work around buggy clients like very old MSIE or broken HTTP proxy servers. Most clients today don't (and shouldn't) care about HTTP header order but if you're too pedantic or have some environments where you need to deal with buggy clients like above, this might be useful. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<HTTP::Headers> =cut �����������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Recursive.pm������������������������������������������������������000644 �000765 �000024 �00000006740 12244057435 021770� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Recursive; use strict; use parent qw(Plack::Middleware); use Try::Tiny; use Scalar::Util qw(blessed); open my $null_io, "<", \""; sub call { my($self, $env) = @_; $env->{'plack.recursive.include'} = $self->recurse_callback($env, 1); my $res = try { $self->app->($env); } catch { if (blessed $_ && $_->isa('Plack::Recursive::ForwardRequest')) { return $self->recurse_callback($env)->($_->path); } else { die $_; # rethrow } }; return $res if ref $res eq 'ARRAY'; return sub { my $respond = shift; my $writer; try { $res->(sub { return $writer = $respond->(@_) }); } catch { if (!$writer && blessed $_ && $_->isa('Plack::Recursive::ForwardRequest')) { $res = $self->recurse_callback($env)->($_->path); return ref $res eq 'CODE' ? $res->($respond) : $respond->($res); } else { die $_; } }; }; } sub recurse_callback { my($self, $env, $include) = @_; my $old_path_info = $env->{PATH_INFO}; return sub { my $new_path_info = shift; my($path, $query) = split /\?/, $new_path_info, 2; Scalar::Util::weaken($env); $env->{PATH_INFO} = $path; $env->{QUERY_STRING} = $query; $env->{REQUEST_METHOD} = 'GET'; $env->{CONTENT_LENGTH} = 0; $env->{CONTENT_TYPE} = ''; $env->{'psgi.input'} = $null_io; push @{$env->{'plack.recursive.old_path_info'}}, $old_path_info; $include ? $self->app->($env) : $self->call($env); }; } package Plack::Recursive::ForwardRequest; use overload q("") => \&as_string, fallback => 1; sub new { my($class, $path) = @_; bless { path => $path }, $class; } sub path { $_[0]->{path} } sub throw { my($class, @args) = @_; die $class->new(@args); } sub as_string { my $self = shift; return "Forwarding to $self->{path}: Your application should be wrapped with Plack::Middleware::Recursive."; } package Plack::Middleware::Recursive; 1; __END__ =head1 NAME Plack::Middleware::Recursive - Allows PSGI apps to include or forward requests recursively =head1 SYNOPSIS # with Builder enable "Recursive"; # in apps my $res = $env->{'plack.recursive.include'}->("/new_path"); # Or, use exceptions my $app = sub { # ... Plack::Recursive::ForwardRequest->throw("/new_path"); }; =head1 DESCRIPTION Plack::Middleware::Recursive allows PSGI applications to recursively include or forward requests to other paths. Applications can make use of callbacks stored in C<< $env->{'plack.recursive.include'} >> to I<include> another path to get the response (whether it's an array ref or a code ref depending on your application), or throw an exception Plack::Recursive::ForwardRequest anywhere in the code to I<forward> the current request (i.e. abort the current and redo the request). =head1 EXCEPTIONS This middleware passes through unknown exceptions to the outside middleware stack, so if you use this middleware with other exception handlers such as L<Plack::Middleware::StackTrace> or L<Plack::Middleware::HTTPExceptions>, be sure to wrap this so L<Plack::Middleware::Recursive> gets as inner as possible. =head1 AUTHORS Tatsuhiko Miyagawa Masahiro Honma =head1 SEE ALSO L<Plack> L<Plack::Middleware::HTTPExceptions> The idea, code and interface are stolen from Rack::Recursive and paste.recursive. =cut ��������������������������������Plack-1.0030/lib/Plack/Middleware/Refresh.pm��������������������������������������������������������000644 �000765 �000024 �00000003051 12244057435 021407� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Refresh; use strict; use parent qw(Plack::Middleware); use Module::Refresh; use Plack::Util::Accessor qw(last cooldown); sub prepare_app { my $self = shift; $self->cooldown(10) unless defined $self->cooldown; Module::Refresh->new; $self->last(time - $self->cooldown); } sub call { my($self, $env) = @_; if (time > $self->last + $self->cooldown) { Module::Refresh->refresh; $self->last(time); } $self->app->($env); } 1; __END__ =head1 NAME Plack::Middleware::Refresh - Refresh all modules in %INC =head1 SYNOPSIS enable "Refresh", cooldown => 3; $app; =head1 DESCRIPTION This is I<yet another> approach to refresh modules in C<%INC> during the development cycle, without the need to have a forking process to watch for filesystem updates. This middleware, in a request time, compares the last refresh time and the current time and if the difference is bigger than I<cooldown> seconds which defaults to 10, call L<Module::Refresh> to reload all Perl modules in C<%INC> if the files have been modified. Note that this only reloads modules and not other files such as templates. This middleware is quite similar to what Rack::Reoader does. If you have issues with this reloading technique, for instance when you have in-file templates that needs to be recompiled, or Moose classes that has C<make_immutable>, take a look at L<plackup>'s default -r option or L<Plack::Loader::Shotgun> instead. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Module::Refresh> Rack::Reloader =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Runtime.pm��������������������������������������������������������000644 �000765 �000024 �00000001726 12244057435 021443� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Runtime; use strict; use parent qw(Plack::Middleware); use Plack::Util; use Plack::Util::Accessor qw(header_name); use Time::HiRes; sub call { my($self, $env) = @_; my $start = [ Time::HiRes::gettimeofday ]; my $res = $self->app->($env); $self->response_cb($res, sub { my $res = shift; my $req_time = sprintf '%.6f', Time::HiRes::tv_interval($start); Plack::Util::header_set($res->[1], 'X-Runtime', $req_time); }); } 1; __END__ =head1 NAME Plack::Middleware::Runtime - Sets an X-Runtime response header =head1 SYNOPSIS enable "Runtime"; =head1 DESCRIPTION Plack::Middleware::Runtime is a Plack middleware component that sets the application's response time (in seconds) in the I<X-Runtime> HTTP response header. =head1 OPTIONS =over 4 =item header_name Name of the header. Defaults to I<X-Runtime>. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Time::HiRes> Rack::Runtime =cut ������������������������������������������Plack-1.0030/lib/Plack/Middleware/SimpleContentFilter.pm��������������������������������������������000644 �000765 �000024 �00000002641 12244057435 023747� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::SimpleContentFilter; use strict; use warnings; use parent qw( Plack::Middleware ); use Plack::Util; use Plack::Util::Accessor qw( filter ); sub call { my $self = shift; my $res = $self->app->(@_); $self->response_cb($res, sub { my $res = shift; my $h = Plack::Util::headers($res->[1]); return unless $h->get('Content-Type'); if ($h->get('Content-Type') =~ m!^text/!) { return sub { my $chunk = shift; return unless defined $chunk; local $_ = $chunk; $self->filter->(); return $_; }; } }); } 1; __END__ =head1 NAME Plack::Middleware::SimpleContentFilter - Filters response content =head1 SYNOPSIS use Plack::Builder; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable "Plack::Middleware::SimpleContentFilter", filter => sub { s/Foo/Bar/g; }; $app; }; =head1 DESCRIPTION B<This middleware should be considered as a demo. Running this against your application might break your HTML unless you code the filter callback carefully>. Plack::Middleware::SimpleContentFilter is a simple content text filter to run against response body. This middleware is only enabled against responses with C<text/*> Content-Type. =head1 AUTHOR Tatsuhiko Miyagawa =cut �����������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/SimpleLogger.pm���������������������������������������������������000644 �000765 �000024 �00000003157 12244057435 022411� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::SimpleLogger; use strict; use parent qw(Plack::Middleware); use Plack::Util::Accessor qw(level); use POSIX (); use Scalar::Util (); # Should this be in Plack::Util? my $i = 0; my %level_numbers = map { $_ => $i++ } qw(debug info warn error fatal); sub call { my($self, $env) = @_; my $min = $level_numbers{ $self->level || "debug" }; my $env_ref = $env; Scalar::Util::weaken($env_ref); $env->{'psgix.logger'} = sub { my $args = shift; if ($level_numbers{$args->{level}} >= $min) { $env_ref->{'psgi.errors'}->print($self->format_message($args->{level}, $args->{message})); } }; $self->app->($env); } sub format_time { my $old_locale = POSIX::setlocale(&POSIX::LC_ALL); POSIX::setlocale(&POSIX::LC_ALL, 'C'); my $out = POSIX::strftime(@_); POSIX::setlocale(&POSIX::LC_ALL, $old_locale); return $out; } sub format_message { my($self, $level, $message) = @_; my $time = format_time("%Y-%m-%dT%H:%M:%S", localtime); sprintf "%s [%s #%d] %s: %s\n", uc substr($level, 0, 1), $time, $$, uc $level, $message; } 1; __END__ =head1 NAME Plack::Middleware::SimpleLogger - Simple logger that prints to psgi.errors =head1 SYNOPSIS enable "SimpleLogger", level => "warn"; =head1 DESCRIPTION SimpleLogger is a middleware component that formats the log message with information such as the time and PID and prints them to I<psgi.errors> stream, which is mostly STDERR or server log output. =head1 SEE ALSO L<Plack::Middleware::LogErrors>, essentially the opposite of this module =head1 AUTHOR Tatsuhiko Miyagawa =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/StackTrace.pm�����������������������������������������������������000644 �000765 �000024 �00000011727 12244057435 022046� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::StackTrace; use strict; use warnings; use parent qw/Plack::Middleware/; use Devel::StackTrace; use Devel::StackTrace::AsHTML; use Try::Tiny; use Plack::Util::Accessor qw( force no_print_errors ); our $StackTraceClass = "Devel::StackTrace"; # Optional since it needs PadWalker if (try { require Devel::StackTrace::WithLexicals; Devel::StackTrace::WithLexicals->VERSION(0.08); 1 }) { $StackTraceClass = "Devel::StackTrace::WithLexicals"; } sub call { my($self, $env) = @_; my $trace; local $SIG{__DIE__} = sub { $trace = $StackTraceClass->new( indent => 1, message => munge_error($_[0], [ caller ]), ignore_package => __PACKAGE__, ); die @_; }; my $caught; my $res = try { $self->app->($env); } catch { $caught = $_; [ 500, [ "Content-Type", "text/plain; charset=utf-8" ], [ no_trace_error(utf8_safe($caught)) ] ]; }; if ($trace && ($caught || ($self->force && ref $res eq 'ARRAY' && $res->[0] == 500)) ) { my $text = $trace->as_string; my $html = $trace->as_html; $env->{'plack.stacktrace.text'} = $text; $env->{'plack.stacktrace.html'} = $html; $env->{'psgi.errors'}->print($text) unless $self->no_print_errors; if (($env->{HTTP_ACCEPT} || '*/*') =~ /html/) { $res = [500, ['Content-Type' => 'text/html; charset=utf-8'], [ utf8_safe($html) ]]; } else { $res = [500, ['Content-Type' => 'text/plain; charset=utf-8'], [ utf8_safe($text) ]]; } } # break $trace here since $SIG{__DIE__} holds the ref to it, and # $trace has refs to Standalone.pm's args ($conn etc.) and # prevents garbage collection to be happening. undef $trace; return $res; } sub no_trace_error { my $msg = shift; chomp($msg); return <<EOF; The application raised the following error: $msg and the StackTrace middleware couldn't catch its stack trace, possibly because your application overrides \$SIG{__DIE__} by itself, preventing the middleware from working correctly. Remove the offending code or module that does it: known examples are CGI::Carp and Carp::Always. EOF } sub munge_error { my($err, $caller) = @_; return $err if ref $err; # Ugly hack to remove " at ... line ..." automatically appended by perl # If there's a proper way to do this, please let me know. $err =~ s/ at \Q$caller->[1]\E line $caller->[2]\.\n$//; return $err; } sub utf8_safe { my $str = shift; # NOTE: I know messing with utf8:: in the code is WRONG, but # because we're running someone else's code that we can't # guarantee which encoding an exception is encoded, there's no # better way than doing this. The latest Devel::StackTrace::AsHTML # (0.08 or later) encodes high-bit chars as HTML entities, so this # path won't be executed. if (utf8::is_utf8($str)) { utf8::encode($str); } $str; } 1; __END__ =head1 NAME Plack::Middleware::StackTrace - Displays stack trace when your app dies =head1 SYNOPSIS enable "StackTrace"; =head1 DESCRIPTION This middleware catches exceptions (run-time errors) happening in your application and displays nice stack trace screen. The stack trace is also stored in the environment as a plaintext and HTML under the key C<plack.stacktrace.text> and C<plack.stacktrace.html> respectively, so that middleware further up the stack can reference it. This middleware is enabled by default when you run L<plackup> in the default I<development> mode. You're recommended to use this middleware during the development and use L<Plack::Middleware::HTTPExceptions> in the deployment mode as a replacement, so that all the exceptions thrown from your application still get caught and rendered as a 500 error response, rather than crashing the web server. Catching errors in streaming response is not supported. =head1 CONFIGURATION =over 4 =item force enable "StackTrace", force => 1; Force display the stack trace when an error occurs within your application and the response code from your application is 500. Defaults to off. The use case of this option is that when your framework catches all the exceptions in the main handler and returns all failures in your code as a normal 500 PSGI error response. In such cases, this middleware would never have a chance to display errors because it can't tell if it's an application error or just random C<eval> in your code. This option enforces the middleware to display stack trace even if it's not the direct error thrown by the application. =item no_print_errors enable "StackTrace", no_print_errors => 1; Skips printing the text stacktrace to console (C<psgi.errors>). Defaults to 0, which means the text version of the stack trace error is printed to the errors handle, which usually is a standard error. =back =head1 AUTHOR Tokuhiro Matsuno Tatsuhiko Miyagawa =head1 SEE ALSO L<Devel::StackTrace::AsHTML> L<Plack::Middleware> L<Plack::Middleware::HTTPExceptions> =cut �����������������������������������������Plack-1.0030/lib/Plack/Middleware/Static.pm���������������������������������������������������������000644 �000765 �000024 �00000010402 12244057435 021236� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Static; use strict; use warnings; use parent qw/Plack::Middleware/; use Plack::App::File; use Plack::Util::Accessor qw( path root encoding pass_through content_type ); sub call { my $self = shift; my $env = shift; my $res = $self->_handle_static($env); if ($res && not ($self->pass_through and $res->[0] == 404)) { return $res; } return $self->app->($env); } sub _handle_static { my($self, $env) = @_; my $path_match = $self->path or return; my $path = $env->{PATH_INFO}; for ($path) { my $matched = 'CODE' eq ref $path_match ? $path_match->($_, $env) : $_ =~ $path_match; return unless $matched; } $self->{file} ||= Plack::App::File->new({ root => $self->root || '.', encoding => $self->encoding, content_type => $self->content_type }); local $env->{PATH_INFO} = $path; # rewrite PATH return $self->{file}->call($env); } 1; __END__ =head1 NAME Plack::Middleware::Static - serve static files with Plack =head1 SYNOPSIS use Plack::Builder; builder { enable "Plack::Middleware::Static", path => qr{^/(images|js|css)/}, root => './htdocs/'; $app; }; =head1 DESCRIPTION This middleware allows your Plack-based application to serve static files. Note that if you are building an app using L<Plack::App::URLMap>, you should consider using L<Plack::App::File> to serve static files instead. This makes the overall routing of your application simpler to understand. With this middleware, if a static file exists for the requested path, it will be served. If it does not exist, by default this middleware returns a 404, but you can set the C<pass_through> option to change this behavior. If the requested document is not within the C<root> or the file is there but not readable, this middleware will return a 403 Forbidden response. The content type returned will be determined from the file extension by using L<Plack::MIME> or using C<content_type>. =head1 CONFIGURATIONS =over 4 =item path, root enable "Plack::Middleware::Static", path => qr{^/static/}, root => 'htdocs/'; The C<path> option specifies the URL pattern (regular expression) or a callback to match against requests. If the <path> option matches, the middleware looks in C<root> to find the static files to serve. The default value of C<root> is the current directory. This example configuration serves C</static/foo.jpg> from C<htdocs/static/foo.jpg>. Note that the matched portion of the path, C</static/>, still appears in the locally mapped path under C<root>. If you don't want this to happen, you can use a callback to munge the path as you match it: enable "Plack::Middleware::Static", path => sub { s!^/static/!! }, root => 'static-files/'; The callback should operate on C<$_> and return a true or false value. Any changes it makes to C<$_> are used when looking for the static file in the C<root>. The configuration above serves C</static/foo.png> from C<static-files/foo.png>, not C<static-files/static/foo.png>. The callback specified in the C<path> option matches against C<$_> munges this value using C<s///>. The substitution operator returns the number of matches it made, so it will return true when the path matches C<^/static>. For more complex static handling in the C<path> callback, in addition to C<$_> being set the callback receives two arguments, C<PATH_INFO> (same as C<$_>) and C<$env>. If you want to map multiple static directories from different roots, simply add this middleware multiple times with different configuration options. =item pass_through When this option is set to a true value, then this middleware will never return a 404 if it cannot find a matching file. Instead, it will simply pass the request on to the application it is wrapping. =item content_type The C<content_type> option can be used to provide access to a different MIME database than L<Plack::MIME>. L<Plack::MIME> works fast and good for a list of well known file endings, but if you need a more accurate content based checking you can use modules like L<File::MimeInfo> or L<File::MMagic> for example. The callback should work on $_[0] which is the filename of the file. =back =head1 AUTHOR Tokuhiro Matsuno, Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::Middleware> L<Plack::Builder> =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/XFramework.pm�����������������������������������������������������000644 �000765 �000024 �00000001573 12244057435 022105� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::XFramework; use strict; use warnings; use parent qw/Plack::Middleware/; use Plack::Util; use Plack::Util::Accessor qw( framework ); sub call { my $self = shift; my $res = $self->app->( @_ ); $self->response_cb($res, sub { my $res = shift; if ($self->framework) { Plack::Util::header_set $res->[1], 'X-Framework' => $self->framework; } }); } 1; __END__ =head1 NAME Plack::Middleware::XFramework - Sample middleware to add X-Framework =head1 SYNOPSIS enable "Plack::Middleware::XFramework", framework => "Catalyst"; =head1 DESCRIPTION This middleware adds C<X-Framework> header to the HTTP response. =head1 CONFIGURATION =over 4 =item framework Sets the string value of C<X-Framework> header. If not set, the header is not set to the response. =back =head1 SEE ALSO L<Plack::Middleware> =cut �������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/XSendfile.pm������������������������������������������������������000644 �000765 �000024 �00000004155 12244057435 021700� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::XSendfile; use strict; use warnings; use parent qw(Plack::Middleware); use Plack::Util; use Scalar::Util; use Plack::Util::Accessor qw( variation ); sub call { my $self = shift; my $env = shift; my $res = $self->app->($env); $self->response_cb($res, sub { my $res = shift; my($status, $headers, $body) = @$res; return unless defined $body; if (Scalar::Util::blessed($body) && $body->can('path')) { my $type = $self->_variation($env) || ''; my $h = Plack::Util::headers($headers); if ($type && !$h->exists($type)) { if ($type eq 'X-Accel-Redirect') { my $path = $body->path; my $url = $self->map_accel_path($env, $path); $h->set($type => $url) if $url; $body = []; } elsif ($type eq 'X-Sendfile' or $type eq 'X-Lighttpd-Send-File') { my $path = $body->path; $h->set($type => $path) if defined $path; $body = []; } else { $env->{'psgi.errors'}->print("Unknown x-sendfile variation: $type"); } } } @$res = ( $status, $headers, $body ); }); } sub map_accel_path { my($self, $env, $path) = @_; if (my $mapping = $env->{HTTP_X_ACCEL_MAPPING}) { my($internal, $external) = split /=/, $mapping, 2; $path =~ s!^\Q$internal\E!$external!i; } return $path; } sub _variation { my($self, $env) = @_; $self->variation || $env->{'plack.xsendfile.type'} || $env->{HTTP_X_SENDFILE_TYPE}; } 1; __END__ =head1 NAME Plack::Middleware::XSendfile - Sets X-Sendfile (or a like) header for frontends =head1 SYNOPSIS enable "Plack::Middleware::XSendfile"; =head1 DESCRIPTION You should use L<IO::File::WithPath> or L<Plack::Util>'s C<set_io_path> to add C<path> method to an IO object in the body. See L<http://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/sendfile.rb> for the frontend configuration. =head1 AUTHOR Tatsuhiko Miyagawa =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/Auth/Basic.pm�����������������������������������������������������000644 �000765 �000024 �00000006651 12244057435 021744� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::Auth::Basic; use strict; use parent qw(Plack::Middleware); use Plack::Util::Accessor qw( realm authenticator ); use Scalar::Util; use MIME::Base64; sub prepare_app { my $self = shift; my $auth = $self->authenticator or die 'authenticator is not set'; if (Scalar::Util::blessed($auth) && $auth->can('authenticate')) { $self->authenticator(sub { $auth->authenticate(@_[0,1]) }); # because Authen::Simple barfs on 3 params } elsif (ref $auth ne 'CODE') { die 'authenticator should be a code reference or an object that responds to authenticate()'; } } sub call { my($self, $env) = @_; my $auth = $env->{HTTP_AUTHORIZATION} or return $self->unauthorized; # note the 'i' on the regex, as, according to RFC2617 this is a # "case-insensitive token to identify the authentication scheme" if ($auth =~ /^Basic (.*)$/i) { my($user, $pass) = split /:/, (MIME::Base64::decode($1) || ":"), 2; $pass = '' unless defined $pass; if ($self->authenticator->($user, $pass, $env)) { $env->{REMOTE_USER} = $user; return $self->app->($env); } } return $self->unauthorized; } sub unauthorized { my $self = shift; my $body = 'Authorization required'; return [ 401, [ 'Content-Type' => 'text/plain', 'Content-Length' => length $body, 'WWW-Authenticate' => 'Basic realm="' . ($self->realm || "restricted area") . '"' ], [ $body ], ]; } 1; __END__ =head1 NAME Plack::Middleware::Auth::Basic - Simple basic authentication middleware =head1 SYNOPSIS use Plack::Builder; my $app = sub { ... }; builder { enable "Auth::Basic", authenticator => \&authen_cb; $app; }; sub authen_cb { my($username, $password, $env) = @_; return $username eq 'admin' && $password eq 's3cr3t'; } =head1 DESCRIPTION Plack::Middleware::Auth::Basic is a basic authentication handler for Plack. =head1 CONFIGURATION =over 4 =item authenticator A callback function that takes username, password and PSGI environment supplied and returns whether the authentication succeeds. Required. Authenticator can also be an object that responds to C<authenticate> method that takes username and password and returns boolean, so backends for L<Authen::Simple> is perfect to use: use Authen::Simple::LDAP; enable "Auth::Basic", authenticator => Authen::Simple::LDAP->new(...); =item realm Realm name to display in the basic authentication dialog. Defaults to I<restricted area>. =back =head1 LIMITATIONS This middleware expects that the application has a full access to the headers sent by clients in PSGI environment. That is normally the case with standalone Perl PSGI web servers such as L<Starman> or L<HTTP::Server::Simple::PSGI>. However, in a web server configuration where you can't achieve this (i.e. using your application via Apache's mod_cgi), this middleware does not work since your application can't know the value of C<Authorization:> header. If you use Apache as a web server and CGI to run your PSGI application, you can either a) compile Apache with C<-DSECURITY_HOLE_PASS_AUTHORIZATION> option, or b) use mod_rewrite to pass the Authorization header to the application with the rewrite rule like following. RewriteEngine on RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack> =cut ���������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Middleware/AccessLog/Timed.pm������������������������������������������������000644 �000765 �000024 �00000005756 12244057435 022734� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Middleware::AccessLog::Timed; use strict; use warnings; use parent qw( Plack::Middleware::AccessLog ); use Time::HiRes; use Plack::Util; sub call { my $self = shift; my($env) = @_; my $time = Time::HiRes::gettimeofday; my $length = 0; my $logger = $self->logger || sub { $env->{'psgi.errors'}->print(@_) }; my $res = $self->app->($env); return $self->response_cb($res, sub { my $res = shift; my($status, $header, $body) = @$res; if (!defined $body) { my $length; return sub { my $line = shift; $length += length $line if defined $line; unless( defined $line ) { my $now = Time::HiRes::gettimeofday; $logger->( $self->log_line($status, $header, $env, { time => $now - $time, content_length => $length }) ); } return $line; }; } my $getline = ref $body eq 'ARRAY' ? sub { shift @$body } : sub { $body->getline }; my $timer_body = Plack::Util::inline_object( getline => sub { my $line = $getline->(); $length += length $line if defined $line; return $line; }, close => sub { $body->close if ref $body ne 'ARRAY'; my $now = Time::HiRes::gettimeofday; $logger->( $self->log_line($status, $header, $env, { time => $now - $time, content_length => $length }) ); }, ); @$res = ($status, $header, $timer_body); }); } 1; __END__ =head1 NAME Plack::Middleware::AccessLog::Timed - Logs requests with time and accurate body size =head1 SYNOPSIS # in app.psgi use Plack::Builder; builder { enable "Plack::Middleware::AccessLog::Timed", format => "%v %h %l %u %t \"%r\" %>s %b %D"; $app; }; =head1 DESCRIPTION Plack::Middleware::AccessLog::Timed is a subclass of L<Plack::Middleware::AccessLog> but uses a wrapped body handle to get the actual response body size C<%b> (even if it's not a chunk of array or a real filehandle) and the time taken to serve the request: C<%T> or C<%D>. This wraps the response body output stream to capture the time taken for the PSGI server to read the whole response body. This would mean, if the middleware is in use, it will prevent some server-side optimizations like sendfile(2) from working, as well as middleware like L<Plack::Middleware::ContentLength> can't guess the body size out of the file handle. If all you want is to capture the time taken in your PSGI application and do not want the wrapped body behavior described above, consider instead applying L<Plack::Middleware::Runtime> and using L<Plack::Middleware::AccessLog> to log the C<X-Runtime> header. =head1 CONFIGURATION Same as L<Plack::Middleware::AccessLog>. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::Middleware::AccessLog> L<Plack::Middleware::Runtime> =cut ������������������Plack-1.0030/lib/Plack/Loader/Delayed.pm������������������������������������������������������������000644 �000765 �000024 �00000003064 12244057435 020515� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Loader::Delayed; use strict; use parent qw(Plack::Loader); sub preload_app { my($self, $builder) = @_; $self->{builder} = $builder; } sub run { my($self, $server) = @_; my $compiled; my $app = sub { $compiled ||= $self->{builder}->(); $compiled->(@_); }; $server->{psgi_app_builder} = $self->{builder}; $server->run($app); } 1; __END__ =head1 NAME Plack::Loader::Delayed - Delay the loading of .psgi until the first run =head1 SYNOPSIS plackup -s Starlet -L Delayed myapp.psgi =head1 DESCRIPTION This loader delays the compilation of specified PSGI application until the first request time. This prevents bad things from happening with preforking web servers like L<Starlet>, when your application manipulates resources such as sockets or database connections in the master startup process and then shared by children. You can combine this loader with C<-M> command line option, like: plackup -s Starlet -MCatalyst -L Delayed myapp.psgi loads the module Catalyst in the master process for the better process management with copy-on-write, however the application C<myapp.psgi> is loaded per children. L<Starman> since version 0.2000 loads this loader by default unless you specify the command line option C<--preload-app> for the L<starman> executable. =head1 DEVELOPERS Web server developers can make use of C<psgi_app_builder> attribute callback set in Plack::Handler, to load the application earlier than the first request time. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<plackup> =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Loader/Restarter.pm����������������������������������������������������������000644 �000765 �000024 �00000005065 12244057435 021124� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Loader::Restarter; use strict; use warnings; use parent qw(Plack::Loader); use Plack::Util; use Try::Tiny; sub new { my($class, $runner) = @_; bless { watch => [] }, $class; } sub preload_app { my($self, $builder) = @_; $self->{builder} = $builder; } sub watch { my($self, @dir) = @_; push @{$self->{watch}}, @dir; } sub _fork_and_start { my($self, $server) = @_; delete $self->{pid}; # re-init in case it's a restart my $pid = fork; die "Can't fork: $!" unless defined $pid; if ($pid == 0) { # child return $server->run($self->{builder}->()); } else { $self->{pid} = $pid; } } sub _kill_child { my $self = shift; my $pid = $self->{pid} or return; warn "Killing the existing server (pid:$pid)\n"; kill 'TERM' => $pid; waitpid($pid, 0); } sub valid_file { my($self, $file) = @_; # vim temporary file is 4913 to 5036 # http://www.mail-archive.com/vim_dev@googlegroups.com/msg07518.html if ( $file->{path} =~ m{(\d+)$} && $1 >= 4913 && $1 <= 5036) { return 0; } $file->{path} !~ m!\.(?:git|svn)[/\\]|\.(?:bak|swp|swpx|swx)$|~$|_flymake\.p[lm]$|\.#!; } sub run { my($self, $server, $builder) = @_; $self->_fork_and_start($server, $builder); return unless $self->{pid}; require Filesys::Notify::Simple; my $watcher = Filesys::Notify::Simple->new($self->{watch}); warn "Watching @{$self->{watch}} for file updates.\n"; local $SIG{TERM} = sub { $self->_kill_child; exit(0); }; while (1) { my @restart; # this is blocking $watcher->wait(sub { my @events = @_; @events = grep $self->valid_file($_), @events; return unless @events; @restart = @events; }); next unless @restart; for my $ev (@restart) { warn "-- $ev->{path} updated.\n"; } $self->_kill_child; warn "Successfully killed! Restarting the new server process.\n"; $self->_fork_and_start($server, $builder); return unless $self->{pid}; } } 1; __END__ =head1 NAME Plack::Loader::Restarter - Restarting loader =head1 SYNOPSIS plackup -r -R paths =head1 DESCRIPTION Plack::Loader::Restarter is a loader backend that implements C<-r> and C<-R> option for the L<plackup> script. It forks the server as a child process and the parent watches the directories for file updates, and whenever it receives the notification, kills the child server and restart. =head1 SEE ALSO L<Plack::Runner>, L<Catalyst::Restarter> =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Loader/Shotgun.pm������������������������������������������������������������000644 �000765 �000024 �00000005467 12244057435 020606� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Loader::Shotgun; use strict; use parent qw(Plack::Loader); use Storable; use Try::Tiny; use Plack::Middleware::BufferedStreaming; die <<DIE if $^O eq 'MSWin32' && !$ENV{PLACK_SHOTGUN_MEMORY_LEAK}; Shotgun loader uses fork(2) system call to create a fresh Perl interpreter, that is known to not work properly in a fork-emulation layer on Windows and cause huge memory leaks. If you're aware of this and still want to run the loader, run it with the environment variable PLACK_SHOTGUN_MEMORY_LEAK on. DIE sub preload_app { my($self, $builder) = @_; $self->{builder} = sub { Plack::Middleware::BufferedStreaming->wrap($builder->()) }; } sub run { my($self, $server) = @_; my $app = sub { my $env = shift; pipe my $read, my $write; my $pid = fork; if ($pid) { # parent close $write; my $res = Storable::thaw(join '', <$read>); close $read; waitpid($pid, 0); return $res; } else { # child close $read; my $res; try { $env->{'psgi.streaming'} = 0; $res = $self->{builder}->()->($env); my @body; Plack::Util::foreach($res->[2], sub { push @body, $_[0] }); $res->[2] = \@body; } catch { $env->{'psgi.errors'}->print($_); $res = [ 500, [ "Content-Type", "text/plain" ], [ "Internal Server Error" ] ]; }; print {$write} Storable::freeze($res); close $write; exit; } }; $server->run($app); } 1; __END__ =head1 NAME Plack::Loader::Shotgun - forking implementation of plackup =head1 SYNOPSIS plackup -L Shotgun =head1 DESCRIPTION Shotgun loader delays the compilation and execution of your application until the runtime. When a new request comes in, this forks a new child, compiles your code and runs the application. This should be an ultimate alternative solution when reloading with L<Plack::Middleware::Refresh> doesn't work, or plackup's default C<-r> filesystem watcher causes problems. I can imagine this is useful for applications which expects their application is only evaluated once (like in-file templates) or on operating systems with broken fork implementation, etc. This is much like good old CGI's fork and run but you don't need a web server, and there's a benefit of preloading modules that are not likely to change. For instance if you develop a web application using Moose and DBIx::Class, plackup -MMoose -MDBIx::Class -L Shotgun yourapp.psgi would preload those modules and only re-evaluates your code in every request. =head1 AUTHOR Tatsuhiko Miyagawa with an inspiration from L<http://github.com/rtomayko/shotgun> =head1 SEE ALSO L<plackup> =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/HTTPParser/PP.pm�������������������������������������������������������������000644 �000765 �000024 �00000004615 12244057435 020216� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::HTTPParser::PP; use strict; use warnings; use URI::Escape; sub parse_http_request { my($chunk, $env) = @_; $env ||= {}; # pre-header blank lines are allowed (RFC 2616 4.1) $chunk =~ s/^(\x0d?\x0a)+//; return -2 unless length $chunk; # double line break indicates end of header; parse it if ($chunk =~ /^(.*?\x0d?\x0a\x0d?\x0a)/s) { return _parse_header($chunk, length $1, $env); } return -2; # still waiting for unknown amount of header lines } sub _parse_header { my($chunk, $eoh, $env) = @_; my $header = substr($chunk, 0, $eoh,''); $chunk =~ s/^\x0d?\x0a\x0d?\x0a//; # parse into lines my @header = split /\x0d?\x0a/,$header; my $request = shift @header; # join folded lines my @out; for(@header) { if(/^[ \t]+/) { return -1 unless @out; $out[-1] .= $_; } else { push @out, $_; } } # parse request or response line my $obj; my ($major, $minor); my ($method,$uri,$http) = split / /,$request; return -1 unless $http and $http =~ /^HTTP\/(\d+)\.(\d+)$/i; ($major, $minor) = ($1, $2); $env->{REQUEST_METHOD} = $method; $env->{SERVER_PROTOCOL} = "HTTP/$major.$minor"; $env->{REQUEST_URI} = $uri; my($path, $query) = ( $uri =~ /^([^?]*)(?:\?(.*))?$/s ); for ($path, $query) { s/\#.*$// if defined && length } # dumb clients sending URI fragments $env->{PATH_INFO} = URI::Escape::uri_unescape($path); $env->{QUERY_STRING} = $query || ''; $env->{SCRIPT_NAME} = ''; # import headers my $token = qr/[^][\x00-\x1f\x7f()<>@,;:\\"\/?={} \t]+/; my $k; for my $header (@out) { if ( $header =~ s/^($token): ?// ) { $k = $1; $k =~ s/-/_/g; $k = uc $k; if ($k !~ /^(?:CONTENT_LENGTH|CONTENT_TYPE)$/) { $k = "HTTP_$k"; } } elsif ( $header =~ /^\s+/) { # multiline header } else { return -1; } if (exists $env->{$k}) { $env->{$k} .= ", $header"; } else { $env->{$k} = $header; } } return $eoh; } 1; __END__ =head1 NAME Plack::HTTPParser::PP - Pure perl fallback of HTTP::Parser::XS =head1 DESCRIPTION Do not use this module directly. Use L<Plack::HTTPParser> instead. =head1 AUTHOR Tatsuhiko Miyagawa =cut �������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/Apache1.pm�����������������������������������������������������������000644 �000765 �000024 �00000007757 12244057435 020574� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler::Apache1; use strict; use Apache::Request; use Apache::Constants qw(:common :response); use Plack::Util; use Scalar::Util; my %apps; # psgi file to $app mapping sub new { bless {}, shift } sub preload { my $class = shift; for my $app (@_) { $class->load_app($app); } } sub load_app { my($class, $app) = @_; return $apps{$app} ||= do { # Trick Catalyst, CGI.pm, CGI::Cookie and others that check # for $ENV{MOD_PERL}. # # Note that we delete it instead of just localizing # $ENV{MOD_PERL} because some users may check if the key # exists, and we do it this way because "delete local" is new # in 5.12: # http://perldoc.perl.org/5.12.0/perldelta.html#delete-local local $ENV{MOD_PERL}; delete $ENV{MOD_PERL}; Plack::Util::load_psgi $app; }; } sub handler { my $class = __PACKAGE__; my $r = shift; my $psgi = $r->dir_config('psgi_app'); $class->call_app($r, $class->load_app($psgi)); } sub call_app { my ($class, $r, $app) = @_; $r->subprocess_env; # let Apache create %ENV for us :) my $env = { %ENV, 'psgi.version' => [ 1, 1 ], 'psgi.url_scheme' => ($ENV{HTTPS}||'off') =~ /^(?:on|1)$/i ? 'https' : 'http', 'psgi.input' => $r, 'psgi.errors' => *STDERR, 'psgi.multithread' => Plack::Util::FALSE, 'psgi.multiprocess' => Plack::Util::TRUE, 'psgi.run_once' => Plack::Util::FALSE, 'psgi.streaming' => Plack::Util::TRUE, 'psgi.nonblocking' => Plack::Util::FALSE, 'psgix.harakiri' => Plack::Util::TRUE, }; if (defined(my $HTTP_AUTHORIZATION = $r->headers_in->{Authorization})) { $env->{HTTP_AUTHORIZATION} = $HTTP_AUTHORIZATION; } my $vpath = $env->{SCRIPT_NAME} . ($env->{PATH_INFO} || ''); my $location = $r->location || "/"; $location =~ s{/$}{}; (my $path_info = $vpath) =~ s/^\Q$location\E//; $env->{SCRIPT_NAME} = $location; $env->{PATH_INFO} = $path_info; my $res = $app->($env); if (ref $res eq 'ARRAY') { _handle_response($r, $res); } elsif (ref $res eq 'CODE') { $res->(sub { _handle_response($r, $_[0]); }); } else { die "Bad response $res"; } if ($env->{'psgix.harakiri.commit'}) { $r->child_terminate; } return OK; } sub _handle_response { my ($r, $res) = @_; my ($status, $headers, $body) = @{ $res }; my $hdrs = ($status >= 200 && $status < 300) ? $r->headers_out : $r->err_headers_out; Plack::Util::header_iter($headers, sub { my($h, $v) = @_; if (lc $h eq 'content-type') { $r->content_type($v); } else { $hdrs->add($h => $v); } }); $r->status($status); $r->send_http_header; if (defined $body) { if (Plack::Util::is_real_fh($body)) { $r->send_fd($body); } else { Plack::Util::foreach($body, sub { $r->print(@_) }); } } else { return Plack::Util::inline_object write => sub { $r->print(@_) }, close => sub { }; } } 1; __END__ =head1 NAME Plack::Handler::Apache1 - Apache 1.3.x mod_perl handlers to run PSGI application =head1 SYNOPSIS <Location /> SetHandler perl-script PerlHandler Plack::Handler::Apache1 PerlSetVar psgi_app /path/to/app.psgi </Location> <Perl> use Plack::Handler::Apache1; Plack::Handler::Apache1->preload("/path/to/app.psgi"); </Perl> =head1 DESCRIPTION This is a mod_perl handler module to run any PSGI application with mod_perl on Apache 1.3.x. If you want to run PSGI applications I<behind> Apache instead of using mod_perl, see L<Plack::Handler::FCGI> to run with FastCGI, or use standalone HTTP servers such as L<Starman> or L<Starlet> proxied with mod_proxy. =head1 AUTHOR Aaron Trevena Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack> =cut �����������������Plack-1.0030/lib/Plack/Handler/Apache2/�������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 020217� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/Apache2.pm�����������������������������������������������������������000644 �000765 �000024 �00000024143 12244057435 020561� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler::Apache2; use strict; use warnings; use Apache2::RequestRec; use Apache2::RequestIO; use Apache2::RequestUtil; use Apache2::Response; use Apache2::Const -compile => qw(OK); use Apache2::Log; use APR::Table; use IO::Handle; use Plack::Util; use Scalar::Util; use URI; use URI::Escape; my %apps; # psgi file to $app mapping sub new { bless {}, shift } sub preload { my $class = shift; for my $app (@_) { $class->load_app($app); } } sub load_app { my($class, $app) = @_; return $apps{$app} ||= do { # Trick Catalyst, CGI.pm, CGI::Cookie and others that check # for $ENV{MOD_PERL}. # # Note that we delete it instead of just localizing # $ENV{MOD_PERL} because some users may check if the key # exists, and we do it this way because "delete local" is new # in 5.12: # http://perldoc.perl.org/5.12.0/perldelta.html#delete-local local $ENV{MOD_PERL}; delete $ENV{MOD_PERL}; Plack::Util::load_psgi $app; }; } sub call_app { my ($class, $r, $app) = @_; $r->subprocess_env; # let Apache create %ENV for us :) my $env = { %ENV, 'psgi.version' => [ 1, 1 ], 'psgi.url_scheme' => ($ENV{HTTPS}||'off') =~ /^(?:on|1)$/i ? 'https' : 'http', 'psgi.input' => $r, 'psgi.errors' => *STDERR, 'psgi.multithread' => Plack::Util::FALSE, 'psgi.multiprocess' => Plack::Util::TRUE, 'psgi.run_once' => Plack::Util::FALSE, 'psgi.streaming' => Plack::Util::TRUE, 'psgi.nonblocking' => Plack::Util::FALSE, 'psgix.harakiri' => Plack::Util::TRUE, 'psgix.cleanup' => Plack::Util::TRUE, 'psgix.cleanup.handlers' => [], }; if (defined(my $HTTP_AUTHORIZATION = $r->headers_in->{Authorization})) { $env->{HTTP_AUTHORIZATION} = $HTTP_AUTHORIZATION; } # If you supply more than one Content-Length header Apache will # happily concat the values with ", ", e.g. "72, 72". This # violates the PSGI spec so fix this up and just take the first # one. if (exists $env->{CONTENT_LENGTH} && $env->{CONTENT_LENGTH} =~ /,/) { no warnings qw(numeric); $env->{CONTENT_LENGTH} = int $env->{CONTENT_LENGTH}; } # Actually, we can not trust PATH_INFO from mod_perl because mod_perl squeezes multiple slashes into one slash. my $uri = URI->new("http://".$r->hostname.$r->unparsed_uri); $env->{PATH_INFO} = uri_unescape($uri->path); $class->fixup_path($r, $env); my $res = $app->($env); if (ref $res eq 'ARRAY') { _handle_response($r, $res); } elsif (ref $res eq 'CODE') { $res->(sub { _handle_response($r, $_[0]); }); } else { die "Bad response $res"; } if (@{ $env->{'psgix.cleanup.handlers'} }) { $r->push_handlers( PerlCleanupHandler => sub { for my $cleanup_handler (@{ $env->{'psgix.cleanup.handlers'} }) { $cleanup_handler->($env); } if ($env->{'psgix.harakiri.commit'}) { $r->child_terminate; } }, ); } else { if ($env->{'psgix.harakiri.commit'}) { $r->child_terminate; } } return Apache2::Const::OK; } sub handler { my $class = __PACKAGE__; my $r = shift; my $psgi = $r->dir_config('psgi_app'); $class->call_app($r, $class->load_app($psgi)); } # The method for PH::Apache2::Registry to override. sub fixup_path { my ($class, $r, $env) = @_; # $env->{PATH_INFO} is created from unparsed_uri so it is raw. my $path_info = $env->{PATH_INFO} || ''; # Get argument of <Location> or <LocationMatch> directive # This may be string or regexp and we can't know either. my $location = $r->location; # Let's *guess* if we're in a LocationMatch directive if ($location eq '/') { # <Location /> could be handled as a 'root' case where we make # everything PATH_INFO and empty SCRIPT_NAME as in the PSGI spec $env->{SCRIPT_NAME} = ''; } elsif ($path_info =~ s{^($location)/?}{/}) { $env->{SCRIPT_NAME} = $1 || ''; } else { # Apache's <Location> is matched but here is not. # This is something wrong. We can only respect original. $r->server->log_error( "Your request path is '$path_info' and it doesn't match your Location(Match) '$location'. " . "This should be due to the configuration error. See perldoc Plack::Handler::Apache2 for details." ); } $env->{PATH_INFO} = $path_info; } sub _handle_response { my ($r, $res) = @_; my ($status, $headers, $body) = @{ $res }; my $hdrs = ($status >= 200 && $status < 300) ? $r->headers_out : $r->err_headers_out; Plack::Util::header_iter($headers, sub { my($h, $v) = @_; if (lc $h eq 'content-type') { $r->content_type($v); } elsif (lc $h eq 'content-length') { $r->set_content_length($v); } else { $hdrs->add($h => $v); } }); $r->status($status); if (Scalar::Util::blessed($body) and $body->can('path') and my $path = $body->path) { $r->sendfile($path); } elsif (defined $body) { Plack::Util::foreach($body, sub { $r->print(@_) }); $r->rflush; } else { return Plack::Util::inline_object write => sub { $r->print(@_); $r->rflush }, close => sub { $r->rflush }; } return Apache2::Const::OK; } 1; __END__ =encoding utf-8 =head1 NAME Plack::Handler::Apache2 - Apache 2.0 mod_perl handler to run PSGI application =head1 SYNOPSIS # in your httpd.conf <Location /> SetHandler perl-script PerlResponseHandler Plack::Handler::Apache2 PerlSetVar psgi_app /path/to/app.psgi </Location> # Optionally preload your apps in startup PerlPostConfigRequire /etc/httpd/startup.pl See L</STARTUP FILE> for more details on writing a C<startup.pl>. =head1 DESCRIPTION This is a mod_perl handler module to run any PSGI application with mod_perl on Apache 2.x. If you want to run PSGI applications I<behind> Apache instead of using mod_perl, see L<Plack::Handler::FCGI> to run with FastCGI, or use standalone HTTP servers such as L<Starman> or L<Starlet> proxied with mod_proxy. =head1 CREATING CUSTOM HANDLER If you want to create a custom handler that loads or creates PSGI applications using other means than loading from C<.psgi> files, you can create your own handler class and use C<call_app> class method to run your application. package My::ModPerl::Handler; use Plack::Handler::Apache2; sub get_app { # magic! } sub handler { my $r = shift; my $app = get_app(); Plack::Handler::Apache2->call_app($r, $app); } =head1 STARTUP FILE Here is an example C<startup.pl> to preload PSGI applications: #!/usr/bin/env perl use strict; use warnings; use Apache2::ServerUtil (); BEGIN { return unless Apache2::ServerUtil::restart_count() > 1; require lib; lib->import('/path/to/my/perl/libs'); require Plack::Handler::Apache2; my @psgis = ('/path/to/app1.psgi', '/path/to/app2.psgi'); foreach my $psgi (@psgis) { Plack::Handler::Apache2->preload($psgi); } } 1; # file must return true! See L<http://perl.apache.org/docs/2.0/user/handlers/server.html#Startup_File> for general information on the C<startup.pl> file for preloading perl modules and your apps. Some things to keep in mind when writing this file: =over 4 =item * multiple init phases You have to check that L<Apache2::ServerUtil/restart_count> is C<< > 1 >>, otherwise your app will load twice and the env vars you set with L<PerlSetEnv|http://perl.apache.org/docs/2.0/user/config/config.html#C_PerlSetEnv_> will not be available when your app is loading the first time. Use the example above as a template. =item * C<@INC> The C<startup.pl> file is a good place to add entries to your C<@INC>. Use L<lib> to add entries, they can be in your app or C<.psgi> as well, but if your modules are in a L<local::lib> or some such, you will need to add the path for anything to load. Alternately, if you follow the example above, you can use: PerlSetEnv PERL5LIB /some/path or PerlSwitches -I/some/path in your C<httpd.conf>, which will also work. =item * loading errors Any exceptions thrown in your C<startup.pl> will stop Apache from starting at all. You probably don't want a stray syntax error to bring your whole server down in a shared or development environment, in which case it's a good idea to wrap the L</preload> call in an eval, using something like this: require Plack::Handler::Apache2; my @psgis = ('/path/to/app1.psgi', '/path/to/app2.psgi'); foreach my $psgi (@psgis) { eval { Plack::Handler::Apache2->preload($psgi); 1; } or do { my $error = $@ || 'Unknown Error'; # STDERR goes to the error_log print STDERR "Failed to load psgi '$psgi': $error\n"; }; } =item * dynamically loaded modules Some modules load their dependencies at runtime via e.g. L<Class::Load>. These modules will not get preloaded into your parent process by just including the app/module you are using. As an optimization, you can dump C<%INC> from a request to see if you are using any such modules and preload them in your C<startup.pl>. Another method is dumping the difference between the C<%INC> on process start and process exit. You can use something like this to accomplish this: my $start_inc = { %INC }; END { my @m; foreach my $m (keys %INC) { push @m, $m unless exists $start_inc->{$m}; } if (@m) { # STDERR goes to the error_log print STDERR "The following modules need to be preloaded:\n"; print STDERR "$_\n" for @m; } } =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 CONTRIBUTORS Paul Driver Ævar Arnfjörð Bjarmason Rafael Kitover =head1 SEE ALSO L<Plack> =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/CGI.pm���������������������������������������������������������������000644 �000765 �000024 �00000013103 12244057435 017712� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler::CGI; use strict; use warnings; use IO::Handle; # copied from HTTP::Status my %StatusCode = ( 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', # RFC 2518 (WebDAV) 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', # RFC 2518 (WebDAV) 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Request Range Not Satisfiable', 417 => 'Expectation Failed', 422 => 'Unprocessable Entity', # RFC 2518 (WebDAV) 423 => 'Locked', # RFC 2518 (WebDAV) 424 => 'Failed Dependency', # RFC 2518 (WebDAV) 425 => 'No code', # WebDAV Advanced Collections 426 => 'Upgrade Required', # RFC 2817 449 => 'Retry with', # unofficial Microsoft 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates', # RFC 2295 507 => 'Insufficient Storage', # RFC 2518 (WebDAV) 509 => 'Bandwidth Limit Exceeded', # unofficial 510 => 'Not Extended', # RFC 2774 ); sub new { bless {}, shift } sub run { my ($self, $app) = @_; my $env = $self->setup_env(); my $res = $app->($env); if (ref $res eq 'ARRAY') { $self->_handle_response($res); } elsif (ref $res eq 'CODE') { $res->(sub { $self->_handle_response($_[0]); }); } else { die "Bad response $res"; } } sub setup_env { my ( $self, $override_env ) = @_; $override_env ||= {}; binmode STDIN; binmode STDERR; my $env = { %ENV, 'psgi.version' => [ 1, 1 ], 'psgi.url_scheme' => ($ENV{HTTPS}||'off') =~ /^(?:on|1)$/i ? 'https' : 'http', 'psgi.input' => *STDIN, 'psgi.errors' => *STDERR, 'psgi.multithread' => 0, 'psgi.multiprocess' => 1, 'psgi.run_once' => 1, 'psgi.streaming' => 1, 'psgi.nonblocking' => 1, %{ $override_env }, }; delete $env->{HTTP_CONTENT_TYPE}; delete $env->{HTTP_CONTENT_LENGTH}; $env->{'HTTP_COOKIE'} ||= $ENV{COOKIE}; # O'Reilly server bug if (!exists $env->{PATH_INFO}) { $env->{PATH_INFO} = ''; } if ($env->{SCRIPT_NAME} eq '/') { $env->{SCRIPT_NAME} = ''; $env->{PATH_INFO} = '/' . $env->{PATH_INFO}; } return $env; } sub _handle_response { my ($self, $res) = @_; *STDOUT->autoflush(1); binmode STDOUT; my $hdrs; my $message = $StatusCode{$res->[0]}; $hdrs = "Status: $res->[0] $message\015\012"; my $headers = $res->[1]; while (my ($k, $v) = splice(@$headers, 0, 2)) { $hdrs .= "$k: $v\015\012"; } $hdrs .= "\015\012"; print STDOUT $hdrs; my $body = $res->[2]; my $cb = sub { print STDOUT $_[0] }; # inline Plack::Util::foreach here if (ref $body eq 'ARRAY') { for my $line (@$body) { $cb->($line) if length $line; } } elsif (defined $body) { local $/ = \65536 unless ref $/; while (defined(my $line = $body->getline)) { $cb->($line) if length $line; } $body->close; } else { return Plack::Handler::CGI::Writer->new; } } package Plack::Handler::CGI::Writer; sub new { bless \do { my $x }, $_[0] } sub write { print STDOUT $_[1] } sub close { } package Plack::Handler::CGI; 1; __END__ =head1 NAME Plack::Handler::CGI - CGI handler for Plack =head1 SYNOPSIS Want to run PSGI application as a CGI script? Rename .psgi to .cgi and change the shebang line like: #!/usr/bin/env plackup # rest of the file can be the same as other .psgi file You can alternatively create a .cgi file that contains something like: #!/usr/bin/perl use Plack::Loader; my $app = Plack::Util::load_psgi("/path/to/app.psgi"); Plack::Loader->auto->run($app); This will auto-recognize the CGI environment variable to load this class. If you really want to explicitly load the CGI handler, you can. For instance you might do this when you want to embed a PSGI application server built into CGI-compatible perl-based web server: use Plack::Handler::CGI; Plack::Handler::CGI->new->run($app); =head1 DESCRIPTION This is a handler module to run any PSGI application as a CGI script. =head1 UTILITY METHODS =head2 setup_env() my $env = Plack::Handler::CGI->setup_env(); my $env = Plack::Handler::CGI->setup_env(\%override_env); Sets up the PSGI environment hash for a CGI request from C<< %ENV >>> and returns it. You can provide a hashref of key/value pairs to override the defaults if you would like. =head1 SEE ALSO L<Plack> =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/FCGI.pm��������������������������������������������������������������000644 �000765 �000024 �00000026701 12244057435 020030� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler::FCGI; use strict; use warnings; use constant RUNNING_IN_HELL => $^O eq 'MSWin32'; use Scalar::Util qw(blessed); use Plack::Util; use FCGI; use HTTP::Status qw(status_message); use URI; use URI::Escape; sub new { my $class = shift; my $self = bless {@_}, $class; $self->{leave_umask} ||= 0; $self->{keep_stderr} ||= 0; $self->{nointr} ||= 0; $self->{daemonize} ||= $self->{detach}; # compatibility $self->{nproc} ||= 1 unless blessed $self->{manager}; $self->{pid} ||= $self->{pidfile}; # compatibility $self->{listen} ||= [ ":$self->{port}" ] if $self->{port}; # compatibility $self->{backlog} ||= 100; $self->{manager} = 'FCGI::ProcManager' unless exists $self->{manager}; $self; } sub run { my ($self, $app) = @_; my $sock = 0; if (-S STDIN) { # running from web server. Do nothing # Note it should come before listen check because of plackup's default } elsif ($self->{listen}) { my $old_umask = umask; unless ($self->{leave_umask}) { umask(0); } $sock = FCGI::OpenSocket( $self->{listen}->[0], $self->{backlog} ) or die "failed to open FastCGI socket: $!"; unless ($self->{leave_umask}) { umask($old_umask); } } elsif (!RUNNING_IN_HELL) { die "STDIN is not a socket: specify a listen location"; } @{$self}{qw(stdin stdout stderr)} = (IO::Handle->new, IO::Handle->new, IO::Handle->new); my %env; my $request = FCGI::Request( $self->{stdin}, $self->{stdout}, $self->{stderr}, \%env, $sock, ($self->{nointr} ? 0 : &FCGI::FAIL_ACCEPT_ON_INTR), ); my $proc_manager; if ($self->{listen}) { $self->daemon_fork if $self->{daemonize}; if ($self->{manager}) { if (blessed $self->{manager}) { for (qw(nproc pid proc_title)) { die "Don't use '$_' when passing in a 'manager' object" if $self->{$_}; } $proc_manager = $self->{manager}; } else { Plack::Util::load_class($self->{manager}); $proc_manager = $self->{manager}->new({ n_processes => $self->{nproc}, pid_fname => $self->{pid}, (exists $self->{proc_title} ? (pm_title => $self->{proc_title}) : ()), }); } # detach *before* the ProcManager inits $self->daemon_detach if $self->{daemonize}; $proc_manager->pm_manage; } elsif ($self->{daemonize}) { $self->daemon_detach; } } while ($request->Accept >= 0) { $proc_manager && $proc_manager->pm_pre_dispatch; my $env = { %env, 'psgi.version' => [1,1], 'psgi.url_scheme' => ($env{HTTPS}||'off') =~ /^(?:on|1)$/i ? 'https' : 'http', 'psgi.input' => $self->{stdin}, 'psgi.errors' => ($self->{keep_stderr} ? \*STDERR : $self->{stderr}), 'psgi.multithread' => Plack::Util::FALSE, 'psgi.multiprocess' => Plack::Util::TRUE, 'psgi.run_once' => Plack::Util::FALSE, 'psgi.streaming' => Plack::Util::TRUE, 'psgi.nonblocking' => Plack::Util::FALSE, 'psgix.harakiri' => defined $proc_manager, }; delete $env->{HTTP_CONTENT_TYPE}; delete $env->{HTTP_CONTENT_LENGTH}; # lighttpd munges multiple slashes in PATH_INFO into one. Try recovering it my $uri = URI->new("http://localhost" . $env->{REQUEST_URI}); $env->{PATH_INFO} = uri_unescape($uri->path); $env->{PATH_INFO} =~ s/^\Q$env->{SCRIPT_NAME}\E//; # root access for mod_fastcgi if (!exists $env->{PATH_INFO}) { $env->{PATH_INFO} = ''; } # typical fastcgi_param from nginx might get empty values for my $key (qw(CONTENT_TYPE CONTENT_LENGTH)) { no warnings; delete $env->{$key} if exists $env->{$key} && $env->{$key} eq ''; } if (defined(my $HTTP_AUTHORIZATION = $env->{Authorization})) { $env->{HTTP_AUTHORIZATION} = $HTTP_AUTHORIZATION; } my $res = Plack::Util::run_app $app, $env; if (ref $res eq 'ARRAY') { $self->_handle_response($res); } elsif (ref $res eq 'CODE') { $res->(sub { $self->_handle_response($_[0]); }); } else { die "Bad response $res"; } # give pm_post_dispatch the chance to do things after the client thinks # the request is done $request->Finish; $proc_manager && $proc_manager->pm_post_dispatch(); if ($proc_manager && $env->{'psgix.harakiri.commit'}) { $proc_manager->pm_exit("safe exit with harakiri"); } } } sub _handle_response { my ($self, $res) = @_; $self->{stdout}->autoflush(1); binmode $self->{stdout}; my $hdrs; my $message = status_message($res->[0]); $hdrs = "Status: $res->[0] $message\015\012"; my $headers = $res->[1]; while (my ($k, $v) = splice @$headers, 0, 2) { $hdrs .= "$k: $v\015\012"; } $hdrs .= "\015\012"; print { $self->{stdout} } $hdrs; my $cb = sub { print { $self->{stdout} } $_[0] }; my $body = $res->[2]; if (defined $body) { Plack::Util::foreach($body, $cb); } else { return Plack::Util::inline_object write => $cb, close => sub { }; } } sub daemon_fork { require POSIX; fork && exit; } sub daemon_detach { my $self = shift; print "FastCGI daemon started (pid $$)\n"; open STDIN, "+</dev/null" or die $!; ## no critic open STDOUT, ">&STDIN" or die $!; open STDERR, ">&STDIN" or die $!; POSIX::setsid(); } 1; __END__ =head1 NAME Plack::Handler::FCGI - FastCGI handler for Plack =head1 SYNOPSIS # Run as a standalone daemon plackup -s FCGI --listen /tmp/fcgi.sock --daemonize --nproc 10 # Run from your web server like mod_fastcgi #!/usr/bin/env plackup -s FCGI my $app = sub { ... }; # Roll your own my $server = Plack::Handler::FCGI->new( nproc => $num_proc, listen => [ $port_or_socket ], detach => 1, ); $server->run($app); =head1 DESCRIPTION This is a handler module to run any PSGI application as a standalone FastCGI daemon or a .fcgi script. =head2 OPTIONS =over 4 =item listen listen => [ '/path/to/socket' ] listen => [ ':8080' ] Listen on a socket path, hostname:port, or :port. =item port listen via TCP on port on all interfaces (Same as C<< listen => ":$port" >>) =item leave-umask Set to 1 to disable setting umask to 0 for socket open =item nointr Do not allow the listener to be interrupted by Ctrl+C =item nproc Specify a number of processes for FCGI::ProcManager =item pid Specify a filename for the pid file =item manager Specify either a FCGI::ProcManager subclass, or an actual FCGI::ProcManager-compatible object. use FCGI::ProcManager::Dynamic; Plack::Handler::FCGI->new( manager => FCGI::ProcManager::Dynamic->new(...), ); =item daemonize Daemonize the process. =item proc-title Specify process title =item keep-stderr Send psgi.errors to STDERR instead of to the FCGI error stream. =item backlog Maximum length of the queue of pending connections =back =head2 WEB SERVER CONFIGURATIONS In all cases, you will want to install L<FCGI> and L<FCGI::ProcManager>. You may find it most convenient to simply install L<Task::Plack> which includes both of these. =head3 nginx This is an example nginx configuration to run your FCGI daemon on a Unix domain socket and run it at the server's root URL (/). http { server { listen 3001; location / { set $script ""; set $path_info $uri; fastcgi_pass unix:/tmp/fastcgi.sock; fastcgi_param SCRIPT_NAME $script; fastcgi_param PATH_INFO $path_info; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param REQUEST_URI $request_uri; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; } } } If you want to host your application in a non-root path, then you should mangle this configuration to set the path to C<SCRIPT_NAME> and the rest of the path in C<PATH_INFO>. See L<http://wiki.nginx.org/NginxFcgiExample> for more details. =head3 Apache mod_fastcgi After installing C<mod_fastcgi>, you should add the C<FastCgiExternalServer> directive to your Apache config: FastCgiExternalServer /tmp/myapp.fcgi -socket /tmp/fcgi.sock ## Then set up the location that you want to be handled by fastcgi: # EITHER from a given path Alias /myapp/ /tmp/myapp.fcgi/ # OR at the root Alias / /tmp/myapp.fcgi/ Now you can use plackup to listen to the socket that you've just configured in Apache. $ plackup -s FCGI --listen /tmp/myapp.sock psgi/myapp.psgi The above describes the "standalone" method, which is usually appropriate. There are other methods, described in more detail at L<Catalyst::Engine::FastCGI/Standalone_server_mode> (with regards to Catalyst, but which may be set up similarly for Plack). See also L<http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html#FastCgiExternalServer> for more details. =head3 lighttpd To host the app in the root path, you're recommended to use lighttpd 1.4.23 or newer with C<fix-root-scriptname> flag like below. fastcgi.server = ( "/" => (( "socket" => "/tmp/fcgi.sock", "check-local" => "disable", "fix-root-scriptname" => "enable", )) If you use lighttpd older than 1.4.22 where you don't have C<fix-root-scriptname>, mounting apps under the root causes wrong C<SCRIPT_NAME> and C<PATH_INFO> set. Also, mounting under the empty root (C<"">) or a path that has a trailing slash would still cause weird values set even with C<fix-root-scriptname>. In such cases you can use L<Plack::Middleware::LighttpdScriptNameFix> to fix it. To mount in the non-root path over TCP: fastcgi.server = ( "/foo" => (( "host" = "127.0.0.1", "port" = "5000", "check-local" => "disable", )) It's recommended that your mount path does B<NOT> have the trailing slash. If you I<really> need to have one, you should consider using L<Plack::Middleware::LighttpdScriptNameFix> to fix the wrong B<PATH_INFO> values set by lighttpd. =cut =head2 Authorization Most fastcgi configuration does not pass C<Authorization> headers to C<HTTP_AUTHORIZATION> environment variable by default for security reasons. Authentication middleware such as L<Plack::Middleware::Auth::Basic> or L<Catalyst::Authentication::Credential::HTTP> requires the variable to be set up. Plack::Handler::FCGI supports extracting the C<Authorization> environment variable when it is configured that way. Apache2 with mod_fastcgi: --pass-header Authorization mod_fcgid: FcgiPassHeader Authorization =head1 SEE ALSO L<Plack> =cut ���������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/HTTP/����������������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 017533� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/Standalone.pm��������������������������������������������������������000644 �000765 �000024 �00000001045 12244057435 021402� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler::Standalone; use strict; use warnings; use parent qw( Plack::Handler::HTTP::Server::PSGI ); 1; __END__ =head1 NAME Plack::Handler::Standalone - adapter for HTTP::Server::PSGI =head1 SYNOPSIS % plackup -s Standalone \ --host 127.0.0.1 --port 9091 --timeout 120 =head1 DESCRIPTION Plack::Handler::Standalone is an adapter for default Plack server implementation L<HTTP::Server::PSGI>. This is just an alias for L<Plack::Handler::HTTP::Server::PSGI>. =head1 SEE ALSO L<Plack::Handler::HTTP::Server::PSGI> =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/HTTP/Server/���������������������������������������������������������000755 �000765 �000024 �00000000000 12244057435 021001� 5����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/HTTP/Server/PSGI.pm��������������������������������������������������000644 �000765 �000024 �00000002221 12244057435 022076� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler::HTTP::Server::PSGI; use strict; # for temporary backward compat use parent qw( HTTP::Server::PSGI ); sub new { my($class, %args) = @_; bless { %args }, $class; } sub run { my($self, $app) = @_; $self->_server->run($app); } sub _server { my $self = shift; HTTP::Server::PSGI->new(%$self); } 1; __END__ =head1 NAME Plack::Handler::HTTP::Server::PSGI - adapter for HTTP::Server::PSGI =head1 SYNOPSIS % plackup -s HTTP::Server::PSGI \ --host 127.0.0.1 --port 9091 --timeout 120 =head1 BACKWARD COMPATIBLITY Since Plack 0.99_22 this handler doesn't support preforking configuration i.e. C<--max-workers>. Use L<Starman> or L<Starlet> if you need preforking PSGI web server. =head1 CONFIGURATIONS =over 4 =item host Host the server binds to. Defaults to all interfaces. =item port Port number the server listens on. Defaults to 8080. =item timeout Number of seconds a request times out. Defaults to 300. =item max-reqs-per-child Number of requests per worker to process. Defaults to 100. =back =head1 AUTHOR Kazuho Oku Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack> L<HTTP::Server::PSGI> =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/Handler/Apache2/Registry.pm��������������������������������������������������000644 �000765 �000024 �00000002214 12244057435 022364� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::Handler::Apache2::Registry; use strict; use warnings; use Try::Tiny; use Apache2::Const; use Apache2::Log; use parent qw/Plack::Handler::Apache2/; sub handler { my $class = __PACKAGE__; my ($r) = @_; return try { my $app = $class->load_app( $r->filename ); $class->call_app( $r, $app ); }catch{ if(/no such file/i){ $r->log_error( $_ ); return Apache2::Const::NOT_FOUND; }else{ $r->log_error( $_ ); return Apache2::Const::SERVER_ERROR; } }; } # Overriding sub fixup_path { my ($class, $r, $env) = @_; $env->{PATH_INFO} =~ s{^$env->{SCRIPT_NAME}}{}; } 1; __END__ =head1 NAME Plack::Handler::Apache2::Registry - Runs .psgi files. =head1 SYNOPSIS PerlModule Plack::Handler::Apache2::Registry; <Location /psgi-bin> SetHandler modperl PerlHandler Plack::Handler::Apache2::Registry </Location> =head1 DESCRIPTION This is a handler module to run any *.psgi files with mod_perl2, just like ModPerl::Registry. =head1 AUTHOR Masahiro Honma E<lt>hiratara@cpan.orgE<gt> =head1 SEE ALSO L<Plack::Handler::Apache2> =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/App/Cascade.pm���������������������������������������������������������������000644 �000765 �000024 �00000005256 12244057435 020010� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::App::Cascade; use strict; use base qw(Plack::Component); use Plack::Util; use Plack::Util::Accessor qw(apps catch codes); sub add { my $self = shift; $self->apps([]) unless $self->apps; push @{$self->apps}, @_; } sub prepare_app { my $self = shift; my %codes = map { $_ => 1 } @{ $self->catch || [ 404 ] }; $self->codes(\%codes); } sub call { my($self, $env) = @_; return sub { my $respond = shift; my $done; my $respond_wrapper = sub { my $res = shift; if ($self->codes->{$res->[0]}) { # suppress output by giving the app an # output spool which drops everything on the floor return Plack::Util::inline_object write => sub { }, close => sub { }; } else { $done = 1; return $respond->($res); } }; my @try = @{$self->apps || []}; my $tries_left = 0 + @try; if (not $tries_left) { return $respond->([ 404, [ 'Content-Type' => 'text/html' ], [ '404 Not Found' ] ]) } for my $app (@try) { my $res = $app->($env); if ($tries_left-- == 1) { $respond_wrapper = sub { $respond->(shift) }; } if (ref $res eq 'CODE') { $res->($respond_wrapper); } else { $respond_wrapper->($res); } return if $done; } }; } 1; __END__ =head1 NAME Plack::App::Cascade - Cascadable compound application =head1 SYNOPSIS use Plack::App::Cascade; use Plack::App::URLMap; use Plack::App::File; # Serve static files from multiple search paths my $cascade = Plack::App::Cascade->new; $cascade->add( Plack::App::File->new(root => "/www/example.com/foo")->to_app ); $cascade->add( Plack::App::File->new(root => "/www/example.com/bar")->to_app ); my $app = Plack::App::URLMap->new; $app->map("/static", $cascade); $app->to_app; =head1 DESCRIPTION Plack::App::Cascade is a Plack middleware component that compounds several apps and tries them to return the first response that is not 404. =head1 METHODS =over 4 =item new $app = Plack::App::Cascade->new(apps => [ $app1, $app2 ]); Creates a new Cascade application. =item add $app->add($app1); $app->add($app2, $app3); Appends a new application to the list of apps to try. You can pass the multiple apps to the one C<add> call. =item catch $app->catch([ 403, 404 ]); Sets which error codes to catch and process onwards. Defaults to C<404>. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::App::URLMap> Rack::Cascade =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/App/CGIBin.pm����������������������������������������������������������������000644 �000765 �000024 �00000006332 12244057435 017514� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::App::CGIBin; use strict; use warnings; use parent qw/Plack::App::File/; use Plack::Util::Accessor qw( exec_cb ); use Plack::App::WrapCGI; sub allow_path_info { 1 } my %exec_cache; sub would_exec { my($self, $file) = @_; return $exec_cache{$file} if exists $exec_cache{$file}; my $exec_cb = $self->exec_cb || sub { $self->exec_cb_default(@_) }; return $exec_cache{$file} = $exec_cb->($file); } sub exec_cb_default { my($self, $file) = @_; if ($file =~ /\.pl$/i) { return 0; } elsif ($self->shebang_for($file) =~ /^\#\!.*perl/) { return 0; } else { return 1; } } sub shebang_for { my($self, $file) = @_; open my $fh, "<", $file or return ''; my $line = <$fh>; return $line; } sub serve_path { my($self, $env, $file) = @_; local @{$env}{qw(SCRIPT_NAME PATH_INFO)} = @{$env}{qw( plack.file.SCRIPT_NAME plack.file.PATH_INFO )}; my $app = $self->{_compiled}->{$file} ||= Plack::App::WrapCGI->new( script => $file, execute => $self->would_exec($file), )->to_app; $app->($env); } 1; __END__ =head1 NAME Plack::App::CGIBin - cgi-bin replacement for Plack servers =head1 SYNOPSIS use Plack::App::CGIBin; use Plack::Builder; my $app = Plack::App::CGIBin->new(root => "/path/to/cgi-bin")->to_app; builder { mount "/cgi-bin" => $app; }; # Or from the command line plackup -MPlack::App::CGIBin -e 'Plack::App::CGIBin->new(root => "/path/to/cgi-bin")->to_app' =head1 DESCRIPTION Plack::App::CGIBin allows you to load CGI scripts from a directory and convert them into a PSGI application. This would give you the extreme easiness when you have bunch of old CGI scripts that is loaded using I<cgi-bin> of Apache web server. =head1 HOW IT WORKS This application checks if a given file path is a perl script and if so, uses L<CGI::Compile> to compile a CGI script into a sub (like L<ModPerl::Registry>) and then run it as a persistent application using L<CGI::Emulate::PSGI>. If the given file is not a perl script, it executes the script just like a normal CGI script with fork & exec. This is like a normal web server mode and no performance benefit is achieved. The default mechanism to determine if a given file is a Perl script is as follows: =over 4 =item * Check if the filename ends with C<.pl>. If yes, it is a Perl script. =item * Open the file and see if the shebang (first line of the file) contains the word C<perl> (like C<#!/usr/bin/perl>). If yes, it is a Perl script. =back You can customize this behavior by passing C<exec_cb> callback, which takes a file path to its first argument. For example, if your perl-based CGI script uses lots of global variables and such and are not ready to run on a persistent environment, you can do: my $app = Plack::App::CGIBin->new( root => "/path/to/cgi-bin", exec_cb => sub { 1 }, )->to_app; to always force the execute option for any files. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L<Plack::App::File> L<CGI::Emulate::PSGI> L<CGI::Compile> L<Plack::App::WrapCGI> See also L<Plack::App::WrapCGI> if you compile one CGI script into a PSGI application without serving CGI scripts from a directory, to remove overhead of filesystem lookups, etc. =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Plack-1.0030/lib/Plack/App/Directory.pm�������������������������������������������������������������000644 �000765 �000024 �00000006265 12244057435 020432� 0����������������������������������������������������������������������������������������������������ustar�00miyagawa������������������������staff���������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Plack::App::Directory; use parent qw(Plack::App::File); use strict; use warnings; use Plack::Util; use HTTP::Date; use Plack::MIME; use DirHandle; use URI::Escape; use Plack::Request; # Stolen from rack/directory.rb my $dir_file = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>"; my $dir_page = <<PAGE; <html><head> <title>%s

%s


%s
Name Size Type Last Modified

PAGE sub should_handle { my($self, $file) = @_; return -d $file || -f $file; } sub return_dir_redirect { my ($self, $env) = @_; my $uri = Plack::Request->new($env)->uri; return [ 301, [ 'Location' => $uri . '/', 'Content-Type' => 'text/plain', 'Content-Length' => 8, ], [ 'Redirect' ], ]; } sub serve_path { my($self, $env, $dir, $fullpath) = @_; if (-f $dir) { return $self->SUPER::serve_path($env, $dir, $fullpath); } my $dir_url = $env->{SCRIPT_NAME} . $env->{PATH_INFO}; if ($dir_url !~ m{/$}) { return $self->return_dir_redirect($env); } my @files = ([ "../", "Parent Directory", '', '', '' ]); my $dh = DirHandle->new($dir); my @children; while (defined(my $ent = $dh->read)) { next if $ent eq '.' or $ent eq '..'; push @children, $ent; } for my $basename (sort { $a cmp $b } @children) { my $file = "$dir/$basename"; my $url = $dir_url . $basename; my $is_dir = -d $file; my @stat = stat _; $url = join '/', map {uri_escape($_)} split m{/}, $url; if ($is_dir) { $basename .= "/"; $url .= "/"; } my $mime_type = $is_dir ? 'directory' : ( Plack::MIME->mime_type($file) || 'text/plain' ); push @files, [ $url, $basename, $stat[7], $mime_type, HTTP::Date::time2str($stat[9]) ]; } my $path = Plack::Util::encode_html("Index of $env->{PATH_INFO}"); my $files = join "\n", map { my $f = $_; sprintf $dir_file, map Plack::Util::encode_html($_), @$f; } @files; my $page = sprintf $dir_page, $path, $path, $files; return [ 200, ['Content-Type' => 'text/html; charset=utf-8'], [ $page ] ]; } 1; __END__ =head1 NAME Plack::App::Directory - Serve static files from document root with directory index =head1 SYNOPSIS # app.psgi use Plack::App::Directory; my $app = Plack::App::Directory->new({ root => "/path/to/htdocs" })->to_app; =head1 DESCRIPTION This is a static file server PSGI application with directory index a la Apache's mod_autoindex. =head1 CONFIGURATION =over 4 =item root Document root directory. Defaults to the current directory. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L =cut Plack-1.0030/lib/Plack/App/File.pm000644 000765 000024 00000010617 12244057435 017341 0ustar00miyagawastaff000000 000000 package Plack::App::File; use strict; use warnings; use parent qw/Plack::Component/; use File::Spec::Unix; use Cwd (); use Plack::Util; use Plack::MIME; use HTTP::Date; use Plack::Util::Accessor qw( root file content_type encoding ); sub should_handle { my($self, $file) = @_; return -f $file; } sub call { my $self = shift; my $env = shift; my($file, $path_info) = $self->file || $self->locate_file($env); return $file if ref $file eq 'ARRAY'; if ($path_info) { $env->{'plack.file.SCRIPT_NAME'} = $env->{SCRIPT_NAME} . $env->{PATH_INFO}; $env->{'plack.file.SCRIPT_NAME'} =~ s/\Q$path_info\E$//; $env->{'plack.file.PATH_INFO'} = $path_info; } else { $env->{'plack.file.SCRIPT_NAME'} = $env->{SCRIPT_NAME} . $env->{PATH_INFO}; $env->{'plack.file.PATH_INFO'} = ''; } return $self->serve_path($env, $file); } sub locate_file { my($self, $env) = @_; my $path = $env->{PATH_INFO} || ''; if ($path =~ /\0/) { return $self->return_400; } my $docroot = $self->root || "."; my @path = split /[\\\/]/, $path; if (@path) { shift @path if $path[0] eq ''; } else { @path = ('.'); } if (grep $_ eq '..', @path) { return $self->return_403; } my($file, @path_info); while (@path) { my $try = File::Spec::Unix->catfile($docroot, @path); if ($self->should_handle($try)) { $file = $try; last; } elsif (!$self->allow_path_info) { last; } unshift @path_info, pop @path; } if (!$file) { return $self->return_404; } if (!-r $file) { return $self->return_403; } return $file, join("/", "", @path_info); } sub allow_path_info { 0 } sub serve_path { my($self, $env, $file) = @_; my $content_type = $self->content_type || Plack::MIME->mime_type($file) || 'text/plain'; if ("CODE" eq ref $content_type) { $content_type = $content_type->($file); } if ($content_type =~ m!^text/!) { $content_type .= "; charset=" . ($self->encoding || "utf-8"); } open my $fh, "<:raw", $file or return $self->return_403; my @stat = stat $file; Plack::Util::set_io_path($fh, Cwd::realpath($file)); return [ 200, [ 'Content-Type' => $content_type, 'Content-Length' => $stat[7], 'Last-Modified' => HTTP::Date::time2str( $stat[9] ) ], $fh, ]; } sub return_403 { my $self = shift; return [403, ['Content-Type' => 'text/plain', 'Content-Length' => 9], ['forbidden']]; } sub return_400 { my $self = shift; return [400, ['Content-Type' => 'text/plain', 'Content-Length' => 11], ['Bad Request']]; } # Hint: subclasses can override this to return undef to pass through 404 sub return_404 { my $self = shift; return [404, ['Content-Type' => 'text/plain', 'Content-Length' => 9], ['not found']]; } 1; __END__ =head1 NAME Plack::App::File - Serve static files from root directory =head1 SYNOPSIS use Plack::App::File; my $app = Plack::App::File->new(root => "/path/to/htdocs")->to_app; # Or map the path to a specific file use Plack::Builder; builder { mount "/favicon.ico" => Plack::App::File->new(file => '/path/to/favicon.ico'); }; =head1 DESCRIPTION This is a static file server PSGI application, and internally used by L. This application serves file from document root if the path matches with the local file. Use L if you want to list files in the directory as well. =head1 CONFIGURATION =over 4 =item root Document root directory. Defaults to C<.> (current directory) =item file The file path to create responses from. Optional. If it's set the application would B create a response out of the file and there will be no security check etc. (hence fast). If it's not set, the application uses C to find the matching file. =item encoding Set the file encoding for text files. Defaults to C. =item content_type Set the file content type. If not set L will try to detect it based on the file extension or fall back to C. Can be set to a callback which should work on $_[0] to check full path file name. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L L =cut Plack-1.0030/lib/Plack/App/PSGIBin.pm000644 000765 000024 00000002661 12244057435 017655 0ustar00miyagawastaff000000 000000 package Plack::App::PSGIBin; use strict; use warnings; use parent qw/Plack::App::File/; use Plack::Util; sub allow_path_info { 1 } sub serve_path { my($self, $env, $file) = @_; local @{$env}{qw(SCRIPT_NAME PATH_INFO)} = @{$env}{qw( plack.file.SCRIPT_NAME plack.file.PATH_INFO )}; my $app = $self->{_compiled}->{$file} ||= Plack::Util::load_psgi($file); $app->($env); } 1; __END__ =head1 NAME Plack::App::PSGIBin - Run .psgi files from a directory =head1 SYNOPSIS use Plack::App::PSGIBin; use Plack::Builder; my $app = Plack::App::PSGIBin->new(root => "/path/to/psgi/scripts")->to_app; builder { mount "/psgi" => $app; }; # Or from the command line plackup -MPlack::App::PSGIBin -e 'Plack::App::PSGIBin->new(root => "/path/psgi/scripts")->to_app' =head1 DESCRIPTION This application loads I<.psgi> files (or actually whichever filename extensions) from the root directory and run it as a PSGI application. Suppose you have a directory containing C and C, map this application to C with L and you can access them via the URL: http://example.com/app/foo.psgi http://example.com/app/bar.psgi to load them. You can rename the file to the one without C<.psgi> extension to make the URL look nicer, or use the URL rewriting tools like L to do the same thing. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L Plack-1.0030/lib/Plack/App/URLMap.pm000644 000765 000024 00000013012 12244057435 017552 0ustar00miyagawastaff000000 000000 package Plack::App::URLMap; use strict; use warnings; use parent qw(Plack::Component); use constant DEBUG => $ENV{PLACK_URLMAP_DEBUG}; use Carp (); sub mount { shift->map(@_) } sub map { my $self = shift; my($location, $app) = @_; my $host; if ($location =~ m!^https?://(.*?)(/.*)!) { $host = $1; $location = $2; } if ($location !~ m!^/!) { Carp::croak("Paths need to start with /"); } $location =~ s!/$!!; push @{$self->{_mapping}}, [ $host, $location, qr/^\Q$location\E/, $app ]; } sub prepare_app { my $self = shift; # sort by path length $self->{_sorted_mapping} = [ map { [ @{$_}[2..5] ] } sort { $b->[0] <=> $a->[0] || $b->[1] <=> $a->[1] } map { [ ($_->[0] ? length $_->[0] : 0), length($_->[1]), @$_ ] } @{$self->{_mapping}}, ]; } sub call { my ($self, $env) = @_; my $path_info = $env->{PATH_INFO}; my $script_name = $env->{SCRIPT_NAME}; my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )}; if ($http_host and my $port = $env->{SERVER_PORT}) { $http_host =~ s/:$port$//; } for my $map (@{ $self->{_sorted_mapping} }) { my($host, $location, $location_re, $app) = @$map; my $path = $path_info; # copy no warnings 'uninitialized'; DEBUG && warn "Matching request (Host=$http_host Path=$path) and the map (Host=$host Path=$location)\n"; next unless not defined $host or $http_host eq $host or $server_name eq $host; next unless $location eq '' or $path =~ s!$location_re!!; next unless $path eq '' or $path =~ m!^/!; DEBUG && warn "-> Matched!\n"; my $orig_path_info = $env->{PATH_INFO}; my $orig_script_name = $env->{SCRIPT_NAME}; $env->{PATH_INFO} = $path; $env->{SCRIPT_NAME} = $script_name . $location; return $self->response_cb($app->($env), sub { $env->{PATH_INFO} = $orig_path_info; $env->{SCRIPT_NAME} = $orig_script_name; }); } DEBUG && warn "All matching failed.\n"; return [404, [ 'Content-Type' => 'text/plain' ], [ "Not Found" ]]; } 1; __END__ =head1 NAME Plack::App::URLMap - Map multiple apps in different paths =head1 SYNOPSIS use Plack::App::URLMap; my $app1 = sub { ... }; my $app2 = sub { ... }; my $app3 = sub { ... }; my $urlmap = Plack::App::URLMap->new; $urlmap->map("/" => $app1); $urlmap->map("/foo" => $app2); $urlmap->map("http://bar.example.com/" => $app3); my $app = $urlmap->to_app; =head1 DESCRIPTION Plack::App::URLMap is a PSGI application that can dispatch multiple applications based on URL path and host names (a.k.a "virtual hosting") and takes care of rewriting C and C (See L for details). This module is inspired by Ruby's Rack::URLMap. =head1 METHODS =over 4 =item map $urlmap->map("/foo" => $app); $urlmap->map("http://bar.example.com/" => $another_app); Maps URL path or an absolute URL to a PSGI application. The match order is sorted by host name length and then path length (longest strings first). URL paths need to match from the beginning and should match completely until the path separator (or the end of the path). For example, if you register the path C, it I match with the request C, C or C but it I match with C. Mapping URLs with host names is also possible, and in that case the URL mapping works like a virtual host. Mappings will nest. If $app is already mapped to C it will match a request for C but not C. See L for more details. =item mount Alias for C. =item to_app my $handler = $urlmap->to_app; Returns the PSGI application code reference. Note that the Plack::App::URLMap object is callable (by overloading the code dereference), so returning the object itself as a PSGI application should also work. =back =head1 PERFORMANCE If you C (or C with Plack::Builder) N applications, Plack::App::URLMap will need to at most iterate through N paths to match incoming requests. It is a good idea to use C only for a known, limited amount of applications, since mounting hundreds of applications could affect runtime request performance. =head1 DEBUGGING You can set the environment variable C to see how this application matches with the incoming request host names and paths. =head1 HOW THIS WORKS This application works by I C and C before dispatching the incoming request to the relocated applications. Say you have a Wiki application that takes C and C and makes a PSGI application C<$wiki_app> out of it, using one of supported web frameworks, you can put the whole application under C by: # MyWikiApp looks at PATH_INFO and handles /index and /page/* my $wiki_app = sub { MyWikiApp->run(@_) }; use Plack::App::URLMap; my $app = Plack::App::URLMap->new; $app->mount("/wiki" => $wiki_app); When a request comes in with C set to C, the URLMap application C<$app> strips the C part from C and B that to C. That way, if the C<$app> is mounted under the root (i.e. C is C<"">) with standalone web servers like L, C is now locally set to C and C is changed to C when C<$wiki_app> gets called. =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L =cut Plack-1.0030/lib/Plack/App/WrapCGI.pm000644 000765 000024 00000006723 12244057435 017721 0ustar00miyagawastaff000000 000000 package Plack::App::WrapCGI; use strict; use warnings; use parent qw(Plack::Component); use Plack::Util::Accessor qw(script execute _app); use File::Spec; use CGI::Emulate::PSGI; use CGI::Compile; use Carp; use POSIX ":sys_wait_h"; sub prepare_app { my $self = shift; my $script = $self->script or croak "'script' is not set"; $script = File::Spec->rel2abs($script); if ($self->execute) { my $app = sub { my $env = shift; pipe( my $stdoutr, my $stdoutw ); pipe( my $stdinr, my $stdinw ); my $pid = fork(); Carp::croak("fork failed: $!") unless defined $pid; if ($pid == 0) { # child local $SIG{__DIE__} = sub { print STDERR @_; exit(1); }; close $stdoutr; close $stdinw; local %ENV = (%ENV, CGI::Emulate::PSGI->emulate_environment($env)); open( STDOUT, ">&=" . fileno($stdoutw) ) or Carp::croak "Cannot dup STDOUT: $!"; open( STDIN, "<&=" . fileno($stdinr) ) or Carp::croak "Cannot dup STDIN: $!"; chdir(File::Basename::dirname($script)); exec($script) or Carp::croak("cannot exec: $!"); exit(2); } close $stdoutw; close $stdinr; syswrite($stdinw, do { local $/; my $fh = $env->{'psgi.input'}; <$fh>; }); # close STDIN so child will stop waiting close $stdinw; my $res = ''; while (waitpid($pid, WNOHANG) <= 0) { $res .= do { local $/; <$stdoutr> }; } $res .= do { local $/; <$stdoutr> }; if (POSIX::WIFEXITED($?)) { return CGI::Parse::PSGI::parse_cgi_output(\$res); } else { Carp::croak("Error at run_on_shell CGI: $!"); } }; $self->_app($app); } else { my $sub = CGI::Compile->compile($script); my $app = CGI::Emulate::PSGI->handler($sub); $self->_app($app); } } sub call { my($self, $env) = @_; $self->_app->($env); } 1; __END__ =head1 NAME Plack::App::WrapCGI - Compiles a CGI script as PSGI application =head1 SYNOPSIS use Plack::App::WrapCGI; my $app = Plack::App::WrapCGI->new(script => "/path/to/script.pl")->to_app; # if you want to execute as a real CGI script my $app = Plack::App::WrapCGI->new(script => "/path/to/script.rb", execute => 1)->to_app; =head1 DESCRIPTION Plack::App::WrapCGI compiles a CGI script into a PSGI application using L and L, and runs it with any PSGI server as a PSGI application. See also L if you have a directory that contains a lot of CGI scripts and serve them like Apache's mod_cgi. =head1 METHODS =over 4 =item new my $app = Plack::App::WrapCGI->new(%args); Creates a new PSGI application using the given script. I<%args> has two parameters: =over 8 =item script The path to a CGI-style program. This is a required parameter. =item execute An optional parameter. When set to a true value, this app will run the script with a CGI-style C/C model. Note that you may run programs written in other languages with this approach. =back =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L =cut Plack-1.0030/lib/HTTP/Message/000755 000765 000024 00000000000 12244057435 016510 5ustar00miyagawastaff000000 000000 Plack-1.0030/lib/HTTP/Server/000755 000765 000024 00000000000 12244057435 016372 5ustar00miyagawastaff000000 000000 Plack-1.0030/lib/HTTP/Server/PSGI.pm000644 000765 000024 00000024073 12244057435 017500 0ustar00miyagawastaff000000 000000 package HTTP::Server::PSGI; use strict; use warnings; use Carp (); use Plack; use Plack::HTTPParser qw( parse_http_request ); use IO::Socket::INET; use HTTP::Date; use HTTP::Status; use List::Util qw(max sum); use Plack::Util; use Stream::Buffered; use Plack::Middleware::ContentLength; use POSIX qw(EINTR); use Socket qw(IPPROTO_TCP TCP_NODELAY); use Try::Tiny; use Time::HiRes qw(time); my $alarm_interval; BEGIN { if ($^O eq 'MSWin32') { $alarm_interval = 1; } else { Time::HiRes->import('alarm'); $alarm_interval = 0.1; } } use constant MAX_REQUEST_SIZE => 131072; use constant MSWin32 => $^O eq 'MSWin32'; sub new { my($class, %args) = @_; my $self = bless { host => $args{host} || 0, port => $args{port} || 8080, timeout => $args{timeout} || 300, server_software => $args{server_software} || $class, server_ready => $args{server_ready} || sub {}, ssl => $args{ssl}, ipv6 => $args{ipv6}, ssl_key_file => $args{ssl_key_file}, ssl_cert_file => $args{ssl_cert_file}, }, $class; $self; } sub run { my($self, $app) = @_; $self->setup_listener(); $self->accept_loop($app); } sub prepare_socket_class { my($self, $args) = @_; if ($self->{ssl} && $self->{ipv6}) { Carp::croak("SSL and IPv6 are not supported at the same time (yet). Choose one."); } if ($self->{ssl}) { eval { require IO::Socket::SSL; 1 } or Carp::croak("SSL suport requires IO::Socket::SSL"); $args->{SSL_key_file} = $self->{ssl_key_file}; $args->{SSL_cert_file} = $self->{ssl_cert_file}; return "IO::Socket::SSL"; } elsif ($self->{ipv6}) { eval { require IO::Socket::IP; 1 } or Carp::croak("IPv6 support requires IO::Socket::IP"); $self->{host} ||= '::'; $args->{LocalAddr} ||= '::'; return "IO::Socket::IP"; } return "IO::Socket::INET"; } sub setup_listener { my $self = shift; my %args = ( Listen => SOMAXCONN, LocalPort => $self->{port}, LocalAddr => $self->{host}, Proto => 'tcp', ReuseAddr => 1, ); my $class = $self->prepare_socket_class(\%args); $self->{listen_sock} ||= $class->new(%args) or die "failed to listen to port $self->{port}: $!"; $self->{server_ready}->({ %$self, proto => $self->{ssl} ? 'https' : 'http' }); } sub accept_loop { my($self, $app) = @_; $app = Plack::Middleware::ContentLength->wrap($app); while (1) { local $SIG{PIPE} = 'IGNORE'; if (my $conn = $self->{listen_sock}->accept) { $conn->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) or die "setsockopt(TCP_NODELAY) failed:$!"; my $env = { SERVER_PORT => $self->{port}, SERVER_NAME => $self->{host}, SCRIPT_NAME => '', REMOTE_ADDR => $conn->peerhost, REMOTE_PORT => $conn->peerport || 0, 'psgi.version' => [ 1, 1 ], 'psgi.errors' => *STDERR, 'psgi.url_scheme' => $self->{ssl} ? 'https' : 'http', 'psgi.run_once' => Plack::Util::FALSE, 'psgi.multithread' => Plack::Util::FALSE, 'psgi.multiprocess' => Plack::Util::FALSE, 'psgi.streaming' => Plack::Util::TRUE, 'psgi.nonblocking' => Plack::Util::FALSE, 'psgix.harakiri' => Plack::Util::TRUE, 'psgix.input.buffered' => Plack::Util::TRUE, 'psgix.io' => $conn, }; $self->handle_connection($env, $conn, $app); $conn->close; last if $env->{'psgix.harakiri.commit'}; } } } sub handle_connection { my($self, $env, $conn, $app) = @_; my $buf = ''; my $res = [ 400, [ 'Content-Type' => 'text/plain' ], [ 'Bad Request' ] ]; while (1) { my $rlen = $self->read_timeout( $conn, \$buf, MAX_REQUEST_SIZE - length($buf), length($buf), $self->{timeout}, ) or return; my $reqlen = parse_http_request($buf, $env); if ($reqlen >= 0) { $buf = substr $buf, $reqlen; if (my $cl = $env->{CONTENT_LENGTH}) { my $buffer = Stream::Buffered->new($cl); while ($cl > 0) { my $chunk; if (length $buf) { $chunk = $buf; $buf = ''; } else { $self->read_timeout($conn, \$chunk, $cl, 0, $self->{timeout}) or return; } $buffer->print($chunk); $cl -= length $chunk; } $env->{'psgi.input'} = $buffer->rewind; } else { open my $input, "<", \$buf; $env->{'psgi.input'} = $input; } $res = Plack::Util::run_app $app, $env; last; } if ($reqlen == -2) { # request is incomplete, do nothing } elsif ($reqlen == -1) { # error, close conn last; } } if (ref $res eq 'ARRAY') { $self->_handle_response($res, $conn); } elsif (ref $res eq 'CODE') { $res->(sub { $self->_handle_response($_[0], $conn); }); } else { die "Bad response $res"; } return; } sub _handle_response { my($self, $res, $conn) = @_; my @lines = ( "Date: @{[HTTP::Date::time2str()]}\015\012", "Server: $self->{server_software}\015\012", ); Plack::Util::header_iter($res->[1], sub { my ($k, $v) = @_; push @lines, "$k: $v\015\012"; }); unshift @lines, "HTTP/1.0 $res->[0] @{[ HTTP::Status::status_message($res->[0]) ]}\015\012"; push @lines, "\015\012"; $self->write_all($conn, join('', @lines), $self->{timeout}) or return; if (defined $res->[2]) { my $err; my $done; { local $@; eval { Plack::Util::foreach( $res->[2], sub { $self->write_all($conn, $_[0], $self->{timeout}) or die "failed to send all data\n"; }, ); $done = 1; }; $err = $@; }; unless ($done) { if ($err =~ /^failed to send all data\n/) { return; } else { die $err; } } } else { return Plack::Util::inline_object write => sub { $self->write_all($conn, $_[0], $self->{timeout}) }, close => sub { }; } } # returns 1 if socket is ready, undef on timeout sub do_timeout { my ($self, $cb, $timeout) = @_; local $SIG{ALRM} = sub {}; my $wait_until = time + $timeout; alarm($timeout); my $ret; while (1) { if ($ret = $cb->()) { last; } elsif (! (! defined($ret) && $! == EINTR)) { undef $ret; last; } # got EINTR my $left = $wait_until - time; last if $left <= 0; alarm($left + $alarm_interval); } alarm(0); $ret; } # returns (positive) number of bytes read, or undef if the socket is to be closed sub read_timeout { my ($self, $sock, $buf, $len, $off, $timeout) = @_; $self->do_timeout(sub { $sock->sysread($$buf, $len, $off) }, $timeout); } # returns (positive) number of bytes written, or undef if the socket is to be closed sub write_timeout { my ($self, $sock, $buf, $len, $off, $timeout) = @_; $self->do_timeout(sub { $sock->syswrite($buf, $len, $off) }, $timeout); } # writes all data in buf and returns number of bytes written or undef if failed sub write_all { my ($self, $sock, $buf, $timeout) = @_; return 0 unless defined $buf; _encode($buf); my $off = 0; while (my $len = length($buf) - $off) { my $ret = $self->write_timeout($sock, $buf, $len, $off, $timeout) or return; $off += $ret; } return length $buf; } # syswrite() will crash when given wide characters sub _encode { if ($_[0] =~ /[^\x00-\xff]/) { Carp::carp("Wide character outside byte range in response. Encoding data as UTF-8"); utf8::encode($_[0]); } } 1; __END__ =head1 NAME HTTP::Server::PSGI - Standalone PSGI compatible HTTP server =head1 SYNOPSIS use HTTP::Server::PSGI; my $server = HTTP::Server::PSGI->new( host => "127.0.0.1", port => 9091, timeout => 120, ); $server->run($app); =head1 DESCRIPTION HTTP::Server::PSGI is a standalone, single-process and PSGI compatible HTTP server implementations. This server should be great for the development and testing, but might not be suitable for a production use. Some features in HTTP/1.1, notably chunked requests, responses and pipeline requests are B supported, and it also does not support HTTP/0.9. See L or uWSGI server if you want HTTP/1.1 and other features ready for a production use. =head1 PREFORKING L does B support preforking. See L or L if you want a multi-process prefork web servers. =head1 HARAKIRI SUPPORT This web server supports `psgix.harakiri` extension defined in the L. This application is a non-forking single process web server (i.e. `psgi.multiprocess` is false), and if your application commits harakiri, the entire web server stops too. In case this behavior is not what you want, be sure to check `psgi.multiprocess` as well to enable harakiri only in the preforking servers such as L. On the other hand, this behavior might be handy if you want to embed this module in your application and serve HTTP requests for only short period of time, then go back to your main program. =head1 AUTHOR Kazuho Oku Tatsuhiko Miyagawa =head1 SEE ALSO L L L =cut Plack-1.0030/lib/HTTP/Message/PSGI.pm000644 000765 000024 00000015006 12244057435 017612 0ustar00miyagawastaff000000 000000 package HTTP::Message::PSGI; use strict; use warnings; use parent qw(Exporter); our @EXPORT = qw( req_to_psgi res_from_psgi ); use Carp (); use HTTP::Status qw(status_message); use URI::Escape (); use Plack::Util; use Try::Tiny; my $TRUE = (1 == 1); my $FALSE = !$TRUE; sub req_to_psgi { my $req = shift; unless (try { $req->isa('HTTP::Request') }) { Carp::croak("Request is not HTTP::Request: $req"); } # from HTTP::Request::AsCGI my $host = $req->header('Host'); my $uri = $req->uri->clone; $uri->scheme('http') unless $uri->scheme; $uri->host('localhost') unless $uri->host; $uri->port(80) unless $uri->port; $uri->host_port($host) unless !$host || ( $host eq $uri->host_port ); my $input; my $content = $req->content; if (ref $content eq 'CODE') { if (defined $req->content_length) { $input = HTTP::Message::PSGI::ChunkedInput->new($content); } else { $req->header("Transfer-Encoding" => "chunked"); $input = HTTP::Message::PSGI::ChunkedInput->new($content, 1); } } else { open $input, "<", \$content; $req->content_length(length $content) unless defined $req->content_length; } my $env = { PATH_INFO => URI::Escape::uri_unescape($uri->path || '/'), QUERY_STRING => $uri->query || '', SCRIPT_NAME => '', SERVER_NAME => $uri->host, SERVER_PORT => $uri->port, SERVER_PROTOCOL => $req->protocol || 'HTTP/1.1', REMOTE_ADDR => '127.0.0.1', REMOTE_HOST => 'localhost', REMOTE_PORT => int( rand(64000) + 1000 ), # not in RFC 3875 REQUEST_URI => $uri->path_query || '/', # not in RFC 3875 REQUEST_METHOD => $req->method, 'psgi.version' => [ 1, 1 ], 'psgi.url_scheme' => $uri->scheme eq 'https' ? 'https' : 'http', 'psgi.input' => $input, 'psgi.errors' => *STDERR, 'psgi.multithread' => $FALSE, 'psgi.multiprocess' => $FALSE, 'psgi.run_once' => $TRUE, 'psgi.streaming' => $TRUE, 'psgi.nonblocking' => $FALSE, @_, }; for my $field ( $req->headers->header_field_names ) { my $key = uc("HTTP_$field"); $key =~ tr/-/_/; $key =~ s/^HTTP_// if $field =~ /^Content-(Length|Type)$/; unless ( exists $env->{$key} ) { $env->{$key} = $req->headers->header($field); } } if ($env->{SCRIPT_NAME}) { $env->{PATH_INFO} =~ s/^\Q$env->{SCRIPT_NAME}\E/\//; $env->{PATH_INFO} =~ s/^\/+/\//; } if (!defined($env->{HTTP_HOST}) && $req->uri->can('host')) { $env->{HTTP_HOST} = $req->uri->host; $env->{HTTP_HOST} .= ':' . $req->uri->port if $req->uri->port ne $req->uri->default_port; } return $env; } sub res_from_psgi { my ($psgi_res) = @_; require HTTP::Response; my $res; if (ref $psgi_res eq 'ARRAY') { _res_from_psgi($psgi_res, \$res); } elsif (ref $psgi_res eq 'CODE') { $psgi_res->(sub { _res_from_psgi($_[0], \$res); }); } return $res; } sub _res_from_psgi { my ($status, $headers, $body) = @{+shift}; my $res_ref = shift; my $convert_resp = sub { my $res = HTTP::Response->new($status); $res->message(status_message($status)); $res->headers->header(@$headers) if @$headers; if (ref $body eq 'ARRAY') { $res->content(join '', grep defined, @$body); } else { local $/ = \4096; my $content = ''; while (defined(my $buf = $body->getline)) { $content .= $buf; } $body->close; $res->content($content); } ${ $res_ref } = $res; return; }; if (!defined $body) { my $o = Plack::Util::inline_object write => sub { push @{ $body ||= [] }, @_ }, close => $convert_resp; return $o; } $convert_resp->(); } sub HTTP::Request::to_psgi { req_to_psgi(@_); } sub HTTP::Response::from_psgi { my $class = shift; res_from_psgi(@_); } package HTTP::Message::PSGI::ChunkedInput; sub new { my($class, $content, $chunked) = @_; my $content_cb; if ($chunked) { my $done; $content_cb = sub { my $chunk = $content->(); return if $done; unless (defined $chunk) { $done = 1; return "0\015\012\015\012"; } return '' unless length $chunk; return sprintf('%x', length $chunk) . "\015\012$chunk\015\012"; }; } else { $content_cb = $content; } bless { content => $content_cb }, $class; } sub read { my $self = shift; my $chunk = $self->{content}->(); return 0 unless defined $chunk; $_[0] = ''; substr($_[0], $_[2] || 0, length $chunk) = $chunk; return length $chunk; } sub close { } package HTTP::Message::PSGI; 1; __END__ =head1 NAME HTTP::Message::PSGI - Converts HTTP::Request and HTTP::Response from/to PSGI env and response =head1 SYNOPSIS use HTTP::Message::PSGI; # $req is HTTP::Request, $res is HTTP::Response my $env = req_to_psgi($req); my $res = res_from_psgi([ $status, $headers, $body ]); # Adds methods to HTTP::Request/Response class as well my $env = $req->to_psgi; my $res = HTTP::Response->from_psgi([ $status, $headers, $body ]); =head1 DESCRIPTION HTTP::Message::PSGI gives you convenient methods to convert an L object to a PSGI env hash and convert a PSGI response arrayref to a L object. If you want the other way around, see L and L. =head1 METHODS =over 4 =item req_to_psgi my $env = req_to_psgi($req [, $key => $val ... ]); Converts a L object into a PSGI env hash reference. =item HTTP::Request::to_psgi my $env = $req->to_psgi; Same as C but an instance method in L. =item res_from_psgi my $res = res_from_psgi([ $status, $headers, $body ]); Creates a L object from a PSGI response array ref. =item HTTP::Response->from_psgi my $res = HTTP::Response->from_psgi([ $status, $headers, $body ]); Same as C, but is a class method in L. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L L L =cut Plack-1.0030/eg/dot-psgi/000755 000765 000024 00000000000 12244057435 015720 5ustar00miyagawastaff000000 000000 Plack-1.0030/eg/dot-psgi/cgi-pm.psgi000644 000765 000024 00000000406 12244057435 017760 0ustar00miyagawastaff000000 000000 use CGI::PSGI; sub { my $env = shift; my $query = CGI::PSGI->new($env); # return [ 200, [ "Content-Type" => "text/plain" ], [ "Hello ", $query->param('name') ] ]; return [ $query->psgi_header('text/plain'), [ "Hello ", $query->param('name') ] ]; }Plack-1.0030/eg/dot-psgi/cgi-script.psgi000644 000765 000024 00000000243 12244057435 020647 0ustar00miyagawastaff000000 000000 use CGI::Emulate::PSGI; my $handler = CGI::Emulate::PSGI->handler(sub { do "hello.cgi"; CGI::initialize_globals() if defined &CGI::initialize_globals; }); Plack-1.0030/eg/dot-psgi/Dumper.psgi000644 000765 000024 00000000207 12244057435 020037 0ustar00miyagawastaff000000 000000 use Data::Dumper; my $handler = sub { my $env = shift; return [ 200, [ "Content-Type" => "text/plain" ], [ Dumper $env ] ]; }; Plack-1.0030/eg/dot-psgi/echo-stream-sync.psgi000644 000765 000024 00000000417 12244057435 021767 0ustar00miyagawastaff000000 000000 my $app = sub { my $env = shift; return sub { my $respond = shift; my $w = $respond->([ 200, ['X-Foo' => 'bar', 'Content-Type' => 'text/plain'] ]); for (1..5) { sleep 1; $w->write(time . "\n"); } }; }; Plack-1.0030/eg/dot-psgi/echo-stream.psgi000644 000765 000024 00000000736 12244057435 021021 0ustar00miyagawastaff000000 000000 use AnyEvent; my $app = sub { my $env = shift; warn "This app needs a server that supports psgi.streaming" unless $env->{'psgi.streaming'}; return sub { my $respond = shift; my $w = $respond->([ 200, ['X-Foo' => 'bar', 'Content-Type' => 'text/plain'] ]); my $t; $t = AE::timer 0, 1, sub { $t; # TODO handle client disconnect (broken pipe) and poll_cb $w->write(time . "\n"); }; }; }; Plack-1.0030/eg/dot-psgi/echo.psgi000644 000765 000024 00000000623 12244057435 017523 0ustar00miyagawastaff000000 000000 my $app = sub { my $env = shift; warn "This app would block with sleep(): try echo-stream.psgi" if $env->{'psgi.nonblocking'}; my $count; my $body = Plack::Util::inline_object getline => sub { return if $count++ > 5; sleep 1; return time . "\n"; }, close => sub {}; return [ 200, ['X-Foo' => 'bar'], $body ]; }; Plack-1.0030/eg/dot-psgi/error.psgi000644 000765 000024 00000000200 12244057435 017725 0ustar00miyagawastaff000000 000000 sub { my $x = "bar"; my $y = [1,2,3]; my $z = { x => 1 }; my @y = qw(foo bar); my %z = (x => 1, y => 2); die "Oops"; }; Plack-1.0030/eg/dot-psgi/Hello.psgi000644 000765 000024 00000000172 12244057435 017647 0ustar00miyagawastaff000000 000000 my $handler = sub { return [ 200, [ "Content-Type" => "text/plain", "Content-Length" => 11 ], [ "Hello World" ] ]; }; Plack-1.0030/eg/dot-psgi/image.psgi000644 000765 000024 00000000434 12244057435 017667 0ustar00miyagawastaff000000 000000 use File::Basename; my $path = $ENV{PSGI_IMAGE_FILE} || dirname(__FILE__) . "/../../share/baybridge.jpg"; my $handler = sub { open my $fh, "<:raw", $path or die $!; return [ 200, [ "Content-Type" => "image/jpeg", "X-Sendfile" => $path, "Content-Length" => -s $fh ], $fh ]; }; Plack-1.0030/eg/dot-psgi/nonblock-hello.psgi000644 000765 000024 00000000763 12244057435 021520 0ustar00miyagawastaff000000 000000 use AnyEvent; my $app = sub { my $env = shift; warn "This app needs a server that supports psgi.streaming and psgi.nonblocking" unless $env->{'psgi.streaming'} && $env->{'psgi.nonblocking'}; return sub { my $respond = shift; my $w = $respond->([ 200, ['Content-Type' => 'text/plain'] ]); $w->write("Hello\n"); my $t; $t = AE::timer 2, 0, sub { undef $t; $w->write("World\n"); $w->close; }; }; }; Plack-1.0030/eg/dot-psgi/plack-req.psgi000644 000765 000024 00000000400 12244057435 020455 0ustar00miyagawastaff000000 000000 use Plack::Request; use Plack::Response; sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->content_type('text/plain'); $res->body("Hello " . $req->param('name')); $res->finalize; } Plack-1.0030/eg/dot-psgi/runnable.psgi000755 000765 000024 00000000335 12244057435 020416 0ustar00miyagawastaff000000 000000 #!/usr/bin/perl unless (caller) { require Plack::Runner; Plack::Runner->run(@ARGV, $0); } my $handler = sub { return [ 200, [ "Content-Type" => "text/plain", "Content-Length" => 11 ], [ "Hello World" ] ]; }; Plack-1.0030/eg/dot-psgi/slowapp.psgi000644 000765 000024 00000001141 12244057435 020266 0ustar00miyagawastaff000000 000000 # emulate a slow web app that does DB query etc. use Time::HiRes; sub _sleep { # If it's running in Coro, you can use Coro's co-operative multi # tasking to do time-consuming task by yeilding to other threads: # we use Coro::Timer::sleep to demonstrate that: if ($INC{"Coro.pm"}) { require Coro::Timer; Coro::Timer::sleep( $_[0] ); } else { Time::HiRes::sleep( $_[0] ); } } my $handler = sub { _sleep 0.1; # emulate the DB/IO task that takes 0.1 second return [ 200, [ "Content-Type" => "text/plain", "Content-Length" => 11 ], [ "Hello World" ] ]; }; Plack-1.0030/eg/dot-psgi/static/000755 000765 000024 00000000000 12244057435 017207 5ustar00miyagawastaff000000 000000 Plack-1.0030/eg/dot-psgi/static.psgi000644 000765 000024 00000000507 12244057435 020075 0ustar00miyagawastaff000000 000000 use Plack::Builder; use File::Basename qw(dirname); my $handler = sub { return [ 404, [ "Content-Type" => "text/plain" ], [ "Not Found" ] ]; }; builder { enable "Plack::Middleware::ConditionalGET"; enable "Plack::Middleware::Static", path => qr/./, root => dirname(__FILE__) . '/static'; $handler; }; Plack-1.0030/eg/dot-psgi/twitter-stream.psgi000644 000765 000024 00000001274 12244057435 021603 0ustar00miyagawastaff000000 000000 use AnyEvent::Twitter::Stream; use Encode; my $app = sub { my $env = shift; my $keyword = $env->{PATH_INFO}; $keyword =~ s!^/!!; my $cb = sub { }; # track keywords my $guard = AnyEvent::Twitter::Stream->new( username => $ENV{TWITTER_USERNAME}, password => $ENV{TWITTER_PASSWORD}, method => "filter", track => $keyword || "twitter", on_tweet => sub { $cb->(@_) }, ); return sub { my $respond = shift; my $w = $respond->([ 200, ['Content-Type' => 'text/plain'] ]); $cb = sub { my $tweet = shift; $w->write(Encode::encode_utf8($tweet->{text}) . "\n"); }; }; }; Plack-1.0030/eg/dot-psgi/static/index.html000644 000765 000024 00000000231 12244057435 021200 0ustar00miyagawastaff000000 000000 Hello Plack-1.0030/eg/dot-psgi/static/test.css000644 000765 000024 00000000031 12244057435 020672 0ustar00miyagawastaff000000 000000 body { font-size: 20px } Plack-1.0030/eg/dot-psgi/static/test.js000644 000765 000024 00000000021 12244057435 020515 0ustar00miyagawastaff000000 000000 function foo() {}Plack-1.0030/benchmarks/ab.pl000755 000765 000024 00000003266 12244057435 016645 0ustar00miyagawastaff000000 000000 #!/usr/bin/perl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../lib"; use Plack::Loader; use Test::TCP; use Getopt::Long; use URI; use String::ShellQuote; my $app = 'eg/dot-psgi/Hello.psgi'; my $ab = 'ab -t 1 -c 10 -k -q'; my $url = 'http://127.0.0.1/'; my @try = ( [ 'AnyEvent::HTTPD' ], [ 'HTTP::Server::PSGI' ], [ 'Twiggy' ], [ 'HTTP::Server::Simple' ], [ 'Corona' ], [ 'Danga::Socket' ], [ '+POE::Component::Server::PSGI' ], [ 'Starlet', ' (workers=10)', max_workers => 10 ], [ 'Starman', ' (workers=10)', workers => 10 ], [ 'Feersum' ], ); my @backends; for my $handler (@try) { eval { Plack::Loader->load($handler->[0]) }; push @backends, $handler unless $@; } warn "Testing implementations: ", join(", ", map $_->[0], @backends), "\n"; GetOptions( 'a|app=s' => \$app, 'b|bench=s' => \$ab, 'u|url=s' => \$url, ) or die; &main; sub main { print < sub { my $port = shift; my $uri = URI->new($url); $uri->port($port); $uri = shell_quote($uri); system "ab -n 20 $uri > /dev/null"; # warmup print `$ab $uri | grep 'Requests per '`; }, server => sub { my $port = shift; my $handler = Plack::Util::load_psgi $app; my $server = Plack::Loader->load($server_class, port => $port, @args); $server->run($handler); }, ); } Plack-1.0030/benchmarks/fcgi.pl000755 000765 000024 00000001766 12244057435 017176 0ustar00miyagawastaff000000 000000 #!/usr/bin/env perl use strict; use warnings; use autodie; use FCGI::Client; use Plack::Loader; use Getopt::Long; use Pod::Usage; use File::Temp (); use IO::Socket::UNIX; use Benchmark ':all'; my %opts = (app => "eg/dot-psgi/Hello.psgi"); GetOptions(\%opts, "app=s", "impl=s", "help"); pod2usage(0) if $opts{help}; my $fname = File::Temp::tmpnam(); my $env = +{ }; my $content = ''; my $pid = fork(); if ($pid > 0) { timethis( 10000 => sub { my $sock = IO::Socket::UNIX->new( Peer => $fname ) or die $!; my $conn = FCGI::Client::Connection->new( sock => $sock ); my ( $stdout, $stderr ) = $conn->request( $env, $content ); } ); kill 9, $pid; wait; } else { my $sock = IO::Socket::UNIX->new( Local => $fname, Listen => 10 ) or die $!; open *STDIN, '>&', $sock; # dup my $handler = Plack::Util::load_psgi($opts{app}); my $impl = Plack::Loader->load('FCGI'); $impl->run($handler); die 'should not reach here'; }