SlideShare una empresa de Scribd logo
1 de 29
Descargar para leer sin conexión
REAL TIME WEB
                          WITH NODE
                           By Tim Caswell




Saturday, June 5, 2010
Live Interaction
            The web is about doing things, not just tourism anymore.

Saturday, June 5, 2010
Uses of Live Interaction
                         Chat Widget
                         Twitter Feed
                         Stock Ticker
                         Real-Time Game
                         Collaborative
                         Documents
                         TXJS Demos


Saturday, June 5, 2010
Why we need non-blocking
                  Polling is too slow and inefficient.
                  Live interaction requires the server to push data.
                  In order to push data, the connections need to be
                  persistent.
                  The server needs to handle thousands of persistent
                  connections
                  Threads aren’t going to scale.


Saturday, June 5, 2010
This is your server.
                         (with blocking I/O)
Saturday, June 5, 2010
This is your server
                  with low concurrency.




Saturday, June 5, 2010
This is your server
               with high concurrency.

Saturday, June 5, 2010
Connect
              We’ll use a new node
                framework that
            “connects” the web users
                 to each other.




Saturday, June 5, 2010
It’s like Japanese Legos
Saturday, June 5, 2010
Pre-Built Blocks
         Connect.createServer([
             {filter: "log"},
             {filter: "body-decoder", route: "/stream"},
             {provider: "pubsub", route: "/stream"},
             {filter: "conditional-get"},
             {filter: "cache"},
             {filter: "gzip"},
             {provider: "cache-manifest", root: root},
             {provider: "static", root: root}
         ]);



Saturday, June 5, 2010
And easy too!
Saturday, June 5, 2010
method-override.js
           var key;
           // Initialize any state (on server startup)
           exports.setup = function (env) {
               key = this.key || "_method";
           };
           // Modify the request stream (on request)
           exports.handle = function(err, req, res, next){
               if (key in req.body) {
                    req.method = req.body[key].toUpperCase();
               }
               next();
           };

Saturday, June 5, 2010
response-time.js
        exports.handle = function(err, req, res, next){
            var start = new Date,
                writeHead = res.writeHead;

                     res.writeHead = function(code, headers){
                         res.writeHead = writeHead;
                         headers['X-Response-Time'] =
                                        (new Date - start) + "ms";
                         res.writeHead(code, headers);
                     };

                     next();
        };

Saturday, June 5, 2010
Well, actually, it’s
             not always easy.
Saturday, June 5, 2010
static.js




