<!DOCTYPE html>

<meta charset="utf-8">
<title>Introduction to FastCGI</title>

<body>
<section class="title">
 <h1 class="title">Introduction to FastCGI</h1>
 <h2 class="subtitle">Group 10</h1>
 <footer>
   <span class="author">Andrew Fuller</span>
 </footer>
</section>

<section class="slide level3">
<h3>In the beginning</h3>
<p>One app, one web server</p>
<p>apache.c:</p>
<pre><code>    if (page == &quot;/loginform.html&quot;) {
       sendfile(&quot;templates/loginform.html&quot;);
   } else if (page == &quot;/profile.html&quot;) {
       printf(&quot;Status: 200 OK\r\n&quot;);
       printf(&quot;Content-type: text/html\r\n&quot;);
       printf(&quot;\r\n&quot;);
       printf(&quot;&lt;h1&gt;Hello %s&lt;/h1&gt;&quot;, user);</code></pre>
</section>
<section class="slide level3">
<h3>CGI</h3>
<p>Generate responses with an external process</p>
<p>Web server does:</p>
<pre><code>$ export REQUEST_URI=&quot;/loginform.asp&quot;
$ ./mycgi.exe
Status: 200 OK
Content-Type: text/html

&lt;h1&gt;Please login&lt;/h1&gt;
&lt;form action=&quot;/dologin&quot;&gt;
..</code></pre>
</section>
<section class="slide level3">
<h3>CGI</h3>
<p>Requests are passed through envp and stdin</p>
<pre><code>int main(int argc, char **argv, char **envp){
   int len = atoi(getenv(&quot;CONTENT_LENGTH&quot;));
   char *req = malloc(len);

   read(STDIN_FILENO, len, req);

{&#39;u&#39;,&#39;s&#39;,&#39;e&#39;,&#39;r&#39;,&#39;=&#39;,&#39;m&#39;,&#39;i&#39;,&#39;k&#39;,&#39;e&#39;,&#39;6&#39;,&#39;9&#39;,&#39;&amp;&#39;
&#39;p&#39;,&#39;a&#39;,&#39;s&#39;,&#39;s&#39;,&#39;=&#39;,&#39;t&#39;,&#39;h&#39;,&#39;r&#39;,&#39;i&#39;,&#39;l&#39;,&#39;l&#39;,&#39;7&#39;}</code></pre>
</section>
<section class="slide level3">
<h3>CGI</h3>
<p>HTTP GET query string</p>
<pre><code>char *query = getenv(&quot;QUERY_STR&quot;);</code></pre>
<p>Which page was requested?</p>
<pre><code>char *uri = getenv(&quot;REQUEST_URI&quot;);</code></pre>
<p>HTTP POST vars? File uploads?</p>
<pre><code>fread(buf, count, 1, stdin);</code></pre>
</section>
<section class="slide level3">
<h3>CGI</h3>
<p>Decouples web server from web app</p>
<p>No more embedded:</p>
<ul>
<li>mod_php</li>
<li>mod_python</li>
<li>mod_ruby</li>
<li>mod_rack / phusion</li>
</ul>
</section>
<section class="slide level3">
<h3>CGI</h3>
<p>Spawn a new process for each request?</p>
<pre><code>$ export REQUEST_URI=&quot;/loginform.asp&quot;
$ ./mycgi.exe
$ export REQUEST_URI=&quot;/login.asp&quot;
$ ./mycgi.exe
$ export REQUEST_URI=&quot;/profile.asp&quot;
$ ./mycgi.exe
$ export REQUEST_URI=&quot;/deleteprofile.asp&quot;
$ ./mycgi.exe
$ export REQUEST_URI=&quot;/redirect.asp&quot;
$ ./mycgi.exe</code></pre>
</section>
<section class="slide level3">
<h3>FastCGI</h3>
<p>Advantages:</p>
<ul>
<li>Fast</li>
</ul>
</section>
<section class="slide level3">
<h3>FastCGI</h3>
<p>Fast:</p>
<pre><code>$ ./myfcgi.exe
   Listening on 127.0.0.1:1337</code></pre>
</section>
<section class="slide level3">
<h3>FastCGI</h3>
<p>For each request, web server:</p>
<ul>
<li>Connects to fastcgi app</li>
<li>Sends the request variables</li>
<li>Reads the reply, forwards it to client</li>
</ul>
</section>
<section class="slide level3">
<h3>FastCGI</h3>
<pre><code>127.0.0.1:1337</code></pre>
<p>or</p>
<pre><code>/tmp/myapp.sock</code></pre>
<p>or</p>
<pre><code>192.168.1.{90,91,92}:1337</code></pre>
</section>
<section class="slide level3">
<h3>Terminal App</h3>
<p><a href="http://cmpt470.csil.sfu.ca:9010/terminal.html">Terminal</a></p>
</section>
<section class="slide level3">
<h3>Problems with FastCGI</h3>
<p><img src="photos/jerrychenme.png" /></p>
</section>
<section class="slide level3">
<h3>Alternatives to FastCGI</h3>
<p>SCGI - Simple Common Gateway Interface</p>
<p>WSGI - Web Server Gateway Interface</p>
<p>PSGI - Perl Server Gateway Interface</p>
<p>ISAPI - Internet Server Application Programming Interface</p>
</section>
<section class="slide level3">
<h3>SCGI</h3>
<p>Simple</p>
</section>
<section class="slide level3">
<h3>SCGI</h3>
<p>Simple protocol</p>
<p>New (2006)</p>
<p>Not widely used</p>
</section>
<section class="slide level3">
<h3>WSGI</h3>
<p>Web Server Gateway Interface</p>
<p>Made for Python</p>
<p>They don’t care about everyone else</p>
</section>
<section class="slide level3">
<h3>PSGI</h3>
<p>Perl Server Gateway Interface</p>
<p>Made for Perl</p>
<p>They don’t care about everyone else</p>
</section>
<section class="slide level3">
<h3>ISAPI</h3>
<p>Internet Server API</p>
<p>Made by Microsoft</p>
<p>They don’t care about everyone else</p>
<p>Uses DLLs and OS services, much tighter coupling</p>
</section>
<section class="slide level3">
<h3>FastCGI Support</h3>
<p>Every major webserver can forward requests over FastCGI</p>
<p>Every major language can respond to FastCGI requests</p>
</section>
<section class="slide level3">
<h3>Q&amp;A</h3>
<p>Any questions?</p>
</section>

<!-- Your Style -->
<!-- Define the style of your presentation -->

<style>
 html, .view body { background-color: black; counter-reset: slideidx; }
 body, .view section {
   background-color: white; /*border-radius: 12px;*/
   font-family: arial, serif;
 }

 section, .view head > title {
     font-size: 2rem;
 }

 .view section:after {
   counter-increment: slideidx;
   content: counter(slideidx, decimal-leading-zero);
   position: absolute; bottom: -80px; right: 100px;
   color: white;
 }

 .view head > title {
   color: white;
   text-align: center;
   margin: 1em 0 1em 0;
 }

 h1 {
   margin: 120px 0 30px 0;
   text-align: center;
   font-size: 5rem;
 }

 h2 {
   text-align: center;
 }

 section > h3 {
   margin: 50px 50px 40px 50px;
   border-bottom: 0.1px solid;
 }

 pre {
   font-size: 1.25rem;
   margin: 0 75px 0 75px;
   padding: 10px;
/*TODO(abf)    border: 1px solid; */
/*    font-weight: bold; */
/*    overflow: hidden; */
   background-color: #F7F7F7;
   width:80%
 }

 code {
  font-family: courier;
 }

 ul, ol {
     margin: 40px 100px 0 100px;
 }

 li > ul, ol {
     margin: 0 0 15px 50px;
     list-style-image: none; /* in case parent list has some */
 }

 mark.next:not([active]) {
   visibility: visible; /* override the default behavior where next is hidden */
   background-color: inherit; /* and disable highlighting instead */
 }

 footer p {
   font-size: 1rem;
   margin: 0;
 }

 p {
   margin: 45px 75px 5px 75px;
/*TODO(abf)    font-size: 3rem; */
   font-size: 2.2rem;
 }

 table {
   margin: auto;
   font-size:2.5rem;
   text-align: center;
 }

 blockquote {
   height: 100%;
   background-color: black;
   color: white;
   font-size: 3.75rem;
   padding: 50px;
 }
 blockquote:before {
   content: open-quote;
 }
 blockquote:after {
   content: close-quote;
 }

 /* Figures are displayed full-page, with the caption
    on top of the image/video */
 figure {
   background-color: black;
   width: 100%;
   height: 100%;
 }
 figure > * {
   position: absolute;
 }
 figure > img, figure > video {
   width: 100%; height: 100%;
 }
 figcaption {
   margin: 70px;
 /*TODO(abf)  font-size: 3rem; */
 }

 header {
   background-color: #F3F4F8;
   border-bottom: 1px solid #CCC;
 }

 footer {
   background-color: #F3F4F8;
   border-top: 1px solid #CCC;
   padding-bottom: 4px; /* remember progress bar */
   text-align: right;
   position: absolute;
   bottom: 0;
   width: 100%;
 }

 section footer {
   padding: 10px;
 }

 /* Transition effect */
 /* Feel free to change the transition effect for original
    animations. See here:
    https://developer.mozilla.org/en/CSS/CSS_transitions
    How to use CSS3 Transitions: */
 section {
/*    transition: left 400ms linear 0s; */
 }
 .view section {
   transition: none;
 }

 .view section[aria-selected] {
   border: 5px red solid;
 }

@media screen {
 /* Before */
 section { left: -150%; }
 /* Now */
 section[aria-selected] { left: 0; }
 /* After */
 section[aria-selected] ~ section { left: +150%; }
}

 /* The progressbar, at the bottom of the slides, show the global
    progress of the presentation. */
 #progress-bar {
   height: 4px;
   background: #AAA;
   display: none;
 }
</style>

<!-- {{{{ dzslides core
#
#
#     __  __  __       .  __   ___  __
#    |  \  / /__` |    | |  \ |__  /__`
#    |__/ /_ .__/ |___ | |__/ |___ .__/ core :€
#
#
# The following block of code is not supposed to be edited.
# But if you want to change the behavior of these slides,
# feel free to hack it!
#
-->

<div id="progress-bar"></div>

<!-- Default Style -->
<style>
 * { margin: 0; padding: 0; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
 [role="note"] { display: none; }
 body {
   width: 800px; height: 600px;
   margin-left: -400px; margin-top: -300px;
   position: absolute; top: 50%; left: 50%;
   overflow: hidden;
   display: none;
 }
 .view body {
   position: static;
   margin: 0; padding: 0;
   width: 100%; height: 100%;
   display: inline-block;
   overflow: visible; overflow-x: hidden;
   /* undo Dz.onresize */
   transform: none !important;
   -moz-transform: none !important;
   -webkit-transform: none !important;
   -o-transform: none !important;
   -ms-transform: none !important;
 }
 .view head, .view head > title { display: block }
 section {
   position: absolute;
   pointer-events: none;
   width: 100%; height: 100%;
 }
 .view section {
   pointer-events: auto;
   position: static;
   width: 800px; height: 600px;
   margin: -150px -200px;
   float: left;

   transform: scale(.4);
   -moz-transform: scale(.4);
   -webkit-transform: scale(.4);
   -o-transform: scale(.4);
   -ms-transform: scale(.4);
 }
 .view section > * { pointer-events: none; }
 section[aria-selected] { pointer-events: auto; }
 html { overflow: hidden; }
 html.view { overflow: visible; }
 body.loaded { display: block; }
 .incremental {visibility: hidden; }
 .incremental[active] {visibility: visible; }
 #progress-bar{
   bottom: 0;
   position: absolute;
   -moz-transition: width 400ms linear 0s;
   -webkit-transition: width 400ms linear 0s;
   -ms-transition: width 400ms linear 0s;
   transition: width 400ms linear 0s;
 }
 .view #progress-bar {
   display: none;
 }
</style>

<script>
 var Dz = {
   remoteWindows: [],
   idx: -1,
   step: 0,
   html: null,
   slides: null,
   progressBar : null,
   params: {
     autoplay: "1"
   }
 };

 Dz.init = function() {
   document.body.className = "loaded";
   this.slides = Array.prototype.slice.call($$("body > section"));
   this.progressBar = $("#progress-bar");
   this.html = document.body.parentNode;
   this.setupParams();
   this.onhashchange();
   this.setupTouchEvents();
   this.onresize();
   this.setupView();
 }

 Dz.setupParams = function() {
   var p = window.location.search.substr(1).split('&');
   p.forEach(function(e, i, a) {
     var keyVal = e.split('=');
     Dz.params[keyVal[0]] = decodeURIComponent(keyVal[1]);
   });
 // Specific params handling
   if (!+this.params.autoplay)
     $$.forEach($$("video"), function(v){ v.controls = true });
 }

 Dz.onkeydown = function(aEvent) {
   // Don't intercept keyboard shortcuts
   if (aEvent.altKey
     || aEvent.ctrlKey
     || aEvent.metaKey
     || aEvent.shiftKey) {
     return;
   }
   if ( aEvent.keyCode == 37 // left arrow
     || aEvent.keyCode == 38 // up arrow
     || aEvent.keyCode == 33 // page up
   ) {
     aEvent.preventDefault();
     this.back();
   }
   if ( aEvent.keyCode == 39 // right arrow
     || aEvent.keyCode == 40 // down arrow
     || aEvent.keyCode == 34 // page down
   ) {
     aEvent.preventDefault();
     this.forward();
   }
   if (aEvent.keyCode == 35) { // end
     aEvent.preventDefault();
     this.goEnd();
   }
   if (aEvent.keyCode == 36) { // home
     aEvent.preventDefault();
     this.goStart();
   }
   if (aEvent.keyCode == 32) { // space
     aEvent.preventDefault();
     this.toggleContent();
   }
   if (aEvent.keyCode == 70) { // f
     aEvent.preventDefault();
     this.goFullscreen();
   }
   if (aEvent.keyCode == 79) { // o
     aEvent.preventDefault();
     this.toggleView();
   }
 }

 /* Touch Events */

 Dz.setupTouchEvents = function() {
   var orgX, newX;
   var tracking = false;

   var db = document.body;
   db.addEventListener("touchstart", start.bind(this), false);
   db.addEventListener("touchmove", move.bind(this), false);

   function start(aEvent) {
     aEvent.preventDefault();
     tracking = true;
     orgX = aEvent.changedTouches[0].pageX;
   }

   function move(aEvent) {
     if (!tracking) return;
     newX = aEvent.changedTouches[0].pageX;
     if (orgX - newX > 100) {
       tracking = false;
       this.forward();
     } else {
       if (orgX - newX < -100) {
         tracking = false;
         this.back();
       }
     }
   }
 }

 Dz.setupView = function() {
   document.body.addEventListener("click", function ( e ) {
     if (!Dz.html.classList.contains("view")) return;
     if (!e.target || e.target.nodeName != "SECTION") return;

     Dz.html.classList.remove("view");
     Dz.setCursor(Dz.slides.indexOf(e.target) + 1);
   }, false);
 }

 /* Adapt the size of the slides to the window */

 Dz.onresize = function() {
   var db = document.body;
   var sx = db.clientWidth / window.innerWidth;
   var sy = db.clientHeight / window.innerHeight;
   var transform = "scale(" + (1/Math.max(sx, sy)) + ")";

   db.style.MozTransform = transform;
   db.style.WebkitTransform = transform;
   db.style.OTransform = transform;
   db.style.msTransform = transform;
   db.style.transform = transform;
 }


 Dz.getNotes = function(aIdx) {
   var s = $("section:nth-of-type(" + aIdx + ")");
   var d = s.$("[role='note']");
   return d ? d.innerHTML : "";
 }

 Dz.onmessage = function(aEvent) {
   var argv = aEvent.data.split(" "), argc = argv.length;
   argv.forEach(function(e, i, a) { a[i] = decodeURIComponent(e) });
   var win = aEvent.source;
   if (argv[0] === "REGISTER" && argc === 1) {
     this.remoteWindows.push(win);
     this.postMsg(win, "REGISTERED", document.title, this.slides.length);
     this.postMsg(win, "CURSOR", this.idx + "." + this.step);
     return;
   }
   if (argv[0] === "BACK" && argc === 1)
     this.back();
   if (argv[0] === "FORWARD" && argc === 1)
     this.forward();
   if (argv[0] === "START" && argc === 1)
     this.goStart();
   if (argv[0] === "END" && argc === 1)
     this.goEnd();
   if (argv[0] === "TOGGLE_CONTENT" && argc === 1)
     this.toggleContent();
   if (argv[0] === "SET_CURSOR" && argc === 2)
     window.location.hash = "#" + argv[1];
   if (argv[0] === "GET_CURSOR" && argc === 1)
     this.postMsg(win, "CURSOR", this.idx + "." + this.step);
   if (argv[0] === "GET_NOTES" && argc === 1)
     this.postMsg(win, "NOTES", this.getNotes(this.idx));
 }

 Dz.toggleContent = function() {
   // If a Video is present in this new slide, play it.
   // If a Video is present in the previous slide, stop it.
   var s = $("section[aria-selected]");
   if (s) {
     var video = s.$("video");
     if (video) {
       if (video.ended || video.paused) {
         video.play();
       } else {
         video.pause();
       }
     }
   }
 }

 Dz.setCursor = function(aIdx, aStep) {
   // If the user change the slide number in the URL bar, jump
   // to this slide.
   aStep = (aStep != 0 && typeof aStep !== "undefined") ? "." + aStep : ".0";
   window.location.hash = "#" + aIdx + aStep;
 }

 Dz.onhashchange = function() {
   var cursor = window.location.hash.split("#"),
       newidx = 1,
       newstep = 0;
   if (cursor.length == 2) {
     newidx = ~~cursor[1].split(".")[0];
     newstep = ~~cursor[1].split(".")[1];
     if (newstep > Dz.slides[newidx - 1].$$('.incremental > *').length) {
       newstep = 0;
       newidx++;
     }
   }
   this.setProgress(newidx, newstep);
   if (newidx != this.idx) {
     this.setSlide(newidx);
   }
   if (newstep != this.step) {
     this.setIncremental(newstep);
   }
   for (var i = 0; i < this.remoteWindows.length; i++) {
     this.postMsg(this.remoteWindows[i], "CURSOR", this.idx + "." + this.step);
   }
 }

 Dz.back = function() {
   if (this.idx == 1 && this.step == 0) {
     return;
   }
   if (this.step == 0) {
     this.setCursor(this.idx - 1,
                    this.slides[this.idx - 2].$$('.incremental > *').length);
   } else {
     this.setCursor(this.idx, this.step - 1);
   }
 }

 Dz.forward = function() {
   if (this.idx >= this.slides.length &&
       this.step >= this.slides[this.idx - 1].$$('.incremental > *').length) {
       return;
   }
   if (this.step >= this.slides[this.idx - 1].$$('.incremental > *').length) {
     this.setCursor(this.idx + 1, 0);
   } else {
     this.setCursor(this.idx, this.step + 1);
   }
 }

 Dz.goStart = function() {
   this.setCursor(1, 0);
 }

 Dz.goEnd = function() {
   var lastIdx = this.slides.length;
   var lastStep = this.slides[lastIdx - 1].$$('.incremental > *').length;
   this.setCursor(lastIdx, lastStep);
 }

 Dz.toggleView = function() {
   this.html.classList.toggle("view");

   if (this.html.classList.contains("view")) {
     $("section[aria-selected]").scrollIntoView(true);
   }
 }

 Dz.setSlide = function(aIdx) {
   this.idx = aIdx;
   var old = $("section[aria-selected]");
   var next = $("section:nth-of-type("+ this.idx +")");
   if (old) {
     old.removeAttribute("aria-selected");
     var video = old.$("video");
     if (video) {
       video.pause();
     }
   }
   if (next) {
     next.setAttribute("aria-selected", "true");
     if (this.html.classList.contains("view")) {
       next.scrollIntoView();
     }
     var video = next.$("video");
     if (video && !!+this.params.autoplay) {
       video.play();
     }
   } else {
     // That should not happen
     this.idx = -1;
     // console.warn("Slide doesn't exist.");
   }
 }

 Dz.setIncremental = function(aStep) {
   this.step = aStep;
   var old = this.slides[this.idx - 1].$('.incremental > *[aria-selected]');
   if (old) {
     old.removeAttribute('aria-selected');
   }
   var incrementals = $$('.incremental');
   if (this.step <= 0) {
     $$.forEach(incrementals, function(aNode) {
       aNode.removeAttribute('active');
     });
     return;
   }
   var next = this.slides[this.idx - 1].$$('.incremental > *')[this.step - 1];
   if (next) {
     next.setAttribute('aria-selected', true);
     next.parentNode.setAttribute('active', true);
     var found = false;
     $$.forEach(incrementals, function(aNode) {
       if (aNode != next.parentNode)
         if (found)
           aNode.removeAttribute('active');
         else
           aNode.setAttribute('active', true);
       else
         found = true;
     });
   } else {
     setCursor(this.idx, 0);
   }
   return next;
 }

 Dz.goFullscreen = function() {
   var html = $('html'),
       requestFullscreen = html.requestFullscreen || html.requestFullScreen || html.mozRequestFullScreen || html.webkitRequestFullScreen;
   if (requestFullscreen) {
     requestFullscreen.apply(html);
   }
 }

 Dz.setProgress = function(aIdx, aStep) {
   var slide = $("section:nth-of-type("+ aIdx +")");
   if (!slide)
     return;
   var steps = slide.$$('.incremental > *').length + 1,
       slideSize = 100 / (this.slides.length - 1),
       stepSize = slideSize / steps;
   this.progressBar.style.width = ((aIdx - 1) * slideSize + aStep * stepSize) + '%';
 }

 Dz.postMsg = function(aWin, aMsg) { // [arg0, [arg1...]]
   aMsg = [aMsg];
   for (var i = 2; i < arguments.length; i++)
     aMsg.push(encodeURIComponent(arguments[i]));
   aWin.postMessage(aMsg.join(" "), "*");
 }

 function init() {
   Dz.init();
   window.onkeydown = Dz.onkeydown.bind(Dz);
   window.onresize = Dz.onresize.bind(Dz);
   window.onhashchange = Dz.onhashchange.bind(Dz);
   window.onmessage = Dz.onmessage.bind(Dz);
 }

 window.onload = init;
</script>


<script> // Helpers
 if (!Function.prototype.bind) {
   Function.prototype.bind = function (oThis) {

     // closest thing possible to the ECMAScript 5 internal IsCallable
     // function
     if (typeof this !== "function")
     throw new TypeError(
       "Function.prototype.bind - what is trying to be fBound is not callable"
     );

     var aArgs = Array.prototype.slice.call(arguments, 1),
         fToBind = this,
         fNOP = function () {},
         fBound = function () {
           return fToBind.apply( this instanceof fNOP ? this : oThis || window,
                  aArgs.concat(Array.prototype.slice.call(arguments)));
         };

     fNOP.prototype = this.prototype;
     fBound.prototype = new fNOP();

     return fBound;
   };
 }

 var $ = (HTMLElement.prototype.$ = function(aQuery) {
   return this.querySelector(aQuery);
 }).bind(document);

 var $$ = (HTMLElement.prototype.$$ = function(aQuery) {
   return this.querySelectorAll(aQuery);
 }).bind(document);

 $$.forEach = function(nodeList, fun) {
   Array.prototype.forEach.call(nodeList, fun);
 }

</script>
<!-- vim: set fdm=marker: }}} -->