Connect

High performance middleware for node.

bodyDecoder

lib/connect/middleware/bodyDecoder.js

Module dependencies.

var queryString = require('querystring');

Supported decoders.

  • application/x-www-form-urlencoded
  • application/json
exports.decode = {
    'application/x-www-form-urlencoded': queryString.parse,
    'application/json': JSON.parse
};

Decode request bodies.

  • return: Function

  • api: public


module.exports = function bodyDecoder(){
    return function handle(req, res, next) {
        var decoder = exports.decode[mime(req)];
        if (decoder) {
            var data = '';
            req.setEncoding('utf8');
            req.addListener('data', function(chunk) { data += chunk; });
            req.addListener('end', function() {
                req.rawBody = data;
                try {
                    req.body = data
                        ? decoder(data)
                        : {};
                } catch (err) {
                    return next(err);
                }
                next();
            });
        } else {
            next();
        }
    }
};

cache

lib/connect/middleware/cache.js

Module dependencies.

var Buffer = require('buffer').Buffer;

Cache in memory for the given cacheDuration.

  • param: Number cacheDuration

  • return: Function

  • api: public

module.exports = function cache(cacheDuration){
    var cache = {},
        queue = {};

    return function handle(req, res, next) {

        // Skip all requests that are not GET method
        if (req.method !== "GET") {
            next();
            return;
        }

        var key = req.headers["accept-encoding"] + req.url,
            writeHead = res.writeHead,
            write = res.write,
            end = res.end,
            code,
            headers,
            chunks = [],
            totalSize = 0;

        function serve() {
            var resp = cache[key];
            var headers = resp.headers;
            headers["Date"] = (new Date).toUTCString();
            headers["Content-Length"] = resp.body.length;

            if (localQueue) {
                // Send everyone in the wait queue the response.
                for (var i = 0, l = localQueue.length; i < l; i++) {
                    var localRes = localQueue[i];
                    localRes.writeHead(resp.code, headers);
                    localRes.end(resp.body);
                }
                // Kill the wait queue
                delete queue[key];
            } else {
                res.writeHead(resp.code, headers);
                res.end(resp.body);
            }

        }

        // If there is a cache-hit, serve it right away!
        if (cache[key]) {
            serve();
            return;
        }

        var localQueue = queue[key];
        if (localQueue) {
            localQueue[localQueue.length] = res;
            return;
        }
        localQueue = queue[key] = [res];

        // Defer the call to writeHead
        res.writeHead = function (setCode, setHeaders) {
            code = setCode;
            headers = setHeaders;
        };

        // Buffer the response body as an array of Buffer objects
        res.write = function (chunk, encoding) {
            if (typeof chunk === 'string') {
                var length;
                if (!encoding || encoding === 'utf8') {
                    length = Buffer.byteLength(chunk);
                }
                var buffer = new Buffer(length);
                buffer.write(chunk, encoding);
                chunks.push(buffer);
            } else {
                chunks.push(chunk);
            }
            totalSize += chunk.length;
        };

        res.end = function (chunk, encoding) {
            if (chunk) {
                res.write(chunk, encoding);
            }

            // Combine the buffer array into a single buffer
            var body = new Buffer(totalSize);
            var offset = 0;
            chunks.forEach(function (chunk) {
                chunk.copy(body, offset);
                offset += chunk.length;
            });

            // Store the result in the cache
            cache[key] = {
                body: body,
                code: code,
                headers: headers
            };

            // Put the original methods back in place
            res.writeHead = writeHead;
            res.write = write;
            res.end = end;

            // Serve the response from the cache
            serve();

            if (cacheDuration) {
                // Expire the ram cache after 100ms or the specified length
                setTimeout(function(){
                    delete cache[key];
                }, cacheDuration);
            } else {
                // When the timeout is zero, just kill it right away
                delete cache[key];
            }

        };

        next();
    };
};

cacheManifest

lib/connect/middleware/cacheManifest.js

Module dependencies.

var fs = require('fs'),
    Utils = require('../utils'),
    Url = require('url'),
    Path = require('path');

Generate cache manifest for the given root, networks, and fallbacks.

  • param: String root

  • param: Array networks

  • param: Array fallbacks

  • return: Function

  • api: public