Saturday, June 5, 2010
static.js
   var fs = require('fs'),
       Url = require('url'),
       Path = require('path');

   var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime

   var DEFAULT_MIME = 'application/octet-stream';

   module.exports = {

         setup: function (env) {
             this.root = this.root || process.cwd();
         },

         handle: function (err, req, res, next) {
             // Skip on error
             if (err) {
                 next();
                 return;
             }
             var url = Url.parse(req.url);

               var pathname = url.pathname.replace(/..+/g, '.'),
                   filename = Path.join(this.root, pathname);

               if (filename[filename.length - 1] === "/") {
                   filename += "index.html";
               }


Saturday, June 5, 2010
static.js
                                           // Buffer any events that fire while waiting on the stat.
   var fs = require('fs'),                 var events = [];
       Url = require('url'),               function onData() {
       Path = require('path');                  events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() {
                                             browser cache lifetime
                                                events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var DEFAULT_MIME = 'application/octet-stream';
                                           req.addListener("data", onData);
   module.exports = {                      req.addListener("end", onEnd);


         setup: function (env) {             fs.stat(filename, function (err, stat) {
             this.root = this.root || process.cwd();
         },                                      // Stop buffering events
                                                 req.removeListener("data", onData);
         handle: function (err, req, res, next) {req.removeListener("end", onEnd);
               // Skip on error
               if (err) {                          // Fall through for missing files, thow error for other problems
                   next();                         if (err) {
                   return;                             if (err.errno === process.ENOENT) {
               }                                           next();
               var url = Url.parse(req.url);               // Refire the buffered events
                                                           events.forEach(function (args) {
                                                               req.emit.apply(req, args);
               var pathname = url.pathname.replace(/..+/g, '.'),
                                                           });
                   filename = Path.join(this.root, pathname);
                                                           return;
               if (filename[filename.length - 1] === "/") {
                   filename += "index.html";
               }


Saturday, June 5, 2010
static.js
                                            // Buffer any events that fire while waiting on the stat.
    var fs = require('fs'),                 var events = [];
        Url = require('url'),               function onData() {
        Path = require('path');                  events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                            }
    var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() {
                                              browser cache lifetime
                                                 events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                            }
    var DEFAULT_MIME = 'application/octet-stream';
                                            req.addListener("data", onData);
    module.exports = {                      req.addListener("end", onEnd);

(err);
        setup: function (env) {             fs.stat(filename, function (err, stat) {
rn;
            this.root = this.root || process.cwd();
        },                                      // Stop buffering events
                                                req.removeListener("data", onData);
                                                req.removeListener("end", onEnd);
 the file directly using (err, req, res, next) {
        handle: function buffers
ile(filename, function error data) {
            // Skip on (err,
            if (err) {                          // Fall through for missing files, thow error for other problems
err) {
                next();                         if (err) {
next(err);
                return;                             if (err.errno === process.ENOENT) {
return;
            }                                           next();
            var url = Url.parse(req.url);               // Refire the buffered events
writeHead(200, {
                                                        events.forEach(function (args) {
ontent-Type": Mime.type(filename),
                                                            req.emit.apply(req, args);
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'),
            var data.length,
                                                        });
                filename = Path.join(this.root, pathname);
ast-Modified": stat.mtime.toUTCString(),
                                                        return;
/ Cache in browser for 1 year
ache-Control": (filename[filename.length - 1] === "/") {
            if "public max-age=" + 31536000
                filename += "index.html";
end(data); }

 Saturday, June 5, 2010
static.js
                                           // Buffer any events that fire while waiting on the stat.
   var fs = require('fs'),                 var events = [];
       Url = require('url'),               function onData() {
       Path = require('path');                  events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() {
                                             browser cache lifetime
                                       };       events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var DEFAULT_MIME = 'application/octet-stream';
                                       // Mini mime module for static file serving
                                           req.addListener("data", onData);
   module.exports = {                  var req.addListener("end", onEnd);
                                            Mime = {

(err);                                          type: function getMime(path) (err, stat) {
          setup: function (env) {                 fs.stat(filename, function {
rn;                                                var index = path.lastIndexOf(".");
                this.root = this.root || process.cwd();
          },                                       if (index < buffering events
                                                       // Stop 0) {
                                                     return DEFAULT_MIME;
                                                       req.removeListener("data", onData);
                                                   } req.removeListener("end", onEnd);
 the file directly using (err, req, res, next) { type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
          handle: function buffers
                                                   var
ile(filename,   // function error data) {
                     Skip on (err,
                if (err) {                         return Fall through for missing files, thow error "; charset=utf-8" : type;
                                                       // (/(text|javascript)/).test(type) ? type + for other problems
err) {                                          },
                      next();                          if (err) {
next(err);
                      return;                              if (err.errno === process.ENOENT) {
return;                                         TYPES : { ".3gp"
                }                                              next(); "video/3gpp",
                                                                     :
                var url = Url.parse(req.url);               ".a" Refire the buffered events
                                                               //    : "application/octet-stream",
writeHead(200, {                                            ".ai"    : "application/postscript",
                                                               events.forEach(function (args) {
ontent-Type": Mime.type(filename),                          ".aif" req.emit.apply(req, args);
                                                                     : "audio/x-aiff",
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length,
                                                            ".aiff" :
                                                               });
                      filename = Path.join(this.root, pathname);
ast-Modified": stat.mtime.toUTCString(),                    ".asc"
                                                               return; "application/pgp-signature",
                                                                     :
/ Cache in browser for 1 year                               ".asf"   : "video/x-ms-asf",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                                                            ".asm"   : "text/x-asm",
                      filename += "index.html";
                                                            ".asx"   : "video/x-ms-asf",
end(data);      }
                                                            ".atom" : "application/atom+xml",
                                                            ".au"    : "audio/basic",
 Saturday, June 5, 2010                                     ".avi"   : "video/x-msvideo",
static.js
                                            // Buffer any events that fire while waiting on the stat.
   var fs = require('fs'), : "video/x-flv",var events = [];
                  ".flv"
                  ".for"   :                function onData() {
       Url = require('url'), "text/x-fortran",
       Path = require('path');
                  ".gem"                        events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                           : "application/octet-stream",
                                            }
                  ".gemspec" : "text/x-script.ruby",
   var lifetime = ".gif" 60: *"image/gif", function onEnd() {
                  1000 *       60; // 1 hour browser cache lifetime
                                         };     events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                  ".gz"    : "application/x-gzip",
                  ".h"     : "text/x-c",    }
   var DEFAULT_MIME = 'application/octet-stream';
                                         // Mini mime module for static file serving
                                            req.addListener("data", onData);
                  ".hh"    : "text/x-c",
   module.exports ".htm"
                  = {                    var Mime = {
                           : "text/html", req.addListener("end", onEnd);
(err);                   ".html" : "text/html",
rn;       setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) {
                         ".ico"   : {            type: function
                                                  fs.stat(filename, function {
                                                    var index = path.lastIndexOf(".");
                this.root = this.root || process.cwd();
                         ".ics"   : "text/calendar",
          },             ".ifb"   : "text/calendar", (index < buffering events
                                                    if // Stop 0) {
                                                      return DEFAULT_MIME;
                                                       req.removeListener("data", onData);
                         ".iso"   : "application/octet-stream",
                                                    }
                                  : req, res, next) {req.removeListener("end", onEnd);
 the file directly ".jar" (err,"application/java-archive",
          handle: function buffers
                          using                     var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
ile(filename,   // function error: data) {
                     Skip".java"
                          on (err, "text/x-java-source",
                if (err) {                          return Fall through for missing files, thow error "; charset=utf-8" : type;
                                                       // (/(text|javascript)/).test(type) ? type + for other problems
                         ".jnlp" : "application/x-java-jnlp-file",
err) {                                           },
                      next();
                         ".jpeg" : "image/jpeg",       if (err) {
next(err);
                      return;
                         ".jpg"   : "image/jpeg",           if (err.errno === process.ENOENT) {
return;                                          TYPES : { ".3gp"     : "video/3gpp",
                }        ".js"    : "application/javascript", next();
                var url = Url.parse(req.url);                ".a" Refire the buffered events
                                                                //    : "application/octet-stream",
writeHead(200, { ".json" : "application/json",               ".ai"    : "application/postscript",
                         ".log"   : "text/plain",               events.forEach(function (args) {
ontent-Type": Mime.type(filename),                           ".aif" req.emit.apply(req, args);
                                                                      : "audio/x-aiff",
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length, "audio/x-mpegurl",
                         ".m3u"   :
                                                             ".aiff" :
                      filename = Path.join(this.root, pathname);
                         ".m4v"   : "video/mp4",                });
ast-Modified": stat.mtime.toUTCString(),                     ".asc"
                                                                return; "application/pgp-signature",
                                                                      :
/ Cache in browser ".man" year "text/troff",
                          for 1   :
                                                             ".asf"   : "video/x-ms-asf",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                         ".mathml" : "application/mathml+xml",
                                                             ".asm"   : "text/x-asm",
                      filename += : "application/mbox",
                         ".mbox" "index.html";
                                                             ".asx"   : "video/x-ms-asf",
end(data);      }        ".mdoc" : "text/troff",
                         ".me"    : "text/troff",            ".atom" : "application/atom+xml",
                                                             ".au"    : "audio/basic",
                         ".mid"   : "audio/midi",
 Saturday, June 5, 2010                                      ".avi"   : "video/x-msvideo",
static.js
                                                   // Buffer any events that fire while waiting "text/x-c",
                                                                                       ".cc"    : on the stat.
    var fs = require('fs'), : "video/x-flv",var events = [];
                         ".flv"                                                        ".chm"   : "application/vnd.ms-htmlhelp",
          Url = require('url'), "text/x-fortran",
                         ".for"   :                function onData() {                 ".class"   : "application/octet-stream",
          Path = require('path');
                         ".gem"                         events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                  : "application/octet-stream",                        ".com"   : "application/x-msdownload",
                                                   }
                         ".gemspec" : "text/x-script.ruby",                            ".conf" : "text/plain",
    var lifetime = ".gif" 60: *"image/gif", function onEnd() {
                         1000 *       60; // 1 hour browser cache lifetime             ".cpp"   : "text/x-c",
                                               };       events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                                                                       ".crt"   : "application/x-x509-ca-cert",
                         ".gz"    : "application/x-gzip",
    var DEFAULT_MIME = 'application/octet-stream';
                         ".h"     : "text/x-c",    }                                   ".css"   : "text/css",
                                               // Mini mime module for static file serving
                                                   req.addListener("data", onData); ".csv"      : "text/csv",
                         ".hh"    : "text/x-c",
    module.exports ".htm"= {                   var Mime = {
                                  : "text/html", req.addListener("end", onEnd);        ".cxx"   : "text/x-c",
                         ".html" : "text/html",                                        ".deb"   : "application/x-debian-package",
(err);
rn;       setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) {
                         ".ico"   : {             type: function
                                                   fs.stat(filename, function {        ".der"   : "application/x-x509-ca-cert",
                                                     var index = path.lastIndexOf(".");".diff" : "text/x-diff",
                this.root = this.root || process.cwd();
                         ".ics"   : "text/calendar",
          },             ".ifb"   : "text/calendar", (index < buffering events
                                                     if // Stop 0) {                   ".djv"   : "image/vnd.djvu",
                                                       return DEFAULT_MIME;
                                                        req.removeListener("data", onData);
                                                                                       ".djvu" : "image/vnd.djvu",
                         ".iso"   : "application/octet-stream",
                                                     }
                                  : req, res, next) {req.removeListener("end", onEnd); ".dll"   : "application/x-msdownload",
 the file directly ".jar" (err,"application/java-archive",
          handle: function buffers
                          using                      var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
                // function error: data) {
                     Skip".java"
                          on (err, "text/x-java-source",                               ".dmg"   : "application/octet-stream",
ile(filename,                                        return Fall through for missing files, thow error "; charset=utf-8" : type;
                if (err) {                              // (/(text|javascript)/).test(type) ? : "application/msword",
                         ".jnlp" : "application/x-java-jnlp-file",                     ".doc"   type + for other problems
err) {                                            },
                      next();
                         ".jpeg" : "image/jpeg",        if (err) {                     ".dot"   : "application/msword",
next(err);
                      return;
                         ".jpg"   : "image/jpeg",            if (err.errno === process.ENOENT) { "application/xml-dtd",
                                                                                       ".dtd"   :
return;                                           TYPES : { ".3gp"     : "video/3gpp", ".dvi"
                }        ".js"    : "application/javascript", next();                           : "application/x-dvi",
                var url = Url.parse(req.url);                 ".a" Refire the buffered events : "application/java-archive",
                                                                 //    : "application/octet-stream",
                                                                                       ".ear"
writeHead(200, { ".json" : "application/json",                ".ai"    : "application/postscript",
                         ".log"   : "text/plain",                events.forEach(function (args) : "message/rfc822",
                                                                                       ".eml"   {
ontent-Type": Mime.type(filename),                            ".aif" req.emit.apply(req, args); : "application/postscript",
                                                                       : "audio/x-aiff",
                                                                                       ".eps"
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length, "audio/x-mpegurl",
                         ".m3u"   :
                                                              ".aiff" :
                      filename = Path.join(this.root, pathname);
                         ".m4v"   : "video/mp4",                 });                   ".exe"   : "application/x-msdownload",
ast-Modified": stat.mtime.toUTCString(),                      ".asc"
                                                                 return; "application/pgp-signature",
                                                                       :               ".f"     : "text/x-fortran",
/ Cache in browser ".man" year "text/troff",
                          for 1   :
                                                              ".asf"   : "video/x-ms-asf",
                                                                                       ".f77"   : "text/x-fortran",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                         ".mathml" : "application/mathml+xml",
                                                              ".asm"   : "text/x-asm", ".f90"
                      filename += : "application/mbox",
                         ".mbox" "index.html";                                                  : "text/x-fortran",
                                                              ".asx"   : "video/x-ms-asf",
end(data);      }        ".mdoc" : "text/troff",
                         ".me"    : "text/troff",             ".atom" : "application/atom+xml",
                                                              ".au"    : "audio/basic",
                         ".mid"   : "audio/midi",
 Saturday, June 5, 2010                                       ".avi"   : "video/x-msvideo",
static.js
                                                     // Buffer any events that fire while waiting "text/x-c",
                                                                                         ".cc"    : on the stat.
    var fs = require('fs'), : "video/x-flv",var events = [];
                         ".flv"                                                          ".chm"   : "application/vnd.ms-htmlhelp",
          Url = require('url'), "text/x-fortran",
                         ".for"    :                 function onData() {                 ".class"   : "application/octet-stream",
          Path = require('path');
                         ".gem"                            events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                   : "application/octet-stream",                         ".com"   : "application/x-msdownload",
                                                     }
                         ".gemspec" : "text/x-script.ruby",                              ".conf" : "text/plain",
    var lifetime = ".gif" 60: *"image/gif", function onEnd() {
                         1000 *        60; // 1 hour browser cache lifetime              ".cpp"   : "text/x-c",
                                                 };        events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                                                                         ".crt"   : "application/x-x509-ca-cert",
                         ".gz"     : "application/x-gzip",
    var DEFAULT_MIME = 'application/octet-stream';
                         ".h"      : "text/x-c",     }                                   ".css"   : "text/css",
                                                 // Mini mime module for static file serving
                                                     req.addListener("data", onData); ".csv"      : "text/csv",
                         ".hh"     : "text/x-c",
    module.exports ".htm"= {                     var Mime = {
                                   : "text/html", req.addListener("end", onEnd);         ".cxx"   : "text/x-c",
                         ".html" : "text/html",                                          ".deb"   : "application/x-debian-package",
(err);
rn;       setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) {
                         ".ico"    : {              type: function
                                                     fs.stat(filename, function {        ".der"   : "application/x-x509-ca-cert",
                                                       var index = path.lastIndexOf(".");".diff" : "text/x-diff",
                this.root = this.root || process.cwd();
                         ".ics"    : "text/calendar",
          },             ".ifb"    : "text/calendar", (index < buffering events
                                                       if // Stop 0) {                   ".djv"   : "image/vnd.djvu",
                                                         return DEFAULT_MIME;
                                                           req.removeListener("data", onData);
                                                                                         ".djvu" : "image/vnd.djvu",
                         ".iso"    : "application/octet-stream",
                                                       } req.removeListener("end", onEnd);
                                                                                         ".dll"   : "application/x-msdownload",
 the file directly ".jar"".bmp" req, "image/bmp", type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
          handle: function (err,"application/java-archive",
                          using buffers : res, next) {
                                   :
                                                       var
                // function error: data) {
                     Skip".java"
                          on (err, "text/x-java-source",
                                ".bz2"    : "application/x-bzip2",                       ".dmg"   : "application/octet-stream",
ile(filename,                                          return (/(text|javascript)/).test(type) ? : "application/msword", : type;
                                                                                                  type + "; charset=utf-8"
err) {          if (err) {                : "text/x-c", // Fall through for missing files, thow error for other problems
                         ".jnlp" : "application/x-java-jnlp-file",
                                ".c"                                                     ".doc"
                                                    },     if (err) {                    ".dot"   : "application/msword",
next(err);            next(); ".cab"
                         ".jpeg" : "image/jpeg",
                                          : "application/vnd.ms-cab-compressed",
                      return;
                         ".jpg"    : "image/jpeg",             if (err.errno === process.ENOENT) { "application/xml-dtd",
                                                                                         ".dtd"   :
return;                                             TYPES : { ".3gp"     : "video/3gpp", ".dvi"
                }        ".js"     : "application/javascript", next();                            : "application/x-dvi",
                var url = Url.parse(req.url);                   ".a" Refire the buffered events : "application/java-archive",
                                                                   //    : "application/octet-stream",
                                                                                         ".ear"
writeHead(200, { ".json" : "application/json",                  ".ai"    : "application/postscript",
                         ".log"    : "text/plain",                 events.forEach(function (args) : "message/rfc822",
                                                                                         ".eml"   {
ontent-Type": Mime.type(filename),                              ".aif" req.emit.apply(req, args); : "application/postscript",
                                                                         : "audio/x-aiff",
                                                                                         ".eps"
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length, "audio/x-mpegurl",
                         ".m3u"    :
                                                                ".aiff" :
                      filename = Path.join(this.root, pathname);
                         ".m4v"    : "video/mp4",                  });                   ".exe"   : "application/x-msdownload",
ast-Modified": stat.mtime.toUTCString(),                        ".asc"
                                                                   return; "application/pgp-signature",
                                                                         :               ".f"     : "text/x-fortran",
/ Cache in browser ".man" year "text/troff",
                          for 1    :
                                                                ".asf"   : "video/x-ms-asf",
                                                                                         ".f77"   : "text/x-fortran",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                         ".mathml" : "application/mathml+xml",
                                                                ".asm"   : "text/x-asm", ".f90"
                      filename += : "application/mbox",
                         ".mbox" "index.html";                                                    : "text/x-fortran",
                                                                ".asx"   : "video/x-ms-asf",
end(data);      }        ".mdoc" : "text/troff",
                         ".me"     : "text/troff",              ".atom" : "application/atom+xml",
                                                                ".au"    : "audio/basic",
                         ".mid"    : "audio/midi",
 Saturday, June 5, 2010                                         ".avi"   : "video/x-msvideo",
Built-in Filter Modules
                   Authentication     Error Handler
                   Authorization      Gzip
                   Body Decoder       Log
                   Cache              Method Override
                   Conditional Get    Response Time
                   Debug              Session


Saturday, June 5, 2010
Built-in Data Providers

                         Static        Cache Manifest
                         Rest          Direct
                         Router        JSON-RPC
                         PubSub        More...


Saturday, June 5, 2010
Demo Time!


Saturday, June 5, 2010
app.js (stack)
                   require.paths.unshift("./lib");
                   var Connect = require('connect');
                   var root = __dirname + "/public";

                   module.exports = Connect.createServer([
                       {filter: "log"},
                       {filter: "body-decoder"},
                       {provider: "pubsub", route: "/stream",
                           logic: Backend},
                       {filter: "conditional-get"},
                       {filter: "cache"},
                       {filter: "gzip"},
                       {provider: "cache-manifest", root: root},
                       {provider: "static", root: root}
                   ]);

Saturday, June 5, 2010
app.js (Backend)
                         var Backend = {
                             subscribe: function (subscriber) {
                                 if (subscribers.indexOf(subscriber) < 0) {
                                     subscribers.push(subscriber);
                                 }
                             },
                             unsubscribe: function (subscriber) {
                                 var pos = subscribers.indexOf(subscriber);
                                 if (pos >= 0) {
                                     subscribers.slice(pos);
                                 }
                             },
                             publish: function (message, callback) {
                                 subscribers.forEach(function (subscriber) {
                                     subscriber.send(message);
                                 });
                                 callback();
                             }
                         };

Saturday, June 5, 2010
http://github.com/extjs/connect

             http://twitter.com/creationix

             http://raphaeljs.com

             http://nodejs.org


Saturday, June 5, 2010
Any
                         Questions
                            ?




Saturday, June 5, 2010

Más contenido relacionado

La actualidad más candente

JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
Kiyotaka Oku
 
Java Concurrency in Practice
Java Concurrency in PracticeJava Concurrency in Practice
Java Concurrency in Practice
ericbeyeler
 
Pry, the good parts
Pry, the good partsPry, the good parts
Pry, the good parts
Conrad Irwin
 
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Arian Gutierrez
 
Introduce leo-redundant-manager
Introduce leo-redundant-managerIntroduce leo-redundant-manager
Introduce leo-redundant-manager
Paras Patel
 

La actualidad más candente (19)

Connectivity coding for java and mysql
Connectivity coding for java and mysqlConnectivity coding for java and mysql
Connectivity coding for java and mysql
 
Ejemplo radio
Ejemplo radioEjemplo radio
Ejemplo radio
 
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
 
занятие8
занятие8занятие8
занятие8
 
Micro services workshop
Micro services workshopMicro services workshop
Micro services workshop
 
node.js Module Development
node.js Module Developmentnode.js Module Development
node.js Module Development
 
Intro to Redis
Intro to RedisIntro to Redis
Intro to Redis
 
Debugging: Rules & Tools
Debugging: Rules & ToolsDebugging: Rules & Tools
Debugging: Rules & Tools
 
Capistrano Rails
Capistrano RailsCapistrano Rails
Capistrano Rails
 
The Ring programming language version 1.10 book - Part 92 of 212
The Ring programming language version 1.10 book - Part 92 of 212The Ring programming language version 1.10 book - Part 92 of 212
The Ring programming language version 1.10 book - Part 92 of 212
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
zinno
zinnozinno
zinno
 
Java Concurrency in Practice
Java Concurrency in PracticeJava Concurrency in Practice
Java Concurrency in Practice
 
Swing database(mysql)
Swing database(mysql)Swing database(mysql)
Swing database(mysql)
 
Serial Killers - or Deserialization for fun and profit
Serial Killers - or Deserialization for fun and profitSerial Killers - or Deserialization for fun and profit
Serial Killers - or Deserialization for fun and profit
 
Pry, the good parts
Pry, the good partsPry, the good parts
Pry, the good parts
 
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
 
Monitoring MongoDB (MongoSV)
Monitoring MongoDB (MongoSV)Monitoring MongoDB (MongoSV)
Monitoring MongoDB (MongoSV)
 
Introduce leo-redundant-manager
Introduce leo-redundant-managerIntroduce leo-redundant-manager
Introduce leo-redundant-manager
 

Destacado (6)

Node Powered Mobile
Node Powered MobileNode Powered Mobile
Node Powered Mobile
 
Open design at large scale
Open design at large scaleOpen design at large scale
Open design at large scale
 
Perspectives on Docker
Perspectives on DockerPerspectives on Docker
Perspectives on Docker
 
Open Design at large scale by Solomon Hykes
Open Design at large scale by Solomon HykesOpen Design at large scale by Solomon Hykes
Open Design at large scale by Solomon Hykes
 
Docker Deployments
Docker DeploymentsDocker Deployments
Docker Deployments
 
Why Zsh is Cooler than Your Shell
Why Zsh is Cooler than Your ShellWhy Zsh is Cooler than Your Shell
Why Zsh is Cooler than Your Shell
 

Similar a Real Time Web with Node

node.js practical guide to serverside javascript
node.js practical guide to serverside javascriptnode.js practical guide to serverside javascript
node.js practical guide to serverside javascript
Eldar Djafarov
 
Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010
Ismael Celis
 
Jersey framework
Jersey frameworkJersey framework
Jersey framework
knight1128
 
Build web application by express
Build web application by expressBuild web application by express
Build web application by express
Shawn Meng
 

Similar a Real Time Web with Node (20)

NodeJs
NodeJsNodeJs
NodeJs
 
JS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js AntipatternsJS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js Antipatterns
 
Txjs
TxjsTxjs
Txjs
 
JSConf: All You Can Leet
JSConf: All You Can LeetJSConf: All You Can Leet
JSConf: All You Can Leet
 
Node.js - A Quick Tour
Node.js - A Quick TourNode.js - A Quick Tour
Node.js - A Quick Tour
 
node.js practical guide to serverside javascript
node.js practical guide to serverside javascriptnode.js practical guide to serverside javascript
node.js practical guide to serverside javascript
 
Node.js - Best practices
Node.js  - Best practicesNode.js  - Best practices
Node.js - Best practices
 
Node.js - A practical introduction (v2)
Node.js  - A practical introduction (v2)Node.js  - A practical introduction (v2)
Node.js - A practical introduction (v2)
 
Security Challenges in Node.js
Security Challenges in Node.jsSecurity Challenges in Node.js
Security Challenges in Node.js
 
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
 
Node.js - A Quick Tour II
Node.js - A Quick Tour IINode.js - A Quick Tour II
Node.js - A Quick Tour II
 
JavaScript - Like a Box of Chocolates
JavaScript - Like a Box of ChocolatesJavaScript - Like a Box of Chocolates
JavaScript - Like a Box of Chocolates
 
Impress Your Friends with EcmaScript 2015
Impress Your Friends with EcmaScript 2015Impress Your Friends with EcmaScript 2015
Impress Your Friends with EcmaScript 2015
 
Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
Jersey framework
Jersey frameworkJersey framework
Jersey framework
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Node.js
Node.jsNode.js
Node.js
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 
Build web application by express
Build web application by expressBuild web application by express
Build web application by express
 

Último

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
Earley Information Science
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 

Último (20)

A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 

Real Time Web with Node

  • 1. REAL TIME WEB WITH NODE By Tim Caswell Saturday, June 5, 2010
  • 2. Live Interaction The web is about doing things, not just tourism anymore. Saturday, June 5, 2010
  • 3. Uses of Live Interaction Chat Widget Twitter Feed Stock Ticker Real-Time Game Collaborative Documents TXJS Demos Saturday, June 5, 2010
  • 4. Why we need non-blocking Polling is too slow and inefficient. Live interaction requires the server to push data. In order to push data, the connections need to be persistent. The server needs to handle thousands of persistent connections Threads aren’t going to scale. Saturday, June 5, 2010
  • 5. This is your server. (with blocking I/O) Saturday, June 5, 2010
  • 6. This is your server with low concurrency. Saturday, June 5, 2010
  • 7. This is your server with high concurrency. Saturday, June 5, 2010
  • 8. Connect We’ll use a new node framework that “connects” the web users to each other. Saturday, June 5, 2010
  • 9. It’s like Japanese Legos Saturday, June 5, 2010
  • 10. Pre-Built Blocks Connect.createServer([ {filter: "log"}, {filter: "body-decoder", route: "/stream"}, {provider: "pubsub", route: "/stream"}, {filter: "conditional-get"}, {filter: "cache"}, {filter: "gzip"}, {provider: "cache-manifest", root: root}, {provider: "static", root: root} ]); Saturday, June 5, 2010
  • 11. And easy too! Saturday, June 5, 2010
  • 12. method-override.js var key; // Initialize any state (on server startup) exports.setup = function (env) { key = this.key || "_method"; }; // Modify the request stream (on request) exports.handle = function(err, req, res, next){ if (key in req.body) { req.method = req.body[key].toUpperCase(); } next(); }; Saturday, June 5, 2010
  • 13. response-time.js exports.handle = function(err, req, res, next){ var start = new Date, writeHead = res.writeHead; res.writeHead = function(code, headers){ res.writeHead = writeHead; headers['X-Response-Time'] = (new Date - start) + "ms"; res.writeHead(code, headers); }; next(); }; Saturday, June 5, 2010
  • 14. Well, actually, it’s not always easy. Saturday, June 5, 2010
  • 16. static.js var fs = require('fs'), Url = require('url'), Path = require('path'); var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime var DEFAULT_MIME = 'application/octet-stream'; module.exports = { setup: function (env) { this.root = this.root || process.cwd(); }, handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url); var pathname = url.pathname.replace(/..+/g, '.'), filename = Path.join(this.root, pathname); if (filename[filename.length - 1] === "/") { filename += "index.html"; } Saturday, June 5, 2010
  • 17. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), var events = []; Url = require('url'), function onData() { Path = require('path'); events.push(["data"].concat(Array.prototype.slice.call(arguments))); } var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() { browser cache lifetime events.push(["end"].concat(Array.prototype.slice.call(arguments))); } var DEFAULT_MIME = 'application/octet-stream'; req.addListener("data", onData); module.exports = { req.addListener("end", onEnd); setup: function (env) { fs.stat(filename, function (err, stat) { this.root = this.root || process.cwd(); }, // Stop buffering events req.removeListener("data", onData); handle: function (err, req, res, next) {req.removeListener("end", onEnd); // Skip on error if (err) { // Fall through for missing files, thow error for other problems next(); if (err) { return; if (err.errno === process.ENOENT) { } next(); var url = Url.parse(req.url); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); var pathname = url.pathname.replace(/..+/g, '.'), }); filename = Path.join(this.root, pathname); return; if (filename[filename.length - 1] === "/") { filename += "index.html"; } Saturday, June 5, 2010
  • 18. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), var events = []; Url = require('url'), function onData() { Path = require('path'); events.push(["data"].concat(Array.prototype.slice.call(arguments))); } var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() { browser cache lifetime events.push(["end"].concat(Array.prototype.slice.call(arguments))); } var DEFAULT_MIME = 'application/octet-stream'; req.addListener("data", onData); module.exports = { req.addListener("end", onEnd); (err); setup: function (env) { fs.stat(filename, function (err, stat) { rn; this.root = this.root || process.cwd(); }, // Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd); the file directly using (err, req, res, next) { handle: function buffers ile(filename, function error data) { // Skip on (err, if (err) { // Fall through for missing files, thow error for other problems err) { next(); if (err) { next(err); return; if (err.errno === process.ENOENT) { return; } next(); var url = Url.parse(req.url); // Refire the buffered events writeHead(200, { events.forEach(function (args) { ontent-Type": Mime.type(filename), req.emit.apply(req, args); ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), var data.length, }); filename = Path.join(this.root, pathname); ast-Modified": stat.mtime.toUTCString(), return; / Cache in browser for 1 year ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 filename += "index.html"; end(data); } Saturday, June 5, 2010
  • 19. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), var events = []; Url = require('url'), function onData() { Path = require('path'); events.push(["data"].concat(Array.prototype.slice.call(arguments))); } var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() { browser cache lifetime }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); } var DEFAULT_MIME = 'application/octet-stream'; // Mini mime module for static file serving req.addListener("data", onData); module.exports = { var req.addListener("end", onEnd); Mime = { (err); type: function getMime(path) (err, stat) { setup: function (env) { fs.stat(filename, function { rn; var index = path.lastIndexOf("."); this.root = this.root || process.cwd(); }, if (index < buffering events // Stop 0) { return DEFAULT_MIME; req.removeListener("data", onData); } req.removeListener("end", onEnd); the file directly using (err, req, res, next) { type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; handle: function buffers var ile(filename, // function error data) { Skip on (err, if (err) { return Fall through for missing files, thow error "; charset=utf-8" : type; // (/(text|javascript)/).test(type) ? type + for other problems err) { }, next(); if (err) { next(err); return; if (err.errno === process.ENOENT) { return; TYPES : { ".3gp" } next(); "video/3gpp", : var url = Url.parse(req.url); ".a" Refire the buffered events // : "application/octet-stream", writeHead(200, { ".ai" : "application/postscript", events.forEach(function (args) { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "audio/x-aiff", ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, ".aiff" : }); filename = Path.join(this.root, pathname); ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : / Cache in browser for 1 year ".asf" : "video/x-ms-asf", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".asm" : "text/x-asm", filename += "index.html"; ".asx" : "video/x-ms-asf", end(data); } ".atom" : "application/atom+xml", ".au" : "audio/basic", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 20. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), : "video/x-flv",var events = []; ".flv" ".for" : function onData() { Url = require('url'), "text/x-fortran", Path = require('path'); ".gem" events.push(["data"].concat(Array.prototype.slice.call(arguments))); : "application/octet-stream", } ".gemspec" : "text/x-script.ruby", var lifetime = ".gif" 60: *"image/gif", function onEnd() { 1000 * 60; // 1 hour browser cache lifetime }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); ".gz" : "application/x-gzip", ".h" : "text/x-c", } var DEFAULT_MIME = 'application/octet-stream'; // Mini mime module for static file serving req.addListener("data", onData); ".hh" : "text/x-c", module.exports ".htm" = { var Mime = { : "text/html", req.addListener("end", onEnd); (err); ".html" : "text/html", rn; setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) { ".ico" : { type: function fs.stat(filename, function { var index = path.lastIndexOf("."); this.root = this.root || process.cwd(); ".ics" : "text/calendar", }, ".ifb" : "text/calendar", (index < buffering events if // Stop 0) { return DEFAULT_MIME; req.removeListener("data", onData); ".iso" : "application/octet-stream", } : req, res, next) {req.removeListener("end", onEnd); the file directly ".jar" (err,"application/java-archive", handle: function buffers using var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; ile(filename, // function error: data) { Skip".java" on (err, "text/x-java-source", if (err) { return Fall through for missing files, thow error "; charset=utf-8" : type; // (/(text|javascript)/).test(type) ? type + for other problems ".jnlp" : "application/x-java-jnlp-file", err) { }, next(); ".jpeg" : "image/jpeg", if (err) { next(err); return; ".jpg" : "image/jpeg", if (err.errno === process.ENOENT) { return; TYPES : { ".3gp" : "video/3gpp", } ".js" : "application/javascript", next(); var url = Url.parse(req.url); ".a" Refire the buffered events // : "application/octet-stream", writeHead(200, { ".json" : "application/json", ".ai" : "application/postscript", ".log" : "text/plain", events.forEach(function (args) { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "audio/x-aiff", ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, "audio/x-mpegurl", ".m3u" : ".aiff" : filename = Path.join(this.root, pathname); ".m4v" : "video/mp4", }); ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : / Cache in browser ".man" year "text/troff", for 1 : ".asf" : "video/x-ms-asf", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".mathml" : "application/mathml+xml", ".asm" : "text/x-asm", filename += : "application/mbox", ".mbox" "index.html"; ".asx" : "video/x-ms-asf", end(data); } ".mdoc" : "text/troff", ".me" : "text/troff", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".mid" : "audio/midi", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 21. static.js // Buffer any events that fire while waiting "text/x-c", ".cc" : on the stat. var fs = require('fs'), : "video/x-flv",var events = []; ".flv" ".chm" : "application/vnd.ms-htmlhelp", Url = require('url'), "text/x-fortran", ".for" : function onData() { ".class" : "application/octet-stream", Path = require('path'); ".gem" events.push(["data"].concat(Array.prototype.slice.call(arguments))); : "application/octet-stream", ".com" : "application/x-msdownload", } ".gemspec" : "text/x-script.ruby", ".conf" : "text/plain", var lifetime = ".gif" 60: *"image/gif", function onEnd() { 1000 * 60; // 1 hour browser cache lifetime ".cpp" : "text/x-c", }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); ".crt" : "application/x-x509-ca-cert", ".gz" : "application/x-gzip", var DEFAULT_MIME = 'application/octet-stream'; ".h" : "text/x-c", } ".css" : "text/css", // Mini mime module for static file serving req.addListener("data", onData); ".csv" : "text/csv", ".hh" : "text/x-c", module.exports ".htm"= { var Mime = { : "text/html", req.addListener("end", onEnd); ".cxx" : "text/x-c", ".html" : "text/html", ".deb" : "application/x-debian-package", (err); rn; setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) { ".ico" : { type: function fs.stat(filename, function { ".der" : "application/x-x509-ca-cert", var index = path.lastIndexOf(".");".diff" : "text/x-diff", this.root = this.root || process.cwd(); ".ics" : "text/calendar", }, ".ifb" : "text/calendar", (index < buffering events if // Stop 0) { ".djv" : "image/vnd.djvu", return DEFAULT_MIME; req.removeListener("data", onData); ".djvu" : "image/vnd.djvu", ".iso" : "application/octet-stream", } : req, res, next) {req.removeListener("end", onEnd); ".dll" : "application/x-msdownload", the file directly ".jar" (err,"application/java-archive", handle: function buffers using var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; // function error: data) { Skip".java" on (err, "text/x-java-source", ".dmg" : "application/octet-stream", ile(filename, return Fall through for missing files, thow error "; charset=utf-8" : type; if (err) { // (/(text|javascript)/).test(type) ? : "application/msword", ".jnlp" : "application/x-java-jnlp-file", ".doc" type + for other problems err) { }, next(); ".jpeg" : "image/jpeg", if (err) { ".dot" : "application/msword", next(err); return; ".jpg" : "image/jpeg", if (err.errno === process.ENOENT) { "application/xml-dtd", ".dtd" : return; TYPES : { ".3gp" : "video/3gpp", ".dvi" } ".js" : "application/javascript", next(); : "application/x-dvi", var url = Url.parse(req.url); ".a" Refire the buffered events : "application/java-archive", // : "application/octet-stream", ".ear" writeHead(200, { ".json" : "application/json", ".ai" : "application/postscript", ".log" : "text/plain", events.forEach(function (args) : "message/rfc822", ".eml" { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "application/postscript", : "audio/x-aiff", ".eps" ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, "audio/x-mpegurl", ".m3u" : ".aiff" : filename = Path.join(this.root, pathname); ".m4v" : "video/mp4", }); ".exe" : "application/x-msdownload", ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : ".f" : "text/x-fortran", / Cache in browser ".man" year "text/troff", for 1 : ".asf" : "video/x-ms-asf", ".f77" : "text/x-fortran", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".mathml" : "application/mathml+xml", ".asm" : "text/x-asm", ".f90" filename += : "application/mbox", ".mbox" "index.html"; : "text/x-fortran", ".asx" : "video/x-ms-asf", end(data); } ".mdoc" : "text/troff", ".me" : "text/troff", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".mid" : "audio/midi", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 22. static.js // Buffer any events that fire while waiting "text/x-c", ".cc" : on the stat. var fs = require('fs'), : "video/x-flv",var events = []; ".flv" ".chm" : "application/vnd.ms-htmlhelp", Url = require('url'), "text/x-fortran", ".for" : function onData() { ".class" : "application/octet-stream", Path = require('path'); ".gem" events.push(["data"].concat(Array.prototype.slice.call(arguments))); : "application/octet-stream", ".com" : "application/x-msdownload", } ".gemspec" : "text/x-script.ruby", ".conf" : "text/plain", var lifetime = ".gif" 60: *"image/gif", function onEnd() { 1000 * 60; // 1 hour browser cache lifetime ".cpp" : "text/x-c", }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); ".crt" : "application/x-x509-ca-cert", ".gz" : "application/x-gzip", var DEFAULT_MIME = 'application/octet-stream'; ".h" : "text/x-c", } ".css" : "text/css", // Mini mime module for static file serving req.addListener("data", onData); ".csv" : "text/csv", ".hh" : "text/x-c", module.exports ".htm"= { var Mime = { : "text/html", req.addListener("end", onEnd); ".cxx" : "text/x-c", ".html" : "text/html", ".deb" : "application/x-debian-package", (err); rn; setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) { ".ico" : { type: function fs.stat(filename, function { ".der" : "application/x-x509-ca-cert", var index = path.lastIndexOf(".");".diff" : "text/x-diff", this.root = this.root || process.cwd(); ".ics" : "text/calendar", }, ".ifb" : "text/calendar", (index < buffering events if // Stop 0) { ".djv" : "image/vnd.djvu", return DEFAULT_MIME; req.removeListener("data", onData); ".djvu" : "image/vnd.djvu", ".iso" : "application/octet-stream", } req.removeListener("end", onEnd); ".dll" : "application/x-msdownload", the file directly ".jar"".bmp" req, "image/bmp", type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; handle: function (err,"application/java-archive", using buffers : res, next) { : var // function error: data) { Skip".java" on (err, "text/x-java-source", ".bz2" : "application/x-bzip2", ".dmg" : "application/octet-stream", ile(filename, return (/(text|javascript)/).test(type) ? : "application/msword", : type; type + "; charset=utf-8" err) { if (err) { : "text/x-c", // Fall through for missing files, thow error for other problems ".jnlp" : "application/x-java-jnlp-file", ".c" ".doc" }, if (err) { ".dot" : "application/msword", next(err); next(); ".cab" ".jpeg" : "image/jpeg", : "application/vnd.ms-cab-compressed", return; ".jpg" : "image/jpeg", if (err.errno === process.ENOENT) { "application/xml-dtd", ".dtd" : return; TYPES : { ".3gp" : "video/3gpp", ".dvi" } ".js" : "application/javascript", next(); : "application/x-dvi", var url = Url.parse(req.url); ".a" Refire the buffered events : "application/java-archive", // : "application/octet-stream", ".ear" writeHead(200, { ".json" : "application/json", ".ai" : "application/postscript", ".log" : "text/plain", events.forEach(function (args) : "message/rfc822", ".eml" { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "application/postscript", : "audio/x-aiff", ".eps" ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, "audio/x-mpegurl", ".m3u" : ".aiff" : filename = Path.join(this.root, pathname); ".m4v" : "video/mp4", }); ".exe" : "application/x-msdownload", ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : ".f" : "text/x-fortran", / Cache in browser ".man" year "text/troff", for 1 : ".asf" : "video/x-ms-asf", ".f77" : "text/x-fortran", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".mathml" : "application/mathml+xml", ".asm" : "text/x-asm", ".f90" filename += : "application/mbox", ".mbox" "index.html"; : "text/x-fortran", ".asx" : "video/x-ms-asf", end(data); } ".mdoc" : "text/troff", ".me" : "text/troff", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".mid" : "audio/midi", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 23. Built-in Filter Modules Authentication Error Handler Authorization Gzip Body Decoder Log Cache Method Override Conditional Get Response Time Debug Session Saturday, June 5, 2010
  • 24. Built-in Data Providers Static Cache Manifest Rest Direct Router JSON-RPC PubSub More... Saturday, June 5, 2010
  • 26. app.js (stack) require.paths.unshift("./lib"); var Connect = require('connect'); var root = __dirname + "/public"; module.exports = Connect.createServer([ {filter: "log"}, {filter: "body-decoder"}, {provider: "pubsub", route: "/stream", logic: Backend}, {filter: "conditional-get"}, {filter: "cache"}, {filter: "gzip"}, {provider: "cache-manifest", root: root}, {provider: "static", root: root} ]); Saturday, June 5, 2010
  • 27. app.js (Backend) var Backend = { subscribe: function (subscriber) { if (subscribers.indexOf(subscriber) < 0) { subscribers.push(subscriber); } }, unsubscribe: function (subscriber) { var pos = subscribers.indexOf(subscriber); if (pos >= 0) { subscribers.slice(pos); } }, publish: function (message, callback) { subscribers.forEach(function (subscriber) { subscriber.send(message); }); callback(); } }; Saturday, June 5, 2010
  • 28. http://github.com/extjs/connect http://twitter.com/creationix http://raphaeljs.com http://nodejs.org Saturday, June 5, 2010
  • 29. Any Questions ? Saturday, June 5, 2010