]> Dogcows Code - chaz/talk-introduction-to-psgi/blob - slides.html
61f2b9c68ed9043130856df539d16457969f131e
[chaz/talk-introduction-to-psgi] / slides.html
1 <!DOCTYPE html>
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">
3
4 class: center, middle
5 name: title
6
7 # Introduction to PSGI
8
9 Charles McGarvey
10
11 ---
12
13 class: center, middle
14 name: bluehost
15
16 ![Bluehost](img/bluehost.png)
17
18 ### https://bluehost.com/careers
19
20 ---
21
22 ## Agenda
23
24 - Answer "What is PSGI?"
25 - Examine some alternatives to PSGI.
26 - Examine PSGI.
27 - Examine Plack.
28
29 ---
30
31 ## What is PSGI?
32
33 ### **P**erl [web] **S**erver **G**ateway **I**nterface
34
35 --
36 - It is an interface between Perl web applications and web servers.
37
38 --
39 - It is a *specification*, not code.
40
41 --
42 - First released to the CPAN on 13 Oct 2009.
43
44 --
45 - Originally written by Miyagawa.
46
47 .center[![Tatsuhiko Miyagawa](img/avatar-miyagawa.jpg)]
48
49 ???
50 Written by **Tatsuhiko Miyagawa**, author of:
51 - cpanm
52 - carton
53 - way too many Perl modules on CPAN to list
54
55 --
56 - Inspired by WSGI (Python) and Rack (Ruby).
57
58 ???
59 - PEP-333 (WSGI 1.0) was released on 07 Dec 2003.
60 - Rack 0.1 was released on 03 Mar 2007.
61
62 Despite Perl's long history of powering the web, we were well behind the curve on this.
63
64 ---
65
66 class: center, middle
67 name: psgi-flow1
68
69 ## Extremely High-level Overview
70
71 .basic-flow[
72 ![Basic Flow](img/basic-flow1.svg)
73 ]
74
75 ???
76 - PSGI is the language your app speaks in order to communicate with user agents.
77 - User agents are browsers.
78 - I've glossed over some important details, like the fact that users don't speak PSGI.
79
80 ---
81
82 class: center, middle
83 name: psgi-flow2
84
85 ## Pretty High-level Overview
86
87 .basic-flow[
88 ![Basic Flow](img/basic-flow2.svg)
89 ]
90
91 ???
92 - In reality, your app speaks PSGI but user agents speak HTTP.
93 - You need software in the middle that can speak both, and that's usually a web server.
94
95 ---
96
97 class: center, middle
98 name: psgi-flow3
99
100 ## Somewhat High-level Overview
101
102 .basic-flow[
103 ![Basic Flow](img/basic-flow3.svg)
104 ]
105
106 ???
107 - In reality, most web servers don't speak PSGI. :-(
108 - There are "handlers" that speak both PSGI and another language that web servers do speak.
109 - CGI
110 - mod_perl
111 - FastCGI
112 - **HTTP**
113 - Yes, HTTP. Many modern web servers speak HTTP not only as a server but also as a client.
114 - This allows them to *proxy* (act as middleman) between user agents and other servers.
115 - In the world of PSGI (and "Plack"), handlers are also called adapters or connectors.
116 - There are already adapters for every major web server.
117 - Apache
118 - nginx
119 - IIS
120
121 ---
122
123 class: center, middle
124 name: psgi-flow4
125
126 ## Somewhat High-level Overview
127
128 .basic-flow[
129 ![Basic Flow](img/basic-flow4.svg)
130 ]
131
132 ???
133 - Notice that as we've zoomed in, the interaction between the web app and the rest has remained the
134 same: PSGI.
135 - From a developer perspective, the etremely high-level overview is sufficient.
136 - This is one of the benefits of PSGI:
137 - Write your application once and leave deployment details to devops.
138 - The intrastructure details can change (swap in a different web server) and the app will still work.
139 - Maybe you're both developer and system architect, but the separation between developer and
140 devops is valuable.
141 - In fairness, this isn't a new concept.
142 - The way this has been achieved before is using a *web framework*.
143
144 ---
145
146 class: center, middle
147 name: psgi-flow5
148
149 ## High-level Overview
150
151 .basic-flow[
152 ![Basic Flow](img/basic-flow5.svg)
153 ]
154
155 ???
156 - A web framework makes it so your app doesn't need to speak HTTP or CGI or even PSGI.
157
158 ---
159
160 class: middle
161
162 ## Word of Caution
163
164 > Writing your web application directly using [PSGI/Plack] is certainly possible but not recommended.
165 >
166 > […]
167 >
168 > If you're writing a web application, not a framework, then you're encouraged to use one of the web
169 > application frameworks that support PSGI (http://plackperl.org/#frameworks), or see modules like
170 > HTTP::Engine to provide higher level Request and Response API on top of PSGI.
171 >
172 > -- [Plack::Request Documentation](https://search.cpan.org/~miyagawa/Plack/lib/Plack/Request.pm)
173
174 ???
175 - When you start learning about Plack, you'll realize that it is actually pretty capable.
176 - You may start to think that everything should be developed on that level -- don't do it!
177 - For most stuff, you'll still want to use a web framework.
178 - Web frameworks often offer more convenient abstractions than raw PSGI.
179
180 ---
181
182 ## Why care?
183
184 ???
185 If you're a developer writing a web app, you're probably asking why then you should care about PSGI.
186
187 --
188
189 - So that you can understand how things work.
190
191 ???
192 - I'll never understood people who don't want to understand things.
193 - Knowledge is cool!
194 - I recommend you learn and understand as much as you can about the entire request-response cycle of
195 your web app; it will help you troubleshoot when things go wrong or aren't behaving as expected.
196 - Be an expert!
197
198 --
199 - So that you can do DevOps (if you want).
200
201 ???
202 - New skills make you more marketable.
203
204 --
205 - So that you understand it when you see PSGI exposed through your web framework.
206
207 --
208 - You can do cool things with PSGI!
209
210 ???
211 - Even if you do most of your work using your framework, you can do some useful things with PSGI.
212 - We will get to some of those cool things, so hang tight.
213
214 ---
215
216 class: http
217 layout: true
218
219 ## HTTP
220
221 ---
222
223 ### **H**yper**t**ext **T**ransfer **P**rotocol
224
225 --
226 - Invented by Tim Berners-Lee in 1989, first specified as [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html) in 1991.
227
228 --
229 - The IETF and W3C took over standards development, resulting in [RFC 1945](https://tools.ietf.org/html/rfc1945) ("HTTP 1.0") in 1996.
230
231 ???
232 - IETF = Internet Engineering Task Force
233 - W3C = World Wide Web Consortium
234
235 --
236 - [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.
237
238 ???
239 RFC 2616 was then superceded in 2014 by:
240 - [RFC 7230](https://tools.ietf.org/html/rfc7230)
241 - [RFC 7231](https://tools.ietf.org/html/rfc7231)
242 - [RFC 7232](https://tools.ietf.org/html/rfc7232)
243 - [RFC 7233](https://tools.ietf.org/html/rfc7233)
244 - [RFC 7234](https://tools.ietf.org/html/rfc7234)
245 - [RFC 7235](https://tools.ietf.org/html/rfc7235)
246
247 --
248 - Oh yeah, and HTTP2 came out in 2015, defined in [RFC 7540](https://tools.ietf.org/html/rfc7540).
249
250 ---
251
252 .col.marginfix[
253 ### Request
254
255 ```http
256 GET /ip HTTP/1.1
257 User-Agent: Mozilla/5.0 (X11; Linux x86_64)
258 Host: localhost
259 Accept-Language: en-us
260 Connection: Keep-Alive
261 ```
262 ]
263
264 .col.marginfix[
265 ### Response
266
267 ```http
268 HTTP/1.1 200 OK
269 Date: Thu, 07 Jul 2016 11:56:23 GMT
270 Server: nginx
271 Content-Length: 29
272 Content-Type: text/plain
273 Connection: Closed
274
275 Your IP address is 127.0.0.1.
276 ```
277 ]
278
279 ---
280
281 .col.marginfix[
282 ### Request
283
284 ```http
285 *GET /ip HTTP/1.1
286 User-Agent: Mozilla/5.0 (X11; Linux x86_64)
287 Host: localhost
288 Accept-Language: en-us
289 Connection: Keep-Alive
290 ```
291 ]
292
293 .col.marginfix[
294 ### Response
295
296 ```http
297 HTTP/1.1 200 OK
298 Date: Thu, 07 Jul 2016 11:56:23 GMT
299 Server: nginx
300 Content-Length: 29
301 Content-Type: text/plain
302 Connection: Closed
303
304 Your IP address is 127.0.0.1.
305 ```
306 ]
307
308 .col[
309 1. Method, path, protocol/version
310 ]
311
312 ???
313 - Methods defined in HTTP/1.1: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT
314 - RFC 5789 defined PATCH in March 2010.
315
316 ---
317
318 .col.marginfix[
319 ### Request
320
321 ```http
322 GET /ip HTTP/1.1
323 *User-Agent: Mozilla/5.0 (X11; Linux x86_64)
324 *Host: localhost
325 *Accept-Language: en-us
326 *Connection: Keep-Alive
327 ```
328 ]
329
330 .col.marginfix[
331 ### Response
332
333 ```http
334 HTTP/1.1 200 OK
335 Date: Thu, 07 Jul 2016 11:56:23 GMT
336 Server: nginx
337 Content-Length: 29
338 Content-Type: text/plain
339 Connection: Closed
340
341 Your IP address is 127.0.0.1.
342 ```
343 ]
344
345 .col[
346 1. Method, path, protocol/version
347 2. Headers (key-value pairs)
348 ]
349
350 ---
351
352 .col.marginfix[
353 ### Request
354
355 ```http
356 GET /ip HTTP/1.1
357 User-Agent: Mozilla/5.0 (X11; Linux x86_64)
358 Host: localhost
359 Accept-Language: en-us
360 Connection: Keep-Alive
361 ```
362 ]
363
364 .col.marginfix[
365 ### Response
366
367 ```http
368 HTTP/1.1 200 OK
369 Date: Thu, 07 Jul 2016 11:56:23 GMT
370 Server: nginx
371 Content-Length: 29
372 Content-Type: text/plain
373 Connection: Closed
374
375 Your IP address is 127.0.0.1.
376 ```
377 ]
378
379 .col[
380 1. Method, path, protocol/version
381 2. Headers (key-value pairs)
382 3. Optional document (or "body")
383 ]
384
385 ---
386
387 .col.marginfix[
388 ### Request
389
390 ```http
391 GET /ip HTTP/1.1
392 User-Agent: Mozilla/5.0 (X11; Linux x86_64)
393 Host: localhost
394 Accept-Language: en-us
395 Connection: Keep-Alive
396 ```
397 ]
398
399 .col.marginfix[
400 ### Response
401
402 ```http
403 *HTTP/1.1 200 OK
404 Date: Thu, 07 Jul 2016 11:56:23 GMT
405 Server: nginx
406 Content-Length: 29
407 Content-Type: text/plain
408 Connection: Closed
409
410 Your IP address is 127.0.0.1.
411 ```
412 ]
413
414 .col[
415 1. Method, path, protocol/version
416 2. Headers (key-value pairs)
417 3. Optional document (or "body")
418 ]
419
420 .col[
421 1. Protocol/version, status code, reason phrase
422 ]
423
424 ---
425
426 .col.marginfix[
427 ### Request
428
429 ```http
430 GET /ip HTTP/1.1
431 User-Agent: Mozilla/5.0 (X11; Linux x86_64)
432 Host: localhost
433 Accept-Language: en-us
434 Connection: Keep-Alive
435 ```
436 ]
437
438 .col.marginfix[
439 ### Response
440
441 ```http
442 HTTP/1.1 200 OK
443 *Date: Thu, 07 Jul 2016 11:56:23 GMT
444 *Server: nginx
445 *Content-Length: 29
446 *Content-Type: text/plain
447 *Connection: Closed
448
449 Your IP address is 127.0.0.1.
450 ```
451 ]
452
453 .col[
454 1. Method, path, protocol/version
455 2. Headers (key-value pairs)
456 3. Optional document (or "body")
457 ]
458
459 .col[
460 1. Protocol/version, status code, reason phrase
461 2. Headers (key-value pairs)
462 ]
463
464 ---
465
466 .col.marginfix[
467 ### Request
468
469 ```http
470 GET /ip HTTP/1.1
471 User-Agent: Mozilla/5.0 (X11; Linux x86_64)
472 Host: localhost
473 Accept-Language: en-us
474 Connection: Keep-Alive
475 ```
476 ]
477
478 .col.marginfix[
479 ### Response
480
481 ```http
482 HTTP/1.1 200 OK
483 Date: Thu, 07 Jul 2016 11:56:23 GMT
484 Server: nginx
485 Content-Length: 29
486 Content-Type: text/plain
487 Connection: Closed
488
489 *Your IP address is 127.0.0.1.
490 ```
491 ]
492
493 .col[
494 1. Method, path, protocol/version
495 2. Headers (key-value pairs)
496 3. Optional document (or "body")
497 ]
498
499 .col[
500 1. Protocol/version, status code, reason phrase
501 2. Headers (key-value pairs)
502 3. Optional document (or "body")
503 ]
504
505 ---
506
507 layout: false
508
509 ## Alternatives to PSGI
510
511 - CGI
512 - mod_perl
513 - FastCGI
514 - SCGI
515 - WSGI
516 - JSGI
517 - Rack
518 - ISAPI
519 - many more...
520
521
522 ???
523 - All of these still exist, and actually all of these are still in common use.
524 - We're going to take a closer look at three of these.
525
526 ---
527
528 ## Alternatives to PSGI
529
530 - .highlight[CGI]
531 - .highlight[mod_perl]
532 - .highlight[FastCGI]
533 - SCGI
534 - WSGI
535 - JSGI
536 - Rack
537 - ISAPI
538 - many more...
539
540 ---
541
542 class: cgi
543 layout: true
544
545 .top-right[
546 ![CGI](img/cgi.gif)
547 ]
548
549 ## CGI
550
551 ---
552
553 ### **C**ommon **G**ateway **I**nterface
554
555 --
556 - Created by the NCSA in 1993.
557
558 ???
559 - NCSA = National Center for Supercomputing Applications
560
561 --
562 - More formally defined in [RFC 3875](https://tools.ietf.org/html/rfc3875) ("CGI Version 1.1") in October 2004.
563
564 ---
565
566 ```perl
567 my $client_ip = $ENV{'REMOTE_ADDR'};
568
569 print "Content-Type: text/plain\n";
570 print "Status: 200 OK\n";
571
572 print "\n";
573 print "Your IP address is ${client_ip}.";
574 ```
575
576 ---
577
578 ```perl
579 *my $client_ip = $ENV{'REMOTE_ADDR'};
580
581 print "Content-Type: text/plain\n";
582 print "Status: 200 OK\n";
583
584 print "\n";
585 print "Your IP address is ${client_ip}.";
586 ```
587
588 1. Gateway sets information about the request in the environment.
589
590 .condensed.marginfix[
591 .col[
592 - `AUTH_TYPE`
593 - `CONTENT_LENGTH`
594 - `CONTENT_TYPE`
595 - `GATEWAY_INTERFACE`
596 - `PATH_INFO`
597 - `PATH_TRANSLATED`
598 - `QUERY_STRING`
599 - `REMOTE_ADDR`
600 - `REMOTE_HOST`
601 ]
602 .col[
603 - `REMOTE_IDENT`
604 - `REMOTE_USER`
605 - `REQUEST_METHOD`
606 - `SCRIPT_NAME`
607 - `SERVER_NAME`
608 - `SERVER_PORT`
609 - `SERVER_PROTOCOL`
610 - `SERVER_SOFTWARE`
611 - other "protocol-specific" variables
612 ]
613 ]
614
615 ???
616 - In Perl, you can get these using %ENV.
617 - Or getenv from stdlib.
618 - If you've done some web programming before, you're probably familiar with at least a few of these.
619
620 ---
621
622 ```perl
623 my $client_ip = $ENV{'REMOTE_ADDR'};
624
625 *print "Content-Type: text/plain\n";
626 *print "Status: 200 OK\n";
627
628 print "\n";
629 print "Your IP address is ${client_ip}.";
630 ```
631
632 1. Gateway sets information about the request in the environment.
633 2. Print response headers to `STDOUT`.
634
635 .condensed.marginfix[
636 .col[
637 - `Content-Type`
638 - `Location`
639 - `Status`
640 - other "protocol-specific" header fields
641 ]
642 ]
643
644 ---
645
646 ```perl
647 my $client_ip = $ENV{'REMOTE_ADDR'};
648
649 print "Content-Type: text/plain\n";
650 print "Status: 200 OK\n";
651
652 *print "\n";
653 print "Your IP address is ${client_ip}.";
654 ```
655
656 1. Gateway sets information about the request in the environment.
657 2. Print response headers to `STDOUT`.
658 3. Print newline.
659
660 ---
661
662 ```perl
663 my $client_ip = $ENV{'REMOTE_ADDR'};
664
665 print "Content-Type: text/plain\n";
666 print "Status: 200 OK\n";
667
668 print "\n";
669 *print "Your IP address is ${client_ip}.";
670 ```
671
672 1. Gateway sets information about the request in the environment.
673 2. Print response headers to `STDOUT`.
674 3. Print newline.
675 4. Print response document (if any).
676
677 ---
678
679 ```perl
680 my $client_ip = $ENV{'REMOTE_ADDR'};
681
682 print "Content-Type: text/plain\n";
683 print "Status: 200 OK\n";
684
685 print "\n";
686 print "Your IP address is ${client_ip}.";
687 ```
688
689 1. Gateway sets information about the request in the environment.
690 2. Print response headers to `STDOUT`.
691 3. Print newline.
692 4. Print response document (if any).
693 5. Read request document from `STDIN` (if any).
694
695 ???
696 - CGI.pm helps cut down boilerplate by helping parse things like `QUERY_STRING` and `HTTP_COOKIE`,
697 producing correctly-formatted headers, and even producing HTML.
698 - CGI.pm was deprecated in perl 5.20 and remove from core in perl 5.22.
699
700 Good:
701 - Conceptually simple.
702 - Only requires the use of the most basic and primitive program constructs (stdin, stdout, env).
703 - Only other primitive construct that could have been used is that of passing program arguments.
704 - Actually, the spec does specify behavior for "search-strings" as program arguments.
705
706 Bad:
707 - Details can get complicated.
708 - Although the de facto standard for years, modern web servers are choosing to not support it
709 directly any longer.
710 - There is too much overhead in forking and execing.
711
712 ---
713
714 class: fastcgi
715 layout: true
716
717 .top-right[
718 ![FastCGI](img/fastcgi.png)
719 ]
720
721 ## FastCGI
722
723 ---
724
725 ### a low-overhead variation on CGI
726
727 --
728 - Binary protocol with support for pipelining and multiplexing.
729
730 --
731 - Open Market wrote the [specification](http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html) on 29 Apr 1996.
732
733 ???
734 - Open Market was an ecommerce startup based in Massachusetts.
735 - Developed one of the first HTTP servers.
736
737 ---
738
739 ```perl
740 use FCGI;
741
742 my $request = FCGI::Request();
743
744 while (0 <= $request->Accept()) {
745 my $client_ip = $ENV{'REMOTE_ADDR'};
746
747 print "Content-Type: text/html\n\n";
748 print "Your IP address is ${client_ip}.";
749 }
750 ```
751
752 ---
753
754 ```perl
755 *use FCGI;
756 *
757 *my $request = FCGI::Request();
758
759 while (0 <= $request->Accept()) {
760 my $client_ip = $ENV{'REMOTE_ADDR'};
761
762 print "Content-Type: text/html\n\n";
763 print "Your IP address is ${client_ip}.";
764 }
765 ```
766
767 1. Use `FCGI` and instantiate an object.
768
769 ---
770
771 ```perl
772 use FCGI;
773
774 my $request = FCGI::Request();
775
776 *while (0 <= $request->Accept()) {
777 my $client_ip = $ENV{'REMOTE_ADDR'};
778
779 print "Content-Type: text/html\n\n";
780 print "Your IP address is ${client_ip}.";
781 }
782 ```
783
784 1. Use `FCGI` and instantiate an object.
785 2. Loop on `Accept()` which blocks until the next request is received.
786
787 ---
788
789 ```perl
790 use FCGI;
791
792 my $request = FCGI::Request();
793
794 while (0 <= $request->Accept()) {
795 * my $client_ip = $ENV{'REMOTE_ADDR'};
796 *
797 * print "Content-Type: text/html\n\n";
798 * print "Your IP address is ${client_ip}.";
799 }
800 ```
801
802 1. Use `FCGI` and instantiate an object.
803 2. Loop on `Accept()` which blocks until the next request is received.
804 3. Otherwise appears similar to a CGI program.
805
806 ---
807
808 ```perl
809 use FCGI;
810
811 my $request = FCGI::Request();
812
813 while (0 <= $request->Accept()) {
814 my $client_ip = $ENV{'REMOTE_ADDR'};
815
816 print "Content-Type: text/html\n\n";
817 print "Your IP address is ${client_ip}.";
818 }
819 ```
820
821 1. Use `FCGI` and instantiate an object.
822 2. Loop on `Accept()` which blocks until the next request is received.
823 3. Otherwise appears similar to a CGI program.
824
825 * IPC actually happens over a socket!
826
827 ???
828 - Can be run unmodified as a CGI script by detecting that stdin is not a socket.
829 - Can read from stdin and write to stdout via the miracle of tied filehandles.
830
831 ---
832
833 class: mod_perl
834 layout: true
835
836 .top-right[
837 ![mod_perl](img/mod_perl.gif)
838 ]
839
840 ## mod_perl
841
842 ---
843
844 --
845 - First released on March 25, 1996.
846
847 ???
848 - Unlike the interfaces we have examined so far, mod_perl is code.
849 - About the same time as FastCGI.
850
851 --
852 - Became an Apache Software Foundation project at ApacheCon 1999 in Orlando.
853
854 --
855
856 ```perl
857 package GetIP;
858
859 use Apache::RequestRec ();
860 use Apache::Connection ();
861 use Apache::Const -compile => qw(OK);
862
863 sub handler {
864 my $r = shift;
865 my $client_ip = $r->connection->remote_addr;
866
867 $r->content_type('text/plain');
868 $r->print("Your IP address is ${client_ip}.");
869 return Apache::Const::OK;
870 }
871
872 1;
873 ```
874
875 ???
876 - Notice how we're not using STDOUT (or even pretending to).
877 - This program actually runs on a perl interpreter inside the web server.
878 - It also has access to more information through the exposed API.
879
880 Good:
881 - Can run CGI programs as-is.
882
883 Bad:
884 - Can tie you to specific web servers.
885 - There's a separate mod_perl for nginx.
886 - Code runs in the same process as the HTTP server -- kinda scary.
887 - Using Apache's API feels heavy.
888
889 ---
890
891 class: psgi
892 layout: true
893
894 ## PSGI
895
896 ---
897
898 ```perl
899 my $app = sub {
900 my $env = shift;
901 my $client_id = $env->{'REMOTE_ADDR'};
902
903 return [
904 '200',
905 [ 'Content-Type' => 'text/plain' ],
906 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
907 ];
908 };
909 ```
910
911 ???
912 - I think it's good to understand CGI et al. so that you can understand why it was designed the way
913 it was.
914 - Hopefully the ideas behind PSGI are based off of the best that CGI et al. had to offer without any
915 of the drawbacks.
916
917
918 - Notice how the program is a subroutine.
919 - By being a subrouting rather than a script that is executed, we're already in a form that can be
920 called repeatedly without incurring fork-exec overhead. Nice!
921
922 ---
923
924 ```perl
925 my $app = sub {
926 * my $env = shift;
927 * my $client_id = $env->{'REMOTE_ADDR'};
928
929 return [
930 '200',
931 [ 'Content-Type' => 'text/plain' ],
932 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
933 ];
934 };
935 ```
936
937 .col[
938 ### Request
939
940 1. Hashref of request information.
941 ]
942
943 ???
944 - Oh, look! The variable is the same as is specified by CGI.
945 - Why throw away over a decade of convention?
946
947 ---
948
949 ```perl
950 my $app = sub {
951 my $env = shift;
952 my $client_id = $env->{'REMOTE_ADDR'};
953
954 return [
955 * '200',
956 [ 'Content-Type' => 'text/plain' ],
957 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
958 ];
959 };
960 ```
961
962 .col[
963 ### Request
964
965 1. Hashref of request information.
966 ]
967
968 .col[
969 ### Response
970
971 1. HTTP status code.
972 ]
973
974 ???
975 - Then you'll notice that instead of printing anything, we return an arrayref as the response.
976
977 ---
978
979 ```perl
980 my $app = sub {
981 my $env = shift;
982 my $client_id = $env->{'REMOTE_ADDR'};
983
984 return [
985 '200',
986 * [ 'Content-Type' => 'text/plain' ],
987 [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
988 ];
989 };
990 ```
991
992 .col[
993 ### Request
994
995 1. Hashref of request information.
996 ]
997
998 .col[
999 ### Response
1000
1001 1. HTTP status code.
1002 2. Arrayref of response headers.
1003 ]
1004
1005 ???
1006 - Why not a hashref?
1007 - To support multiple headers (e.g. Set-Cookie)
1008 - It more closely resembles how the WSGI folks did it (i.e. list of tuples).
1009
1010 ---
1011
1012 ```perl
1013 my $app = sub {
1014 my $env = shift;
1015 my $client_id = $env->{'REMOTE_ADDR'};
1016
1017 return [
1018 '200',
1019 [ 'Content-Type' => 'text/plain' ],
1020 * [ "Your IP address is ${client_id}." ], # or IO::Handle-like object
1021 ];
1022 };
1023 ```
1024
1025 .col[
1026 ### Request
1027
1028 1. Hashref of request information.
1029 ]
1030
1031 .col[
1032 ### Response
1033
1034 1. HTTP status code.
1035 2. Arrayref of response headers.
1036 3. Response document.
1037 ]
1038
1039 ???
1040 - Body may be a list of chunks that are concatenated together or a handle to read from.
1041
1042 ---
1043
1044 layout: false
1045
1046 ## Benefits of PSGI
1047
1048 - Everything is a data structure (almost).
1049
1050 ???
1051 - Makes it easier to write tests because mocking either the app or server is clear.
1052 - Don't necessarily need to parse a bytestream to check stuff.
1053
1054 --
1055 - No global data or shared IO handles.
1056
1057 ???
1058 - This lets you service multiple requests asynchronously in the same process/thread.
1059
1060 --
1061 - Takes deployment details out of web frameworks.
1062
1063 ???
1064 - Web frameworks only need to target PSGI.
1065 - No need to worry about the boring stuff; they can focus on the abstractions that make them unique
1066 and useful.
1067
1068 --
1069 - End-users of your app have many deployment options for free.
1070
1071 ---
1072
1073 ## Web Frameworks
1074
1075 - [Catalyst](http://www.catalystframework.org/)
1076 - [Mojolicious](http://mojolicious.org/)
1077 - [Dancer](http://perldancer.org/)
1078 - [CGI::Application](http://cgi-app.org/)
1079 - [CGI::Ex](https://github.com/chazmcgarvey/CGI-Ex/tree/psgi-2)
1080 - [Web::Simple](https://metacpan.org/pod/Web::Simple)
1081 - [Amon2](https://metacpan.org/pod/Amon2)
1082 - [Poet](https://metacpan.org/pod/Poet)
1083 - [Kelp](https://metacpan.org/pod/Kelp)
1084 - [Raisin](https://metacpan.org/pod/Raisin)
1085 - many more...
1086
1087 ---
1088
1089 class: plack
1090
1091 ## Plack
1092
1093 - Provides tools for building, running, and testing PSGI apps.
1094
1095 --
1096 - [Plack::Handler](https://metacpan.org/pod/Plack::Handler)
1097
1098 ???
1099 - Connects PSGI apps and web servers.
1100 - Takes a request from the server,
1101 - converts it to the PSGI-specified environment,
1102 - runs your app,
1103 - converts the response back to a format the server understands.
1104
1105 --
1106 - [Plack::Loader](https://metacpan.org/pod/Plack::Loader)
1107
1108 ???
1109 - Picks an appropriate Plack::Handler (based on ENV, loaded modules, or arguments) and loads it.
1110 - Can also do stuff like restart the loader when files change.
1111
1112 --
1113 - [Plack::Runner](https://metacpan.org/pod/Plack::Runner), [plackup](https://metacpan.org/pod/plackup)
1114
1115 ???
1116 - Run PSGI apps from the command-line.
1117
1118 --
1119 - [Plack::Middleware](https://metacpan.org/pod/Plack::Middleware)
1120
1121 ???
1122 - Create subroutines that run between the handler and your app.
1123 - Can alter the request your app receives and modify the response your app returns.
1124
1125 --
1126 - [Plack::Request](https://metacpan.org/pod/Plack::Request), [Plack::Response](https://metacpan.org/pod/Plack::Response)
1127
1128 ???
1129 - Request and response wrappers can help simplify writing middleware.
1130
1131 --
1132 - [Plack::Builder](https://metacpan.org/pod/Plack::Builder)
1133
1134 ???
1135 - Provides DSL for composing apps and middleware.
1136
1137 --
1138 - [Plack::Test](https://metacpan.org/pod/Plack::Test), [Plack::Test::Suite](https://metacpan.org/pod/Plack::Test::Suite)
1139
1140 ???
1141 - Use Plack::Test for testing apps.
1142 - Plack::Test::Suite is a series of tests for testing handlers.
1143
1144 --
1145 - [Plack::Util](https://metacpan.org/pod/Plack::Util)
1146
1147 ???
1148 - Provides random useful stuff for handler and middleware developers.
1149 - Stuff like determing the length of a document or getting PSGI response headers from the arrayref.
1150
1151 ---
1152
1153 class: plackup
1154
1155 ## plackup
1156
1157 - Run PSGI apps from the command-line.
1158
1159 ```sh
1160 # read your app from app.psgi file
1161 plackup
1162
1163 # choose .psgi file from ARGV[0] (or with -a option)
1164 plackup hello.psgi
1165
1166 # switch server implementation with --server (or -s)
1167 plackup --server HTTP::Server::Simple --port 9090 --host 127.0.0.1 test.psgi
1168
1169 # use UNIX socket to run FCGI daemon
1170 plackup -s FCGI --listen /tmp/fcgi.sock myapp.psgi
1171
1172 # launch FCGI external server on port 9090
1173 plackup -s FCGI --port 9090
1174 ```
1175
1176 ---
1177
1178 class: app-psgi
1179 layout: true
1180
1181 ## app.psgi
1182
1183 ---
1184
1185 ```perl
1186 #!/usr/bin/env perl
1187
1188 my $app = sub {
1189 my $env = shift;
1190 my $client_id = $env->{'REMOTE_ADDR'};
1191
1192 return [
1193 '200',
1194 [ 'Content-Type' => 'text/plain' ],
1195 [ "Your IP address is ${client_id}." ],
1196 ];
1197 };
1198 ```
1199
1200 ---
1201
1202 ```perl
1203 #!/usr/bin/env perl
1204
1205 *use Plack::Builder;
1206
1207 my $app = sub {
1208 my $env = shift;
1209 my $client_id = $env->{'REMOTE_ADDR'};
1210
1211 return [
1212 '200',
1213 [ 'Content-Type' => 'text/plain' ],
1214 [ "Your IP address is ${client_id}." ],
1215 ];
1216 };
1217
1218 *builder {
1219 * enable 'Runtime';
1220 * mount '/' => $app;
1221 *};
1222 ```
1223
1224 ???
1225 - The `Runtime` middleware adds an `X-Runtime` header to the response with the number of seconds it
1226 took to process the request.
1227
1228 ---
1229
1230 class: env
1231 layout: true
1232
1233 ## Plack `$env`
1234
1235 ---
1236
1237 .condensed.marginfix[
1238 .col[
1239 - `HTTP_ACCEPT`
1240 - `HTTP_ACCEPT_ENCODING`
1241 - `HTTP_ACCEPT_LANGUAGE`
1242 - `HTTP_CACHE_CONTROL`
1243 - `HTTP_CONNECTION`
1244 - `HTTP_DNT`
1245 - `HTTP_HOST`
1246 - `HTTP_USER_AGENT`
1247 - `PATH_INFO`
1248 - `QUERY_STRING `
1249 - `REMOTE_ADDR`
1250 - `REMOTE_PORT`
1251 - `REQUEST_METHOD`
1252 - `REQUEST_URI`
1253 - `SCRIPT_NAME`
1254 ]
1255 .col[
1256 - `SERVER_NAME`
1257 - `SERVER_PORT`
1258 - `SERVER_PROTOCOL`
1259 - `psgi.errors`
1260 - `psgi.input`
1261 - `psgi.multiprocess`
1262 - `psgi.multithread`
1263 - `psgi.nonblocking`
1264 - `psgi.run_once`
1265 - `psgi.streaming`
1266 - `psgi.url_scheme`
1267 - `psgi.version`
1268 - `psgix.harakiri`
1269 - `psgix.input.buffered`
1270 - `psgix.io`
1271 ]
1272 ]
1273
1274 ---
1275
1276 .condensed.marginfix[
1277 .col[
1278 - .highlight[`HTTP_ACCEPT`]
1279 - .highlight[`HTTP_ACCEPT_ENCODING`]
1280 - .highlight[`HTTP_ACCEPT_LANGUAGE`]
1281 - .highlight[`HTTP_CACHE_CONTROL`]
1282 - .highlight[`HTTP_CONNECTION`]
1283 - .highlight[`HTTP_DNT`]
1284 - .highlight[`HTTP_HOST`]
1285 - .highlight[`HTTP_USER_AGENT`]
1286 - .highlight[`PATH_INFO`]
1287 - .highlight[`QUERY_STRING `]
1288 - .highlight[`REMOTE_ADDR`]
1289 - .highlight[`REMOTE_PORT`]
1290 - .highlight[`REQUEST_METHOD`]
1291 - .highlight[`REQUEST_URI`]
1292 - .highlight[`SCRIPT_NAME`]
1293 ]
1294 .col[
1295 - .highlight[`SERVER_NAME`]
1296 - .highlight[`SERVER_PORT`]
1297 - .highlight[`SERVER_PROTOCOL`]
1298 - `psgi.errors`
1299 - `psgi.input`
1300 - `psgi.multiprocess`
1301 - `psgi.multithread`
1302 - `psgi.nonblocking`
1303 - `psgi.run_once`
1304 - `psgi.streaming`
1305 - `psgi.url_scheme`
1306 - `psgi.version`
1307 - `psgix.harakiri`
1308 - `psgix.input.buffered`
1309 - `psgix.io`
1310 ]
1311 ]
1312
1313 ---
1314
1315 .condensed.marginfix[
1316 .col[
1317 - `HTTP_ACCEPT`
1318 - `HTTP_ACCEPT_ENCODING`
1319 - `HTTP_ACCEPT_LANGUAGE`
1320 - `HTTP_CACHE_CONTROL`
1321 - `HTTP_CONNECTION`
1322 - `HTTP_DNT`
1323 - `HTTP_HOST`
1324 - `HTTP_USER_AGENT`
1325 - `PATH_INFO`
1326 - `QUERY_STRING `
1327 - `REMOTE_ADDR`
1328 - `REMOTE_PORT`
1329 - `REQUEST_METHOD`
1330 - `REQUEST_URI`
1331 - `SCRIPT_NAME`
1332 ]
1333 .col[
1334 - `SERVER_NAME`
1335 - `SERVER_PORT`
1336 - `SERVER_PROTOCOL`
1337 - .highlight[`psgi.errors`]
1338 - .highlight[`psgi.input`]
1339 - .highlight[`psgi.multiprocess`]
1340 - .highlight[`psgi.multithread`]
1341 - .highlight[`psgi.nonblocking`]
1342 - .highlight[`psgi.run_once`]
1343 - .highlight[`psgi.streaming`]
1344 - .highlight[`psgi.url_scheme`]
1345 - .highlight[`psgi.version`]
1346 - .highlight[`psgix.harakiri`]
1347 - .highlight[`psgix.input.buffered`]
1348 - .highlight[`psgix.io`]
1349 ]
1350 ]
1351
1352 ---
1353
1354 layout: false
1355
1356 ## Plack Handlers
1357
1358 - Can be found on the CPAN in the `Plack::Handler::` namespace.
1359 - [Apache1](https://metacpan.org/pod/Plack::Handler::Apache1), [Apache2](https://metacpan.org/pod/Plack::Handler::Apache2)
1360 - [CGI](https://metacpan.org/pod/Plack::Handler::CGI)
1361 - [FCGI](https://metacpan.org/pod/Plack::Handler::FCGI)
1362 - [HTTP::Server::PSGI](https://metacpan.org/pod/Plack::Handler::HTTP::Server::PSGI)
1363 - [SCGI](https://metacpan.org/pod/Plack::Handler::SCGI)
1364 - [Starman](https://metacpan.org/pod/Plack::Handler::Starman)
1365 - [Twiggy](https://metacpan.org/pod/Plack::Handler::Twiggy)
1366 - [AnyEvent::HTTPD](https://metacpan.org/pod/Plack::Handler::AnyEvent::HTTPD)
1367 - [Thrall](https://metacpan.org/pod/Plack::Handler::Thrall)
1368 - many more...
1369
1370 ---
1371
1372 class: middleware
1373 layout: true
1374
1375 ## Plack Middleware
1376
1377 ---
1378
1379 name: middleware-debug
1380
1381 ### [`Debug`](https://metacpan.org/pod/Plack::Middleware::Debug)
1382
1383 ```perl
1384 enable 'Debug';
1385 ```
1386
1387 ---
1388
1389 ### [`ReverseProxy`](https://metacpan.org/pod/Plack::Middleware::ReverseProxy)
1390
1391 ```perl
1392 enable 'ReverseProxy';
1393 ```
1394
1395 - Fixes `REMOTE_ADDR`, `HTTP_HOST`, `SERVER_PORT`, and `psgi.url_scheme` in the environment.
1396
1397 ---
1398
1399 ### [`LogDispatch`](https://metacpan.org/pod/Plack::Middleware::LogDispatch)
1400
1401 ```perl
1402 use Log::Dispatch;
1403
1404 my $logger = Log::Dispatch->new(
1405 outputs => [
1406 [
1407 'Syslog',
1408 min_level => 'debug',
1409 ident => 'myapp',
1410 ],
1411 ],
1412 );
1413
1414 enable 'LogDispatch', logger => $logger;
1415 ```
1416
1417 ---
1418
1419 ### [`XSRFBlock`](https://metacpan.org/pod/Plack::Middleware::XSRFBlock)
1420
1421 ```perl
1422 enable 'XSRFBlock', cookie_options => { httponly => 1 };
1423 ```
1424
1425 - Blocking cross-site request forgery couldn't be easier.
1426
1427 ---
1428
1429 ### [`RedirectSSL`](https://metacpan.org/pod/Plack::Middleware::RedirectSSL)
1430
1431 ```perl
1432 enable 'RedirectSSL';
1433 ```
1434
1435 - Redirects from http to https (or backwards, if configured).
1436 - Can also set HSTS header with configurable `max-age`.
1437
1438 ---
1439
1440 layout: false
1441
1442 .top-right[
1443 ![CPAN](img/cpan.png)
1444 ]
1445
1446 ## Plack modules in July 2016
1447
1448 **10** `Plack-Handler-*` distributions
1449
1450 **55** `Plack-App-*` distributions
1451
1452 **253** `Plack-Middleware-*` distributions
1453
1454 ---
1455
1456 ## Parting Thoughts
1457
1458 - PSGI also specifies a way to delay or stream responses to the server.
1459
1460 ???
1461 - It's kind of complicated, but you can read the spec to learn more.
1462 - Read the source code of various apps and middlewares to see how it works in practice.
1463
1464 --
1465 - There are tons of great apps and middleware on the CPAN.
1466
1467 --
1468 - Consider writing some of your app as a middleware.
1469
1470 ???
1471 - The concept and implementation of middleware is cool.
1472 - You should consider writing parts of your app as middleware so that functionality is available
1473 under different web frameworks.
1474 - Stuff that makes sense as middleware:
1475 - Auth mechanisms
1476 - Logging
1477 - Error handling
1478 - Sessions
1479 - Rate limiters
1480
1481 ---
1482
1483 class: center, middle
1484 name: conclusion
1485
1486 ## Conclusion:
1487
1488 ### Understand PSGI & Plack, and use them!
1489
1490 ---
1491
1492 class: center, middle
1493 layout: false
1494 name: questions
1495
1496 ## Questions?
1497
1498 ---
1499
1500 class: center, middle
1501 name: last
1502
1503 .col.sep[
1504 ## Thank you
1505
1506 Email me: Charles McGarvey
1507 <chazmcgarvey@brokenzipper.com>
1508
1509 .talkqr.center[
1510 Leave me feedback, if you want:
1511
1512 ![Page on Joind.in](img/talkqr.svg)
1513
1514 <https://joind.in/talk/6e4d2>
1515 ]
1516 ]
1517
1518 .col[
1519 ## Credits
1520
1521 .left[
1522 - Thank you [Tatsuhiko Miyagawa](http://weblog.bulknews.net/) and other contributors for creating PSGI and Plack.
1523 ]
1524 ]
1525
1526 </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><script src="js/common.js"></script><script src="js/slides.js"></script></body></html>
1527 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->
This page took 0.094323 seconds and 3 git commands to generate.