module.exports = function cacheManifest(root, networks, fallbacks) {
    root = root || process.cwd();
    var suffix = "";

    // List of networks as an array of strings
    if (networks) {
        suffix += "\n\nNETWORK:\n" + networks.join("\n");
    }

    // List of fallbacks as key/value pairs
    if (fallbacks) {
        suffix += "\n\nFALLBACK:\n" +
            fallbacks.map(function (second, first) {
                return first + " " + second;
            }).join("\n");
    }

    return function handle(req, res, next) {
        if (Url.parse(req.url).pathname === "/cache.manifest") {
            Utils.find(root, (/./), function (err, files) {
                var latestMtime = 0;
                files = files.map(function (entry) {
                    if (entry.mtime > latestMtime) {
                        latestMtime = entry.mtime;
                    }
                    return entry.path.substr(1);
                });
                var manifest = "CACHE MANIFEST\n"
                    + "# " + latestMtime.toUTCString() + "\n"
                    + files.join("\n")
                    + suffix;

                res.writeHead(200, {
                    "Content-Type": "text/cache-manifest",
                    "Last-Modified": latestMtime.toUTCString(),
                    "Content-Length": manifest.length
                });
                res.end(manifest);
            });
            return;
        }
        next();
    };
};

conditionalGet

lib/connect/middleware/conditionalGet.js

Conditional GET request support.

  • return: Function

  • api: public

module.exports = function conditionalGet(){

    return function handle(req, res, next) {
        // Skip all requests that are not conditional gets.
        if (!(req.method === "GET" &&
             (req.headers["if-modified-since"] || req.headers["if-none-match"])
           )) {
            next();
            return;
        }

        var since = req.headers["if-modified-since"],
            oldEtag = req.headers["if-none-match"],
            writeHead = res.writeHead,
            write = res.write,
            end = res.end;

        since = since && Date.parse(since).valueOf();

        res.writeHead = function (code, headers) {
            var lastModified = headers["Last-Modified"],
                etag = headers["Etag"];
            lastModified = lastModified && Date.parse(lastModified).valueOf();

            // If there is no match, then move on.
            if (!(code === 200 &&
                  (lastModified === since || oldEtag === etag)
               )) {
                res.writeHead = writeHead;
                res.writeHead(code, headers);
                return;
            }

            // Ignore writes
            res.write = function () {};

            res.end = function () {
                // Put the original functions back on.
                res.writeHead = writeHead;
                res.write = write;
                res.end = end;

                // Filter out any Content based headers since there is no
                // content.
                var newHeaders = {};
                headers.forEach(function (value, key) {
                    if (key.indexOf("Content") < 0) {
                        newHeaders[key] = value;
                    }
                });
                res.writeHead(304, newHeaders);
                res.end();
            };
        };

        next();
    };
};

compiler

lib/connect/middleware/compiler.js

Module dependencies.

var fs = require('fs'),
    path = require('path');

Bundled compilers:

,P
var compilers = exports.compilers = {
    sass: {
        match: /\.css$/,
        ext: '.sass',
        compile: function(str, fn){
            try {
                var css = require('sass').render(str);
            } catch (err) {
                return fn(err);
            }
            fn(null, css);
        }
    },
    less: {
        match: /\.css$/,
        ext: '.less',
        compile: function(str, fn){
            try {
                require('less').render(str, fn);
            } catch (err) {
                fn(err);
            }
        }
    }
};

Setup compiler.

Options

  • src Source directory, defaults to CWD.
  • dest Destination directory, defaults src.
  • enable Array of enabled compilers.

Compilers

  • sass Compiles cass to css
  • less Compiles less to css

  • param: Object options

  • api: public

