--- /dev/null
+
+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
+
--- /dev/null
+
+# 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
+
--- /dev/null
+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.
--- /dev/null
+@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;
+}
+
--- /dev/null
+
+.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;
+}
+
--- /dev/null
+
+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]
+}
+
--- /dev/null
+
+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"]
+}
+
--- /dev/null
+
+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"]
+}
+
--- /dev/null
+
+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"
+ }
+}
+
--- /dev/null
+
+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"
+ }
+}
+
--- /dev/null
+<?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>
--- /dev/null
+
+- 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?
+
--- /dev/null
+<!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: -->