2 <html><head><meta charset=
"utf-8"><title>Introduction to PSGI
</title><link rel=
"stylesheet" href=
"css/common.css"><link rel=
"stylesheet" href=
"css/slides.css"></head><body><textarea id=
"source">
16 ![Bluehost](img/bluehost.png)
18 ### https://bluehost.com/careers
24 - Answer "What is PSGI?"
25 - Examine some alternatives to PSGI.
33 ### **P**erl [web] **S**erver **G**ateway **I**nterface
36 - It is an interface between Perl web applications and web servers.
39 - It is a *specification*, not code.
42 - First released to the CPAN on
13 Oct
2009.
45 - Originally written by Miyagawa.
47 .center[![Tatsuhiko Miyagawa](img/avatar-miyagawa.jpg)]
50 Written by **Tatsuhiko Miyagawa**, author of:
53 - way too many Perl modules on CPAN to list
56 - Inspired by WSGI (Python) and Rack (Ruby).
59 - PEP-
333 (WSGI
1.0) was released on
07 Dec
2003.
60 - Rack
0.1 was released on
03 Mar
2007.
62 Despite Perl's long history of powering the web, we were well behind the curve on this.
68 ## Extremely High-level Overview
71 ![Basic Flow](img/basic-flow1.svg)
75 - PSGI is the language your app speaks in order to communicate with user agents.
76 - User agents are browsers.
77 - I've glossed over some important details, like the fact that users don't speak PSGI.
83 ## Pretty High-level Overview
86 ![Basic Flow](img/basic-flow2.svg)
90 - In reality, your app speaks PSGI but user agents speak HTTP.
91 - You need software in the middle that can speak both, and that's usually a web server.
97 ## Somewhat High-level Overview
100 ![Basic Flow](img/basic-flow3.svg)
104 - In reality, most web servers don't speak PSGI. :-(
105 - There are "handlers" that speak both PSGI and another language that web servers do speak.
110 - Yes, HTTP. Many modern web servers speak HTTP not only as a server but also as a client.
111 - This allows them to *proxy* (act as middleman) between user agents and other servers.
112 - In the world of PSGI (and "Plack"), handlers are also called adapters or connectors.
113 - There are already adapters for every major web server.
120 class: center, middle
122 ## Somewhat High-level Overview
125 ![Basic Flow](img/basic-flow4.svg)
129 - Notice that as we've zoomed in, the interaction between the web app and the rest has remained the
131 - From a developer perspective, the etremely high-level overview is sufficient.
132 - This is one of the benefits of PSGI:
133 - Write your application once and leave deployment details to devops.
134 - The intrastructure details can change (swap in a different web server) and the app will still work.
135 - Maybe you're both developer and system architect, but the separation between developer and
137 - In fairness, this isn't a new concept.
138 - The way this has been achieved before is using a *web framework*.
142 class: center, middle
144 ## High-level Overview
147 ![Basic Flow](img/basic-flow5.svg)
151 - A web framework makes it so your app doesn't need to speak HTTP or CGI or even PSGI.
160 > Writing your web application directly using [PSGI/Plack] is certainly possible but not recommended.
164 > If you're writing a web application, not a framework, then you're encouraged to use one of the web
165 > application frameworks that support PSGI (http://plackperl.org/#frameworks), or see modules like
166 > HTTP::Engine to provide higher level Request and Response API on top of PSGI.
168 > -- [Plack::Request Documentation](https://search.cpan.org/~miyagawa/Plack/lib/Plack/Request.pm)
171 - When you start learning about Plack, you'll realize that it is actually pretty capable.
172 - You may start to think that everything should be developed on that level -- don't do it!
173 - For most stuff, you'll still want to use a web framework.
174 - Web frameworks often offer more convenient abstractions than raw PSGI.
181 If you're a developer writing a web app, you're probably asking why then you should care about PSGI.
185 - So that you can understand how things work.
188 - I'll never understood people who don't want to understand things.
190 - I recommend you learn and understand as much as you can about the entire request-response cycle of
191 your web app; it will help you troubleshoot when things go wrong or aren't behaving as expected.
195 - So that you can do DevOps (if you want).
198 - New skills make you more marketable.
201 - So that you it when you see PSGI exposed through your web framework.
204 - You can do cool things with PSGI!
207 - Even if you do most of your work using your framework, you can do some useful things with PSGI.
208 - We will get to some of those cool things, so hang tight.
219 ### **H**yper**t**ext **T**ransfer **P**rotocol
222 - Invented by Tim Berners-Lee in
1989, first specified as [HTTP
0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html) in
1991.
225 - The IETF and W3C took over standards development, resulting in [RFC
1945](https://tools.ietf.org/html/rfc1945) ("HTTP
1.0") in
1996.
228 - [RFC
2068](https://tools.ietf.org/html/rfc2068) ("HTTP
1.1") happened in
1997, superceded by [RFC
2616](https://tools.ietf.org/html/rfc2616) in
1999.
231 RFC
2616 was then superceded in
2014 by:
232 - [RFC
7230](https://tools.ietf.org/html/rfc7230)
233 - [RFC
7231](https://tools.ietf.org/html/rfc7231)
234 - [RFC
7232](https://tools.ietf.org/html/rfc7232)
235 - [RFC
7233](https://tools.ietf.org/html/rfc7233)
236 - [RFC
7234](https://tools.ietf.org/html/rfc7234)
237 - [RFC
7235](https://tools.ietf.org/html/rfc7235)
240 - Oh yeah, and HTTP2 came out in
2015, defined in [RFC
7540](https://tools.ietf.org/html/rfc7540).
249 User-Agent: Mozilla/
5.0 (X11; Linux x86_64)
251 Accept-Language: en-us
252 Connection: Keep-Alive
261 Date: Thu,
07 Jul
2016 11:
56:
23 GMT
264 Content-Type: text/plain
267 Your IP address is
127.0.0.1.
278 User-Agent: Mozilla/
5.0 (X11; Linux x86_64)
280 Accept-Language: en-us
281 Connection: Keep-Alive
290 Date: Thu,
07 Jul
2016 11:
56:
23 GMT
293 Content-Type: text/plain
296 Your IP address is
127.0.0.1.
301 1. Method, path, protocol/version
305 - Methods defined in HTTP/
1.1: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT
306 - RFC
5789 defined PATCH in March
2010.
315 *User-Agent: Mozilla/
5.0 (X11; Linux x86_64)
317 *Accept-Language: en-us
318 *Connection: Keep-Alive
327 Date: Thu,
07 Jul
2016 11:
56:
23 GMT
330 Content-Type: text/plain
333 Your IP address is
127.0.0.1.
338 1. Method, path, protocol/version
339 2. Headers (key-value pairs)
349 User-Agent: Mozilla/
5.0 (X11; Linux x86_64)
351 Accept-Language: en-us
352 Connection: Keep-Alive
361 Date: Thu,
07 Jul
2016 11:
56:
23 GMT
364 Content-Type: text/plain
367 Your IP address is
127.0.0.1.
372 1. Method, path, protocol/version
373 2. Headers (key-value pairs)
374 3. Optional document (or "body")
384 User-Agent: Mozilla/
5.0 (X11; Linux x86_64)
386 Accept-Language: en-us
387 Connection: Keep-Alive
396 Date: Thu,
07 Jul
2016 11:
56:
23 GMT
399 Content-Type: text/plain
402 Your IP address is
127.0.0.1.
407 1. Method, path, protocol/version
408 2. Headers (key-value pairs)
409 3. Optional document (or "body")
413 1. Protocol/version, status code, reason phrase
423 User-Agent: Mozilla/
5.0 (X11; Linux x86_64)
425 Accept-Language: en-us
426 Connection: Keep-Alive
435 *Date: Thu,
07 Jul
2016 11:
56:
23 GMT
438 *Content-Type: text/plain
441 Your IP address is
127.0.0.1.
446 1. Method, path, protocol/version
447 2. Headers (key-value pairs)
448 3. Optional document (or "body")
452 1. Protocol/version, status code, reason phrase
453 2. Headers (key-value pairs)
463 User-Agent: Mozilla/
5.0 (X11; Linux x86_64)
465 Accept-Language: en-us
466 Connection: Keep-Alive
475 Date: Thu,
07 Jul
2016 11:
56:
23 GMT
478 Content-Type: text/plain
481 *Your IP address is
127.0.0.1.
486 1. Method, path, protocol/version
487 2. Headers (key-value pairs)
488 3. Optional document (or "body")
492 1. Protocol/version, status code, reason phrase
493 2. Headers (key-value pairs)
494 3. Optional document (or "body")
501 ## Alternatives to PSGI
515 - All of these still exist, and actually all of these are still in common use.
516 - We're going to take a closer look at three of these.
520 ## Alternatives to PSGI
523 - .highlight[mod_perl]
524 - .highlight[FastCGI]
545 ### **C**ommon **G**ateway **I**nterface
548 - Created by the NCSA in
1993.
551 - NCSA = National Center for Supercomputing Applications
554 - More formally defined in [RFC
3875](https://tools.ietf.org/html/rfc3875) ("CGI Version
1.1") in October
2004.
559 my $client_ip = $ENV{'REMOTE_ADDR'};
561 print "Content-Type: text/plain\n";
562 print "Status:
200 OK\n";
565 print "Your IP address is ${client_ip}.";
571 *my $client_ip = $ENV{'REMOTE_ADDR'};
573 print "Content-Type: text/plain\n";
574 print "Status:
200 OK\n";
577 print "Your IP address is ${client_ip}.";
580 1. Gateway sets information about the request in the environment.
587 - `GATEWAY_INTERFACE`
603 - other "protocol-specific" variables
610 my $client_ip = $ENV{'REMOTE_ADDR'};
612 *print "Content-Type: text/plain\n";
613 *print "Status:
200 OK\n";
616 print "Your IP address is ${client_ip}.";
619 1. Gateway sets information about the request in the environment.
620 2. Print response headers to `STDOUT`.
627 - other "protocol-specific" header fields
634 my $client_ip = $ENV{'REMOTE_ADDR'};
636 print "Content-Type: text/plain\n";
637 print "Status:
200 OK\n";
640 print "Your IP address is ${client_ip}.";
643 1. Gateway sets information about the request in the environment.
644 2. Print response headers to `STDOUT`.
650 my $client_ip = $ENV{'REMOTE_ADDR'};
652 print "Content-Type: text/plain\n";
653 print "Status:
200 OK\n";
656 *print "Your IP address is ${client_ip}.";
659 1. Gateway sets information about the request in the environment.
660 2. Print response headers to `STDOUT`.
662 4. Print response document (if any).
667 my $client_ip = $ENV{'REMOTE_ADDR'};
669 print "Content-Type: text/plain\n";
670 print "Status:
200 OK\n";
673 print "Your IP address is ${client_ip}.";
676 1. Gateway sets information about the request in the environment.
677 2. Print response headers to `STDOUT`.
679 4. Print response document (if any).
680 5. Read request document from `STDIN` (if any).
683 - CGI.pm helps cut down boilerplate by helping parse things like `QUERY_STRING` and `HTTP_COOKIE`,
684 producing correctly-formatted headers, and even producing HTML.
685 - CGI.pm was deprecated in perl
5.20 and remove from core in perl
5.22.
687 TODO make a slide for this
689 - Conceptually simple.
690 - Only requires the use of the most basic and primitive program constructs (stdin, stdout, env).
693 - Details can get complicated.
694 - Although the de facto standard for years, modern web servers are choosing to not support it
696 - There is too much overhead in forking and execing.
704 ![FastCGI](img/fastcgi.png)
711 ### a low-overhead variation on CGI
714 - Binary protocol with support for pipelining and multiplexing.
717 - Open Market wrote the [specification](http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html) on
29 Apr
1996.
720 - Open Market was an ecommerce startup based in Massachusetts.
721 - Developed one of the first HTTP servers.
728 my $request = FCGI::Request();
730 while (
0 <= $request-
>Accept()) {
731 my $client_ip = $ENV{'REMOTE_ADDR'};
733 print "Content-Type: text/html\n\n";
734 print "Your IP address is ${client_ip}.";
743 *my $request = FCGI::Request();
745 while (
0 <= $request-
>Accept()) {
746 my $client_ip = $ENV{'REMOTE_ADDR'};
748 print "Content-Type: text/html\n\n";
749 print "Your IP address is ${client_ip}.";
753 1. Use `FCGI` and instantiate an object.
760 my $request = FCGI::Request();
762 *while (
0 <= $request-
>Accept()) {
763 my $client_ip = $ENV{'REMOTE_ADDR'};
765 print "Content-Type: text/html\n\n";
766 print "Your IP address is ${client_ip}.";
770 1. Use `FCGI` and instantiate an object.
771 2. Loop on `Accept()` which blocks until the next request is received.
778 my $request = FCGI::Request();
780 while (
0 <= $request-
>Accept()) {
781 * my $client_ip = $ENV{'REMOTE_ADDR'};
783 * print "Content-Type: text/html\n\n";
784 * print "Your IP address is ${client_ip}.";
788 1. Use `FCGI` and instantiate an object.
789 2. Loop on `Accept()` which blocks until the next request is received.
790 3. Otherwise appears similar to a CGI program.
797 my $request = FCGI::Request();
799 while (
0 <= $request-
>Accept()) {
800 my $client_ip = $ENV{'REMOTE_ADDR'};
802 print "Content-Type: text/html\n\n";
803 print "Your IP address is ${client_ip}.";
807 1. Use `FCGI` and instantiate an object.
808 2. Loop on `Accept()` which blocks until the next request is received.
809 3. Otherwise appears similar to a CGI program.
811 * IPC actually happens over a socket!
814 - Can be run unmodified as a CGI script by detecting that stdin is not a socket.
815 - Can read from stdin and write to stdout via the miracle of tied filehandles.
823 ![mod_perl](img/mod_perl.gif)
831 - First released on March
25,
1996.
834 - Unlike the interfaces we have examined so far, mod_perl is code.
837 - Became an Apache Software Foundation project at ApacheCon
1999 in Orlando.
844 use Apache::RequestRec ();
845 use Apache::Connection ();
846 use Apache::Const -compile =
> qw(OK);
850 my $client_ip = $r-
>connection-
>remote_addr;
852 $r-
>content_type('text/plain');
853 $r-
>print("Your IP address is ${client_ip}.");
854 return Apache::Const::OK;
861 - There's a separate mod_perl for nginx.
864 - Can run CGI programs as-is.
867 - Can tie you to specific web servers.
868 - Code runs in the same process as the HTTP server -- kinda scary.
869 - Using Apache's API feels heavy.
883 my $client_id = $env-
>{'REMOTE_ADDR'};
887 [ 'Content-Type' =
> 'text/plain' ],
888 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
898 * my $client_id = $env-
>{'REMOTE_ADDR'};
902 [ 'Content-Type' =
> 'text/plain' ],
903 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
911 1. Hashref of request information.
919 my $client_id = $env-
>{'REMOTE_ADDR'};
923 [ 'Content-Type' =
> 'text/plain' ],
924 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
932 1. Hashref of request information.
946 my $client_id = $env-
>{'REMOTE_ADDR'};
950 * [ 'Content-Type' =
> 'text/plain' ],
951 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
959 1. Hashref of request information.
966 2. Arrayref of response headers.
971 - To support multiple headers (e.g. Set-Cookie)
972 - It more closely resembles how the WSGI folks did it (i.e. list of tuples).
979 my $client_id = $env-
>{'REMOTE_ADDR'};
983 [ 'Content-Type' =
> 'text/plain' ],
984 * [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
992 1. Hashref of request information.
999 2. Arrayref of response headers.
1000 3. Response document.
1004 - Body may be a list of chunks that are concatenated together or a handle to read from.
1012 - Everything is a data structure (almost).
1015 - Makes it easier to write tests because mocking either the app or server is clear.
1016 - Don't necessarily need to parse a bytestream to check stuff.
1019 - No global data or shared IO handles.
1022 - This lets you service multiple requests asynchronously in the same process/thread.
1025 - Takes deployment details out of web frameworks.
1028 - Web frameworks only need to target PSGI.
1029 - No need to worry about the boring stuff; they can focus on the abstractions that make them unique
1033 - End-users of your app have many deployment options for free.
1039 - [Catalyst](http://www.catalystframework.org/)
1040 - [Mojolicious](http://mojolicious.org/)
1041 - [Dancer](http://perldancer.org/)
1042 - [CGI::Application](http://cgi-app.org/)
1043 - [CGI::Ex](https://github.com/chazmcgarvey/CGI-Ex/tree/psgi-
2)
1044 - [Web::Simple](https://metacpan.org/pod/Web::Simple)
1045 - [Amon2](https://metacpan.org/pod/Amon2)
1046 - [Poet](https://metacpan.org/pod/Poet)
1047 - [Kelp](https://metacpan.org/pod/Kelp)
1048 - [Raisin](https://metacpan.org/pod/Raisin)
1055 - Provides tools for building, running, and testing PSGI apps.
1058 - [Plack::Handler](https://metacpan.org/pod/Plack::Handler)
1061 - Connects PSGI apps and web servers.
1062 - Takes a request from the server,
1063 - converts it to the PSGI-specified environment,
1065 - converts the response back to a format the server understands.
1068 - [Plack::Loader](https://metacpan.org/pod/Plack::Loader)
1071 - Picks an appropriate Plack::Handler (based on ENV, loaded modules, or arguments) and loads it.
1072 - Can also do stuff like restart the loader when files change.
1075 - [Plack::Runner](https://metacpan.org/pod/Plack::Runner), [plackup](https://metacpan.org/pod/plackup)
1078 - Run PSGI apps from the command-line.
1081 - [Plack::Middleware](https://metacpan.org/pod/Plack::Middleware)
1084 - Create subroutines that run between the handler and your app.
1085 - Can alter the request your app receives and modify the response your app returns.
1088 - [Plack::Request](https://metacpan.org/pod/Plack::Request), [Plack::Response](https://metacpan.org/pod/Plack::Response)
1091 - Request and response wrappers can help simplify writing middleware.
1094 - [Plack::Builder](https://metacpan.org/pod/Plack::Builder)
1097 - Provides DSL for composing apps and middleware.
1100 - [Plack::Test](https://metacpan.org/pod/Plack::Test), [Plack::Test::Suite](https://metacpan.org/pod/Plack::Test::Suite)
1103 - Use Plack::Test for testing apps.
1104 - Plack::Test::Suite is a series of tests for testing handlers.
1107 - [Plack::Util](https://metacpan.org/pod/Plack::Util)
1110 - Provides random useful stuff for handler and middleware developers.
1111 - Stuff like determing the length of a document or getting PSGI response headers from the arrayref.
1119 - Run PSGI apps from the command-line.
1122 # read your app from app.psgi file
1125 # choose .psgi file from ARGV[
0] (or with -a option)
1128 # switch server implementation with --server (or -s)
1129 plackup --server HTTP::Server::Simple --port
9090 --host
127.0.0.1 test.psgi
1131 # use UNIX socket to run FCGI daemon
1132 plackup -s FCGI --listen /tmp/fcgi.sock myapp.psgi
1134 # launch FCGI external server on port
9090
1135 plackup -s FCGI --port
9090
1152 my $client_id = $env-
>{'REMOTE_ADDR'};
1156 [ 'Content-Type' =
> 'text/plain' ],
1157 [ "Your IP address is ${client_id}." ],
1167 *use Plack::Builder;
1171 my $client_id = $env-
>{'REMOTE_ADDR'};
1175 [ 'Content-Type' =
> 'text/plain' ],
1176 [ "Your IP address is ${client_id}." ],
1182 * mount '/' =
> $app;
1187 - The `Runtime` middleware adds an `X-Runtime` header to the response with the number of seconds it
1188 took to process the request.
1202 - `HTTP_ACCEPT_ENCODING`
1203 - `HTTP_ACCEPT_LANGUAGE`
1204 - `HTTP_CACHE_CONTROL`
1223 - `psgi.multiprocess`
1224 - `psgi.multithread`
1225 - `psgi.nonblocking`
1231 - `psgix.input.buffered`
1240 - .highlight[`HTTP_ACCEPT`]
1241 - .highlight[`HTTP_ACCEPT_ENCODING`]
1242 - .highlight[`HTTP_ACCEPT_LANGUAGE`]
1243 - .highlight[`HTTP_CACHE_CONTROL`]
1244 - .highlight[`HTTP_CONNECTION`]
1245 - .highlight[`HTTP_DNT`]
1246 - .highlight[`HTTP_HOST`]
1247 - .highlight[`HTTP_USER_AGENT`]
1248 - .highlight[`PATH_INFO`]
1249 - .highlight[`QUERY_STRING `]
1250 - .highlight[`REMOTE_ADDR`]
1251 - .highlight[`REMOTE_PORT`]
1252 - .highlight[`REQUEST_METHOD`]
1253 - .highlight[`REQUEST_URI`]
1254 - .highlight[`SCRIPT_NAME`]
1257 - .highlight[`SERVER_NAME`]
1258 - .highlight[`SERVER_PORT`]
1259 - .highlight[`SERVER_PROTOCOL`]
1262 - `psgi.multiprocess`
1263 - `psgi.multithread`
1264 - `psgi.nonblocking`
1270 - `psgix.input.buffered`
1280 - `HTTP_ACCEPT_ENCODING`
1281 - `HTTP_ACCEPT_LANGUAGE`
1282 - `HTTP_CACHE_CONTROL`
1299 - .highlight[`psgi.errors`]
1300 - .highlight[`psgi.input`]
1301 - .highlight[`psgi.multiprocess`]
1302 - .highlight[`psgi.multithread`]
1303 - .highlight[`psgi.nonblocking`]
1304 - .highlight[`psgi.run_once`]
1305 - .highlight[`psgi.streaming`]
1306 - .highlight[`psgi.url_scheme`]
1307 - .highlight[`psgi.version`]
1308 - .highlight[`psgix.harakiri`]
1309 - .highlight[`psgix.input.buffered`]
1310 - .highlight[`psgix.io`]
1320 - Can be found on the CPAN in the `Plack::Handler::` namespace.
1321 - [Apache1](https://metacpan.org/pod/Plack::Handler::Apache1), [Apache2](https://metacpan.org/pod/Plack::Handler::Apache2)
1322 - [CGI](https://metacpan.org/pod/Plack::Handler::CGI)
1323 - [FCGI](https://metacpan.org/pod/Plack::Handler::FCGI)
1324 - [HTTP::Server::PSGI](https://metacpan.org/pod/Plack::Handler::HTTP::Server::PSGI)
1325 - [SCGI](https://metacpan.org/pod/Plack::Handler::SCGI)
1326 - [Starman](https://metacpan.org/pod/Plack::Handler::Starman)
1327 - [Twiggy](https://metacpan.org/pod/Plack::Handler::Twiggy)
1328 - [AnyEvent::HTTPD](https://metacpan.org/pod/Plack::Handler::AnyEvent::HTTPD)
1329 - [Thrall](https://metacpan.org/pod/Plack::Handler::Thrall)
1341 name: middleware-debug
1343 ### [`Debug`](https://metacpan.org/pod/Plack::Middleware::Debug)
1351 ### [`ReverseProxy`](https://metacpan.org/pod/Plack::Middleware::ReverseProxy)
1354 enable 'ReverseProxy';
1357 - Fixes `REMOTE_ADDR`, `HTTP_HOST`, `SERVER_PORT`, and `psgi.url_scheme` in the environment.
1361 ### [`LogDispatch`](https://metacpan.org/pod/Plack::Middleware::LogDispatch)
1366 my $logger = Log::Dispatch-
>new(
1370 min_level =
> 'debug',
1376 enable 'LogDispatch', logger =
> $logger;
1381 ### [`XSRFBlock`](https://metacpan.org/pod/Plack::Middleware::XSRFBlock)
1384 enable 'XSRFBlock', cookie_options =
> { httponly =
> 1 };
1387 - Blocking cross-site request forgery couldn't be easier.
1391 ### [`RedirectSSL`](https://metacpan.org/pod/Plack::Middleware::RedirectSSL)
1394 enable 'RedirectSSL';
1397 - Redirects from http to https (or backwards, if configured).
1398 - Can also set HSTS header with configurable `max-age`.
1405 ![CPAN](img/cpan.png)
1408 ## Plack modules in July
2016
1410 **
10** `Plack-Handler-*` distributions
1412 **
55** `Plack-App-*` distributions
1414 **
253** `Plack-Middleware-*` distributions
1420 - PSGI also specifies a way to delay or stream responses to the server.
1423 - It's kind of complicated, but you can read the spec to learn more.
1424 - Read the source code of various apps and middlewares to see how it works in practice.
1427 - There are tons of great apps and middleware on the CPAN.
1430 - Consider writing some of your app as a middleware.
1433 - The concept and implementation of middleware is cool.
1434 - You should consider writing parts of your app as middleware so that functionality is available
1435 under different web frameworks.
1436 - Stuff that makes sense as middleware:
1445 class: center, middle
1450 ### Understand PSGI & Plack, and use them!
1454 class: center, middle
1462 class: center, middle
1468 Email me: Charles McGarvey
1469 <cmcgarvey@bluehost.com
>
1470 <chazmcgarvey@brokenzipper.com
>
1473 Leave me feedback, if you want:
1475 ![Page on Joind.in](img/talkqr.svg)
1477 <https://joind.in/talk/
6e4d2
>
1485 - Thank you [Tatsuhiko Miyagawa](http://weblog.bulknews.net/) and other contributors for creating PSGI and Plack.
1489 </textarea><script src=
"https://gnab.github.io/remark/downloads/remark-latest.min.js"></script><script>var slideshow = remark.create({countIncrementalSlides: true, highlightLanguage: 'perl', highlightLines: true, ratio: '
16:
9', /*slideNumberFormat: '',*/ navigation: {scroll: false, touch: false, click: false}})
</script></body></html>
1490 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->