]> Dogcows Code - chaz/talk-introduction-to-psgi/commitdiff
initial commit
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 11 Jul 2016 23:02:10 +0000 (17:02 -0600)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 11 Jul 2016 23:02:10 +0000 (17:02 -0600)
19 files changed:
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
abstract.txt [new file with mode: 0644]
css/common.css [new file with mode: 0644]
css/slides.css [new file with mode: 0644]
img/avatar-miyagawa.jpg [new file with mode: 0644]
img/basic-flow1.dot [new file with mode: 0644]
img/basic-flow2.dot [new file with mode: 0644]
img/basic-flow3.dot [new file with mode: 0644]
img/basic-flow4.dot [new file with mode: 0644]
img/basic-flow5.dot [new file with mode: 0644]
img/bluehost.png [new file with mode: 0644]
img/cgi.gif [new file with mode: 0644]
img/cpan.png [new file with mode: 0644]
img/fastcgi.png [new file with mode: 0644]
img/mod_perl.gif [new file with mode: 0644]
img/talkqr.svg [new file with mode: 0644]
notes.txt [new file with mode: 0644]
slides.html [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..eb0b95f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,38 @@
+
+SLIDES  = introduction-to-psgi
+BROWSER = chrome
+DOT     = dot
+
+dotfiles = $(shell find . -iname '*.dot')
+svgfiles = $(patsubst %.dot,%.svg,$(dotfiles))
+
+all: $(svgfiles)
+
+clean:
+       rm -f slides-offline.html remark.min.js $(SLIDES).pdf $(svgfiles)
+
+offline: slides-offline.html remark.min.js $(svgfiles)
+
+pdf: $(SLIDES).pdf
+
+run: $(svgfiles)
+       $(BROWSER) slides.html
+
+run-offline: offline
+       $(BROWSER) slides-offline.html
+
+%.svg: %.dot
+       $(DOT) -Tsvg -o$@ $<
+
+$(SLIDES).pdf: slides.html $(wildcard css/*) $(wildcard img/*) $(svgfiles)
+       docker run --rm -v `pwd`:/pwd astefanutti/decktape /pwd/slides.html /pwd/$(SLIDES).pdf
+
+slides-offline.html: slides.html
+       sed -e '1 a <!-- This file is auto-generated - DO NOT EDIT!!! -->' \
+           -e 's!https://.*remark-latest\.min\.js!remark.min.js!' <$< >$@
+
+remark.min.js:
+       curl -Lo $@ https://gnab.github.io/remark/downloads/remark-latest.min.js
+
+.PHONY: all clean offline pdf run run-offline
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..9ca631d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+
+# Slides for "Introduction to PSGI"
+
+This directory contains the slides for my talk entitled "Introduction to PSGI".
+
+The slides were written in [Markdown](https://daringfireball.net/projects/markdown/) format and can be rendered with
+[remark](http://remarkjs.com/).
+
+## Options
+
+To view the slides:
+
+    make run
+
+To build a PDF version of the slides:
+
+    make pdf
+
diff --git a/abstract.txt b/abstract.txt
new file mode 100644 (file)
index 0000000..1240399
--- /dev/null
@@ -0,0 +1,18 @@
+If you write web apps in Perl (perhaps using Catalyst, Mojolicious, Dancer, or
+one of the other great web frameworks), you are probably already using PSGI,
+but do you understand how it works and why PSGI is so amazing? You should! If
+you still write web apps using CGI, then there are many reasons why you should
+create your next project using a PSGI-compatible web framework.
+
+PSGI is a specification, like CGI, that describes how web apps and web servers
+can communicate. It stands for Perl web Server Gateway Interface, and the
+first version of the specification was published to the CPAN on Oct 13, 2009.
+
+This talk will go over:
+* what PSGI is and how it works,
+* how it differs and is better than CGI,
+* what makes it so cool,
+* and how you can use it to do awesome things.
+
+You'll get the most out of this talk if you already know Perl and use it to
+build web applications.
diff --git a/css/common.css b/css/common.css
new file mode 100644 (file)
index 0000000..63feb09
--- /dev/null
@@ -0,0 +1,79 @@
+@import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
+@import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic);
+@import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);
+
+body {
+    font-family: 'Droid Serif';
+}
+
+h1, h2, h3 {
+    font-family: 'Yanone Kaffeesatz';
+    font-weight: normal;
+}
+
+.remark-slide-content li {
+    line-height: 2em;
+}
+.remark-slide-content .condensed li {
+    line-height: 1em;
+}
+
+
+.remark-code, .remark-inline-code {
+    font-family: Inconsolata, 'Ubuntu Mono';
+}
+.remark-code {
+    border-radius: 5px;
+    border: 1px solid #ccc;
+}
+
+blockquote {
+    border-radius: 5px;
+    border: 1px solid #ccc;
+    background: #f0f0f0;
+    margin: 1.5em 0;
+    padding: 1em;
+    quotes: "\201C""\201D""\2018""\2019";
+    position: relative;
+}
+blockquote:before {
+    color: #ccc;
+    position: absolute;
+    content: open-quote;
+    font-size: 6em;
+    line-height: 0.1em;
+    margin-right: 0.25em;
+    vertical-align: -0.4em;
+}
+
+.col {
+    box-sizing: border-box;
+    display: block;
+    float: left;
+    border: 0;
+    margin: 0;
+    padding: 0;
+    width: 50%;
+}
+.col:nth-of-type(odd) {
+    padding: 0 1em 0 0;
+}
+.col:nth-of-type(even) {
+    padding: 0 0 0 1em;
+}
+.col.sep:nth-of-type(odd) {
+    border-right: 1px solid #ccc;
+}
+.col.sep:nth-of-type(even) {
+    border-left: 1px solid #ccc;
+}
+
+.top-right {
+    float: right;
+    margin: 50px 0 0 2em;
+}
+
+.highlight {
+    color: #7d9726;
+}
+
diff --git a/css/slides.css b/css/slides.css
new file mode 100644 (file)
index 0000000..210a3db
--- /dev/null
@@ -0,0 +1,61 @@
+
+.basic-flow {
+    line-height: 200px;
+    height: 200px;
+}
+.basic-flow img {
+    display: inline-block;
+    vertical-align: middle;
+    max-width: 100%;
+}
+
+/* fix overzealous syntax highlighter */
+.http .hljs-selector-tag,
+.http .hljs-selector-class {
+    font-weight: normal;
+    color: inherit;
+}
+
+.cgi .remark-code {
+    font-size: 22px;
+}
+
+.fastcgi .remark-code {
+    font-size: 22px;
+}
+
+.mod_perl .remark-code {
+    font-size: 20px;
+}
+
+.psgi .remark-code {
+    font-size: 22px;
+}
+
+/* fix non-collapsing margin */
+.psgi pre {
+    margin-bottom: 0;
+}
+
+.plackup .remark-code {
+    font-size: 24px;
+}
+
+.app-psgi .remark-code {
+    font-size: 22px;
+}
+
+.env .condensed li {
+    line-height: 1.4em;
+}
+
+.middleware .remark-code {
+    font-size: 22px;
+}
+
+#slide-theend .talkqr img {
+    display: inline-block;
+    width: 230px;
+    height: 230px;
+}
+
diff --git a/img/avatar-miyagawa.jpg b/img/avatar-miyagawa.jpg
new file mode 100644 (file)
index 0000000..44112ba
Binary files /dev/null and b/img/avatar-miyagawa.jpg differ
diff --git a/img/basic-flow1.dot b/img/basic-flow1.dot
new file mode 100644 (file)
index 0000000..af995dd
--- /dev/null
@@ -0,0 +1,24 @@
+
+digraph G
+{
+    rankdir = LR
+
+    node
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 22
+        shape       = record
+        style       = rounded
+    ]
+
+    edge
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 20
+        arrowhead   = vee
+        arrowtail   = vee
+    ]
+
+    "user\nagents" -> "your\napp" [label="PSGI",dir=both,fontcolor=blue,fontsize=30]
+}
+
diff --git a/img/basic-flow2.dot b/img/basic-flow2.dot
new file mode 100644 (file)
index 0000000..0cebe6d
--- /dev/null
@@ -0,0 +1,27 @@
+
+digraph G
+{
+    rankdir = LR
+
+    node
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 22
+        shape       = record
+        style       = rounded
+    ]
+
+    edge
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 20
+        arrowhead   = vee
+        arrowtail   = vee
+    ]
+
+    "user\nagents"  -> "web\nserver"    [label="HTTP",dir=both]
+    "web\nserver"   -> "your\napp"      [label="PSGI",dir=both,fontcolor=blue,fontsize=30]
+
+    "web\nserver" [style="rounded,filled",fillcolor="#FFFF88"]
+}
+
diff --git a/img/basic-flow3.dot b/img/basic-flow3.dot
new file mode 100644 (file)
index 0000000..bcaefde
--- /dev/null
@@ -0,0 +1,28 @@
+
+digraph G
+{
+    rankdir = LR
+    
+    node
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 22
+        shape       = record
+        style       = rounded
+    ]
+
+    edge
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 20
+        arrowhead   = vee
+        arrowtail   = vee
+    ]
+
+    "user\nagents"  -> "web\nserver"    [label="HTTP",dir=both]
+    "web\nserver"   -> "PSGI\nhandler"  [label="CGI, mod_perl,\nFastCGI, HTTP",dir=both]
+    "PSGI\nhandler" -> "your\napp"      [label="PSGI",dir=both,fontcolor=blue,fontsize=30]
+
+    "PSGI\nhandler" [style="rounded,filled",fillcolor="#FFFF88"]
+}
+
diff --git a/img/basic-flow4.dot b/img/basic-flow4.dot
new file mode 100644 (file)
index 0000000..83ba026
--- /dev/null
@@ -0,0 +1,35 @@
+
+digraph G
+{
+    rankdir = LR
+    
+    node [
+        fontname    = "Inconsolata"
+        fontsize    = 22
+        shape       = record
+        style       = rounded
+    ]
+
+    edge [
+        fontname    = "Inconsolata"
+        fontsize    = 20
+        arrowhead   = vee
+        arrowtail   = vee
+    ]
+
+    "user\nagents"  -> "web\nserver"    [label="HTTP",dir=both]
+    "web\nserver"   -> "PSGI\nhandler"  [label="CGI, mod_perl,\nFastCGI, HTTP",dir=both]
+    "PSGI\nhandler" -> "your\napp"      [label="PSGI",dir=both,fontcolor=blue,fontsize=30]
+
+    subgraph cluster_devops {
+        fontname    = "Inconsolata"
+        fontsize    = 30
+        label       = "DevOps"
+        style       = filled
+        fillcolor   = "#FFFF88"
+
+        "web\nserver"
+        "PSGI\nhandler"
+    }
+}
+
diff --git a/img/basic-flow5.dot b/img/basic-flow5.dot
new file mode 100644 (file)
index 0000000..5fbef5c
--- /dev/null
@@ -0,0 +1,38 @@
+
+digraph G
+{
+    rankdir = LR
+    
+    node
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 22
+        shape       = record
+        style       = rounded
+    ]
+
+    edge
+    [
+        fontname    = "Inconsolata"
+        fontsize    = 20
+        arrowhead   = vee
+        arrowtail   = vee
+    ]
+
+    "user\nagents"      -> "web\nserver"    [label="HTTP",dir=both]
+    "web\nserver"       -> "PSGI\nhandler"  [label="CGI, mod_perl,\nFastCGI, HTTP",dir=both]
+    "PSGI\nhandler"     -> "web\nframework" [label="PSGI",dir=both,fontcolor=blue,fontsize=30]
+    "web\nframework"    -> "your\napp"      [label="???",dir=both]
+
+    "web\nframework" [style="rounded,filled",fillcolor="#FFFF88"]
+
+    subgraph cluster_devops {
+        fontname    = "Inconsolata"
+        fontsize    = 30
+        label       = "DevOps"
+
+        "web\nserver"
+        "PSGI\nhandler"
+    }
+}
+
diff --git a/img/bluehost.png b/img/bluehost.png
new file mode 100644 (file)
index 0000000..3a83180
Binary files /dev/null and b/img/bluehost.png differ
diff --git a/img/cgi.gif b/img/cgi.gif
new file mode 100644 (file)
index 0000000..2813732
Binary files /dev/null and b/img/cgi.gif differ
diff --git a/img/cpan.png b/img/cpan.png
new file mode 100644 (file)
index 0000000..a3a3d2b
Binary files /dev/null and b/img/cpan.png differ
diff --git a/img/fastcgi.png b/img/fastcgi.png
new file mode 100644 (file)
index 0000000..458c0df
Binary files /dev/null and b/img/fastcgi.png differ
diff --git a/img/mod_perl.gif b/img/mod_perl.gif
new file mode 100644 (file)
index 0000000..a9a3f67
Binary files /dev/null and b/img/mod_perl.gif differ
diff --git a/img/talkqr.svg b/img/talkqr.svg
new file mode 100644 (file)
index 0000000..9904271
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="250" height="250">
+       <title>QR Code</title>
+       <desc>https://joind.in/talk/6e4d2</desc>
+       <rect style="fill:rgb(255, 255, 255);fill-opacity:1" x="0" y="0" width="250" height="250" />
+       <g id="elements">
+               <path style="fill:rgb(0, 0, 0)" d="M 0,0 l 10,0 0,10 -10,0 z M 10,0 l 10,0 0,10 -10,0 z M 20,0 l 10,0 0,10 -10,0 z M 30,0 l 10,0 0,10 -10,0 z M 40,0 l 10,0 0,10 -10,0 z M 50,0 l 10,0 0,10 -10,0 z M 60,0 l 10,0 0,10 -10,0 z M 100,0 l 10,0 0,10 -10,0 z M 120,0 l 10,0 0,10 -10,0 z M 140,0 l 10,0 0,10 -10,0 z M 160,0 l 10,0 0,10 -10,0 z M 180,0 l 10,0 0,10 -10,0 z M 190,0 l 10,0 0,10 -10,0 z M 200,0 l 10,0 0,10 -10,0 z M 210,0 l 10,0 0,10 -10,0 z M 220,0 l 10,0 0,10 -10,0 z M 230,0 l 10,0 0,10 -10,0 z M 240,0 l 10,0 0,10 -10,0 z M 0,10 l 10,0 0,10 -10,0 z M 60,10 l 10,0 0,10 -10,0 z M 80,10 l 10,0 0,10 -10,0 z M 100,10 l 10,0 0,10 -10,0 z M 140,10 l 10,0 0,10 -10,0 z M 150,10 l 10,0 0,10 -10,0 z M 160,10 l 10,0 0,10 -10,0 z M 180,10 l 10,0 0,10 -10,0 z M 240,10 l 10,0 0,10 -10,0 z M 0,20 l 10,0 0,10 -10,0 z M 20,20 l 10,0 0,10 -10,0 z M 30,20 l 10,0 0,10 -10,0 z M 40,20 l 10,0 0,10 -10,0 z M 60,20 l 10,0 0,10 -10,0 z M 120,20 l 10,0 0,10 -10,0 z M 160,20 l 10,0 0,10 -10,0 z M 180,20 l 10,0 0,10 -10,0 z M 200,20 l 10,0 0,10 -10,0 z M 210,20 l 10,0 0,10 -10,0 z M 220,20 l 10,0 0,10 -10,0 z M 240,20 l 10,0 0,10 -10,0 z M 0,30 l 10,0 0,10 -10,0 z M 20,30 l 10,0 0,10 -10,0 z M 30,30 l 10,0 0,10 -10,0 z M 40,30 l 10,0 0,10 -10,0 z M 60,30 l 10,0 0,10 -10,0 z M 80,30 l 10,0 0,10 -10,0 z M 90,30 l 10,0 0,10 -10,0 z M 100,30 l 10,0 0,10 -10,0 z M 120,30 l 10,0 0,10 -10,0 z M 130,30 l 10,0 0,10 -10,0 z M 140,30 l 10,0 0,10 -10,0 z M 180,30 l 10,0 0,10 -10,0 z M 200,30 l 10,0 0,10 -10,0 z M 210,30 l 10,0 0,10 -10,0 z M 220,30 l 10,0 0,10 -10,0 z M 240,30 l 10,0 0,10 -10,0 z M 0,40 l 10,0 0,10 -10,0 z M 20,40 l 10,0 0,10 -10,0 z M 30,40 l 10,0 0,10 -10,0 z M 40,40 l 10,0 0,10 -10,0 z M 60,40 l 10,0 0,10 -10,0 z M 90,40 l 10,0 0,10 -10,0 z M 180,40 l 10,0 0,10 -10,0 z M 200,40 l 10,0 0,10 -10,0 z M 210,40 l 10,0 0,10 -10,0 z M 220,40 l 10,0 0,10 -10,0 z M 240,40 l 10,0 0,10 -10,0 z M 0,50 l 10,0 0,10 -10,0 z M 60,50 l 10,0 0,10 -10,0 z M 80,50 l 10,0 0,10 -10,0 z M 90,50 l 10,0 0,10 -10,0 z M 110,50 l 10,0 0,10 -10,0 z M 120,50 l 10,0 0,10 -10,0 z M 150,50 l 10,0 0,10 -10,0 z M 180,50 l 10,0 0,10 -10,0 z M 240,50 l 10,0 0,10 -10,0 z M 0,60 l 10,0 0,10 -10,0 z M 10,60 l 10,0 0,10 -10,0 z M 20,60 l 10,0 0,10 -10,0 z M 30,60 l 10,0 0,10 -10,0 z M 40,60 l 10,0 0,10 -10,0 z M 50,60 l 10,0 0,10 -10,0 z M 60,60 l 10,0 0,10 -10,0 z M 80,60 l 10,0 0,10 -10,0 z M 100,60 l 10,0 0,10 -10,0 z M 120,60 l 10,0 0,10 -10,0 z M 140,60 l 10,0 0,10 -10,0 z M 160,60 l 10,0 0,10 -10,0 z M 180,60 l 10,0 0,10 -10,0 z M 190,60 l 10,0 0,10 -10,0 z M 200,60 l 10,0 0,10 -10,0 z M 210,60 l 10,0 0,10 -10,0 z M 220,60 l 10,0 0,10 -10,0 z M 230,60 l 10,0 0,10 -10,0 z M 240,60 l 10,0 0,10 -10,0 z M 90,70 l 10,0 0,10 -10,0 z M 100,70 l 10,0 0,10 -10,0 z M 110,70 l 10,0 0,10 -10,0 z M 140,70 l 10,0 0,10 -10,0 z M 160,70 l 10,0 0,10 -10,0 z M 0,80 l 10,0 0,10 -10,0 z M 10,80 l 10,0 0,10 -10,0 z M 20,80 l 10,0 0,10 -10,0 z M 30,80 l 10,0 0,10 -10,0 z M 40,80 l 10,0 0,10 -10,0 z M 60,80 l 10,0 0,10 -10,0 z M 70,80 l 10,0 0,10 -10,0 z M 80,80 l 10,0 0,10 -10,0 z M 90,80 l 10,0 0,10 -10,0 z M 110,80 l 10,0 0,10 -10,0 z M 120,80 l 10,0 0,10 -10,0 z M 130,80 l 10,0 0,10 -10,0 z M 140,80 l 10,0 0,10 -10,0 z M 170,80 l 10,0 0,10 -10,0 z M 190,80 l 10,0 0,10 -10,0 z M 210,80 l 10,0 0,10 -10,0 z M 230,80 l 10,0 0,10 -10,0 z M 10,90 l 10,0 0,10 -10,0 z M 40,90 l 10,0 0,10 -10,0 z M 50,90 l 10,0 0,10 -10,0 z M 70,90 l 10,0 0,10 -10,0 z M 100,90 l 10,0 0,10 -10,0 z M 120,90 l 10,0 0,10 -10,0 z M 130,90 l 10,0 0,10 -10,0 z M 160,90 l 10,0 0,10 -10,0 z M 190,90 l 10,0 0,10 -10,0 z M 230,90 l 10,0 0,10 -10,0 z M 20,100 l 10,0 0,10 -10,0 z M 60,100 l 10,0 0,10 -10,0 z M 100,100 l 10,0 0,10 -10,0 z M 120,100 l 10,0 0,10 -10,0 z M 140,100 l 10,0 0,10 -10,0 z M 150,100 l 10,0 0,10 -10,0 z M 160,100 l 10,0 0,10 -10,0 z M 170,100 l 10,0 0,10 -10,0 z M 200,100 l 10,0 0,10 -10,0 z M 210,100 l 10,0 0,10 -10,0 z M 230,100 l 10,0 0,10 -10,0 z M 240,100 l 10,0 0,10 -10,0 z M 10,110 l 10,0 0,10 -10,0 z M 30,110 l 10,0 0,10 -10,0 z M 50,110 l 10,0 0,10 -10,0 z M 70,110 l 10,0 0,10 -10,0 z M 80,110 l 10,0 0,10 -10,0 z M 110,110 l 10,0 0,10 -10,0 z M 120,110 l 10,0 0,10 -10,0 z M 150,110 l 10,0 0,10 -10,0 z M 170,110 l 10,0 0,10 -10,0 z M 200,110 l 10,0 0,10 -10,0 z M 240,110 l 10,0 0,10 -10,0 z M 0,120 l 10,0 0,10 -10,0 z M 20,120 l 10,0 0,10 -10,0 z M 50,120 l 10,0 0,10 -10,0 z M 60,120 l 10,0 0,10 -10,0 z M 80,120 l 10,0 0,10 -10,0 z M 90,120 l 10,0 0,10 -10,0 z M 100,120 l 10,0 0,10 -10,0 z M 130,120 l 10,0 0,10 -10,0 z M 140,120 l 10,0 0,10 -10,0 z M 170,120 l 10,0 0,10 -10,0 z M 180,120 l 10,0 0,10 -10,0 z M 200,120 l 10,0 0,10 -10,0 z M 220,120 l 10,0 0,10 -10,0 z M 230,120 l 10,0 0,10 -10,0 z M 240,120 l 10,0 0,10 -10,0 z M 0,130 l 10,0 0,10 -10,0 z M 10,130 l 10,0 0,10 -10,0 z M 50,130 l 10,0 0,10 -10,0 z M 70,130 l 10,0 0,10 -10,0 z M 80,130 l 10,0 0,10 -10,0 z M 110,130 l 10,0 0,10 -10,0 z M 120,130 l 10,0 0,10 -10,0 z M 130,130 l 10,0 0,10 -10,0 z M 160,130 l 10,0 0,10 -10,0 z M 190,130 l 10,0 0,10 -10,0 z M 210,130 l 10,0 0,10 -10,0 z M 230,130 l 10,0 0,10 -10,0 z M 0,140 l 10,0 0,10 -10,0 z M 20,140 l 10,0 0,10 -10,0 z M 30,140 l 10,0 0,10 -10,0 z M 60,140 l 10,0 0,10 -10,0 z M 80,140 l 10,0 0,10 -10,0 z M 90,140 l 10,0 0,10 -10,0 z M 100,140 l 10,0 0,10 -10,0 z M 120,140 l 10,0 0,10 -10,0 z M 130,140 l 10,0 0,10 -10,0 z M 150,140 l 10,0 0,10 -10,0 z M 190,140 l 10,0 0,10 -10,0 z M 200,140 l 10,0 0,10 -10,0 z M 210,140 l 10,0 0,10 -10,0 z M 230,140 l 10,0 0,10 -10,0 z M 240,140 l 10,0 0,10 -10,0 z M 0,150 l 10,0 0,10 -10,0 z M 30,150 l 10,0 0,10 -10,0 z M 40,150 l 10,0 0,10 -10,0 z M 50,150 l 10,0 0,10 -10,0 z M 80,150 l 10,0 0,10 -10,0 z M 100,150 l 10,0 0,10 -10,0 z M 140,150 l 10,0 0,10 -10,0 z M 160,150 l 10,0 0,10 -10,0 z M 170,150 l 10,0 0,10 -10,0 z M 180,150 l 10,0 0,10 -10,0 z M 190,150 l 10,0 0,10 -10,0 z M 200,150 l 10,0 0,10 -10,0 z M 240,150 l 10,0 0,10 -10,0 z M 0,160 l 10,0 0,10 -10,0 z M 20,160 l 10,0 0,10 -10,0 z M 30,160 l 10,0 0,10 -10,0 z M 60,160 l 10,0 0,10 -10,0 z M 70,160 l 10,0 0,10 -10,0 z M 80,160 l 10,0 0,10 -10,0 z M 100,160 l 10,0 0,10 -10,0 z M 120,160 l 10,0 0,10 -10,0 z M 130,160 l 10,0 0,10 -10,0 z M 160,160 l 10,0 0,10 -10,0 z M 170,160 l 10,0 0,10 -10,0 z M 180,160 l 10,0 0,10 -10,0 z M 190,160 l 10,0 0,10 -10,0 z M 200,160 l 10,0 0,10 -10,0 z M 220,160 l 10,0 0,10 -10,0 z M 80,170 l 10,0 0,10 -10,0 z M 90,170 l 10,0 0,10 -10,0 z M 110,170 l 10,0 0,10 -10,0 z M 130,170 l 10,0 0,10 -10,0 z M 150,170 l 10,0 0,10 -10,0 z M 160,170 l 10,0 0,10 -10,0 z M 200,170 l 10,0 0,10 -10,0 z M 210,170 l 10,0 0,10 -10,0 z M 0,180 l 10,0 0,10 -10,0 z M 10,180 l 10,0 0,10 -10,0 z M 20,180 l 10,0 0,10 -10,0 z M 30,180 l 10,0 0,10 -10,0 z M 40,180 l 10,0 0,10 -10,0 z M 50,180 l 10,0 0,10 -10,0 z M 60,180 l 10,0 0,10 -10,0 z M 80,180 l 10,0 0,10 -10,0 z M 100,180 l 10,0 0,10 -10,0 z M 120,180 l 10,0 0,10 -10,0 z M 130,180 l 10,0 0,10 -10,0 z M 140,180 l 10,0 0,10 -10,0 z M 160,180 l 10,0 0,10 -10,0 z M 180,180 l 10,0 0,10 -10,0 z M 200,180 l 10,0 0,10 -10,0 z M 220,180 l 10,0 0,10 -10,0 z M 230,180 l 10,0 0,10 -10,0 z M 240,180 l 10,0 0,10 -10,0 z M 0,190 l 10,0 0,10 -10,0 z M 60,190 l 10,0 0,10 -10,0 z M 90,190 l 10,0 0,10 -10,0 z M 100,190 l 10,0 0,10 -10,0 z M 140,190 l 10,0 0,10 -10,0 z M 150,190 l 10,0 0,10 -10,0 z M 160,190 l 10,0 0,10 -10,0 z M 200,190 l 10,0 0,10 -10,0 z M 210,190 l 10,0 0,10 -10,0 z M 230,190 l 10,0 0,10 -10,0 z M 0,200 l 10,0 0,10 -10,0 z M 20,200 l 10,0 0,10 -10,0 z M 30,200 l 10,0 0,10 -10,0 z M 40,200 l 10,0 0,10 -10,0 z M 60,200 l 10,0 0,10 -10,0 z M 80,200 l 10,0 0,10 -10,0 z M 100,200 l 10,0 0,10 -10,0 z M 130,200 l 10,0 0,10 -10,0 z M 150,200 l 10,0 0,10 -10,0 z M 160,200 l 10,0 0,10 -10,0 z M 170,200 l 10,0 0,10 -10,0 z M 180,200 l 10,0 0,10 -10,0 z M 190,200 l 10,0 0,10 -10,0 z M 200,200 l 10,0 0,10 -10,0 z M 220,200 l 10,0 0,10 -10,0 z M 0,210 l 10,0 0,10 -10,0 z M 20,210 l 10,0 0,10 -10,0 z M 30,210 l 10,0 0,10 -10,0 z M 40,210 l 10,0 0,10 -10,0 z M 60,210 l 10,0 0,10 -10,0 z M 80,210 l 10,0 0,10 -10,0 z M 100,210 l 10,0 0,10 -10,0 z M 110,210 l 10,0 0,10 -10,0 z M 120,210 l 10,0 0,10 -10,0 z M 140,210 l 10,0 0,10 -10,0 z M 150,210 l 10,0 0,10 -10,0 z M 170,210 l 10,0 0,10 -10,0 z M 180,210 l 10,0 0,10 -10,0 z M 200,210 l 10,0 0,10 -10,0 z M 210,210 l 10,0 0,10 -10,0 z M 220,210 l 10,0 0,10 -10,0 z M 230,210 l 10,0 0,10 -10,0 z M 240,210 l 10,0 0,10 -10,0 z M 0,220 l 10,0 0,10 -10,0 z M 20,220 l 10,0 0,10 -10,0 z M 30,220 l 10,0 0,10 -10,0 z M 40,220 l 10,0 0,10 -10,0 z M 60,220 l 10,0 0,10 -10,0 z M 80,220 l 10,0 0,10 -10,0 z M 100,220 l 10,0 0,10 -10,0 z M 130,220 l 10,0 0,10 -10,0 z M 140,220 l 10,0 0,10 -10,0 z M 210,220 l 10,0 0,10 -10,0 z M 220,220 l 10,0 0,10 -10,0 z M 240,220 l 10,0 0,10 -10,0 z M 0,230 l 10,0 0,10 -10,0 z M 60,230 l 10,0 0,10 -10,0 z M 80,230 l 10,0 0,10 -10,0 z M 90,230 l 10,0 0,10 -10,0 z M 100,230 l 10,0 0,10 -10,0 z M 150,230 l 10,0 0,10 -10,0 z M 160,230 l 10,0 0,10 -10,0 z M 170,230 l 10,0 0,10 -10,0 z M 190,230 l 10,0 0,10 -10,0 z M 200,230 l 10,0 0,10 -10,0 z M 210,230 l 10,0 0,10 -10,0 z M 240,230 l 10,0 0,10 -10,0 z M 0,240 l 10,0 0,10 -10,0 z M 10,240 l 10,0 0,10 -10,0 z M 20,240 l 10,0 0,10 -10,0 z M 30,240 l 10,0 0,10 -10,0 z M 40,240 l 10,0 0,10 -10,0 z M 50,240 l 10,0 0,10 -10,0 z M 60,240 l 10,0 0,10 -10,0 z M 80,240 l 10,0 0,10 -10,0 z M 100,240 l 10,0 0,10 -10,0 z M 120,240 l 10,0 0,10 -10,0 z M 130,240 l 10,0 0,10 -10,0 z M 140,240 l 10,0 0,10 -10,0 z M 190,240 l 10,0 0,10 -10,0 z M 200,240 l 10,0 0,10 -10,0 z M 210,240 l 10,0 0,10 -10,0 z M 220,240 l 10,0 0,10 -10,0 z M 230,240 l 10,0 0,10 -10,0 z M 240,240 l 10,0 0,10 -10,0 z " />
+       </g>
+</svg>
diff --git a/notes.txt b/notes.txt
new file mode 100644 (file)
index 0000000..49c810b
--- /dev/null
+++ b/notes.txt
@@ -0,0 +1,35 @@
+
+- High-level what is PSGI:
+  - Interface between web app and web server.
+  - a specification (not code)
+
+- Why care about understanding PSGI when you can just use a high-level language.
+
+- Alternatives: CGI, mod_perl, FCGI, SCGI, WSGI (Python), JSGI (JS), Rack (Ruby), ISAPI (IIS)
+
+- Describe how CGI, mod_perl, and FCGI work.
+
+PSGI is easy. RFC 3875 (CGI 1.1) is 36 pages.
+The PSGI spec is shorter and much more consumable.
+
+Overview of HTTP?
+
+Implementations:
+  - Plack
+  - Alternatives: Paste (Python), 
+  - Rack-compliant: Rack (Ruby), Clack (Common Lisp)
+
+Benefits of PSGI:
+- Web frameworks only need to target PSGI.
+- Easy to write tests because requests and responses are data, not necessarily bytestreams.
+
+- What frameworks support PSGI?
+  - [Catalyst](http://www.catalystframework.org/)
+  - [Mojolicious](http://mojolicious.org/)
+  - [Dancer](http://perldancer.org/)
+  - [CGI::Ex](https://github.com/chazmcgarvey/CGI-Ex/tree/psgi-2)
+
+- What handlers does plack support?
+
+- What types of things should be written as middleware?
+
diff --git a/slides.html b/slides.html
new file mode 100644 (file)
index 0000000..5781fc8
--- /dev/null
@@ -0,0 +1,1476 @@
+<!DOCTYPE html>
+<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">
+
+class:  center, middle
+name:   title
+
+# Introduction to PSGI
+
+Charles McGarvey
+
+---
+
+class:  center, middle
+name:   bluehost
+
+![Bluehost](img/bluehost.png)
+
+### https://bluehost.com/careers
+
+---
+
+## Agenda
+
+- Answer "What is PSGI?"
+- Examine some alternatives to PSGI.
+- Examine PSGI.
+- Examine Plack.
+
+---
+
+## What is PSGI?
+
+### **P**erl [web] **S**erver **G**ateway **I**nterface
+
+--
+- It is an interface between Perl web applications and web servers.
+
+--
+- It is a *specification*, not code.
+
+--
+- First released to the CPAN on 13 Oct 2009.
+
+--
+- Originally written by Miyagawa.
+
+.center[![Tatsuhiko Miyagawa](img/avatar-miyagawa.jpg)]
+
+???
+Written by **Tatsuhiko Miyagawa**, author of:
+- cpanm
+- carton
+- way too many Perl modules on CPAN to list
+
+--
+- Inspired by WSGI (Python) and Rack (Ruby).
+
+???
+- PEP-333 (WSGI 1.0) was released on 07 Dec 2003.
+- Rack 0.1 was released on 03 Mar 2007.
+
+Despite Perl's long history of powering the web, we were well behind the curve on this.
+
+---
+
+class: center, middle
+
+## Extremely High-level Overview
+
+.basic-flow[
+![Basic Flow](img/basic-flow1.svg)
+]
+
+???
+- PSGI is the language your app speaks in order to communicate with user agents.
+  - User agents are browsers.
+- I've glossed over some important details, like the fact that users don't speak PSGI.
+
+---
+
+class: center, middle
+
+## Pretty High-level Overview
+
+.basic-flow[
+![Basic Flow](img/basic-flow2.svg)
+]
+
+???
+- In reality, your app speaks PSGI but user agents speak HTTP.
+- You need software in the middle that can speak both, and that's usually a web server.
+
+---
+
+class: center, middle
+
+## Somewhat High-level Overview
+
+.basic-flow[
+![Basic Flow](img/basic-flow3.svg)
+]
+
+???
+- In reality, most web servers don't speak PSGI. :-(
+- There are "handlers" that speak both PSGI and another language that web servers do speak.
+  - CGI
+  - mod_perl
+  - FastCGI
+  - **HTTP**
+- Yes, HTTP. Many modern web servers speak HTTP not only as a server but also as a client.
+  - This allows them to *proxy* (act as middleman) between user agents and other servers.
+- In the world of PSGI (and "Plack"), handlers are also called adapters or connectors.
+- There are already adapters for every major web server.
+  - Apache
+  - nginx
+  - IIS
+
+---
+
+class: center, middle
+
+## Somewhat High-level Overview
+
+.basic-flow[
+![Basic Flow](img/basic-flow4.svg)
+]
+
+???
+- Notice that as we've zoomed in, the interaction between the web app and the rest has remained the
+  same: PSGI.
+  - From a developer perspective, the etremely high-level overview is sufficient.
+  - This is one of the benefits of PSGI:
+    - Write your application once and leave deployment details to devops.
+    - The intrastructure details can change (swap in a different web server) and the app will still work.
+    - Maybe you're both developer and system architect, but the separation between developer and
+      devops is valuable.
+  - In fairness, this isn't a new concept.
+    - The way this has been achieved before is using a *web framework*.
+
+---
+
+class: center, middle
+
+## High-level Overview
+
+.basic-flow[
+![Basic Flow](img/basic-flow5.svg)
+]
+
+???
+- A web framework makes it so your app doesn't need to speak HTTP or CGI or even PSGI.
+  - 
+
+---
+
+class: middle
+
+## Word of Caution
+
+> Writing your web application directly using [PSGI/Plack] is certainly possible but not recommended.
+>
+> […]
+> 
+> If you're writing a web application, not a framework, then you're encouraged to use one of the web
+> application frameworks that support PSGI (http://plackperl.org/#frameworks), or see modules like
+> HTTP::Engine to provide higher level Request and Response API on top of PSGI.
+> 
+> -- [Plack::Request Documentation](https://search.cpan.org/~miyagawa/Plack/lib/Plack/Request.pm)
+
+???
+- When you start learning about Plack, you'll realize that it is actually pretty capable.
+  - You may start to think that everything should be developed on that level -- don't do it!
+- For most stuff, you'll still want to use a web framework.
+- Web frameworks often offer more convenient abstractions than raw PSGI.
+
+---
+
+## Why care?
+
+???
+If you're a developer writing a web app, you're probably asking why then you should care about PSGI.
+
+--
+
+- So that you can understand how things work.
+
+???
+- I'll never understood people who don't want to understand things.
+- Knowledge is cool!
+- I recommend you learn and understand as much as you can about the entire request-response cycle of
+  your web app; it will help you troubleshoot when things go wrong or aren't behaving as expected.
+- Be an expert!
+
+--
+- So that you can do DevOps (if you want).
+
+???
+- New skills make you more marketable.
+
+--
+- So that you it when you see PSGI exposed through your web framework.
+
+--
+- You can do cool things with PSGI!
+
+???
+- Even if you do most of your work using your framework, you can do some useful things with PSGI.
+- We will get to some of those cool things, so hang tight.
+
+---
+
+class:  http
+layout: true
+
+## HTTP
+
+---
+
+### Hypertext Transfer Protocol
+
+--
+- Invented by Tim Berners-Lee in 1989, first specified as [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html) in 1991.
+
+--
+- The IETF and W3C took over standards development, resulting in [RFC 1945](https://tools.ietf.org/html/rfc1945) ("HTTP 1.0") in 1996.
+
+--
+- [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.
+
+???
+RFC 2616 was then superceded in 2014 by:
+- [RFC 7230](https://tools.ietf.org/html/rfc7230)
+- [RFC 7231](https://tools.ietf.org/html/rfc7231)
+- [RFC 7232](https://tools.ietf.org/html/rfc7232)
+- [RFC 7233](https://tools.ietf.org/html/rfc7233)
+- [RFC 7234](https://tools.ietf.org/html/rfc7234)
+- [RFC 7235](https://tools.ietf.org/html/rfc7235)
+
+--
+- Oh yeah, and HTTP2 came out in 2015, defined in [RFC 7540](https://tools.ietf.org/html/rfc7540).
+
+---
+
+.col[
+### Request
+
+```http
+GET /ip HTTP/1.1
+User-Agent: Mozilla/5.0 (X11; Linux x86_64)
+Host: foo.acme.tld
+Accept-Language: en-us
+Connection: Keep-Alive
+```
+]
+
+.col[
+### Response
+
+```http
+HTTP/1.1 200 OK
+Date: Thu, 07 Jul 2016 11:56:23 GMT
+Server: nginx
+Content-Length: 30
+Content-Type: text/plain
+Connection: Closed
+
+Your IP address is 127.0.0.1.
+```
+]
+
+---
+
+.col[
+### Request
+
+```http
+*GET /ip HTTP/1.1
+User-Agent: Mozilla/5.0 (X11; Linux x86_64)
+Host: foo.acme.tld
+Accept-Language: en-us
+Connection: Keep-Alive
+```
+]
+
+.col[
+### Response
+
+```http
+HTTP/1.1 200 OK
+Date: Thu, 07 Jul 2016 11:56:23 GMT
+Server: nginx
+Content-Length: 30
+Content-Type: text/plain
+Connection: Closed
+
+Your IP address is 127.0.0.1.
+```
+]
+
+.col[
+1. Method, path, protocol/version
+]
+
+???
+- Methods defined in HTTP/1.1: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT
+- RFC 5789 defined PATCH in March 2010.
+
+---
+
+.col[
+### Request
+
+```http
+GET /ip HTTP/1.1
+*User-Agent: Mozilla/5.0 (X11; Linux x86_64)
+*Host: foo.acme.tld
+*Accept-Language: en-us
+*Connection: Keep-Alive
+```
+]
+
+.col[
+### Response
+
+```http
+HTTP/1.1 200 OK
+Date: Thu, 07 Jul 2016 11:56:23 GMT
+Server: nginx
+Content-Length: 30
+Content-Type: text/plain
+Connection: Closed
+
+Your IP address is 127.0.0.1.
+```
+]
+
+.col[
+1. Method, path, protocol/version
+2. Headers (key-value pairs)
+]
+
+---
+
+.col[
+### Request
+
+```http
+GET /ip HTTP/1.1
+User-Agent: Mozilla/5.0 (X11; Linux x86_64)
+Host: foo.acme.tld
+Accept-Language: en-us
+Connection: Keep-Alive
+```
+]
+
+.col[
+### Response
+
+```http
+HTTP/1.1 200 OK
+Date: Thu, 07 Jul 2016 11:56:23 GMT
+Server: nginx
+Content-Length: 30
+Content-Type: text/plain
+Connection: Closed
+
+Your IP address is 127.0.0.1.
+```
+]
+
+.col[
+1. Method, path, protocol/version
+2. Headers (key-value pairs)
+3. Optional document (or "body")
+]
+
+---
+
+.col[
+### Request
+
+```http
+GET /ip HTTP/1.1
+User-Agent: Mozilla/5.0 (X11; Linux x86_64)
+Host: foo.acme.tld
+Accept-Language: en-us
+Connection: Keep-Alive
+```
+]
+
+.col[
+### Response
+
+```http
+*HTTP/1.1 200 OK
+Date: Thu, 07 Jul 2016 11:56:23 GMT
+Server: nginx
+Content-Length: 30
+Content-Type: text/plain
+Connection: Closed
+
+Your IP address is 127.0.0.1.
+```
+]
+
+.col[
+1. Method, path, protocol/version
+2. Headers (key-value pairs)
+3. Optional document (or "body")
+]
+
+.col[
+1. Protocol/version, status code, reason phrase
+]
+
+---
+
+.col[
+### Request
+
+```http
+GET /ip HTTP/1.1
+User-Agent: Mozilla/5.0 (X11; Linux x86_64)
+Host: foo.acme.tld
+Accept-Language: en-us
+Connection: Keep-Alive
+```
+]
+
+.col[
+### Response
+
+```http
+HTTP/1.1 200 OK
+*Date: Thu, 07 Jul 2016 11:56:23 GMT
+*Server: nginx
+*Content-Length: 30
+*Content-Type: text/plain
+*Connection: Closed
+
+Your IP address is 127.0.0.1.
+```
+]
+
+.col[
+1. Method, path, protocol/version
+2. Headers (key-value pairs)
+3. Optional document (or "body")
+]
+
+.col[
+1. Protocol/version, status code, reason phrase
+2. Headers (key-value pairs)
+]
+
+---
+
+.col[
+### Request
+
+```http
+GET /ip HTTP/1.1
+User-Agent: Mozilla/5.0 (X11; Linux x86_64)
+Host: foo.acme.tld
+Accept-Language: en-us
+Connection: Keep-Alive
+```
+]
+
+.col[
+### Response
+
+```http
+HTTP/1.1 200 OK
+Date: Thu, 07 Jul 2016 11:56:23 GMT
+Server: nginx
+Content-Length: 30
+Content-Type: text/plain
+Connection: Closed
+
+*Your IP address is 127.0.0.1.
+```
+]
+
+.col[
+1. Method, path, protocol/version
+2. Headers (key-value pairs)
+3. Optional document (or "body")
+]
+
+.col[
+1. Protocol/version, status code, reason phrase
+2. Headers (key-value pairs)
+3. Optional document (or "body")
+]
+
+---
+
+layout: false
+
+## Alternatives to PSGI
+
+- CGI
+- mod_perl
+- FastCGI
+- SCGI
+- WSGI
+- JSGI
+- Rack
+- ISAPI
+- many more...
+
+
+???
+- All of these still exist, and actually all of these are still in common use.
+- We're going to take a closer look at three of these.
+
+---
+
+## Alternatives to PSGI
+
+- .highlight[CGI]
+- .highlight[mod_perl]
+- .highlight[FastCGI]
+- SCGI
+- WSGI
+- JSGI
+- Rack
+- ISAPI
+- many more...
+
+---
+
+class:  cgi
+layout: true
+
+.top-right[
+![CGI](img/cgi.gif)
+]
+
+## CGI
+
+---
+
+### Common Gateway Interface
+
+--
+- Created by the NCSA in 1993.
+
+???
+- NCSA = National Center for Supercomputing Applications
+
+--
+- More formally defined in [RFC 3875](https://tools.ietf.org/html/rfc3875) ("CGI Version 1.1") in October 2004.
+
+---
+
+```perl
+my $client_ip = $ENV{'REMOTE_ADDR'};
+
+print "Content-Type: text/plain\n";
+print "Status: 200 OK\n";
+
+print "\n";
+print "Your IP address is ${client_ip}.";
+```
+
+---
+
+```perl
+*my $client_ip = $ENV{'REMOTE_ADDR'};
+
+print "Content-Type: text/plain\n";
+print "Status: 200 OK\n";
+
+print "\n";
+print "Your IP address is ${client_ip}.";
+```
+
+1. Gateway sets information about the request in the environment.
+
+.condensed[
+.col[
+- AUTH_TYPE
+- CONTENT_LENGTH
+- CONTENT_TYPE
+- GATEWAY_INTERFACE
+- PATH_INFO
+- PATH_TRANSLATED
+- QUERY_STRING
+- REMOTE_ADDR
+- REMOTE_HOST
+]
+.col[
+- REMOTE_IDENT
+- REMOTE_USER
+- REQUEST_METHOD
+- SCRIPT_NAME
+- SERVER_NAME
+- SERVER_PORT
+- SERVER_PROTOCOL
+- SERVER_SOFTWARE
+- other "protocol-specific" variables
+]
+]
+
+---
+
+```perl
+my $client_ip = $ENV{'REMOTE_ADDR'};
+
+*print "Content-Type: text/plain\n";
+*print "Status: 200 OK\n";
+
+print "\n";
+print "Your IP address is ${client_ip}.";
+```
+
+1. Gateway sets information about the request in the environment.
+2. Print response headers to `STDOUT`.
+
+.condensed[
+.col[
+- Content-Type
+- Location
+- Status
+- other "protocol-specific" header fields
+]
+]
+
+---
+
+```perl
+my $client_ip = $ENV{'REMOTE_ADDR'};
+
+print "Content-Type: text/plain\n";
+print "Status: 200 OK\n";
+
+*print "\n";
+print "Your IP address is ${client_ip}.";
+```
+
+1. Gateway sets information about the request in the environment.
+2. Print response headers to `STDOUT`.
+3. Print newline.
+
+---
+
+```perl
+my $client_ip = $ENV{'REMOTE_ADDR'};
+
+print "Content-Type: text/plain\n";
+print "Status: 200 OK\n";
+
+print "\n";
+*print "Your IP address is ${client_ip}.";
+```
+
+1. Gateway sets information about the request in the environment.
+2. Print response headers to `STDOUT`.
+3. Print newline.
+4. Print response document (if any).
+
+---
+
+```perl
+my $client_ip = $ENV{'REMOTE_ADDR'};
+
+print "Content-Type: text/plain\n";
+print "Status: 200 OK\n";
+
+print "\n";
+print "Your IP address is ${client_ip}.";
+```
+
+1. Gateway sets information about the request in the environment.
+2. Print response headers to `STDOUT`.
+3. Print newline.
+4. Print response document (if any).
+5. Read request document from `STDIN` (if any).
+
+???
+- CGI.pm helps cut down boilerplate by helping parse things like `QUERY_STRING` and `HTTP_COOKIE`,
+  producing correctly-formatted headers, and even producing HTML.
+- CGI.pm was deprecated in perl 5.20 and remove from core in perl 5.22.
+
+TODO make a slide for this
+Good:
+- Conceptually simple.
+- Only requires the use of the most basic and primitive program constructs (stdin, stdout, env).
+
+Bad:
+- Details can get complicated.
+- Although the de facto standard for years, modern web servers are choosing to not support it
+  directly any longer.
+- There is too much overhead in forking and execing.
+
+---
+
+class:  fastcgi
+layout: true
+
+.top-right[
+![FastCGI](img/fastcgi.png)
+]
+
+## FastCGI
+
+---
+
+### a low-overhead variation on CGI
+
+--
+- Binary protocol with support for pipelining and multiplexing.
+
+--
+- Open Market wrote the [specification](http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html) on 29 Apr 1996.
+
+???
+- Open Market was an ecommerce startup based in Massachusetts.
+  - Developed one of the first HTTP servers.
+
+---
+
+```perl
+use FCGI;
+
+my $request = FCGI::Request();
+
+while (0 <= $request->Accept()) {
+    my $client_ip = $ENV{'REMOTE_ADDR'};
+
+    print "Content-Type: text/html\n\n";
+    print "Your IP address is ${client_ip}.";
+}
+```
+
+---
+
+```perl
+*use FCGI;
+*
+*my $request = FCGI::Request();
+
+while (0 <= $request->Accept()) {
+    my $client_ip = $ENV{'REMOTE_ADDR'};
+
+    print "Content-Type: text/html\n\n";
+    print "Your IP address is ${client_ip}.";
+}
+```
+
+1. Use `FCGI` and instantiate an object.
+
+---
+
+```perl
+use FCGI;
+
+my $request = FCGI::Request();
+
+*while (0 <= $request->Accept()) {
+    my $client_ip = $ENV{'REMOTE_ADDR'};
+
+    print "Content-Type: text/html\n\n";
+    print "Your IP address is ${client_ip}.";
+}
+```
+
+1. Use `FCGI` and instantiate an object.
+2. Loop on `Accept()` which blocks until the next request is received.
+
+---
+
+```perl
+use FCGI;
+
+my $request = FCGI::Request();
+
+while (0 <= $request->Accept()) {
+*   my $client_ip = $ENV{'REMOTE_ADDR'};
+*
+*   print "Content-Type: text/html\n\n";
+*   print "Your IP address is ${client_ip}.";
+}
+```
+
+1. Use `FCGI` and instantiate an object.
+2. Loop on `Accept()` which blocks until the next request is received.
+3. Otherwise appears similar to a CGI program.
+
+---
+
+```perl
+use FCGI;
+
+my $request = FCGI::Request();
+
+while (0 <= $request->Accept()) {
+    my $client_ip = $ENV{'REMOTE_ADDR'};
+
+    print "Content-Type: text/html\n\n";
+    print "Your IP address is ${client_ip}.";
+}
+```
+
+1. Use `FCGI` and instantiate an object.
+2. Loop on `Accept()` which blocks until the next request is received.
+3. Otherwise appears similar to a CGI program.
+
+* IPC actually happens over a socket!
+
+???
+- Can be run unmodified as a CGI script by detecting that stdin is not a socket.
+- Can read from stdin and write to stdout via the miracle of tied filehandles.
+
+---
+
+class:  mod_perl
+layout: true
+
+.top-right[
+![mod_perl](img/mod_perl.gif)
+]
+
+## mod_perl
+
+---
+
+--
+- First released on March 25, 1996.
+
+???
+- Unlike the interfaces we have examined so far, mod_perl is code.
+
+--
+- Became an Apache Software Foundation project at ApacheCon 1999 in Orlando.
+
+--
+
+```perl
+package GetIP;
+
+use Apache::RequestRec ();
+use Apache::Connection ();
+use Apache::Const -compile => qw(OK);
+
+sub handler {
+    my $r = shift;
+    my $client_ip = $r->connection->remote_addr;
+
+    $r->content_type('text/plain');
+    $r->print("Your IP address is ${client_ip}.");
+    return Apache::Const::OK;
+}
+
+1;
+```
+
+???
+- There's a separate mod_perl for nginx.
+
+Good:
+- Can run CGI programs as-is.
+
+Bad:
+- Can tie you to specific web servers.
+- Code runs in the same process as the HTTP server -- kinda scary.
+- Using Apache's API feels heavy.
+
+---
+
+class:  psgi
+layout: true
+
+## PSGI
+
+---
+
+```perl
+my $app = sub {
+    my $env = shift;
+    my $client_id = $env->{'REMOTE_ADDR'};
+
+    return [
+        '200',
+        [ 'Content-Type' => 'text/plain' ],
+        [ "Your IP address is ${client_id}." ],     # or IO::Handle-like object
+    ];
+};
+```
+
+---
+
+```perl
+my $app = sub {
+*   my $env = shift;
+*   my $client_id = $env->{'REMOTE_ADDR'};
+
+    return [
+        '200',
+        [ 'Content-Type' => 'text/plain' ],
+        [ "Your IP address is ${client_id}." ],     # or IO::Handle-like object
+    ];
+};
+```
+
+.col[
+### Request
+
+1. Hashref of request information.
+]
+
+---
+
+```perl
+my $app = sub {
+    my $env = shift;
+    my $client_id = $env->{'REMOTE_ADDR'};
+
+    return [
+*       '200',
+        [ 'Content-Type' => 'text/plain' ],
+        [ "Your IP address is ${client_id}." ],     # or IO::Handle-like object
+    ];
+};
+```
+
+.col[
+### Request
+
+1. Hashref of request information.
+]
+
+.col[
+### Response
+
+1. HTTP status code.
+]
+
+---
+
+```perl
+my $app = sub {
+    my $env = shift;
+    my $client_id = $env->{'REMOTE_ADDR'};
+
+    return [
+        '200',
+*       [ 'Content-Type' => 'text/plain' ],
+        [ "Your IP address is ${client_id}." ],     # or IO::Handle-like object
+    ];
+};
+```
+
+.col[
+### Request
+
+1. Hashref of request information.
+]
+
+.col[
+### Response
+
+1. HTTP status code.
+2. Arrayref of response headers.
+]
+
+???
+- Why not a hashref?
+  - To support multiple headers (e.g. Set-Cookie)
+  - It more closely resembles how the WSGI folks did it (i.e. list of tuples).
+
+---
+
+```perl
+my $app = sub {
+    my $env = shift;
+    my $client_id = $env->{'REMOTE_ADDR'};
+
+    return [
+        '200',
+        [ 'Content-Type' => 'text/plain' ],
+*       [ "Your IP address is ${client_id}." ],     # or IO::Handle-like object
+    ];
+};
+```
+
+.col[
+### Request
+
+1. Hashref of request information.
+]
+
+.col[
+### Response
+
+1. HTTP status code.
+2. Arrayref of response headers.
+3. Response document.
+]
+
+???
+- Body may be a list of chunks that are concatenated together or a handle to read from.
+
+---
+
+layout: false
+
+## Benefits of PSGI
+
+- Everything is a data structure (almost).
+
+???
+- Makes it easier to write tests because mocking either the app or server is clear.
+- Don't necessarily need to parse a bytestream to check stuff.
+
+--
+- No global data or shared IO handles.
+
+???
+- This lets you service multiple requests asynchronously in the same process/thread.
+
+--
+- Takes deployment details out of web frameworks.
+
+???
+- Web frameworks only need to target PSGI.
+- No need to worry about the boring stuff; they can focus on the abstractions that make them unique
+  and useful.
+
+--
+- End-users of your app have many deployment options for free.
+
+---
+
+## Web Frameworks
+
+- [Catalyst](http://www.catalystframework.org/)
+- [Mojolicious](http://mojolicious.org/)
+- [Dancer](http://perldancer.org/)
+- [CGI::Application](http://cgi-app.org/)
+- [CGI::Ex](https://github.com/chazmcgarvey/CGI-Ex/tree/psgi-2)
+- [Web::Simple](https://metacpan.org/pod/Web::Simple)
+- [Amon2](https://metacpan.org/pod/Amon2)
+- [Poet](https://metacpan.org/pod/Poet)
+- [Kelp](https://metacpan.org/pod/Kelp)
+- [Raisin](https://metacpan.org/pod/Raisin)
+- many more...
+
+---
+
+## Plack
+
+- Provides tools for building, running, and testing PSGI apps.
+
+--
+  - [Plack::Handler](https://metacpan.org/pod/Plack::Handler)
+
+???
+- Connects PSGI apps and web servers.
+  - Takes a request from the server,
+  - converts it to the PSGI-specified environment,
+  - runs your app,
+  - converts the response back to a format the server understands.
+
+--
+  - [Plack::Loader](https://metacpan.org/pod/Plack::Loader)
+
+???
+- Picks an appropriate Plack::Handler (based on ENV, loaded modules, or arguments) and loads it.
+- Can also do stuff like restart the loader when files change.
+
+--
+  - [Plack::Runner](https://metacpan.org/pod/Plack::Runner), [plackup](https://metacpan.org/pod/plackup)
+
+???
+- Run PSGI apps from the command-line.
+
+--
+  - [Plack::Middleware](https://metacpan.org/pod/Plack::Middleware)
+
+???
+- Create subroutines that run between the handler and your app.
+- Can alter the request your app receives and modify the response your app returns.
+
+--
+  - [Plack::Request](https://metacpan.org/pod/Plack::Request), [Plack::Response](https://metacpan.org/pod/Plack::Response)
+
+???
+- Request and response wrappers can help simplify writing middleware.
+
+--
+  - [Plack::Builder](https://metacpan.org/pod/Plack::Builder)
+
+???
+- Provides DSL for composing apps and middleware.
+
+--
+  - [Plack::Test](https://metacpan.org/pod/Plack::Test), [Plack::Test::Suite](https://metacpan.org/pod/Plack::Test::Suite)
+
+???
+- Use Plack::Test for testing apps.
+- Plack::Test::Suite is a series of tests for testing handlers.
+
+--
+  - [Plack::Util](https://metacpan.org/pod/Plack::Util)
+
+???
+- Provides random useful stuff for handler and middleware developers.
+- Stuff like determing the length of a document or getting PSGI response headers from the arrayref.
+
+---
+
+class: plackup
+
+## plackup
+
+- Run PSGI apps from the command-line.
+
+```sh
+# read your app from app.psgi file
+plackup
+# choose .psgi file from ARGV[0] (or with -a option)
+plackup hello.psgi
+# switch server implementation with --server (or -s)
+plackup --server HTTP::Server::Simple --port 9090 --host 127.0.0.1 test.psgi
+# use UNIX socket to run FCGI daemon
+plackup -s FCGI --listen /tmp/fcgi.sock myapp.psgi
+# launch FCGI external server on port 9090
+plackup -s FCGI --port 9090
+```
+
+---
+
+class:  app-psgi
+layout: true
+
+## app.psgi
+
+---
+
+```perl
+#!/usr/bin/env perl
+
+my $app = sub {
+    my $env = shift;
+    my $client_id = $env->{'REMOTE_ADDR'};
+
+    return [
+        '200',
+        [ 'Content-Type' => 'text/plain' ],
+        [ "Your IP address is ${client_id}." ],
+    ];
+};
+```
+
+---
+
+```perl
+#!/usr/bin/env perl
+
+*use Plack::Builder;
+
+my $app = sub {
+    my $env = shift;
+    my $client_id = $env->{'REMOTE_ADDR'};
+
+    return [
+        '200',
+        [ 'Content-Type' => 'text/plain' ],
+        [ "Your IP address is ${client_id}." ],
+    ];
+};
+
+*builder {
+*   enable 'Runtime';
+*   mount '/' => $app;
+*};
+```
+
+???
+- The `Runtime` middleware adds an `X-Runtime` header to the response with the number of seconds it
+  took to process the request.
+
+---
+
+class:  env
+layout: true
+
+## Plack `$env`
+
+---
+
+.condensed[
+.col[
+- HTTP_ACCEPT
+- HTTP_ACCEPT_ENCODING
+- HTTP_ACCEPT_LANGUAGE
+- HTTP_CACHE_CONTROL
+- HTTP_CONNECTION
+- HTTP_DNT
+- HTTP_HOST
+- HTTP_USER_AGENT
+- PATH_INFO
+- QUERY_STRING 
+- REMOTE_ADDR
+- REMOTE_PORT
+- REQUEST_METHOD
+- REQUEST_URI
+- SCRIPT_NAME
+]
+.col[
+- SERVER_NAME
+- SERVER_PORT
+- SERVER_PROTOCOL
+- psgi.errors
+- psgi.input
+- psgi.multiprocess    
+- psgi.multithread     
+- psgi.nonblocking     
+- psgi.run_once        
+- psgi.streaming
+- psgi.url_scheme
+- psgi.version
+- psgix.harakiri
+- psgix.input.buffered
+- psgix.io
+]
+]
+
+---
+
+.condensed[
+.col[
+- .highlight[HTTP_ACCEPT]
+- .highlight[HTTP_ACCEPT_ENCODING]
+- .highlight[HTTP_ACCEPT_LANGUAGE]
+- .highlight[HTTP_CACHE_CONTROL]
+- .highlight[HTTP_CONNECTION]
+- .highlight[HTTP_DNT]
+- .highlight[HTTP_HOST]
+- .highlight[HTTP_USER_AGENT]
+- .highlight[PATH_INFO]
+- .highlight[QUERY_STRING ]
+- .highlight[REMOTE_ADDR]
+- .highlight[REMOTE_PORT]
+- .highlight[REQUEST_METHOD]
+- .highlight[REQUEST_URI]
+- .highlight[SCRIPT_NAME]
+]
+.col[
+- .highlight[SERVER_NAME]
+- .highlight[SERVER_PORT]
+- .highlight[SERVER_PROTOCOL]
+- psgi.errors
+- psgi.input
+- psgi.multiprocess    
+- psgi.multithread     
+- psgi.nonblocking     
+- psgi.run_once        
+- psgi.streaming
+- psgi.url_scheme
+- psgi.version
+- psgix.harakiri
+- psgix.input.buffered
+- psgix.io
+]
+]
+
+---
+
+.condensed[
+.col[
+- HTTP_ACCEPT
+- HTTP_ACCEPT_ENCODING
+- HTTP_ACCEPT_LANGUAGE
+- HTTP_CACHE_CONTROL
+- HTTP_CONNECTION
+- HTTP_DNT
+- HTTP_HOST
+- HTTP_USER_AGENT
+- PATH_INFO
+- QUERY_STRING 
+- REMOTE_ADDR
+- REMOTE_PORT
+- REQUEST_METHOD
+- REQUEST_URI
+- SCRIPT_NAME
+]
+.col[
+- SERVER_NAME
+- SERVER_PORT
+- SERVER_PROTOCOL
+- .highlight[psgi.errors]
+- .highlight[psgi.input]
+- .highlight[psgi.multiprocess         ]
+- .highlight[psgi.multithread  ]
+- .highlight[psgi.nonblocking  ]
+- .highlight[psgi.run_once     ]
+- .highlight[psgi.streaming]
+- .highlight[psgi.url_scheme]
+- .highlight[psgi.version]
+- .highlight[psgix.harakiri]
+- .highlight[psgix.input.buffered]
+- .highlight[psgix.io]
+]
+]
+
+---
+
+layout: false
+
+## Plack Handlers
+
+- Can be found on the CPAN in the `Plack::Handler::` namespace.
+  - [Apache1](https://metacpan.org/pod/Plack::Handler::Apache1), [Apache2](https://metacpan.org/pod/Plack::Handler::Apache2)
+  - [CGI](https://metacpan.org/pod/Plack::Handler::CGI)
+  - [FCGI](https://metacpan.org/pod/Plack::Handler::FCGI)
+  - [HTTP::Server::PSGI](https://metacpan.org/pod/Plack::Handler::HTTP::Server::PSGI)
+  - [SCGI](https://metacpan.org/pod/Plack::Handler::SCGI)
+  - [Starman](https://metacpan.org/pod/Plack::Handler::Starman)
+  - [Twiggy](https://metacpan.org/pod/Plack::Handler::Twiggy)
+  - [AnyEvent::HTTPD](https://metacpan.org/pod/Plack::Handler::AnyEvent::HTTPD)
+  - [Thrall](https://metacpan.org/pod/Plack::Handler::Thrall)
+  - many more...
+
+---
+
+class:  middleware
+layout: true
+
+## Plack Middleware
+
+---
+
+### [`ReverseProxy`](https://metacpan.org/pod/Plack::Middleware::ReverseProxy)
+
+```perl
+enable 'ReverseProxy';
+```
+
+- Fixes `REMOTE_ADDR`, `HTTP_HOST`, `SERVER_PORT`, and `psgi.url_scheme` in the environment.
+
+---
+
+### [`LogDispatch`](https://metacpan.org/pod/Plack::Middleware::LogDispatch)
+
+```perl
+use Log::Dispatch;
+
+my $logger = Log::Dispatch->new(
+    outputs => [
+        [
+            'Syslog',
+            min_level   => 'debug',
+            ident       => 'myapp',
+        ],
+    ],
+);
+
+enable 'LogDispatch', logger => $logger;
+```
+
+---
+
+### [`XSRFBlock`](https://metacpan.org/pod/Plack::Middleware::XSRFBlock)
+
+```perl
+enable 'XSRFBlock';
+```
+
+- Blocking cross-site request forgery couldn't be easier.
+
+---
+
+### [`RedirectSSL`](https://metacpan.org/pod/Plack::Middleware::RedirectSSL)
+
+```perl
+enable 'RedirectSSL';
+```
+
+- Redirects from http to https (or backwards, if configured).
+- Can also set HSTS header with configurable `max-age`. 
+
+---
+
+layout: false
+
+.top-right[
+![CPAN](img/cpan.png)
+]
+
+## Plack modules on the CPAN in July 2016
+
+**10** `Plack-Handler-*` distributions
+
+**55** `Plack-App-*` distributions
+
+**253** `Plack-Middleware-*` distributions
+
+---
+
+## Parting Thoughts
+
+- You should write middleware!
+
+???
+- The concept and implementation of middleware is cool.
+- You should consider writing parts of your app as middleware so that functionality is available
+  under different web frameworks.
+- Stuff that makes sense as middleware:
+  - Auth mechanisms
+  - Logging
+  - Error handling
+  - Sessions
+
+--
+- PSGI also specifies a way to delay or stream responses to the server.
+
+???
+- It's kind of complicated, but you can read the spec to learn more.
+- Read the source code of various apps and middlewares to see how it works in practice.
+
+---
+
+class:  center, middle
+name:   conclusion
+
+## Conclusion:
+
+### Understand PSGI & Plack, and use them!
+
+---
+
+class:  center, middle
+layout: false
+name:   questions
+
+## Questions?
+
+---
+
+class:  center, middle
+name:   last
+
+.col.sep[
+## Thank you
+
+Email me: Charles McGarvey
+<cmcgarvey@bluehost.com>
+<chazmcgarvey@brokenzipper.com>
+
+.talkqr.center[
+Leave me feedback, if you want:
+
+![Page on Joind.in](img/talkqr.svg)
+
+<https://joind.in/talk/6e4d2>
+]
+]
+
+.col[
+## Credits
+
+.left[
+- Thank you [Tatsuhiko Miyagawa](http://weblog.bulknews.net/) and other contributors for creating PSGI and Plack.
+]
+]
+
+</textarea><script src="https://gnab.github.io/remark/downloads/remark-latest.min.js"></script><script>remark.create({countIncrementalSlides: true, highlightLanguage: 'perl', highlightLines: true, ratio: '16:9', /*slideNumberFormat: '',*/ navigation: {scroll: false, touch: false, click: false}})</script></body></html>
+<!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->
This page took 0.059724 seconds and 4 git commands to generate.