ConnectHigh performance middleware for node. | |
| lib/connect/middleware/basicAuth.js |
module.exports = function basicAuth(callback, realm) {
realm = realm || 'Authorization Required';
function unauthorized(res) {
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="' + realm + '"' });
res.end();
}
function badRequest(res) {
res.writeHead(400);
res.end('Bad Request');
}
return function(req, res, next) {
var authorization = req.headers.authorization;
if (!authorization) return unauthorized(res);
var parts = authorization.split(' ')
, scheme = parts[0]
, credentials = new Buffer(parts[1], 'base64').toString().split(':');
if ('Basic' != scheme) return badRequest(res);
if (callback(credentials[0], credentials[1])) {
req.remoteUser = credentials[0];
next();
} else {
unauthorized(res);
}
}
};
|
|
| lib/connect/middleware/bodyDecoder.js |
Module dependencies.
|
var queryString = require('querystring');
|
Decode request bodies.
return: Function api: public
|
exports = module.exports = function bodyDecoder(){
return function bodyDecoder(req, res, next) {
var decoder = exports.decode[mime(req)];
if (decoder && !req.body) {
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();
}
}
};
|
Supported decoders.
- application/x-www-form-urlencoded
- application/json
|
exports.decode = {
'application/x-www-form-urlencoded': queryString.parse,
'application/json': JSON.parse
};
|
| lib/connect/middleware/compiler.js |
Module dependencies.
|
var fs = require('fs'),
path = require('path'),
url = require('url');
|
Require cache.
|
var cache = {};
|
Setup compiler.
Options
src Source directory, defaults to CWD.dest Destination directory, defaults src .enable Array of enabled compilers.
Compilers
sass Compiles cass to cssless Compiles less to csscoffeescript Compiles coffee to js
param: Object options api: public
|
exports = module.exports = function compiler(options){
options = options || {};
var srcDir = process.connectEnv.compilerSrc || options.src || process.cwd(),
destDir = process.connectEnv.compilerDest || options.dest || srcDir,
enable = options.enable;
if (!enable || enable.length === 0) {
throw new Error(s "enable" option is not set, nothing will be compiled.');
}
return function compiler(req, res, next){
if (req.method !== 'GET') return next();
var pathname = url.parse(req.url).pathname;
for (var i = 0, len = enable.length; i < len; ++i) {
var name = enable[i],
compiler = compilers[name];
if (compiler.match.test(pathname)) {
var src = (srcDir + pathname).replace(compiler.match, compiler.ext),
dest = destDir + pathname;
fs.stat(src, function(err, srcStats){
if (err) {
if (err.errno === process.ENOENT) {
next();
} else {
next(err);
}
} else {
fs.stat(dest, function(err, destStats){
if (err) {
if (err.errno === process.ENOENT) {
compile();
} else {
next(err);
}
} else {
if (srcStats.mtime > destStats.mtime) {
compile();
} else {
next();
}
}
});
}
});
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();
};
};
|
Bundled compilers:
|
var compilers = exports.compilers = {
sass: {
match: /\.css$/,
ext: '.sass',
compile: function(str, fn){
var sass = cache.sass || (cache.sass = require('sass'));
try {
fn(null, sass.render(str));
} catch (err) {
fn(err);
}
}
},
less: {
match: /\.css$/,
ext: '.less',
compile: function(str, fn){
var less = cache.less || (cache.less = require('less'));
try {
less.render(str, fn);
} catch (err) {
fn(err);
}
}
},
coffeescript: {
match: /\.js$/,
ext: '.coffee',
compile: function(str, fn){
var coffee = cache.coffee || (cache.coffee = require('coffee-script'));
try {
fn(null, coffee.compile(str));
} catch (err) {
fn(err);
}
}
}
};
|
| 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 cookieDecoder(){
return function cookieDecoder(req, res, next) {
var cookie = req.headers.cookie;
req.cookies = {};
if (cookie) {
try {
req.cookies = utils.parseCookie(cookie);
delete req.headers.cookie;
} catch (err) {
}
next();
} else {
next();
}
};
};
|
| lib/connect/middleware/errorHandler.js |
Module dependencies.
|
var utils = require('./../utils'),
sys = require('sys'),
url = require('url'),
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 || {};
var showStack = options.showStack,
showMessage = options.showMessage,
dumpExceptions = options.dumpExceptions,
formatUrl = options.formatUrl;
if (process.connectEnv.showErrorStack !== undefined) {
showStack = utils.toBoolean(process.connectEnv.showErrorStack);
}
if (process.connectEnv.showErrorMessage !== undefined) {
showMessage = utils.toBoolean(process.connectEnv.showErrorMessage);
}
if (process.connectEnv.dumpExceptions !== undefined) {
dumpExceptions = utils.toBoolean(process.connectEnv.dumpExceptions);
}
var formatLine = function(v){ return '<li>' + v + '</li>'; };
if (formatUrl) {
var parts, re = /(\/[^\(\)]+):(\d+):(\d+)/;
var formatters = {
'file': function(parts) { return {'protocol':'file', 'hostname':''+parts[1]}; },
'txmt': function(parts) { return {
'protocol':'txmt', 'hostname':'//open',
'query':{'url':'file://'+parts[1],'line':parts[2],'column':parts[3]}};
}
};
formatLine = function(v) {
parts = v.match(re);
if (parts) v = v.replace(parts[0],'<a href="'+url.format( formatters[formatUrl](parts) )+'">'+parts[0]+'</a>');
return '<li>' + v + '</li>';
};
}
return function errorHandler(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(formatLine).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': 'text/plain' });
res.end(err.stack);
}
} else {
var body = showMessage
? err.toString()
: 'Internal Server Error';
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end(body);
}
};
};
|
| lib/connect/middleware/favicon.js |
Module dependencies.
|
var fs = require('fs'),
utils = require('../utils');
|
Favicon cache.
|
var icon;
|
By default serves the connect favicon, or the favicon
located by the given path.
Examples
connect.createServer(
connect.favicon()
);
connect.createServer(
connect.favicon(__dirname + '/public/favicon.ico')
);
param: String path return: Function api: public
|
module.exports = function favicon(path){
path = path || __dirname + '/../public/favicon.ico';
return function favicon(req, res, next){
if (req.url === '/favicon.ico') {
if (icon) {
res.writeHead(200, icon.headers);
res.end(icon.body);
} else {
fs.readFile(path, function(err, buf){
if (err) return next(err);
icon = {
headers: {
'Content-Type': 'image/x-icon',
'Content-Length': buf.length,
'ETag': utils.md5(buf),
'Cache-Control': 'public max-age=3600'
},
body: buf
}
res.writeHead(200, icon.headers);
res.end(icon.body);
});
}
} else {
next();
}
};
};
|
| lib/connect/middleware/gzip-proc.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 gzip(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)
&& headers["Content-Encoding"] === undefined)) {
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();
};
};
|
| lib/connect/middleware/gzip.js |
try {
module.exports = require('./gzip-compress');
} catch (e) {
if (/^Cannot find module /.test(e.message))
module.exports = require('./gzip-proc');
else
throw e;
}
|
|
| lib/connect/middleware/gzip-compress.js |
Module dependencies.
|
var compress = require('compress');
|
Provides gzip compression via the node-compress library.
return: Function api: public
|
module.exports = function gzip(){
return function gzip(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)
&& headers["Content-Encoding"] === undefined)) {
res.writeHead = writeHead;
res.writeHead(code, headers);
return;
}
headers["Content-Encoding"] = "gzip";
delete headers["Content-Length"];
var gzip = new compress.GzipStream();
res.write = function (chunk, encoding) {
gzip.setInputEncoding(encoding);
gzip.write(chunk);
};
res.end = function (chunk, encoding) {
if (chunk) {
res.write(chunk, encoding);
}
gzip.close();
};
gzip.addListener('data', function (chunk) {
write.call(res, chunk);
});
gzip.addListener('error', function(err) {
res.write = write;
res.end = end;
next(err);
});
gzip.addListener('end', function (code) {
res.write = write;
res.end = end;
res.end();
});
res.writeHead = writeHead;
res.writeHead(code, headers);
};
next();
};
};
|
| lib/connect/middleware/cache.js |
Module dependencies.
|
var Buffer = require('buffer').Buffer;
|
Cache in memory for the given cacheDuration .
|
module.exports = function cache(cacheDuration){
var cache = {},
queue = {};
return function cache(req, res, next) {
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["Content-Length"] = resp.body.length;
if (!resp.body.length) resp.body = undefined;
if (localQueue) {
for (var i = 0, l = localQueue.length; i < l; i++) {
var localRes = localQueue[i];
localRes.writeHead(resp.code, headers);
localRes.end(resp.body);
}
delete queue[key];
} else {
res.writeHead(resp.code, headers);
res.end(resp.body);
}
}
if (cache[key]) {
serve();
return;
}
var localQueue = queue[key];
if (localQueue) {
localQueue[localQueue.length] = res;
return;
}
localQueue = queue[key] = [res];
res.writeHead = function (setCode, setHeaders) {
code = setCode;
headers = setHeaders;
};
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 && chunk.length) {
res.write(chunk, encoding);
}
var body = new Buffer(totalSize);
var offset = 0;
chunks.forEach(function (chunk) {
chunk.copy(body, offset);
offset += chunk.length;
});
cache[key] = {
body: body,
code: code,
headers: headers
};
res.writeHead = writeHead;
res.write = write;
res.end = end;
serve();
if (cacheDuration) {
setTimeout(function(){
delete cache[key];
}, cacheDuration);
} else {
delete cache[key];
}
};
next();
};
};
|
| lib/connect/middleware/logger.js |
Log buffer.
|
var buf = [];
|
Default log buffer duration.
|
var defaultBufferDuration = 1000;
|
Log requests with the given options .
Options
format Format string, see below for tokensstream Output stream, defaults to stdoutbuffer Buffer duration, defaults to 1000ms when true
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) {
if ('string' == typeof options) {
options = { format: options };
} else {
options = options || {};
}
var fmt = process.connectEnv.logFormat || options.format,
stream = options.stream || process.stdout,
buffer = options.buffer;
if (buffer) {
var realStream = stream;
setInterval(function(){
if (buf.length) {
realStream.write(buf.join(''), 'ascii');
buf.length = 0;
}
}, typeof buffer === 'number' ? buffer : defaultBufferDuration);
stream = {
write: function(str){
buf.push(str);
}
};
}
return function logger(req, res, next) {
var start = +new Date,
statusCode,
resHeaders,
writeHead = res.writeHead,
end = res.end,
url = req.url;
res.writeHead = function(code, headers){
res.writeHead = writeHead;
res.writeHead(code, headers);
res.statusCode = statusCode = code;
res._headers = resHeaders = headers || {};
};
if (fmt) {
res.end = function(chunk, encoding) {
res.end = end;
res.end(chunk, encoding);
res.responseTime = +new Date - start;
stream.write(format(fmt, 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');
};
}
next();
};
};
|
| lib/connect/middleware/methodOverride.js |
Module dependencies.
|
var queryString = require('querystring');
|
Valid http methods.
|
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 methodOverride(req, res, next) {
var method = req.method;
if (typeof req.body === 'object' && key in req.body) {
method = req.body[key];
delete req.body[key];
} else if (req.headers['x-http-method-override']) {
method = req.headers['x-http-method-override'];
}
method = method.toUpperCase();
if (methods.indexOf(method) >= 0) {
req.method = method;
}
next();
};
};
|
| 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 = "";
if (networks) {
suffix += "\n\nNETWORK:\n" + networks.join("\n");
}
if (fallbacks) {
suffix += "\n\nFALLBACK:\n" +
fallbacks.map(function (second, first) {
return first + " " + second;
}).join("\n");
}
return function cacheManifest(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();
};
};
|
| lib/connect/middleware/router.js |
Module dependencies.
|
var parse = require('url').parse
, querystring = require('querystring');
|
Expose router.
|
exports = module.exports = router;
|
Supported HTTP / WebDAV methods.
|
var _methods = exports.methods = [
'get'
, 'head'
, 'post'
, 'put'
, 'delete'
, 'connect'
, 'options'
, 'trace'
, 'copy'
, 'lock'
, 'mkcol'
, 'move'
, 'propfind'
, 'proppatch'
, 'unlock'
, 'report'
, 'mkactivity'
, 'checkout'
, 'merge'
];
|
Provides Sinatra and Express like routing capabilities.
Examples
connect.router(function(app){
app.get('/user/:id', function(req, res, next){
// populates req.params.id
});
})
TODO unroll into regular middleware, we have little need
for the custom dispatcher that I have now.
param: Function fn return: Function api: public
|
function router(fn){
var self = this
, methods = {}
, routes = {}
, params = {};
if (!fn) throw new Error('router provider requires a callback function');
_methods.forEach(function(method){
methods[method] = generateMethodFunction(method.toUpperCase());
});
methods.del = methods.delete;
methods.all = function(path, fn){
_methods.forEach(function(name){
methods[name](path, fn);
});
return self;
};
methods.param = function(name, fn){
params[name] = fn;
};
fn.call(this, methods);
function generateMethodFunction(name) {
var localRoutes = routes[name] = routes[name] || [];
return function(path, fn){
var keys = [];
path = path instanceof RegExp
? path
: normalizePath(path, keys);
localRoutes.push({
fn: fn
, path: path
, keys: keys
});
return self;
};
}
return function router(req, res, next){
var route
, self = this;
(function pass(i){
if (route = match(req, routes, i)) {
var i = 0
, keys = route._keys;
req.params = route._params;
(function param(err) {
try {
var key = keys[i++]
, val = req.params[key]
, fn = params[key];
if (err) {
next(err);
} else if (fn) {
if (1 == fn.length) {
req.params[key] = fn(val);
param();
} else {
fn(req, res, param, val);
}
} else if (!key) {
route.call(self, req, res, function(err){
if (err === true) {
next();
} else if (err) {
next(err);
} else {
pass(route._index+1);
}
});
} else {
param();
}
} catch (err) {
next(err);
}
})();
} else if ('OPTIONS' == req.method) {
options(req, res, routes);
} else {
next();
}
})();
};
}
|
Respond to OPTIONS.
|
function options(req, res, routes) {
var pathname = parse(req.url).pathname
, body = optionsFor(pathname, routes).join(',');
res.writeHead(200, {
'Content-Length': body.length
, 'Allow': body
});
res.end(body);
}
|
| 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){
if (!server) {
throw new Error('lint "server" must be passed.');
}
if (process.connectEnv.name !== 'development') {
warn('"lint" middleware should never be enabled outside of the development environment');
}
checkStack(server.stack);
return function(req, res, next){
next();
}
};
|
| 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
socket Unix domain socket path. Defaults to "/tmp/connect.sock"prompt REPL prompt string. Defaults to "node> "
Example
$ rlwrap telnet /tmp/connect.sock
|
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 repl(req, res, next){
next();
}
};
|
| lib/connect/middleware/session/store.js |
Module dependencies.
|
var Session = require('./session'),
utils = require('./../../utils');
|
Destroy session associated with the given sid
by passing null to Store#get() .
param: String sid param: Function fn api: public
|
Store.prototype.destroy = function(sid, fn){
this.set(sid, null, fn);
};
|
Re-generate the given requests's session.
|
Store.prototype.regenerate = function(req, fn){
var self = this;
this.destroy(req.sessionID, function(err, destroyed){
self.generate();
fn(err, destroyed);
});
};
|
| lib/connect/middleware/session.js |
Module dependencies.
|
var Session = require('./session/session'),
utils = require('./../utils');
|
Setup session store with the given options .
Options
store Session store instancefingerprint Custom fingerprint generating function
param: Object options return: Function api: public
|
exports = module.exports = function sessionSetup(options){
options = options || {};
var key = options.key || 'connect.sid';
var store = options.store || new (require('./session/memory'));
var fingerprint = options.fingerprint || function fingerprint(req) {
return req.headers['user-agent'] || '';
};
var secret = options.secret || "hackme";
return function sessionHandle(req, res, next) {
if (!req.cookies) {
next(new Error("session requires cookieDecoder to work properly"));
return;
}
var pause = utils.pause(req);
var writeHead = res.writeHead;
res.writeHead = function(status, headers){
if (req.session) {
req.session.touch();
store.set(req.sessionID, req.session);
}
var secured = store.cookie.secure && (req.connection.secure || req.connection.proxySecure);
if (secured || !store.cookie.secure) {
store.cookie.expires = false === store.cookie.persistent
? null
: new Date(Date.now() + store.maxAge);
headers = headers || {};
var cookie = utils.serializeCookie(key, req.sessionID, store.cookie);
if (headers['Set-Cookie']) {
headers['Set-Cookie'] += '\r\nSet-Cookie: ' + cookie;
} else {
headers['Set-Cookie'] = cookie;
}
}
res.writeHead = writeHead;
return res.writeHead(status, headers);
};
function hash(base) {
return utils.md5(base + fingerprint(req) + secret, 'base64').replace(/=*$/, '');
}
var generate = store.generate = function(){
var base = utils.uid();
var sessionID = base + "." + hash(base);
req.session = new Session(req, sessionID);
req.sessionID = sessionID;
};
req.sessionStore = store;
req.sessionID = req.cookies[key];
if (!req.sessionID) {
generate();
next();
return;
}
var parts = req.sessionID.split('.');
if (parts[1] !== hash(parts[0])) {
generate();
next();
return;
}
store.get(req.sessionID, function (err, sess) {
pause.end();
if (err) {
if (err.errno === process.ENOENT) {
generate();
next();
} else {
next(err);
}
pause.resume();
return;
}
if (!sess) {
generate();
next();
pause.resume();
return;
}
req.session = new Session(req, sess);
next();
pause.resume();
});
};
};
|
Expose constructors.
|
exports.Session = Session;
exports.Store = require('./session/store');
exports.MemoryStore = require('./session/memory');
|
| lib/connect/middleware/staticGzip.js |
Module dependencies.
|
var fs = require('fs'),
parse = require('url').parse
utils = require('../utils'),
path = require('path'),
exec = require('child_process').exec;
|
Expose staticGzip as the module.
|
exports = module.exports = staticGzip;
|
Gzip binary.
|
exports.bin = 'gzip';
|
Flags passed to gzip.
|
exports.flags = '--best';
|
staticGzip gzips statics via whitelist of mime types specified
by the compress option. Once created staticProvider can continue
on to serve the gzipped version of the file.
Options
root Root direction from which to generate gzipped staticscompress Array of mime types serving as a whitelistflags String of flags passed to the binarybin Binary executable defaulting to "gzip"
param: Object options api: public
|
function staticGzip(options){
var options = options || {},
root = options.root,
compress = options.compress,
flags = options.flags || exports.flags,
bin = options.bin || exports.bin;
if (!root) throw new Error('staticGzip root must be set');
if (!compress) throw new Error('staticGzip compress array must be passed');
return function(req, res, next){
if (req.method !== 'GET') return next();
var acceptEncoding = req.headers['accept-encoding'] || '';
if (acceptEncoding && !~acceptEncoding.indexOf('gzip')) return next();
var url = parse(req.url),
filename = path.join(root, url.pathname),
mime = utils.mime.type(filename).split(';')[0];
if (!~compress.indexOf(mime)) return next();
gzipped(filename, function(err, path, ext){
if (err && err.errno === process.ENOENT) {
next();
if (err.path.indexOf('.gz') === err.path.length - 3) {
gzip(filename, path, flags, bin);
}
} else if (err) {
next(err);
} else {
req.url = url.pathname + ext;
var writeHead = res.writeHead;
res.writeHead = function(status, headers){
headers = headers || {};
res.writeHead = writeHead;
headers['Content-Type'] = mime;
headers['Content-Encoding'] = 'gzip';
res.writeHead(status, headers);
};
next();
}
});
}
};
|
| lib/connect/middleware/staticProvider.js |
Module dependencies.
|
var fs = require('fs'),
Path = require('path'),
utils = require('../utils'),
Buffer = require('buffer').Buffer,
parseUrl = require('url').parse,
queryString = require('querystring');
|
File buffer cache.
|
var _cache = {};
|
Static file server.
Options
root Root path from which to serve static files.maxAge Browser cache maxAge in milliseconds, defaults to 0cache When true cache files in memory indefinitely,
until invalidated by a conditional GET request.
When given, maxAge will be derived from this value.
param: Object options return: Function api: public
|
module.exports = function staticProvider(options){
var cache, maxAge, root;
if (typeof options == 'string') {
root = options;
maxAge = 0;
} else {
options = options || {};
maxAge = options.maxAge;
root = process.connectEnv.staticRoot || options.root || process.cwd();
cache = options.cache;
if (cache && !maxAge) maxAge = cache;
maxAge = maxAge || 0;
}
return function staticProvider(req, res, next) {
if (req.method != 'GET' && req.method != 'HEAD') return next();
var hit,
head = req.method == 'HEAD',
filename, url = parseUrl(req.url);
if (~url.pathname.indexOf('..')) {
return forbidden(res);
}
filename = Path.join(root, queryString.unescape(url.pathname));
if (filename[filename.length - 1] === '/') {
filename += "index.html";
}
if (cache && !conditionalGET(req) && (hit = _cache[req.url])) {
res.writeHead(200, hit.headers);
res.end(head ? undefined : hit.body);
return;
}
fs.stat(filename, function(err, stat){
if (err) {
return err.errno === process.ENOENT
? next()
: next(err);
} else if (stat.isDirectory()) {
return next();
}
function onRead(err, data) {
if (err) return next(err);
var headers = {
"Content-Type": utils.mime.type(filename),
"Content-Length": stat.size,
"Last-Modified": stat.mtime.toUTCString(),
"Cache-Control": "public max-age=" + (maxAge / 1000),
"ETag": etag(stat)
};
if (!modified(req, headers)) {
return notModified(res, headers);
}
res.writeHead(200, headers);
res.end(head ? undefined : data);
if (cache) {
_cache[req.url] = {
headers: headers,
body: data
};
}
}
fs.readFile(filename, onRead);
});
};
};
|
Clear the memory cache for key or the entire store.
param: String key api: public
|
exports.clearCache = function(key){
if (key) {
delete _cache[key];
} else {
_cache = {};
}
};
|
| 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');
var regexp = new RegExp('^' + hostname.replace(/[*]/g, '(.*?)') + '$');
return function vhost(req, res, next){
if (!req.headers.host) return next();
var host = req.headers.host.split(':')[0];
if (req.subdomains = regexp.exec(host)) {
req.subdomains = req.subdomains.slice(1);
server.emit("request", req, res, next);
} else {
next();
}
};
};
|
| lib/connect/middleware/conditionalGet.js |
Conditional GET request support.
return: Function api: public
|
module.exports = function conditionalGet(){
return function conditionalGet(req, res, next) {
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 (!(code === 200 &&
((since && lastModified === since) || (etag && oldEtag === etag))
)) {
res.writeHead = writeHead;
res.writeHead(code, headers);
return;
}
res.write = function () {};
res.end = function () {
res.writeHead = writeHead;
res.write = write;
res.end = end;
var newHeaders = {};
Object.keys(headers).forEach(function (key) {
if (key.indexOf("Content") < 0) {
newHeaders[key] = headers[key];
}
});
res.writeHead(304, newHeaders);
res.end();
};
};
next();
};
};
|
| lib/connect/middleware/session/session.js |
Module dependencies.
|
var utils = require('./../../utils');
|
Update lastAccess timestamp.
|
Session.prototype.touch = function(){
this.lastAccess = +new Date;
};
|
Destroy this session.
param: Function fn api: public
|
Session.prototype.destroy = function(fn){
delete this.req.session;
this.req.sessionStore.destroy(this.req.sessionID, fn);
};
|
Regenerate this request's session.
param: Function fn api: public
|
Session.prototype.regenerate = function(fn){
this.req.sessionStore.regenerate(this.req, fn);
};
|
| lib/connect/middleware/session/memory.js |
Module dependencies.
|
var sys = require('sys'),
Store = require('./store'),
utils = require('./../../utils'),
Session = require('./session');
|
Initialize MemoryStore with the given options.
param: Object options api: public
|
var MemoryStore = module.exports = function MemoryStore(options) {
options = options || {};
Store.call(this, options);
this.sessions = {};
this.reapInterval = options.reapInterval || 600000;
if (this.reapInterval !== -1) {
setInterval(function(self){
self.reap(self.maxAge);
}, this.reapInterval, this);
}
};
sys.inherits(MemoryStore, Store);
|
Attempt to fetch session by the given sid .
param: String sid param: Function fn api: public
|
MemoryStore.prototype.get = function(sid, fn){
if (sid in this.sessions) {
fn(null, this.sessions[sid]);
} else {
fn();
}
};
|
Commit the given sess object associated with the given sid .
param: String sid param: Session sess param: Function fn api: public
|
MemoryStore.prototype.set = function(sid, sess, fn){
this.sessions[sid] = sess;
fn && fn();
};
|
Destroy the session associated with the given sid .
param: String sid api: public
|
MemoryStore.prototype.destroy = function(sid, fn){
delete this.sessions[sid];
fn && fn();
};
|
Invoke the given callback fn with all active sessions.
param: Function fn api: public
|
MemoryStore.prototype.all = function(fn){
var arr = [],
keys = Object.keys(this.sessions);
for (var i = 0, len = keys.length; i < len; ++i) {
arr.push(this.sessions[keys[i]]);
}
fn(null, arr);
};
|
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);
};
|