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