module.exports = function compiler(options){
    options = options || {};
    
    var srcDir = process.connectEnv.compilerSrc || options.src || process.cwd(),
        destDir = process.connectEnv.compilerDest || options.desc || srcDir,
        enable = options.enable;
    
    if (!enable || enable.length === 0) {
        throw new Error(s "enable" option is not set, nothing will be compiled.');
    }
    
    return function(req, res, next){
        for (var i = 0, len = enable.length; i < len; ++i) {
            var name = enable[i],
                compiler = compilers[name];
            if (compiler.match.test(req.url)) {
                var src = (srcDir + req.url).replace(compiler.match, compiler.ext),
                    dest = destDir + req.url;

                // Compare mtimes
                fs.stat(src, function(err, srcStats){
                    if (err) {
                        if (err.errno === process.ENOENT) {
                            res.writeHead(404, { 'Content-Type': 'text/plain' });
                            res.end('Not Found');
                        } else {
                            next(err);
                        }
                    } else {
                        fs.stat(dest, function(err, destStats){
                            if (err) {
                                // Oh snap! it does not exist, compile it
                                if (err.errno === process.ENOENT) {
                                    compile();
                                } else {
                                    next(err);
                                }
                            } else {
                                // Source has changed, compile it
                                if (srcStats.mtime > destStats.mtime) {
                                    compile();
                                } else {
                                    // Defer file serving
                                    next();
                                }
                            }
                        })
                    }
                });

                // Compile to the destination
                function compile() {
                    fs.readFile(src, 'utf8', function(err, str){
                        if (err) {
                            next(err);
                        } else {
                            compiler.compile(str, function(err, str){
                                if (err) {
                                    next(err);
                                } else {
                                    fs.writeFile(dest, str, 'utf8', function(err){
                                        next(err);
                                    });
                                }
                            });
                        }
                    });
                }
                return;
            }
        }
        next();
    };
};

cookieDecoder

lib/connect/middleware/cookieDecoder.js

Module dependencies.

var utils = require('./../utils');

Parse Cookie header and populate req.cookies.

  • return: Function

  • api: public

module.exports = function(){
    return function handle(req, res, next) {
        var cookie = req.headers.cookie;
        req.cookies = {};
        if (cookie) {
            process.nextTick(function(){
                try {
                    req.cookies = utils.parseCookie(cookie);
                    delete req.headers.cookie;
                } catch (err) {
                    // Ignore
                }
                next();
            });
        } else {
            next();
        }
    };
};

errorHandler

lib/connect/middleware/errorHandler.js

Module dependencies.

var utils = require('./../utils'),
    sys = require('sys'),
    fs = require('fs');

Setup error handler with the given options.

Options

  • showStack respond with both the error message and stack trace. Defaults to false
  • showMessage respond with the exception message only. Defaults to false
  • dumpExceptions dump exceptions to stderr (without terminating the process). Defaults to false

  • param: Object options

  • return: Function

  • api: public

module.exports = function errorHandler(options){
    options = options || {};

    // Defaults
    var showStack = options.showStack,
        showMessage = options.showMessage,
        dumpExceptions = options.dumpExceptions;
    
    // --showErrorStack
    if (process.connectEnv.showErrorStack !== undefined) {
        showStack = op = utils.toBoolean(process.connectEnv.showErrorStack);
    }
    
    // --showErrorMessage
    if (process.connectEnv.showErrorMessage !== undefined) {
        showMessage = utils.toBoolean(process.connectEnv.showErrorMessage);
    }
    
    // --dumpExceptions
    if (process.connectEnv.dumpExceptions !== undefined) {
        dumpExceptions = utils.toBoolean(process.connectEnv.dumpExceptions);
    }
    
    return function(err, req, res, next){
        if (dumpExceptions) {
            sys.error(err.stack);
        }
        if (showStack) {
            var accept = req.headers.accept || '';
            if (accept.indexOf('html') !== -1) {
                fs.readFile(__dirname + '/../public/style.css', function(e, style){
                    style = style.toString('ascii');
                    fs.readFile(__dirname + '/../public/error.html', function(e, html){
                        var stack = err.stack
                            .split('\n').slice(1)
                            .map(function(v){ return '
  • ' + v + '
  • '
    ; }).join(''); html = html .toString('utf8') .replace('{style}', style) .replace('{stack}', stack) .replace(/\{error\}/g, err.toString()); res.writeHead(500, { 'Content-Type': 'text/html' }); res.end(html); }); }); } else if (accept.indexOf('json') !== -1) { var json = JSON.stringify({ error: err }); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(json); } else { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(err.stack); } } else { var body = showMessage ? err.toString() : 'Internal Server Error'; res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end(body); } }; };

    flash

    lib/connect/middleware/flash.js

    Module dependencies.

    var utils = require('./../utils');

    Provide req.flash().

    • return: Function

    • api: public

    module.exports = function flash(){
        return function handle(req, res, next) {

    Queue flash msg of the given type.

    Examples

     req.flash('info', 'email sent');
     req.flash('error', 'email delivery failed');
     req.flash('info', 'email re-sent');
     // => 2
    
     req.flash('info');
     // => ['email sent', 'email re-sent']
    
     req.flash('info');
     // => []
    
     req.flash();
     // => { error: ['email delivery failed'], info: [] }
    

    • param: String type

    • param: String msg

    • return: Array | Object | Number

    • api: public

    req.flash = function(type, msg){
                var msgs = this.session.flash = this.session.flash || {};
                if (type && msg) {
                    msg = utils.miniMarkdown(utils.htmlEscape(msg));
                    return (msgs[type] = msgs[type] || []).push(msg);
                } else if (type) {
                    var arr = msgs[type];
                    delete msgs[type];
                    return arr || [];
                } else {
                    this.session.flash = {};
                    return msgs;
                }
            };
        
            next();
        };
    };

    format

    lib/connect/middleware/format.js

    Module dependencies.

    var connect = require('./../index'),
        sys = require('sys');

    Extract url “formats” aka extensions from the request url, and assigns req.format.

    Examples

     // curl http://localhost:3000/users.json
     handle: function(req, res, next){
        switch (req.format) {
            case 'json':
                sys.puts(req.url);
                // => "users"
                // respond with json
                break;
            default:
                // respond with your default format
        }
     }
    

    • return: Function

    • api: public

    module.exports = function format(){
        return function handle(req, res, next) {
            if (/\.(\w+)($|#|\?)/.exec(req.url)) {
                req.format = RegExp.$1;
                req.url = req.url.replace('.' + req.format, '');
            }
            next();
        };
    };

    gzip

    lib/connect/middleware/gzip.js

    Module dependencies.

    var child_process = require('child_process'),
        sys = require('sys');

    Provides gzip compression via the gzip executable.

    • return: Function

    • api: public

    module.exports = function gzip(){
        return function handle(req, res, next) {
            var writeHead = res.writeHead,
                write = res.write,
                end = res.end;
    
            res.writeHead = function (code, headers) {
                var type = headers["Content-Type"],
                    accept = req.headers["accept-encoding"];
                    
                if (!(code === 200 && accept && accept.indexOf('gzip') >= 0
                      && type && (/(text|javascript|json)/).test(type))) {
                    res.writeHead = writeHead;
                    res.writeHead(code, headers);
                    return;
                }
    
                headers["Content-Encoding"] = "gzip";
                delete headers["Content-Length"];
                
                var gzip = child_process.spawn("gzip", ["-9"]);
    
                res.write = function (chunk, encoding) {
                    gzip.stdin.write(chunk, encoding);
                };
    
                res.end = function (chunk, encoding) {
                    if (chunk) {
                        res.write(chunk, encoding);
                    }
                    gzip.stdin.end();
                };
    
                gzip.stdout.addListener('data', function (chunk) {
                    write.call(res, chunk);
                });
    
                gzip.addListener("exit", function (code) {
                    res.write = write;
                    res.end = end;
                    res.end();
                });
                
                res.writeHead = writeHead;
                res.writeHead(code, headers);
    
            };
    
            next();
        };
    };

    jsonrpc

    lib/connect/middleware/jsonrpc.js

    Module dependencies.

    var sys = require('sys'),
        parse = require('url').parse,
        Buffer = require('buffer').Buffer,
        http = require('http');

    Export the setup() function.

    
    exports = module.exports = jsonrpc;

    JSON-RPC version.

    
    var VERSION = exports.VERSION = '2.0';

    JSON parse error.

    
    var PARSE_ERROR = exports.PARSE_ERROR = -32700;

    Invalid request due to invalid or missing properties.

    - **
    var INVALID_REQUEST = exports.INVALID_REQUEST = -32600;

    Service method does not exist.

    sing proper
    var METHOD_NOT_FOUND = exports.METHOD_NOT_FOUND = -32601;

    Invalid parameters.

    var INVALID_PARAMS = exports.INVALID_PARAMS = -32602;

    Internal JSON-RPC error.

    var INTERNAL_ERROR = exports.INTERNAL_ERROR = -32603;

    Default error messages.

    var errorMessages = exports.errorMessages = {};
    errorMessages[PARSE_ERROR] = 'Parse Error.';
    errorMessages[INVALID_REQUEST] = 'Invalid Request.';
    errorMessages[METHOD_NOT_FOUND] = 'Method Not Found.';
    errorMessages[INVALID_PARAMS] = 'Invalid Params.';
    errorMessages[INTERNAL_ERROR] = 'Internal Error.';

    Accepts any number of objects, exposing their methods.

    • param: Object

    • return: Function

    • api: public

    f�Q
    function jsonrpc(services) {
        services = services || {};
        
        // Merge methods
        for (var i = 0, len = arguments.length; i < len; ++i) {
            arguments[i].forEach(function(val, key){
                services[key] = val;
            });
        }

    Handle JSON-RPC request.

    • param: Object rpc

    • param: Function respond

    function handleRequest(rpc, respond){
            if (validRequest(rpc)) {
                var method = services[rpc.method];
                if (typeof method === 'function') {
                    var params = [];
                    if (rpc.params instanceof Array) {
                        params = rpc.params;
                    } else if (typeof rpc.params === 'object') {
                        var names = method.toString().match(/\((.*?)\)/)[1].match(/[\w]+/g);
                        if (names) {
                            for (var i = 0, len = names.length; i < len; ++i) {
                                params.push(rpc.params[names[i]]);
                            }
                        } else {
                            // Function does not have named parameters
                            return respond({ error: { code: INVALID_PARAMS, message: 'This service does not support named parameters.' }});
                        }
                    }
                    function reply(err, result){
                        if (err) {
                            if (typeof err === 'number') {
                                respond({
                                    error: {
                                        code: err
                                    }
                                });
                            } else {
                                respond({
                                    error: {
                                        code: err.code || INTERNAL_ERROR,
                                        message: err.message
                                    }
                                });
                            }
                        } else {
                            respond({
                                result: result
                            });
                        }
                    }
                    method.apply(reply, params);
                } else {
                    respond({ error: { code: METHOD_NOT_FOUND }});
                }
            } else {
                respond({ error: { code: INVALID_REQUEST }});
            }
        }
    
        return function handle(req, res, next) {
            var me = this,
                contentType = req.headers['content-type'] || '';
            if (req.method === 'POST' && contentType === 'application/json') {
                var data = '';
                req.setEncoding('utf8');
                req.addListener('data', function(chunk) { data += chunk; });
                req.addListener('end', function() {
    
                    // Attempt to parse incoming JSON string
    
                    try {
                        var rpc = JSON.parse(data),
                            batch = rpc instanceof Array;
                    } catch (err) {
                        return respond(normalize(rpc, { error: { code: PARSE_ERROR }}));
                    }

    Normalize response object.

    function normalize(rpc, obj) {
                        obj.id = rpc && typeof rpc.id === 'number'
                            ? rpc.id
                            : null;
                        obj.jsonrpc = VERSION;
                        if (obj.error && !obj.error.message) {
                            obj.error.message = errorMessages[obj.error.code];
                        }
                        return obj;
                    }

    Respond with the given response object.

    
    function respond(obj) {
                        var body = JSON.stringify(obj);
                        res.writeHead(200, {
                            'Content-Type': 'application/json',
                            'Content-Length': Buffer.byteLength(body)
                        });
                        res.end(body);
                    }
    
                    // Handle requests
    
                    if (batch) {
                        var responses = [],
                            len = rpc.length,
                            pending = len;
                        for (var i = 0; i < len; ++i) {
                            (function(rpc){
                                handleRequest.call(me, rpc, function(obj){
                                    responses.push(normalize(rpc, obj));
                                    if (!--pending) {
                                        respond(responses);
                                    }
                                });
                            })(rpc[i]);
                        }
                    } else {
                        handleRequest.call(me, rpc, function(obj){
                            respond(normalize(rpc, obj));
                        });
                    }
                });
            } else {
                next();
            }
        };
    };

    lint

    lib/connect/middleware/lint.js

    Module dependencies.

    var connect = require('./../index'),
        sys = require('sys');

    Setup lint for the given server.

    • return: Function

    • api: public

    module.exports = function lint(server){
    
        // Ensure a server is passed
        if (!server) {
            throw new Error('lint "server" must be passed.');
        }
    
        // Warn unless in development mode
        if (process.connectEnv.name !== 'development') {
            warn('"lint" middleware should never be enabled outside of the development environment');
        }
        
        // Check the stack
        checkStack(server.stack);
    
        // Do nothing
        return function(req, res, next){
            next();
        }
    };

    logger

    lib/connect/middleware/logger.js

    Log requests with the given options.

    Options

    • format Format string, see below for tokens
    • stream Output stream, defaults to stdout

    Tokens

    • :req[header] ex: :req[Accept]
    • :res[header] ex: :res[Content-Length]
    • :http-version
    • :response-time
    • :remote-addr
    • :date
    • :method
    • :url
    • :referrer
    • :user-agent
    • :status

    • return: Function

    • api: public

    module.exports = function logger(options) {
        options = options || {};
    
        var format = process.connectEnv.logFormat || options.format,
            stream = options.stream || process.stdout;
    
        return function handle(req, res, next) {
            var start = +new Date,
                statusCode,
                resHeaders,
                writeHead = res.writeHead,
                end = res.end,
                url = req.url;
    
            // Proxy for statusCode.
            res.writeHead = function(code, headers){
                res.writeHead = writeHead;
                res.writeHead(code, headers);
                res.statusCode = statusCode = code;
                res.headers = resHeaders = headers;
            };
    
            // Proxy end to output a line to the provided logger.
            if (format) {
                res.end = function(chunk, encoding) {
                    res.end = end;
                    res.end(chunk, encoding);
                    res.responseTime = +new Date - start;
                    stream.write(format(format, req, res) + '\n', 'ascii');
                };
            } else {
                res.end = function(chunk, encoding) {
                    res.end = end;
                    res.end(chunk, encoding);
    
                    stream.write((req.socket && req.socket.remoteAddress)
                     + ' - - [' + (new Date).toUTCString() + ']'
                     + ' "' + req.method + ' ' + url
                     + ' HTTP/' + req.httpVersionMajor + '.' + req.httpVersionMinor + '" '
                     + statusCode + ' ' + (resHeaders['Content-Length'] || '-')
                     + ' "' + (req.headers['referer'] || req.headers['referrer'] || '')
                     + '" "' + (req.headers['user-agent'] || '') + '"\n', 'ascii');
                };
            }
    
            // Fall through to the next layer.
            next();
        };
        
    };

    methodOverride

    lib/connect/middleware/methodOverride.js

    Module dependencies.

    var queryString = require('querystring');

    Valid http methods.

    • type: Array
    var methods = ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'OPTIONS'];

    Pass an optional key to use when checking for a method override, othewise defaults to _method.

    • param: String key

    • return: Function

    • api: public

    module.exports = function methodOverride(key){
        key = key || "_method";
        return function(req, res, next) {
            if (typeof req.body === 'object' && key in req.body) {
                var method = req.body[key].toUpperCase();
                if (methods.indexOf(method) >= 0) {
                    req.method = method;
                    delete req.body[key];
                }
            }
            next();
        };
    };
    
    

    pubsub

    lib/connect/middleware/pubsub.js

    Module dependencies.

    var sys = require('sys');

    Subscriber cache.

    
    var cache = {};

    redirect

    lib/connect/middleware/redirect.js

    Module dependencies.

    var connect = require('./../index'),
        sys = require('sys');

    Provide res.redirect().

    Examples

    // Absolute
    res.redirect('http://google.com');
    
    // Relative + custom status
    res.redirect('/foo', 301);
    
    // Referr?er 
    res.redirect('back');
    

    • return: Function

    • api: public

    `:
    module.exports = function redirect(){
        return function handle(req, res, next) {
            var map = {
                back: req.headers.referrer || req.headers.referer
            };
    
            res.redirect = function(url, code){
                this.writeHead(code || 302, {
                    'Location': map[url] || url
                });
                this.end();
            };
    
            next();
        };
    };
    

    repl

    lib/connect/middleware/repl.js

    Module dependencies.

    var net = require('net'),
        repl = require('repl');

    Start a REPL on the given unix domain socket path.

    Options

    • sockect Unix domain socket path. Defaults to “/tmp/connect.sock”
    • prompt REPL prompt string. Defaults to “node> ”

    Example

    $ rlwrap telnet /tmp/connect.sock
    

    • param: String prompt

    • param: String socket path

    • return: Function

    • api: public

    module.exports = function repl(prompt, socket){
        prompt = process.connectEnv.replPrompt || prompt || 'node> ';
        socket = process.connectEnv.replSocket || socket || '/tmp/connect.sock';
        net.createServer(function (stream) {
            repl.start(prompt, stream);
        }).listen(socket);
        return function(req, res, next){
            // Pass through for now
            next();
        }
    };
    

    responseTime

    lib/connect/middleware/responseTime.js

    Responds with the X-Response-Time header in milliseconds.

    • return: Function

    • api: public

    Q
    module.exports = function responseTime(){
        return function(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();
        };
    };

    router

    lib/connect/middleware/router.js

    Module dependencies.

    var parse = require('url').parse;

    Provides Sinatra and Express like routing capabilities.

    Examples

    connect.router(function(app){
        app.get('/user/:id', function(req, res, params){
            // populates params.id
        });
    })
    

    • param: Function fn

    • return: Function

    • api: public

    module.exports = function router(fn){
        var routes;
    
        if (fn) {
            routes = {};
            fn.call(this, {
                post: method.call(this, 'post'),
                get: method.call(this, 'get'),
                put: method.call(this, 'put'),
                del: method.call(this, 'del')
            });
        } else {
            throw new Error('router provider requires a callback function');
        }
    
        function method(name) {
            var self = this,
                localRoutes = routes[name] = routes[name] || [];
            return function(path, fn){
                var keys = [];
                path = path instanceof RegExp
                    ? path
                    : normalizePath(path, keys);
                localRoutes.push(fn, path, keys);
                return self;
            };
        }
    
        return function(req, res, next){
            var self = this;
            process.nextTick(function(){
                var route;
                if (route = match(req, routes)) {
                    req.params = req.params || {};
                    req.params.path = route._params;
                    route.call(self, req, res, route._params);
                } else {
                    next();
                }
            });
        };
    
    
    }

    memory

    lib/connect/middleware/session/memory.js

    Module dependencies.

    var sys = require('sys'),
        utils = require('./../../utils'),
        Store = require('./store').Store,
        Session = require('./session').Session;

    Initialize MemoryStore with the given options.

    • param: Object options

    • api: public

    var MemoryStore = exports.MemoryStore = function MemoryStore(options) {
        this.sessions = {};
        options = options || {};
        this.key = 'connect.sid';
        
        // Default reapInterval to 10 minutes
        this.reapInterval = options.reapInterval || 600000;
        
        // Default maxAge to 4 hours
        this.maxAge = options.maxAge || 14400000;
        
        // Cookie options
        this.cookie = utils.merge({ path: '/', httpOnly: true }, options.cookie);
        
        // Reap stale sessions 
        if (this.reapInterval !== -1) {
            setInterval(function(self){
                self.reap(self.maxAge);
            }, this.reapInterval, this);        
        }
    };
    
    sys.inherits(MemoryStore, Store);

    Destroy session for the given request.

    • param: IncomingRequest req

    • param: Function fn

    • api: public

    MemoryStore.prototype.destroy = function(req, fn){
        var key = this.fingerprint(req.session.id, req),
            destroyed = key in this.sessions;
        delete this.sessions[key];
        fn && fn(null, destroyed);
    };

    Fetch Session for the given request.

    • param: IncomingMessage req

    • param: Function fn

    • api: public

    MemoryStore.prototype.fetch = function(req, fn){
        var sid = req.cookies[this.key],
            key = this.fingerprint(sid, req);
        if (sid && this.sessions[key]) {
            fn(null, this.sessions[key]);
        } else {
            fn();
        }
    };

    Commit the given request’s session.

    • param: IncomingMessage req

    • param: Function fn

    • api: public

    MemoryStore.prototype.commit = function(req, fn){
        var key = this.fingerprint(req.session.id, req);
        this.sessions[key] = req.session;
        fn && fn();
    };

    Clear all sessions.

    • param: Function fn

    • api: public

    MemoryStore.prototype.clear = function(fn){
        this.sessions = {};
        fn && fn();
    };

    Fetch number of sessions.

    • param: Function fn

    • api: public

    MemoryStore.prototype.length = function(fn){
        fn(null, Object.keys(this.sessions).length);
    };
    

    session

    lib/connect/middleware/session/session.js

    Module dependencies.

    var utils = require('./../../utils');

    Initialize Session with the given sid.

    • param: String sid

    • api: public

    �dQ
    var Session = exports.Session = function Session(sid) {
        this.id = sid;
        this.touch();
    };

    Update lastAccess timestamp.

    • api: public
    Session.prototype.touch = function(){
        this.lastAccess = +new Date;
    };
    

    store

    lib/connect/middleware/session/store.js

    Module dependencies.

    var Session = require('./session').Session,
        utils = require('./../../utils');

    Fetch session for the given request.

    • param: IncomingMessage req

    • param: Function fn

    • api: public

    p><
    Store.prototype.fetch = function(req, fn){
        throw new Error(this.constructor.name + ' does not implement #fetch().');
    };

    Commit the given request’s session.

    • param: IncomingMessage req

    • param: Function fn

    • api: public

    Store.prototype.commit = function(req, fn){
        throw new Error(this.constructor.name + ' does not implement #commit().');
    };

    Clear all sessions.

    • param: Function fn

    • api: public

    Store.prototype.clear = function(fn){
        throw new Error(this.constructor.name + ' does not implement #clear().');
    };

    Destroy session for the given request.

    • param: IncomingRequest req

    • param: Function fn

    • api: public

    Store.prototype.destroy = function(req, fn){
        throw new Error(this.constructor.name + ' does not implement #destroy().');
    };

    Re-generate the given requests’s session.

    • param: IncomingRequest req

    • return: Function fn

    • api: public

    Store.prototype.regenerate = function(req, fn){
        this.destroy(req, function(){
            req.session = req.sessionStore.createSession();
            fn.apply(this, arguments);
        });
    };

    Fetch number of sessions.

    • param: Function fn

    • api: public

    tion
    Store.prototype.length = function(fn){
        throw new Error(this.constructor.name + ' does not implement #length().');
    };

    Create a new Session instance.

    • return: Session

    • api: public

    Store.prototype.createSession = function(){
        return new Session(utils.uid());
    };
    

    session

    lib/connect/middleware/session.js

    Module dependencies.

    var MemoryStore = require('./session/memory').MemoryStore,
        utils = require('./../utils');

    Setup session store with the given options.

    Options

    • store Session store instance
    • fingerprint Custom fingerprint hashing function

    • param: Object options

    • return: Function

    • api: public

    module.exports = function session(options){
        options = options || {};
        
        // Default memory store
        store = options.store || new MemoryStore;
        
        // Default fingerprint hashing function
        store.fingerprint = options.fingerprint || function(sid, req){
            return sid 
                + req.socket.remoteAddress
                + (req.headers['user-agent'] || '');
        };
        
        return function(req, res, next){
            // Expose store
            req.sessionStore = store;
    
            if (req.cookies) {
                // Commit session
                var writeHead = res.writeHead;
                res.writeHead = function(status, headers){
                    headers = headers || {};
                    res.writeHead = writeHead;
                    // TODO: async? ...
                    store.commit(req);
                    // TODO: abstract
                    store.cookie.expires = new Date(+new Date() + store.maxAge);
                    headers['Set-Cookie'] = utils.serializeCookie(store.key, req.session.id, store.cookie);
                    return res.writeHead(status, headers);
                };
    
                // Fetch session
                store.fetch(req, function(err, sess){
                    req.session = sess || store.createSession();
                    req.session.touch();
                    next(err);
                });
            } else {
                next();
            }
        }
    };

    staticProvider

    lib/connect/middleware/staticProvider.js

    Module dependencies.

    var fs = require('fs'),
        Url = require('url'),
        Buffer = require('buffer').Buffer,
        Path = require('path'),
        utils = require('./../utils');

    Browser cache lifetime of one hour.

    • type: Number
    var lifetime = 1000 * 60 * 60;

    Static file server.

    Options

    • root Root path from which to serve static files.

    • param: String root

    • return: Function

    • api: public

    Q
    module.exports = function staticProvider(root){
        root = process.connectEnv.staticRoot || root || process.cwd();
        return function handle(req, res, next) {
            var url = Url.parse(req.url);
    
            var pathname = url.pathname.replace(/\.\.+/g, '.'),
                filename = Path.join(root, pathname);
    
            if (filename[filename.length - 1] === "/") {
                filename += "index.html";
            }
    
            // Buffer any events that fire while waiting on the stat.
            var events = [];
            function onData() {
                events.push(["data"].concat(toArray(arguments)));
            }
            function onEnd() {
                events.push(["end"].concat(toArray(arguments)));
            }
            req.addListener("data", onData);
            req.addListener("end", onEnd);
    
            fs.stat(filename, function (err, stat) {
    
                // Stop buffering events
                req.removeListener("data", onData);
                req.removeListener("end", onEnd);
    
                // Fall through for missing files, thow error for other problems
                if (err) {
                    if (err.errno === process.ENOENT) {
                        next();
                        // Refire the buffered events
                        for (var i = 0, len = events.length; i < len; ++i) {
                            req.emit.apply(req, events[i]);
                        }
                        return;
                    }
                    next(err);
                    return;
                }
    
                // Serve the file directly using buffers
                function onRead(err, data) {
                    if (err) {
                        next(err);
                        return;
                    }
    
                    // For older versions of node convert the string to a buffer.
                    if (typeof data === 'string') {
                        var b = new Buffer(data.length);
                        b.write(data, "binary");
                        data = b;
                    }
    
                    res.writeHead(200, {
                        "Content-Type": utils.mime.type(filename),
                        "Content-Length": data.length,
                        "Last-Modified": stat.mtime.toUTCString(),
                        // Cache in browser for 1 year
                        "Cache-Control": "public max-age=" + 31536000
                    });
                    res.end(data);
                }
    
                // Node before 0.1.95 doesn't do buffers for fs.readFile
                if (process.version < "0.1.95") {
                    // sys.debug("Warning: Old node version has slower static file loading");
                    fs.readFile(filename, "binary", onRead);
                } else {
                    fs.readFile(filename, onRead);
                }
            });
        };
        
    };

    vhost

    lib/connect/middleware/vhost.js

    Setup vhost for the given hostname and server.

    Examples

    connect.createServer(
      connect.vhost('foo.com', 
         connect.createServer(...middleware...)
     ),
      connect.vhost('bar.com', 
          connect.createServer(...middleware...)
      )
    );
    

    • param: String hostname

    • param: Server server

    • return: Function

    • api: public

    module.exports = function vhost(hostname, server){
        if (!hostname) {
            throw new Error('vhost hostname required');
        }
        if (!server) {
            throw new Error('vhost server required');
        }
        return function(req, res, next){
            var host = req.headers.host.split(':')[0];
            if (host === hostname) {
                server.handle(req, res);
            } else {
                next();
            }
        };
    };