Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d1e0644a4e | ||
![]() |
ca9d4c18f7 | ||
![]() |
f147c05ff1 | ||
![]() |
7a2851aeeb | ||
![]() |
ef96704f2c | ||
![]() |
8b7ac341e7 | ||
![]() |
713914aecd | ||
![]() |
36e7a43c14 | ||
![]() |
047a686248 | ||
![]() |
347ba57ad3 | ||
![]() |
d2bd78956b | ||
![]() |
1fe81d2447 | ||
![]() |
f37d1ad401 | ||
![]() |
483fce891d | ||
![]() |
6e4c087319 |
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
npm-debug.log
|
||||
node_modules
|
||||
*.swp
|
||||
*.swo
|
||||
data
|
||||
|
71
README.md
@@ -1,6 +1,9 @@
|
||||
# Haste
|
||||
|
||||
Haste is an open-source pastebin software written in node.js, which is easily installable in any network. It can be backed by either redis or filesystem, and has a very easy adapter interface for other stores. A publicly available version can be found at [hastebin.com](http://hastebin.com)
|
||||
Haste is an open-source pastebin software written in node.js, which is easily
|
||||
installable in any network. It can be backed by either redis or filesystem,
|
||||
and has a very easy adapter interface for other stores. A publicly available
|
||||
version can be found at [hastebin.com](http://hastebin.com)
|
||||
|
||||
Major design objectives:
|
||||
|
||||
@@ -8,11 +11,14 @@ Major design objectives:
|
||||
* Be really simple
|
||||
* Be easy to set up and use
|
||||
|
||||
Haste works really well with a little utility called haste-client, allowing you to do things like:
|
||||
Haste works really well with a little utility called
|
||||
[haste-client](https://github.com/seejohnrun/haste-client), allowing you
|
||||
to do things like:
|
||||
|
||||
`cat something | haste`
|
||||
|
||||
which will output a URL to share containing the contents of `cat something`'s STDOUT
|
||||
which will output a URL to share containing the contents of `cat something`'s
|
||||
STDOUT. Check the README there for more details and usages.
|
||||
|
||||
## Tested Browsers
|
||||
|
||||
@@ -35,7 +41,8 @@ which will output a URL to share containing the contents of `cat something`'s ST
|
||||
* `maxLength` - maximum length of a paste (default none)
|
||||
* `staticMaxAge` - max age for static assets (86400)
|
||||
* `recompressStatisAssets` - whether or not to compile static js assets (true)
|
||||
* `documents` - static documents to serve (ex: http://hastebin.com/about.com) in addition to static assets. These will never expire.
|
||||
* `documents` - static documents to serve (ex: http://hastebin.com/about.com)
|
||||
in addition to static assets. These will never expire.
|
||||
* `storage` - storage options (see below)
|
||||
* `logging` - logging preferences
|
||||
* `keyGenerator` - key generator options (see below)
|
||||
@@ -63,13 +70,15 @@ Generates a random key
|
||||
}
|
||||
```
|
||||
|
||||
The _optional_ keySpace argument is a string of acceptable characters for the key.
|
||||
The _optional_ keySpace argument is a string of acceptable characters
|
||||
for the key.
|
||||
|
||||
## Storage
|
||||
|
||||
### File
|
||||
|
||||
To use file storage (the default) change the storage section in `config.js` to something like:
|
||||
To use file storage (the default) change the storage section in `config.js` to
|
||||
something like:
|
||||
|
||||
``` json
|
||||
{
|
||||
@@ -82,9 +91,9 @@ Where `path` represents where you want the files stored
|
||||
|
||||
### Redis
|
||||
|
||||
To use redis storage you must install the redis package in npm globall using
|
||||
To use redis storage you must install the redis package in npm
|
||||
|
||||
`npm install redis --global`
|
||||
`npm install redis`
|
||||
|
||||
Once you've done that, your config section should look like:
|
||||
|
||||
@@ -97,10 +106,35 @@ Once you've done that, your config section should look like:
|
||||
}
|
||||
```
|
||||
|
||||
You can also set an `expire` option to the number of seconds to expire keys in. This is off by default, but will constantly kick back expirations on each view or post.
|
||||
You can also set an `expire` option to the number of seconds to expire keys in.
|
||||
This is off by default, but will constantly kick back expirations on each view
|
||||
or post.
|
||||
|
||||
All of which are optional except `type` with very logical default values.
|
||||
|
||||
### Memcached
|
||||
|
||||
To use memcached storage you must install the `memcache` package via npm
|
||||
|
||||
`npm install memcache`
|
||||
|
||||
Once you've done that, your config section should look like:
|
||||
|
||||
``` json
|
||||
{
|
||||
"type": "memcached",
|
||||
"host": "127.0.0.1",
|
||||
"port": 11211
|
||||
}
|
||||
```
|
||||
|
||||
You can also set an `expire` option to the number of seconds to expire keys in.
|
||||
This behaves just like the redis expirations, but does not push expirations
|
||||
forward on GETs.
|
||||
|
||||
All of which are optional except `type` with very logical default values.
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
John Crepezzi <john.crepezzi@gmail.com>
|
||||
@@ -111,13 +145,26 @@ John Crepezzi <john.crepezzi@gmail.com>
|
||||
|
||||
Copyright © 2011 John Crepezzi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the ‘Software’), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
|
||||
### Other components:
|
||||
|
||||
* jQuery: MIT/GPL license
|
||||
* highlight.js: Copyright © 2006, Ivan Sagalaev
|
||||
* highlightjs-coffeescript: WTFPL - Copyright © 2011, Dmytrii Nagirniak
|
||||
|
32
about.md
@@ -1,33 +1,38 @@
|
||||
# Haste
|
||||
|
||||
Sharing code is a good thing, and it should be _really_ easy to do it.
|
||||
A lot of times, I want to show you something I'm seeing - and that's where we use pastebins.
|
||||
A lot of times, I want to show you something I'm seeing - and that's where we
|
||||
use pastebins.
|
||||
|
||||
Haste is the prettiest, easist to use pastebin ever made.
|
||||
Haste is the prettiest, easiest to use pastebin ever made.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Type what you want me to see, click "Save", and then copy the URL. Send that URL
|
||||
to someone and they'll see what you see.
|
||||
Type what you want me to see, click "Save", and then copy the URL. Send that
|
||||
URL to someone and they'll see what you see.
|
||||
|
||||
To make a new entry, click "New" (or type 'control + n')
|
||||
|
||||
## From the Console
|
||||
|
||||
Most of the time I want to show you some text, its coming from my current console session.
|
||||
We should make it really easy to take code from the console and send it to people.
|
||||
Most of the time I want to show you some text, its coming from my current
|
||||
console session. We should make it really easy to take code from the console
|
||||
and send it to people.
|
||||
|
||||
`cat something | haste` # http://hastebin.com/1238193
|
||||
|
||||
You can even take this a step further, and cut out the last step of copying the URL with:
|
||||
You can even take this a step further, and cut out the last step of copying the
|
||||
URL with:
|
||||
|
||||
* osx: `cat something | haste | pbcopy`
|
||||
* linux: `cat something | haste | xsel`
|
||||
* windows: check out [WinHaste](https://github.com/ajryan/WinHaste)
|
||||
|
||||
After running that, the STDOUT output of `cat something` will show up at a URL which has
|
||||
been conveniently copied to your clipboard.
|
||||
After running that, the STDOUT output of `cat something` will show up at a URL
|
||||
which has been conveniently copied to your clipboard.
|
||||
|
||||
That's all there is to that, and you can install it with `gem install haste` right now.
|
||||
That's all there is to that, and you can install it with `gem install haste`
|
||||
right now.
|
||||
* osx: you will need to have an up to date version of Xcode
|
||||
* linux: you will need to have rubygems and ruby-devel installed
|
||||
|
||||
@@ -37,9 +42,10 @@ Pastes will stay for 30 days from their last view.
|
||||
|
||||
## Privacy
|
||||
|
||||
While the contents of hastebin.com are not directly crawled by any search robot that
|
||||
obeys "robots.txt", there should be no great expectation of privacy. Post things at your
|
||||
own risk. Not responsible for any loss of data or removed pastes.
|
||||
While the contents of hastebin.com are not directly crawled by any search robot
|
||||
that obeys "robots.txt", there should be no great expectation of privacy. Post
|
||||
things at your own risk. Not responsible for any loss of data or removed
|
||||
pastes.
|
||||
|
||||
## Open Source
|
||||
|
||||
|
@@ -24,11 +24,8 @@
|
||||
},
|
||||
|
||||
"storage": {
|
||||
"type": "redis",
|
||||
"host": "localhost",
|
||||
"port": 6379,
|
||||
"db": 2,
|
||||
"expire": 2592000
|
||||
"type": "file",
|
||||
"path": "./data"
|
||||
},
|
||||
|
||||
"documents": {
|
||||
|
@@ -54,13 +54,15 @@ DocumentHandler.prototype.handlePost = function(request, response) {
|
||||
request.on('data', function(data) {
|
||||
if (!buffer) {
|
||||
response.writeHead(200, { 'content-type': 'application/json' });
|
||||
}
|
||||
}
|
||||
buffer += data.toString();
|
||||
if (_this.maxLength && buffer.length > _this.maxLength) {
|
||||
cancelled = true;
|
||||
winston.warn('document >maxLength', { maxLength: _this.maxLength });
|
||||
response.writeHead(400, { 'content-type': 'application/json' });
|
||||
response.end(JSON.stringify({ message: 'Document exceeds maximum length.' }));
|
||||
response.end(
|
||||
JSON.stringify({ message: 'Document exceeds maximum length.' })
|
||||
);
|
||||
}
|
||||
});
|
||||
request.on('end', function(end) {
|
||||
@@ -86,9 +88,9 @@ DocumentHandler.prototype.handlePost = function(request, response) {
|
||||
});
|
||||
};
|
||||
|
||||
// Get a random key that hasn't been already used
|
||||
// Keep choosing keys until one isn't taken
|
||||
DocumentHandler.prototype.chooseKey = function(callback) {
|
||||
var key = this.keyGenerator.createKey(this.keyLength);
|
||||
var key = this.acceptableKey();
|
||||
var _this = this;
|
||||
this.store.get(key, function(ret) {
|
||||
if (ret) {
|
||||
@@ -96,7 +98,11 @@ DocumentHandler.prototype.chooseKey = function(callback) {
|
||||
} else {
|
||||
callback(key);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
DocumentHandler.prototype.acceptableKey = function() {
|
||||
return this.keyGenerator.createKey(this.keyLength);
|
||||
};
|
||||
|
||||
module.exports = DocumentHandler;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
var fs = require('fs');
|
||||
var crypto = require('crypto');
|
||||
|
||||
var winston = require('winston');
|
||||
var hashlib = require('hashlib');
|
||||
|
||||
// For storing in files
|
||||
// options[type] = file
|
||||
@@ -12,19 +12,28 @@ var FileDocumentStore = function(options) {
|
||||
this.expire = options.expire;
|
||||
};
|
||||
|
||||
// Save data in a file, key as md5 - since we don't know what we could be passed here
|
||||
// Generate md5 of a string
|
||||
FileDocumentStore.md5 = function(str) {
|
||||
var md5sum = crypto.createHash('md5');
|
||||
md5sum.update(str);
|
||||
return md5sum.digest('hex');
|
||||
};
|
||||
|
||||
// Save data in a file, key as md5 - since we don't know what we could
|
||||
// be passed here
|
||||
FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||
try {
|
||||
var _this = this;
|
||||
fs.mkdir(this.basePath, '700', function() {
|
||||
fs.writeFile(_this.basePath + '/' + hashlib.md5(key), data, 'utf8', function(err) {
|
||||
var fn = _this.basePath + '/' + FileDocumentStore.md5(key);
|
||||
fs.writeFile(fn, data, 'utf8', function(err) {
|
||||
if (err) {
|
||||
callback(false);
|
||||
}
|
||||
else {
|
||||
callback(true);
|
||||
if (_this.expire && !skipExpire) {
|
||||
winston.warn('file store cannot set expirations on keys');
|
||||
winston.warn('file store cannot set expirations on keys');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -37,14 +46,15 @@ FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||
// Get data from a file from key
|
||||
FileDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||
var _this = this;
|
||||
fs.readFile(this.basePath + '/' + hashlib.md5(key), 'utf8', function(err, data) {
|
||||
var fn = this.basePath + '/' + FileDocumentStore.md5(key);
|
||||
fs.readFile(fn, 'utf8', function(err, data) {
|
||||
if (err) {
|
||||
callback(false);
|
||||
}
|
||||
else {
|
||||
callback(data);
|
||||
if (_this.expire && !skipExpire) {
|
||||
winston.warn('file store cannot set expirations on keys');
|
||||
winston.warn('file store cannot set expirations on keys');
|
||||
}
|
||||
}
|
||||
});
|
45
lib/document_stores/memcached.js
Normal file
@@ -0,0 +1,45 @@
|
||||
var memcached = require('memcache');
|
||||
var winston = require('winston');
|
||||
|
||||
// Create a new store with options
|
||||
var MemcachedDocumentStore = function(options) {
|
||||
this.expire = options.expire;
|
||||
if (!MemcachedDocumentStore.client) {
|
||||
MemcachedDocumentStore.connect(options);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a connection
|
||||
MemcachedDocumentStore.connect = function(options) {
|
||||
var host = options.host || '127.0.0.1';
|
||||
var port = options.port || 11211;
|
||||
this.client = new memcached.Client(port, host);
|
||||
this.client.connect();
|
||||
this.client.on('connect', function() {
|
||||
winston.info('connected to memcached on ' + host + ':' + port);
|
||||
});
|
||||
this.client.on('error', function(e) {
|
||||
winston.info('error connecting to memcached', { error: e });
|
||||
});
|
||||
};
|
||||
|
||||
// Save file in a key
|
||||
MemcachedDocumentStore.prototype.set =
|
||||
function(key, data, callback, skipExpire) {
|
||||
MemcachedDocumentStore.client.set(key, data, function(err, reply) {
|
||||
err ? callback(false) : callback(true);
|
||||
}, skipExpire ? 0 : this.expire);
|
||||
};
|
||||
|
||||
// Get a file from a key
|
||||
MemcachedDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||
var _this = this;
|
||||
MemcachedDocumentStore.client.get(key, function(err, reply) {
|
||||
callback(err ? false : reply);
|
||||
if (_this.expire && !skipExpire) {
|
||||
winston.warn('store does not currently push forward expirations on GET');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = MemcachedDocumentStore;
|
@@ -1,6 +1,5 @@
|
||||
var redis = require('redis');
|
||||
var winston = require('winston');
|
||||
var hashlib = require('hashlib');
|
||||
|
||||
// For storing in redis
|
||||
// options[type] = redis
|
||||
@@ -24,7 +23,10 @@ RedisDocumentStore.connect = function(options) {
|
||||
RedisDocumentStore.client = redis.createClient(port, host);
|
||||
RedisDocumentStore.client.select(index, function(err, reply) {
|
||||
if (err) {
|
||||
winston.error('error connecting to redis index ' + index, { error: err.message });
|
||||
winston.error(
|
||||
'error connecting to redis index ' + index,
|
||||
{ error: err.message }
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
else {
|
||||
@@ -68,7 +70,7 @@ RedisDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||
_this.setExpiration(key);
|
||||
}
|
||||
callback(err ? false : reply);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = RedisDocumentStore;
|
@@ -1,12 +1,17 @@
|
||||
var RandomKeyGenerator = function(options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
};
|
||||
|
||||
// Generate a random key
|
||||
RandomKeyGenerator.prototype.createKey = function(keyLength) {
|
||||
var text = '';
|
||||
var index;
|
||||
for (var i = 0; i < keyLength; i++) {
|
||||
text += this.keyspace.charAt(Math.floor(Math.random() * this.keyspace.length));
|
||||
index = Math.floor(Math.random() * this.keyspace.length);
|
||||
text += this.keyspace.charAt(index);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
1
node_modules/.bin/_mocha
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../mocha/bin/_mocha
|
1
node_modules/.bin/mocha
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../mocha/bin/mocha
|
1
node_modules/.bin/uglifyjs
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../uglify-js/bin/uglifyjs
|
11
node_modules/connect/.npmignore
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
*.markdown
|
||||
*.md
|
||||
.git*
|
||||
Makefile
|
||||
benchmarks/
|
||||
docs/
|
||||
examples/
|
||||
install.sh
|
||||
support/
|
||||
test/
|
||||
.DS_Store
|
24
node_modules/connect/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2010 Sencha Inc.
|
||||
Copyright (c) 2011 LearnBoost
|
||||
Copyright (c) 2011 TJ Holowaychuk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
2
node_modules/connect/index.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
module.exports = require('./lib/connect');
|
81
node_modules/connect/lib/cache.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
/*!
|
||||
* Connect - Cache
|
||||
* Copyright(c) 2011 Sencha Inc.
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Expose `Cache`.
|
||||
*/
|
||||
|
||||
module.exports = Cache;
|
||||
|
||||
/**
|
||||
* LRU cache store.
|
||||
*
|
||||
* @param {Number} limit
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Cache(limit) {
|
||||
this.store = {};
|
||||
this.keys = [];
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch `key`, promoting the object.
|
||||
*
|
||||
* @param {String} key
|
||||
* @param {Number} i
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Cache.prototype.touch = function(key, i){
|
||||
this.keys.splice(i,1);
|
||||
this.keys.push(key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove `key`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Cache.prototype.remove = function(key){
|
||||
delete this.store[key];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the object stored for `key`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Cache.prototype.get = function(key){
|
||||
return this.store[key];
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a cache `key`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Cache.prototype.add = function(key){
|
||||
// initialize store
|
||||
var len = this.keys.push(key);
|
||||
|
||||
// limit reached, invalid LRU
|
||||
if (len > this.limit) this.remove(this.keys.shift());
|
||||
|
||||
var arr = this.store[key] = [];
|
||||
arr.createdAt = new Date;
|
||||
return arr;
|
||||
};
|
106
node_modules/connect/lib/connect.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
/*!
|
||||
* Connect
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var HTTPServer = require('./http').Server
|
||||
, HTTPSServer = require('./https').Server
|
||||
, fs = require('fs');
|
||||
|
||||
// node patches
|
||||
|
||||
require('./patch');
|
||||
|
||||
// expose createServer() as the module
|
||||
|
||||
exports = module.exports = createServer;
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
exports.version = '1.8.5';
|
||||
|
||||
/**
|
||||
* Initialize a new `connect.HTTPServer` with the middleware
|
||||
* passed to this function. When an object is passed _first_,
|
||||
* we assume these are the tls options, and return a `connect.HTTPSServer`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* An example HTTP server, accepting several middleware.
|
||||
*
|
||||
* var server = connect.createServer(
|
||||
* connect.logger()
|
||||
* , connect.static(__dirname + '/public')
|
||||
* );
|
||||
*
|
||||
* An HTTPS server, utilizing the same middleware as above.
|
||||
*
|
||||
* var server = connect.createServer(
|
||||
* { key: key, cert: cert }
|
||||
* , connect.logger()
|
||||
* , connect.static(__dirname + '/public')
|
||||
* );
|
||||
*
|
||||
* Alternatively with connect 1.0 we may omit `createServer()`.
|
||||
*
|
||||
* connect(
|
||||
* connect.logger()
|
||||
* , connect.static(__dirname + '/public')
|
||||
* ).listen(3000);
|
||||
*
|
||||
* @param {Object|Function} ...
|
||||
* @return {Server}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function createServer() {
|
||||
if ('object' == typeof arguments[0]) {
|
||||
return new HTTPSServer(arguments[0], Array.prototype.slice.call(arguments, 1));
|
||||
} else {
|
||||
return new HTTPServer(Array.prototype.slice.call(arguments));
|
||||
}
|
||||
};
|
||||
|
||||
// support connect.createServer()
|
||||
|
||||
exports.createServer = createServer;
|
||||
|
||||
// auto-load getters
|
||||
|
||||
exports.middleware = {};
|
||||
|
||||
/**
|
||||
* Auto-load bundled middleware with getters.
|
||||
*/
|
||||
|
||||
fs.readdirSync(__dirname + '/middleware').forEach(function(filename){
|
||||
if (/\.js$/.test(filename)) {
|
||||
var name = filename.substr(0, filename.lastIndexOf('.'));
|
||||
exports.middleware.__defineGetter__(name, function(){
|
||||
return require('./middleware/' + name);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// expose utils
|
||||
|
||||
exports.utils = require('./utils');
|
||||
|
||||
// expose getters as first-class exports
|
||||
|
||||
exports.utils.merge(exports, exports.middleware);
|
||||
|
||||
// expose constructors
|
||||
|
||||
exports.HTTPServer = HTTPServer;
|
||||
exports.HTTPSServer = HTTPSServer;
|
||||
|
217
node_modules/connect/lib/http.js
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
|
||||
/*!
|
||||
* Connect - HTTPServer
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, parse = require('url').parse
|
||||
, assert = require('assert');
|
||||
|
||||
// environment
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
/**
|
||||
* Initialize a new `Server` with the given `middleware`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* var server = connect.createServer(
|
||||
* connect.favicon()
|
||||
* , connect.logger()
|
||||
* , connect.static(__dirname + '/public')
|
||||
* );
|
||||
*
|
||||
* @params {Array} middleware
|
||||
* @return {Server}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var Server = exports.Server = function HTTPServer(middleware) {
|
||||
this.stack = [];
|
||||
middleware.forEach(function(fn){
|
||||
this.use(fn);
|
||||
}, this);
|
||||
http.Server.call(this, this.handle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `http.Server.prototype`.
|
||||
*/
|
||||
|
||||
Server.prototype.__proto__ = http.Server.prototype;
|
||||
|
||||
/**
|
||||
* Utilize the given middleware `handle` to the given `route`,
|
||||
* defaulting to _/_. This "route" is the mount-point for the
|
||||
* middleware, when given a value other than _/_ the middleware
|
||||
* is only effective when that segment is present in the request's
|
||||
* pathname.
|
||||
*
|
||||
* For example if we were to mount a function at _/admin_, it would
|
||||
* be invoked on _/admin_, and _/admin/settings_, however it would
|
||||
* not be invoked for _/_, or _/posts_.
|
||||
*
|
||||
* This is effectively the same as passing middleware to `connect.createServer()`,
|
||||
* however provides a progressive api.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* var server = connect.createServer();
|
||||
* server.use(connect.favicon());
|
||||
* server.use(connect.logger());
|
||||
* server.use(connect.static(__dirname + '/public'));
|
||||
*
|
||||
* If we wanted to prefix static files with _/public_, we could
|
||||
* "mount" the `static()` middleware:
|
||||
*
|
||||
* server.use('/public', connect.static(__dirname + '/public'));
|
||||
*
|
||||
* This api is chainable, meaning the following is valid:
|
||||
*
|
||||
* connect.createServer()
|
||||
* .use(connect.favicon())
|
||||
* .use(connect.logger())
|
||||
* .use(connect.static(__dirname + '/public'))
|
||||
* .listen(3000);
|
||||
*
|
||||
* @param {String|Function} route or handle
|
||||
* @param {Function} handle
|
||||
* @return {Server}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.use = function(route, handle){
|
||||
this.route = '/';
|
||||
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) {
|
||||
handle = route;
|
||||
route = '/';
|
||||
}
|
||||
|
||||
// wrap sub-apps
|
||||
if ('function' == typeof handle.handle) {
|
||||
var server = handle;
|
||||
server.route = route;
|
||||
handle = function(req, res, next) {
|
||||
server.handle(req, res, next);
|
||||
};
|
||||
}
|
||||
|
||||
// wrap vanilla http.Servers
|
||||
if (handle instanceof http.Server) {
|
||||
handle = handle.listeners('request')[0];
|
||||
}
|
||||
|
||||
// normalize route to not trail with slash
|
||||
if ('/' == route[route.length - 1]) {
|
||||
route = route.substr(0, route.length - 1);
|
||||
}
|
||||
|
||||
// add the middleware
|
||||
this.stack.push({ route: route, handle: handle });
|
||||
|
||||
// allow chaining
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle server requests, punting them down
|
||||
* the middleware stack.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.handle = function(req, res, out) {
|
||||
var writeHead = res.writeHead
|
||||
, stack = this.stack
|
||||
, removed = ''
|
||||
, index = 0;
|
||||
|
||||
function next(err) {
|
||||
var layer, path, c;
|
||||
req.url = removed + req.url;
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
removed = '';
|
||||
|
||||
layer = stack[index++];
|
||||
|
||||
// all done
|
||||
if (!layer || res.headerSent) {
|
||||
// but wait! we have a parent
|
||||
if (out) return out(err);
|
||||
|
||||
// error
|
||||
if (err) {
|
||||
var msg = 'production' == env
|
||||
? 'Internal Server Error'
|
||||
: err.stack || err.toString();
|
||||
|
||||
// output to stderr in a non-test env
|
||||
if ('test' != env) console.error(err.stack || err.toString());
|
||||
|
||||
// unable to respond
|
||||
if (res.headerSent) return req.socket.destroy();
|
||||
|
||||
res.statusCode = 500;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
if ('HEAD' == req.method) return res.end();
|
||||
res.end(msg);
|
||||
} else {
|
||||
res.statusCode = 404;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
if ('HEAD' == req.method) return res.end();
|
||||
res.end('Cannot ' + req.method + ' ' + req.url);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
path = parse(req.url).pathname;
|
||||
if (undefined == path) path = '/';
|
||||
|
||||
// skip this layer if the route doesn't match.
|
||||
if (0 != path.indexOf(layer.route)) return next(err);
|
||||
|
||||
c = path[layer.route.length];
|
||||
if (c && '/' != c && '.' != c) return next(err);
|
||||
|
||||
// Call the layer handler
|
||||
// Trim off the part of the url that matches the route
|
||||
removed = layer.route;
|
||||
req.url = req.url.substr(removed.length);
|
||||
|
||||
// Ensure leading slash
|
||||
if ('/' != req.url[0]) req.url = '/' + req.url;
|
||||
|
||||
var arity = layer.handle.length;
|
||||
if (err) {
|
||||
if (arity === 4) {
|
||||
layer.handle(err, req, res, next);
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
} else if (arity < 4) {
|
||||
layer.handle(req, res, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof assert.AssertionError) {
|
||||
console.error(e.stack + '\n');
|
||||
next(e);
|
||||
} else {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
next();
|
||||
};
|
47
node_modules/connect/lib/https.js
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
/*!
|
||||
* Connect - HTTPServer
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var HTTPServer = require('./http').Server
|
||||
, https = require('https');
|
||||
|
||||
/**
|
||||
* Initialize a new `Server` with the given
|
||||
*`options` and `middleware`. The HTTPS api
|
||||
* is identical to the [HTTP](http.html) server,
|
||||
* however TLS `options` must be provided before
|
||||
* passing in the optional middleware.
|
||||
*
|
||||
* @params {Object} options
|
||||
* @params {Array} middleawre
|
||||
* @return {Server}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var Server = exports.Server = function HTTPSServer(options, middleware) {
|
||||
this.stack = [];
|
||||
middleware.forEach(function(fn){
|
||||
this.use(fn);
|
||||
}, this);
|
||||
https.Server.call(this, options, this.handle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `http.Server.prototype`.
|
||||
*/
|
||||
|
||||
Server.prototype.__proto__ = https.Server.prototype;
|
||||
|
||||
// mixin HTTPServer methods
|
||||
|
||||
Object.keys(HTTPServer.prototype).forEach(function(method){
|
||||
Server.prototype[method] = HTTPServer.prototype[method];
|
||||
});
|
46
node_modules/connect/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
/**
|
||||
* # Connect
|
||||
*
|
||||
* Connect is a middleware framework for node,
|
||||
* shipping with over 11 bundled middleware and a rich choice of
|
||||
* [3rd-party middleware](https://github.com/senchalabs/connect/wiki).
|
||||
*
|
||||
* Installation:
|
||||
*
|
||||
* $ npm install connect
|
||||
*
|
||||
* API:
|
||||
*
|
||||
* - [connect](connect.html) general
|
||||
* - [http](http.html) http server
|
||||
* - [https](https.html) https server
|
||||
*
|
||||
* Middleware:
|
||||
*
|
||||
* - [logger](middleware-logger.html) request logger with custom format support
|
||||
* - [csrf](middleware-csrf.html) Cross-site request forgery protection
|
||||
* - [basicAuth](middleware-basicAuth.html) basic http authentication
|
||||
* - [bodyParser](middleware-bodyParser.html) extensible request body parser
|
||||
* - [cookieParser](middleware-cookieParser.html) cookie parser
|
||||
* - [session](middleware-session.html) session management support with bundled [MemoryStore](middleware-session-memory.html)
|
||||
* - [compiler](middleware-compiler.html) static asset compiler (sass, less, coffee-script, etc)
|
||||
* - [methodOverride](middleware-methodOverride.html) faux HTTP method support
|
||||
* - [responseTime](middleware-responseTime.html) calculates response-time and exposes via X-Response-Time
|
||||
* - [router](middleware-router.html) provides rich Sinatra / Express-like routing
|
||||
* - [staticCache](middleware-staticCache.html) memory cache layer for the static() middleware
|
||||
* - [static](middleware-static.html) streaming static file server supporting `Range` and more
|
||||
* - [directory](middleware-directory.html) directory listing middleware
|
||||
* - [vhost](middleware-vhost.html) virtual host sub-domain mapping middleware
|
||||
* - [favicon](middleware-favicon.html) efficient favicon server (with default icon)
|
||||
* - [limit](middleware-limit.html) limit the bytesize of request bodies
|
||||
* - [profiler](middleware-profiler.html) request profiler reporting response-time, memory usage, etc
|
||||
* - [query](middleware-query.html) automatic querystring parser, populating `req.query`
|
||||
* - [errorHandler](middleware-errorHandler.html) flexible error handler
|
||||
*
|
||||
* Internals:
|
||||
*
|
||||
* - connect [utilities](utils.html)
|
||||
* - node monkey [patches](patch.html)
|
||||
*
|
||||
*/
|
93
node_modules/connect/lib/middleware/basicAuth.js
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
|
||||
/*!
|
||||
* Connect - basicAuth
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../utils')
|
||||
, unauthorized = utils.unauthorized
|
||||
, badRequest = utils.badRequest;
|
||||
|
||||
/**
|
||||
* Enfore basic authentication by providing a `callback(user, pass)`,
|
||||
* which must return `true` in order to gain access. Alternatively an async
|
||||
* method is provided as well, invoking `callback(user, pass, callback)`. Populates
|
||||
* `req.remoteUser`. The final alternative is simply passing username / password
|
||||
* strings.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect(connect.basicAuth('username', 'password'));
|
||||
*
|
||||
* connect(
|
||||
* connect.basicAuth(function(user, pass){
|
||||
* return 'tj' == user & 'wahoo' == pass;
|
||||
* })
|
||||
* );
|
||||
*
|
||||
* connect(
|
||||
* connect.basicAuth(function(user, pass, fn){
|
||||
* User.authenticate({ user: user, pass: pass }, fn);
|
||||
* })
|
||||
* );
|
||||
*
|
||||
* @param {Function|String} callback or username
|
||||
* @param {String} realm
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function basicAuth(callback, realm) {
|
||||
var username, password;
|
||||
|
||||
// user / pass strings
|
||||
if ('string' == typeof callback) {
|
||||
username = callback;
|
||||
password = realm;
|
||||
if ('string' != typeof password) throw new Error('password argument required');
|
||||
realm = arguments[2];
|
||||
callback = function(user, pass){
|
||||
return user == username && pass == password;
|
||||
}
|
||||
}
|
||||
|
||||
realm = realm || 'Authorization Required';
|
||||
|
||||
return function(req, res, next) {
|
||||
var authorization = req.headers.authorization;
|
||||
|
||||
if (req.remoteUser) return next();
|
||||
if (!authorization) return unauthorized(res, realm);
|
||||
|
||||
var parts = authorization.split(' ')
|
||||
, scheme = parts[0]
|
||||
, credentials = new Buffer(parts[1], 'base64').toString().split(':');
|
||||
|
||||
if ('Basic' != scheme) return badRequest(res);
|
||||
|
||||
// async
|
||||
if (callback.length >= 3) {
|
||||
var pause = utils.pause(req);
|
||||
callback(credentials[0], credentials[1], function(err, user){
|
||||
if (err || !user) return unauthorized(res, realm);
|
||||
req.remoteUser = user;
|
||||
next();
|
||||
pause.resume();
|
||||
});
|
||||
// sync
|
||||
} else {
|
||||
if (callback(credentials[0], credentials[1])) {
|
||||
req.remoteUser = credentials[0];
|
||||
next();
|
||||
} else {
|
||||
unauthorized(res, realm);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
196
node_modules/connect/lib/middleware/bodyParser.js
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
|
||||
/*!
|
||||
* Connect - bodyParser
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var qs = require('qs')
|
||||
, formidable = require('formidable');
|
||||
|
||||
/**
|
||||
* Extract the mime type from the given request's
|
||||
* _Content-Type_ header.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function mime(req) {
|
||||
var str = req.headers['content-type'] || '';
|
||||
return str.split(';')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse request bodies.
|
||||
*
|
||||
* By default _application/json_, _application/x-www-form-urlencoded_,
|
||||
* and _multipart/form-data_ are supported, however you may map `connect.bodyParser.parse[contentType]`
|
||||
* to a function receiving `(req, options, callback)`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect.createServer(
|
||||
* connect.bodyParser()
|
||||
* , function(req, res) {
|
||||
* res.end('viewing user ' + req.body.user.name);
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* $ curl -d 'user[name]=tj' http://localhost/
|
||||
* $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://localhost/
|
||||
*
|
||||
* Multipart req.files:
|
||||
*
|
||||
* As a security measure files are stored in a separate object, stored
|
||||
* as `req.files`. This prevents attacks that may potentially alter
|
||||
* filenames, and depending on the application gain access to restricted files.
|
||||
*
|
||||
* Multipart configuration:
|
||||
*
|
||||
* The `options` passed are provided to each parser function.
|
||||
* The _multipart/form-data_ parser merges these with formidable's
|
||||
* IncomingForm object, allowing you to tweak the upload directory,
|
||||
* size limits, etc. For example you may wish to retain the file extension
|
||||
* and change the upload directory:
|
||||
*
|
||||
* server.use(bodyParser({ uploadDir: '/www/mysite.com/uploads' }));
|
||||
*
|
||||
* View [node-formidable](https://github.com/felixge/node-formidable) for more information.
|
||||
*
|
||||
* If you wish to use formidable directly within your app, and do not
|
||||
* desire this behaviour for multipart requests simply remove the
|
||||
* parser:
|
||||
*
|
||||
* delete connect.bodyParser.parse['multipart/form-data'];
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* delete express.bodyParser.parse['multipart/form-data'];
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function bodyParser(options){
|
||||
options = options || {};
|
||||
return function bodyParser(req, res, next) {
|
||||
if (req.body) return next();
|
||||
req.body = {};
|
||||
|
||||
if ('GET' == req.method || 'HEAD' == req.method) return next();
|
||||
var parser = exports.parse[mime(req)];
|
||||
if (parser) {
|
||||
parser(req, options, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parsers.
|
||||
*/
|
||||
|
||||
exports.parse = {};
|
||||
|
||||
/**
|
||||
* Parse application/x-www-form-urlencoded.
|
||||
*/
|
||||
|
||||
exports.parse['application/x-www-form-urlencoded'] = function(req, options, fn){
|
||||
var buf = '';
|
||||
req.setEncoding('utf8');
|
||||
req.on('data', function(chunk){ buf += chunk });
|
||||
req.on('end', function(){
|
||||
try {
|
||||
req.body = buf.length
|
||||
? qs.parse(buf)
|
||||
: {};
|
||||
fn();
|
||||
} catch (err){
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse application/json.
|
||||
*/
|
||||
|
||||
exports.parse['application/json'] = function(req, options, fn){
|
||||
var buf = '';
|
||||
req.setEncoding('utf8');
|
||||
req.on('data', function(chunk){ buf += chunk });
|
||||
req.on('end', function(){
|
||||
try {
|
||||
req.body = buf.length
|
||||
? JSON.parse(buf)
|
||||
: {};
|
||||
fn();
|
||||
} catch (err){
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse multipart/form-data.
|
||||
*
|
||||
* TODO: make multiple support optional
|
||||
* TODO: revisit "error" flag if it's a formidable bug
|
||||
*/
|
||||
|
||||
exports.parse['multipart/form-data'] = function(req, options, fn){
|
||||
var form = new formidable.IncomingForm
|
||||
, data = {}
|
||||
, files = {}
|
||||
, done;
|
||||
|
||||
Object.keys(options).forEach(function(key){
|
||||
form[key] = options[key];
|
||||
});
|
||||
|
||||
function ondata(name, val, data){
|
||||
if (Array.isArray(data[name])) {
|
||||
data[name].push(val);
|
||||
} else if (data[name]) {
|
||||
data[name] = [data[name], val];
|
||||
} else {
|
||||
data[name] = val;
|
||||
}
|
||||
}
|
||||
|
||||
form.on('field', function(name, val){
|
||||
ondata(name, val, data);
|
||||
});
|
||||
|
||||
form.on('file', function(name, val){
|
||||
ondata(name, val, files);
|
||||
});
|
||||
|
||||
form.on('error', function(err){
|
||||
fn(err);
|
||||
done = true;
|
||||
});
|
||||
|
||||
form.on('end', function(){
|
||||
if (done) return;
|
||||
try {
|
||||
req.body = qs.parse(data);
|
||||
req.files = qs.parse(files);
|
||||
fn();
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
|
||||
form.parse(req);
|
||||
};
|
163
node_modules/connect/lib/middleware/compiler.js
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
/*!
|
||||
* Connect - compiler
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, path = require('path')
|
||||
, parse = require('url').parse;
|
||||
|
||||
/**
|
||||
* 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 sass to css
|
||||
* - `less` Compiles less to css
|
||||
* - `coffeescript` Compiles coffee to js
|
||||
*
|
||||
* @param {Object} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function compiler(options){
|
||||
options = options || {};
|
||||
|
||||
var srcDir = options.src || process.cwd()
|
||||
, destDir = options.dest || srcDir
|
||||
, enable = options.enable;
|
||||
|
||||
if (!enable || enable.length === 0) {
|
||||
throw new Error('compiler\'s "enable" option is not set, nothing will be compiled.');
|
||||
}
|
||||
|
||||
return function compiler(req, res, next){
|
||||
if ('GET' != req.method) return next();
|
||||
var pathname = 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;
|
||||
|
||||
// Compare mtimes
|
||||
fs.stat(src, function(err, srcStats){
|
||||
if (err) {
|
||||
if ('ENOENT' == err.code) {
|
||||
next();
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
} else {
|
||||
fs.stat(dest, function(err, destStats){
|
||||
if (err) {
|
||||
// Oh snap! it does not exist, compile it
|
||||
if ('ENOENT' == err.code) {
|
||||
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();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Bundled compilers:
|
||||
*
|
||||
* - [sass](http://github.com/visionmedia/sass.js) to _css_
|
||||
* - [less](http://github.com/cloudhead/less.js) to _css_
|
||||
* - [coffee](http://github.com/jashkenas/coffee-script) to _js_
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
46
node_modules/connect/lib/middleware/cookieParser.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
/*!
|
||||
* Connect - cookieParser
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('./../utils');
|
||||
|
||||
/**
|
||||
* Parse _Cookie_ header and populate `req.cookies`
|
||||
* with an object keyed by the cookie names.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect.createServer(
|
||||
* connect.cookieParser()
|
||||
* , function(req, res, next){
|
||||
* res.end(JSON.stringify(req.cookies));
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function cookieParser(){
|
||||
return function cookieParser(req, res, next) {
|
||||
var cookie = req.headers.cookie;
|
||||
if (req.cookies) return next();
|
||||
req.cookies = {};
|
||||
if (cookie) {
|
||||
try {
|
||||
req.cookies = utils.parseCookie(cookie);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
105
node_modules/connect/lib/middleware/csrf.js
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
/*!
|
||||
* Connect - csrf
|
||||
* Copyright(c) 2011 Sencha Inc.
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../utils')
|
||||
, crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* CRSF protection middleware.
|
||||
*
|
||||
* By default this middleware generates a token named "_csrf"
|
||||
* which should be added to requests which mutate
|
||||
* state, within a hidden form field, query-string etc. This
|
||||
* token is validated against the visitor's `req.session._csrf`
|
||||
* property which is re-generated per request.
|
||||
*
|
||||
* The default `value` function checks `req.body` generated
|
||||
* by the `bodyParser()` middleware, `req.query` generated
|
||||
* by `query()`, and the "X-CSRF-Token" header field.
|
||||
*
|
||||
* This middleware requires session support, thus should be added
|
||||
* somewhere _below_ `session()` and `cookieParser()`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* var form = '\n\
|
||||
* <form action="/" method="post">\n\
|
||||
* <input type="hidden" name="_csrf" value="{token}" />\n\
|
||||
* <input type="text" name="user[name]" value="{user}" />\n\
|
||||
* <input type="password" name="user[pass]" />\n\
|
||||
* <input type="submit" value="Login" />\n\
|
||||
* </form>\n\
|
||||
* ';
|
||||
*
|
||||
* connect(
|
||||
* connect.cookieParser()
|
||||
* , connect.session({ secret: 'keyboard cat' })
|
||||
* , connect.bodyParser()
|
||||
* , connect.csrf()
|
||||
*
|
||||
* , function(req, res, next){
|
||||
* if ('POST' != req.method) return next();
|
||||
* req.session.user = req.body.user;
|
||||
* next();
|
||||
* }
|
||||
*
|
||||
* , function(req, res){
|
||||
* res.setHeader('Content-Type', 'text/html');
|
||||
* var body = form
|
||||
* .replace('{token}', req.session._csrf)
|
||||
* .replace('{user}', req.session.user && req.session.user.name || '');
|
||||
* res.end(body);
|
||||
* }
|
||||
* ).listen(3000);
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `value` a function accepting the request, returning the token
|
||||
*
|
||||
* @param {Object} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function csrf(options) {
|
||||
var options = options || {}
|
||||
, value = options.value || defaultValue;
|
||||
|
||||
return function(req, res, next){
|
||||
// generate CSRF token
|
||||
var token = req.session._csrf || (req.session._csrf = utils.uid(24));
|
||||
|
||||
// ignore GET (for now)
|
||||
if ('GET' == req.method) return next();
|
||||
|
||||
// determine value
|
||||
var val = value(req);
|
||||
|
||||
// check
|
||||
if (val != token) return utils.forbidden(res);
|
||||
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default value function, checking the `req.body`
|
||||
* and `req.query` for the CSRF token.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function defaultValue(req) {
|
||||
return (req.body && req.body._csrf)
|
||||
|| (req.query && req.query._csrf)
|
||||
|| (req.headers['x-csrf-token']);
|
||||
}
|
222
node_modules/connect/lib/middleware/directory.js
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
|
||||
/*!
|
||||
* Connect - directory
|
||||
* Copyright(c) 2011 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
// TODO: icon / style for directories
|
||||
// TODO: arrow key navigation
|
||||
// TODO: make icons extensible
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, parse = require('url').parse
|
||||
, utils = require('../utils')
|
||||
, path = require('path')
|
||||
, normalize = path.normalize
|
||||
, extname = path.extname
|
||||
, join = path.join;
|
||||
|
||||
/**
|
||||
* Icon cache.
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Serve directory listings with the given `root` path.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `hidden` display hidden (dot) files. Defaults to false.
|
||||
* - `icons` display icons. Defaults to false.
|
||||
* - `filter` Apply this filter function to files. Defaults to false.
|
||||
*
|
||||
* @param {String} root
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function directory(root, options){
|
||||
options = options || {};
|
||||
|
||||
// root required
|
||||
if (!root) throw new Error('directory() root path required');
|
||||
var hidden = options.hidden
|
||||
, icons = options.icons
|
||||
, filter = options.filter
|
||||
, root = normalize(root);
|
||||
|
||||
return function directory(req, res, next) {
|
||||
var accept = req.headers.accept || 'text/plain'
|
||||
, url = parse(req.url)
|
||||
, dir = decodeURIComponent(url.pathname)
|
||||
, path = normalize(join(root, dir))
|
||||
, originalUrl = parse(req.originalUrl)
|
||||
, originalDir = decodeURIComponent(originalUrl.pathname)
|
||||
, showUp = path != root && path != root + '/';
|
||||
|
||||
// null byte(s)
|
||||
if (~path.indexOf('\0')) return utils.badRequest(res);
|
||||
|
||||
// malicious path
|
||||
if (0 != path.indexOf(root)) return utils.forbidden(res);
|
||||
|
||||
// check if we have a directory
|
||||
fs.stat(path, function(err, stat){
|
||||
if (err) return 'ENOENT' == err.code
|
||||
? next()
|
||||
: next(err);
|
||||
|
||||
if (!stat.isDirectory()) return next();
|
||||
|
||||
// fetch files
|
||||
fs.readdir(path, function(err, files){
|
||||
if (err) return next(err);
|
||||
if (!hidden) files = removeHidden(files);
|
||||
if (filter) files = files.filter(filter);
|
||||
files.sort();
|
||||
// content-negotiation
|
||||
for (var key in exports) {
|
||||
if (~accept.indexOf(key) || ~accept.indexOf('*/*')) {
|
||||
exports[key](req, res, files, next, originalDir, showUp, icons);
|
||||
return;
|
||||
}
|
||||
}
|
||||
utils.notAcceptable(res);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond with text/html.
|
||||
*/
|
||||
|
||||
exports.html = function(req, res, files, next, dir, showUp, icons){
|
||||
fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){
|
||||
if (err) return next(err);
|
||||
fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){
|
||||
if (err) return next(err);
|
||||
if (showUp) files.unshift('..');
|
||||
str = str
|
||||
.replace('{style}', style)
|
||||
.replace('{files}', html(files, dir, icons))
|
||||
.replace('{directory}', dir)
|
||||
.replace('{linked-path}', htmlPath(dir));
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Content-Length', str.length);
|
||||
res.end(str);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond with application/json.
|
||||
*/
|
||||
|
||||
exports.json = function(req, res, files){
|
||||
files = JSON.stringify(files);
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.setHeader('Content-Length', files.length);
|
||||
res.end(files);
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond with text/plain.
|
||||
*/
|
||||
|
||||
exports.plain = function(req, res, files){
|
||||
files = files.join('\n') + '\n';
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.setHeader('Content-Length', files.length);
|
||||
res.end(files);
|
||||
};
|
||||
|
||||
/**
|
||||
* Map html `dir`, returning a linked path.
|
||||
*/
|
||||
|
||||
function htmlPath(dir) {
|
||||
var curr = [];
|
||||
return dir.split('/').map(function(part){
|
||||
curr.push(part);
|
||||
return '<a href="' + curr.join('/') + '">' + part + '</a>';
|
||||
}).join(' / ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Map html `files`, returning an html unordered list.
|
||||
*/
|
||||
|
||||
function html(files, dir, useIcons) {
|
||||
return '<ul id="files">' + files.map(function(file){
|
||||
var icon = ''
|
||||
, classes = [];
|
||||
|
||||
if (useIcons && '..' != file) {
|
||||
icon = icons[extname(file)] || icons.default;
|
||||
icon = '<img src="data:image/png;base64,' + load(icon) + '" />';
|
||||
classes.push('icon');
|
||||
}
|
||||
|
||||
return '<li><a href="'
|
||||
+ join(dir, file)
|
||||
+ '" class="'
|
||||
+ classes.join(' ') + '"'
|
||||
+ ' title="' + file + '">'
|
||||
+ icon + file + '</a></li>';
|
||||
|
||||
}).join('\n') + '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and cache the given `icon`.
|
||||
*
|
||||
* @param {String} icon
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function load(icon) {
|
||||
if (cache[icon]) return cache[icon];
|
||||
return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter "hidden" `files`, aka files
|
||||
* beginning with a `.`.
|
||||
*
|
||||
* @param {Array} files
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function removeHidden(files) {
|
||||
return files.filter(function(file){
|
||||
return '.' != file[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon map.
|
||||
*/
|
||||
|
||||
var icons = {
|
||||
'.js': 'page_white_code_red.png'
|
||||
, '.c': 'page_white_c.png'
|
||||
, '.h': 'page_white_h.png'
|
||||
, '.cc': 'page_white_cplusplus.png'
|
||||
, '.php': 'page_white_php.png'
|
||||
, '.rb': 'page_white_ruby.png'
|
||||
, '.cpp': 'page_white_cplusplus.png'
|
||||
, '.swf': 'page_white_flash.png'
|
||||
, '.pdf': 'page_white_acrobat.png'
|
||||
, 'default': 'page_white.png'
|
||||
};
|
100
node_modules/connect/lib/middleware/errorHandler.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/*!
|
||||
* Connect - errorHandler
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../utils')
|
||||
, url = require('url')
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Flexible error handler, providing (_optional_) stack traces
|
||||
* and error message responses for requests accepting text, html,
|
||||
* or json.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `showStack`, `stack` respond with both the error message and stack trace. Defaults to `false`
|
||||
* - `showMessage`, `message`, respond with the exception message only. Defaults to `false`
|
||||
* - `dumpExceptions`, `dump`, dump exceptions to stderr (without terminating the process). Defaults to `false`
|
||||
*
|
||||
* Text:
|
||||
*
|
||||
* By default, and when _text/plain_ is accepted a simple stack trace
|
||||
* or error message will be returned.
|
||||
*
|
||||
* JSON:
|
||||
*
|
||||
* When _application/json_ is accepted, connect will respond with
|
||||
* an object in the form of `{ "error": error }`.
|
||||
*
|
||||
* HTML:
|
||||
*
|
||||
* When accepted connect will output a nice html stack trace.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function errorHandler(options){
|
||||
options = options || {};
|
||||
|
||||
// defaults
|
||||
var showStack = options.showStack || options.stack
|
||||
, showMessage = options.showMessage || options.message
|
||||
, dumpExceptions = options.dumpExceptions || options.dump
|
||||
, formatUrl = options.formatUrl;
|
||||
|
||||
return function errorHandler(err, req, res, next){
|
||||
res.statusCode = 500;
|
||||
if (dumpExceptions) console.error(err.stack);
|
||||
if (showStack) {
|
||||
var accept = req.headers.accept || '';
|
||||
// html
|
||||
if (~accept.indexOf('html')) {
|
||||
fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){
|
||||
fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){
|
||||
var stack = (err.stack || '')
|
||||
.split('\n').slice(1)
|
||||
.map(function(v){ return '<li>' + v + '</li>'; }).join('');
|
||||
html = html
|
||||
.replace('{style}', style)
|
||||
.replace('{stack}', stack)
|
||||
.replace('{title}', exports.title)
|
||||
.replace(/\{error\}/g, utils.escape(err.toString()));
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(html);
|
||||
});
|
||||
});
|
||||
// json
|
||||
} else if (~accept.indexOf('json')) {
|
||||
var json = JSON.stringify({ error: err });
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(json);
|
||||
// plain text
|
||||
} else {
|
||||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||
res.end(err.stack);
|
||||
}
|
||||
} else {
|
||||
var body = showMessage
|
||||
? err.toString()
|
||||
: 'Internal Server Error';
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.end(body);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Template title.
|
||||
*/
|
||||
|
||||
exports.title = 'Connect';
|
76
node_modules/connect/lib/middleware/favicon.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
/*!
|
||||
* Connect - favicon
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` cache-control max-age directive, defaulting to 1 day
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect.createServer(
|
||||
* connect.favicon()
|
||||
* );
|
||||
*
|
||||
* connect.createServer(
|
||||
* connect.favicon(__dirname + '/public/favicon.ico')
|
||||
* );
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function favicon(path, options){
|
||||
var options = options || {}
|
||||
, path = path || __dirname + '/../public/favicon.ico'
|
||||
, maxAge = options.maxAge || 86400000;
|
||||
|
||||
return function favicon(req, res, next){
|
||||
if ('/favicon.ico' == req.url) {
|
||||
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=' + (maxAge / 1000)
|
||||
},
|
||||
body: buf
|
||||
};
|
||||
res.writeHead(200, icon.headers);
|
||||
res.end(icon.body);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
};
|
82
node_modules/connect/lib/middleware/limit.js
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
|
||||
/*!
|
||||
* Connect - limit
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Limit request bodies to the given size in `bytes`.
|
||||
*
|
||||
* A string representation of the bytesize may also be passed,
|
||||
* for example "5mb", "200kb", "1gb", etc.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* var server = connect(
|
||||
* connect.limit('5.5mb')
|
||||
* ).listen(3000);
|
||||
*
|
||||
* TODO: pause EV_READ
|
||||
*
|
||||
* @param {Number|String} bytes
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function limit(bytes){
|
||||
if ('string' == typeof bytes) bytes = parse(bytes);
|
||||
if ('number' != typeof bytes) throw new Error('limit() bytes required');
|
||||
return function limit(req, res, next){
|
||||
var received = 0
|
||||
, len = req.headers['content-length']
|
||||
? parseInt(req.headers['content-length'], 10)
|
||||
: null;
|
||||
|
||||
// deny the request
|
||||
function deny() {
|
||||
req.destroy();
|
||||
}
|
||||
|
||||
// self-awareness
|
||||
if (req._limit) return next();
|
||||
req._limit = true;
|
||||
|
||||
// limit by content-length
|
||||
if (len && len > bytes) {
|
||||
res.statusCode = 413;
|
||||
res.end('Request Entity Too Large');
|
||||
return;
|
||||
}
|
||||
|
||||
// limit
|
||||
req.on('data', function(chunk){
|
||||
received += chunk.length;
|
||||
if (received > bytes) deny();
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse byte `size` string.
|
||||
*
|
||||
* @param {String} size
|
||||
* @return {Number}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function parse(size) {
|
||||
var parts = size.match(/^(\d+(?:\.\d+)?) *(kb|mb|gb)$/)
|
||||
, n = parseFloat(parts[1])
|
||||
, type = parts[2];
|
||||
|
||||
var map = {
|
||||
kb: 1024
|
||||
, mb: 1024 * 1024
|
||||
, gb: 1024 * 1024 * 1024
|
||||
};
|
||||
|
||||
return map[type] * n;
|
||||
}
|
299
node_modules/connect/lib/middleware/logger.js
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
|
||||
/*!
|
||||
* Connect - logger
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Log buffer.
|
||||
*/
|
||||
|
||||
var buf = [];
|
||||
|
||||
/**
|
||||
* Default log buffer duration.
|
||||
*/
|
||||
|
||||
var defaultBufferDuration = 1000;
|
||||
|
||||
/**
|
||||
* Log requests with the given `options` or a `format` string.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `format` Format string, see below for tokens
|
||||
* - `stream` Output stream, defaults to _stdout_
|
||||
* - `buffer` Buffer duration, defaults to 1000ms when _true_
|
||||
* - `immediate` Write log line on request instead of response (for response times)
|
||||
*
|
||||
* 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`
|
||||
*
|
||||
* Formats:
|
||||
*
|
||||
* Pre-defined formats that ship with connect:
|
||||
*
|
||||
* - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'
|
||||
* - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'
|
||||
* - `tiny` ':method :url :status :res[content-length] - :response-time ms'
|
||||
* - `dev` concise output colored by response status for development use
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect.logger() // default
|
||||
* connect.logger('short')
|
||||
* connect.logger('tiny')
|
||||
* connect.logger('dev')
|
||||
* connect.logger(':method :url - :referrer')
|
||||
* connect.logger(':req[content-type] -> :res[content-type]')
|
||||
* connect.logger(function(req, res){ return 'some format string' })
|
||||
*
|
||||
* Defining Tokens:
|
||||
*
|
||||
* To define a token, simply invoke `connect.logger.token()` with the
|
||||
* name and a callback function. The value returned is then available
|
||||
* as ":type" in this case.
|
||||
*
|
||||
* connect.logger.token('type', function(req, res){ return req.headers['content-type']; })
|
||||
*
|
||||
* Defining Formats:
|
||||
*
|
||||
* All default formats are defined this way, however it's public API as well:
|
||||
*
|
||||
* connect.logger.format('name', 'string or function')
|
||||
*
|
||||
* @param {String|Function|Object} format or options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function logger(options) {
|
||||
if ('object' == typeof options) {
|
||||
options = options || {};
|
||||
} else if (options) {
|
||||
options = { format: options };
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
// output on request instead of response
|
||||
var immediate = options.immediate;
|
||||
|
||||
// format name
|
||||
var fmt = exports[options.format] || options.format || exports.default;
|
||||
|
||||
// compile format
|
||||
if ('function' != typeof fmt) fmt = compile(fmt);
|
||||
|
||||
// options
|
||||
var stream = options.stream || process.stdout
|
||||
, buffer = options.buffer;
|
||||
|
||||
// buffering support
|
||||
if (buffer) {
|
||||
var realStream = stream
|
||||
, interval = 'number' == typeof buffer
|
||||
? buffer
|
||||
: defaultBufferDuration;
|
||||
|
||||
// flush interval
|
||||
setInterval(function(){
|
||||
if (buf.length) {
|
||||
realStream.write(buf.join(''), 'ascii');
|
||||
buf.length = 0;
|
||||
}
|
||||
}, interval);
|
||||
|
||||
// swap the stream
|
||||
stream = {
|
||||
write: function(str){
|
||||
buf.push(str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return function logger(req, res, next) {
|
||||
req._startTime = new Date;
|
||||
|
||||
// mount safety
|
||||
if (req._logging) return next();
|
||||
|
||||
// flag as logging
|
||||
req._logging = true;
|
||||
|
||||
// immediate
|
||||
if (immediate) {
|
||||
var line = fmt(exports, req, res);
|
||||
if (null == line) return;
|
||||
stream.write(line + '\n', 'ascii');
|
||||
} else {
|
||||
// proxy end to output loggging
|
||||
var end = res.end;
|
||||
res.end = function(chunk, encoding){
|
||||
res.end = end;
|
||||
res.end(chunk, encoding);
|
||||
var line = fmt(exports, req, res);
|
||||
if (null == line) return;
|
||||
stream.write(line + '\n', 'ascii');
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compile `fmt` into a function.
|
||||
*
|
||||
* @param {String} fmt
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function compile(fmt) {
|
||||
fmt = fmt.replace(/"/g, '\\"');
|
||||
var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){
|
||||
return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
|
||||
}) + '";'
|
||||
return new Function('tokens, req, res', js);
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a token function with the given `name`,
|
||||
* and callback `fn(req, res)`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @return {Object} exports for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.token = function(name, fn) {
|
||||
exports[name] = fn;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a `fmt` with the given `name`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String|Function} fmt
|
||||
* @return {Object} exports for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.format = function(name, str){
|
||||
exports[name] = str;
|
||||
return this;
|
||||
};
|
||||
|
||||
// default format
|
||||
|
||||
exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"');
|
||||
|
||||
// short format
|
||||
|
||||
exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms');
|
||||
|
||||
// tiny format
|
||||
|
||||
exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms');
|
||||
|
||||
// dev (colored)
|
||||
|
||||
exports.format('dev', function(tokens, req, res){
|
||||
var status = res.statusCode
|
||||
, color = 32;
|
||||
|
||||
if (status >= 500) color = 31
|
||||
else if (status >= 400) color = 33
|
||||
else if (status >= 300) color = 36;
|
||||
|
||||
return '\033[90m' + req.method
|
||||
+ ' ' + req.originalUrl + ' '
|
||||
+ '\033[' + color + 'm' + res.statusCode
|
||||
+ ' \033[90m'
|
||||
+ (new Date - req._startTime)
|
||||
+ 'ms\033[0m';
|
||||
});
|
||||
|
||||
// request url
|
||||
|
||||
exports.token('url', function(req){
|
||||
return req.originalUrl;
|
||||
});
|
||||
|
||||
// request method
|
||||
|
||||
exports.token('method', function(req){
|
||||
return req.method;
|
||||
});
|
||||
|
||||
// response time in milliseconds
|
||||
|
||||
exports.token('response-time', function(req){
|
||||
return new Date - req._startTime;
|
||||
});
|
||||
|
||||
// UTC date
|
||||
|
||||
exports.token('date', function(){
|
||||
return new Date().toUTCString();
|
||||
});
|
||||
|
||||
// response status code
|
||||
|
||||
exports.token('status', function(req, res){
|
||||
return res.statusCode;
|
||||
});
|
||||
|
||||
// normalized referrer
|
||||
|
||||
exports.token('referrer', function(req){
|
||||
return req.headers['referer'] || req.headers['referrer'];
|
||||
});
|
||||
|
||||
// remote address
|
||||
|
||||
exports.token('remote-addr', function(req){
|
||||
return req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress));
|
||||
});
|
||||
|
||||
// HTTP version
|
||||
|
||||
exports.token('http-version', function(req){
|
||||
return req.httpVersionMajor + '.' + req.httpVersionMinor;
|
||||
});
|
||||
|
||||
// UA string
|
||||
|
||||
exports.token('user-agent', function(req){
|
||||
return req.headers['user-agent'];
|
||||
});
|
||||
|
||||
// request header
|
||||
|
||||
exports.token('req', function(req, res, field){
|
||||
return req.headers[field.toLowerCase()];
|
||||
});
|
||||
|
||||
// response header
|
||||
|
||||
exports.token('res', function(req, res, field){
|
||||
return (res._headers || {})[field.toLowerCase()];
|
||||
});
|
||||
|
38
node_modules/connect/lib/middleware/methodOverride.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
/*!
|
||||
* Connect - methodOverride
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides faux HTTP method support.
|
||||
*
|
||||
* Pass an optional `key` to use when checking for
|
||||
* a method override, othewise defaults to _\_method_.
|
||||
* The original method is available via `req.originalMethod`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function methodOverride(key){
|
||||
key = key || "_method";
|
||||
return function methodOverride(req, res, next) {
|
||||
req.originalMethod = req.originalMethod || req.method;
|
||||
|
||||
// req.body
|
||||
if (req.body && key in req.body) {
|
||||
req.method = req.body[key].toUpperCase();
|
||||
delete req.body[key];
|
||||
// check X-HTTP-Method-Override
|
||||
} else if (req.headers['x-http-method-override']) {
|
||||
req.method = req.headers['x-http-method-override'].toUpperCase();
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
100
node_modules/connect/lib/middleware/profiler.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
/*!
|
||||
* Connect - profiler
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Profile the duration of a request.
|
||||
*
|
||||
* Typically this middleware should be utilized
|
||||
* _above_ all others, as it proxies the `res.end()`
|
||||
* method, being first allows it to encapsulate all
|
||||
* other middleware.
|
||||
*
|
||||
* Example Output:
|
||||
*
|
||||
* GET /
|
||||
* response time 2ms
|
||||
* memory rss 52.00kb
|
||||
* memory vsize 2.07mb
|
||||
* heap before 3.76mb / 8.15mb
|
||||
* heap after 3.80mb / 8.15mb
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function profiler(){
|
||||
return function(req, res, next){
|
||||
var end = res.end
|
||||
, start = snapshot();
|
||||
|
||||
// state snapshot
|
||||
function snapshot() {
|
||||
return {
|
||||
mem: process.memoryUsage()
|
||||
, time: new Date
|
||||
};
|
||||
}
|
||||
|
||||
// proxy res.end()
|
||||
res.end = function(data, encoding){
|
||||
res.end = end;
|
||||
res.end(data, encoding);
|
||||
compare(req, start, snapshot())
|
||||
};
|
||||
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare `start` / `end` snapshots.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @param {Object} start
|
||||
* @param {Object} end
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function compare(req, start, end) {
|
||||
console.log();
|
||||
row(req.method, req.url);
|
||||
row('response time:', (end.time - start.time) + 'ms');
|
||||
row('memory rss:', formatBytes(end.mem.rss - start.mem.rss));
|
||||
row('memory vsize:', formatBytes(end.mem.vsize - start.mem.vsize));
|
||||
row('heap before:', formatBytes(start.mem.heapUsed) + ' / ' + formatBytes(start.mem.heapTotal));
|
||||
row('heap after:', formatBytes(end.mem.heapUsed) + ' / ' + formatBytes(end.mem.heapTotal));
|
||||
console.log();
|
||||
}
|
||||
|
||||
/**
|
||||
* Row helper
|
||||
*
|
||||
* @param {String} key
|
||||
* @param {String} val
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function row(key, val) {
|
||||
console.log(' \033[90m%s\033[0m \033[36m%s\033[0m', key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format byte-size.
|
||||
*
|
||||
* @param {Number} bytes
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function formatBytes(bytes) {
|
||||
var kb = 1024
|
||||
, mb = 1024 * kb
|
||||
, gb = 1024 * mb;
|
||||
if (bytes < kb) return bytes + 'b';
|
||||
if (bytes < mb) return (bytes / kb).toFixed(2) + 'kb';
|
||||
if (bytes < gb) return (bytes / mb).toFixed(2) + 'mb';
|
||||
return (bytes / gb).toFixed(2) + 'gb';
|
||||
};
|
40
node_modules/connect/lib/middleware/query.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
/*!
|
||||
* Connect - query
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2011 Sencha Inc.
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var qs = require('qs')
|
||||
, parse = require('url').parse;
|
||||
|
||||
/**
|
||||
* Automatically parse the query-string when available,
|
||||
* populating the `req.query` object.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect(
|
||||
* connect.query()
|
||||
* , function(req, res){
|
||||
* res.end(JSON.stringify(req.query));
|
||||
* }
|
||||
* ).listen(3000);
|
||||
*
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function query(){
|
||||
return function query(req, res, next){
|
||||
req.query = ~req.url.indexOf('?')
|
||||
? qs.parse(parse(req.url).query)
|
||||
: {};
|
||||
next();
|
||||
};
|
||||
};
|
34
node_modules/connect/lib/middleware/responseTime.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
/*!
|
||||
* Connect - responseTime
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds the `X-Response-Time` header displaying the response
|
||||
* duration in milliseconds.
|
||||
*
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function responseTime(){
|
||||
return function(req, res, next){
|
||||
var writeHead = res.writeHead
|
||||
, start = new Date;
|
||||
|
||||
if (res._responseTime) return next();
|
||||
res._responseTime = true;
|
||||
|
||||
// proxy writeHead to calculate duration
|
||||
res.writeHead = function(status, headers){
|
||||
var duration = new Date - start;
|
||||
res.setHeader('X-Response-Time', duration + 'ms');
|
||||
res.writeHead = writeHead;
|
||||
res.writeHead(status, headers);
|
||||
};
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
379
node_modules/connect/lib/middleware/router.js
generated
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
|
||||
/*!
|
||||
* Connect - router
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../utils')
|
||||
, parse = require('url').parse;
|
||||
|
||||
/**
|
||||
* Expose router.
|
||||
*/
|
||||
|
||||
exports = module.exports = router;
|
||||
|
||||
/**
|
||||
* Supported HTTP / WebDAV methods.
|
||||
*/
|
||||
|
||||
var _methods = exports.methods = [
|
||||
'get'
|
||||
, '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
|
||||
* });
|
||||
* app.put('/user/:id', function(req, res, next){
|
||||
* // populates req.params.id
|
||||
* });
|
||||
* })
|
||||
*
|
||||
* @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');
|
||||
|
||||
// Generate method functions
|
||||
_methods.forEach(function(method){
|
||||
methods[method] = generateMethodFunction(method.toUpperCase());
|
||||
});
|
||||
|
||||
// Alias del -> delete
|
||||
methods.del = methods.delete;
|
||||
|
||||
// Apply callback to all methods
|
||||
methods.all = function(){
|
||||
var args = arguments;
|
||||
_methods.forEach(function(name){
|
||||
methods[name].apply(this, args);
|
||||
});
|
||||
return self;
|
||||
};
|
||||
|
||||
// Register param callback
|
||||
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 = []
|
||||
, middleware = [];
|
||||
|
||||
// slice middleware
|
||||
if (arguments.length > 2) {
|
||||
middleware = Array.prototype.slice.call(arguments, 1, arguments.length);
|
||||
fn = middleware.pop();
|
||||
middleware = utils.flatten(middleware);
|
||||
}
|
||||
|
||||
fn.middleware = middleware;
|
||||
|
||||
if (!path) throw new Error(name + ' route requires a path');
|
||||
if (!fn) throw new Error(name + ' route ' + path + ' requires a callback');
|
||||
var regexp = path instanceof RegExp
|
||||
? path
|
||||
: normalizePath(path, keys);
|
||||
localRoutes.push({
|
||||
fn: fn
|
||||
, path: regexp
|
||||
, keys: keys
|
||||
, orig: path
|
||||
, method: name
|
||||
});
|
||||
return self;
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Param preconditions
|
||||
(function param(err) {
|
||||
try {
|
||||
var key = keys[i++]
|
||||
, val = req.params[key]
|
||||
, fn = params[key];
|
||||
|
||||
if ('route' == err) {
|
||||
pass(req._route_index + 1);
|
||||
// Error
|
||||
} else if (err) {
|
||||
next(err);
|
||||
// Param has callback
|
||||
} else if (fn) {
|
||||
// Return style
|
||||
if (1 == fn.length) {
|
||||
req.params[key] = fn(val);
|
||||
param();
|
||||
// Middleware style
|
||||
} else {
|
||||
fn(req, res, param, val);
|
||||
}
|
||||
// Finished processing params
|
||||
} else if (!key) {
|
||||
// route middleware
|
||||
i = 0;
|
||||
(function nextMiddleware(err){
|
||||
var fn = route.middleware[i++];
|
||||
if ('route' == err) {
|
||||
pass(req._route_index + 1);
|
||||
} else if (err) {
|
||||
next(err);
|
||||
} else if (fn) {
|
||||
fn(req, res, nextMiddleware);
|
||||
} else {
|
||||
route.call(self, req, res, function(err){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else {
|
||||
pass(req._route_index + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
// More params
|
||||
} else {
|
||||
param();
|
||||
}
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
})();
|
||||
} else if ('OPTIONS' == req.method) {
|
||||
options(req, res, routes);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
router.remove = function(path, method){
|
||||
var fns = router.lookup(path, method);
|
||||
fns.forEach(function(fn){
|
||||
routes[fn.method].splice(fn.index, 1);
|
||||
});
|
||||
};
|
||||
|
||||
router.lookup = function(path, method, ret){
|
||||
ret = ret || [];
|
||||
|
||||
// method specific lookup
|
||||
if (method) {
|
||||
method = method.toUpperCase();
|
||||
if (routes[method]) {
|
||||
routes[method].forEach(function(route, i){
|
||||
if (path == route.orig) {
|
||||
var fn = route.fn;
|
||||
fn.regexp = route.path;
|
||||
fn.keys = route.keys;
|
||||
fn.path = route.orig;
|
||||
fn.method = route.method;
|
||||
fn.index = i;
|
||||
ret.push(fn);
|
||||
}
|
||||
});
|
||||
}
|
||||
// global lookup
|
||||
} else {
|
||||
_methods.forEach(function(method){
|
||||
router.lookup(path, method, ret);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
router.match = function(url, method, ret){
|
||||
var ret = ret || []
|
||||
, i = 0
|
||||
, fn
|
||||
, req;
|
||||
|
||||
// method specific matches
|
||||
if (method) {
|
||||
method = method.toUpperCase();
|
||||
req = { url: url, method: method };
|
||||
while (fn = match(req, routes, i)) {
|
||||
i = req._route_index + 1;
|
||||
ret.push(fn);
|
||||
}
|
||||
// global matches
|
||||
} else {
|
||||
_methods.forEach(function(method){
|
||||
router.match(url, method, ret);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to OPTIONS.
|
||||
*
|
||||
* @param {ServerRequest} req
|
||||
* @param {ServerResponse} req
|
||||
* @param {Array} routes
|
||||
* @api private
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return OPTIONS array for the given `path`, matching `routes`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Array} routes
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function optionsFor(path, routes) {
|
||||
return _methods.filter(function(method){
|
||||
var arr = routes[method.toUpperCase()];
|
||||
for (var i = 0, len = arr.length; i < len; ++i) {
|
||||
if (arr[i].path.test(path)) return true;
|
||||
}
|
||||
}).map(function(method){
|
||||
return method.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the given path string,
|
||||
* returning a regular expression.
|
||||
*
|
||||
* An empty array should be passed,
|
||||
* which will contain the placeholder
|
||||
* key names. For example "/user/:id" will
|
||||
* then contain ["id"].
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Array} keys
|
||||
* @return {RegExp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function normalizePath(path, keys) {
|
||||
path = path
|
||||
.concat('/?')
|
||||
.replace(/\/\(/g, '(?:/')
|
||||
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
|
||||
keys.push(key);
|
||||
slash = slash || '';
|
||||
return ''
|
||||
+ (optional ? '' : slash)
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (format || '') + (capture || '([^/]+?)') + ')'
|
||||
+ (optional || '');
|
||||
})
|
||||
.replace(/([\/.])/g, '\\$1')
|
||||
.replace(/\*/g, '(.+)');
|
||||
return new RegExp('^' + path + '$', 'i');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to match the given request to
|
||||
* one of the routes. When successful
|
||||
* a route function is returned.
|
||||
*
|
||||
* @param {ServerRequest} req
|
||||
* @param {Object} routes
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function match(req, routes, i) {
|
||||
var captures
|
||||
, method = req.method
|
||||
, i = i || 0;
|
||||
if ('HEAD' == method) method = 'GET';
|
||||
if (routes = routes[method]) {
|
||||
var url = parse(req.url)
|
||||
, pathname = url.pathname;
|
||||
for (var len = routes.length; i < len; ++i) {
|
||||
var route = routes[i]
|
||||
, fn = route.fn
|
||||
, path = route.path
|
||||
, keys = fn.keys = route.keys;
|
||||
if (captures = path.exec(pathname)) {
|
||||
fn.method = method;
|
||||
fn.params = [];
|
||||
for (var j = 1, len = captures.length; j < len; ++j) {
|
||||
var key = keys[j-1],
|
||||
val = typeof captures[j] === 'string'
|
||||
? decodeURIComponent(captures[j])
|
||||
: captures[j];
|
||||
if (key) {
|
||||
fn.params[key] = val;
|
||||
} else {
|
||||
fn.params.push(val);
|
||||
}
|
||||
}
|
||||
req._route_index = i;
|
||||
return fn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
346
node_modules/connect/lib/middleware/session.js
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
|
||||
/*!
|
||||
* Connect - session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Session = require('./session/session')
|
||||
, MemoryStore = require('./session/memory')
|
||||
, Cookie = require('./session/cookie')
|
||||
, Store = require('./session/store')
|
||||
, utils = require('./../utils')
|
||||
, parse = require('url').parse
|
||||
, crypto = require('crypto');
|
||||
|
||||
// environment
|
||||
|
||||
var env = process.env.NODE_ENV;
|
||||
|
||||
/**
|
||||
* Expose the middleware.
|
||||
*/
|
||||
|
||||
exports = module.exports = session;
|
||||
|
||||
/**
|
||||
* Expose constructors.
|
||||
*/
|
||||
|
||||
exports.Store = Store;
|
||||
exports.Cookie = Cookie;
|
||||
exports.Session = Session;
|
||||
exports.MemoryStore = MemoryStore;
|
||||
|
||||
/**
|
||||
* Warning message for `MemoryStore` usage in production.
|
||||
*/
|
||||
|
||||
var warning = 'Warning: connection.session() MemoryStore is not\n'
|
||||
+ 'designed for a production environment, as it will leak\n'
|
||||
+ 'memory, and obviously only work within a single process.';
|
||||
|
||||
/**
|
||||
* Default finger-printing function.
|
||||
*/
|
||||
|
||||
function defaultFingerprint(req) {
|
||||
var ua = req.headers['user-agent'] || '';
|
||||
return ua.replace(/;?\schromeframe\/[\d\.]+/, '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Paths to ignore.
|
||||
*/
|
||||
|
||||
exports.ignore = [];
|
||||
|
||||
/**
|
||||
* Setup session store with the given `options`.
|
||||
*
|
||||
* Session data is _not_ saved in the cookie itself, however
|
||||
* cookies are used, so we must use the [cookieParser()](middleware-cookieParser.html)
|
||||
* middleware _before_ `session()`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect.createServer(
|
||||
* connect.cookieParser()
|
||||
* , connect.session({ secret: 'keyboard cat' })
|
||||
* );
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `key` cookie name defaulting to `connect.sid`
|
||||
* - `store` Session store instance
|
||||
* - `fingerprint` Custom fingerprint generating function
|
||||
* - `cookie` Session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: 14400000 }`
|
||||
* - `secret` Secret string used to compute hash
|
||||
*
|
||||
* Ignore Paths:
|
||||
*
|
||||
* By default `/favicon.ico` is the only ignored path, all others
|
||||
* will utilize sessions, to manipulate the paths ignored, use
|
||||
* `connect.session.ignore.push('/my/path')`. This works for _full_
|
||||
* pathnames only, not segments nor substrings.
|
||||
*
|
||||
* connect.session.ignore.push('/robots.txt');
|
||||
*
|
||||
* ## req.session
|
||||
*
|
||||
* To store or access session data, simply use the request property `req.session`,
|
||||
* which is (generally) serialized as JSON by the store, so nested objects
|
||||
* are typically fine. For example below is a user-specific view counter:
|
||||
*
|
||||
* connect(
|
||||
* connect.cookieParser()
|
||||
* , connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }})
|
||||
* , connect.favicon()
|
||||
* , function(req, res, next){
|
||||
* var sess = req.session;
|
||||
* if (sess.views) {
|
||||
* res.setHeader('Content-Type', 'text/html');
|
||||
* res.write('<p>views: ' + sess.views + '</p>');
|
||||
* res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>');
|
||||
* res.end();
|
||||
* sess.views++;
|
||||
* } else {
|
||||
* sess.views = 1;
|
||||
* res.end('welcome to the session demo. refresh!');
|
||||
* }
|
||||
* }
|
||||
* ).listen(3000);
|
||||
*
|
||||
* ## Session#regenerate()
|
||||
*
|
||||
* To regenerate the session simply invoke the method, once complete
|
||||
* a new SID and `Session` instance will be initialized at `req.session`.
|
||||
*
|
||||
* req.session.regenerate(function(err){
|
||||
* // will have a new session here
|
||||
* });
|
||||
*
|
||||
* ## Session#destroy()
|
||||
*
|
||||
* Destroys the session, removing `req.session`, will be re-generated next request.
|
||||
*
|
||||
* req.session.destroy(function(err){
|
||||
* // cannot access session here
|
||||
* });
|
||||
*
|
||||
* ## Session#reload()
|
||||
*
|
||||
* Reloads the session data.
|
||||
*
|
||||
* req.session.reload(function(err){
|
||||
* // session updated
|
||||
* });
|
||||
*
|
||||
* ## Session#save()
|
||||
*
|
||||
* Save the session.
|
||||
*
|
||||
* req.session.save(function(err){
|
||||
* // session saved
|
||||
* });
|
||||
*
|
||||
* ## Session#touch()
|
||||
*
|
||||
* Updates the `.maxAge`, and `.lastAccess` properties. Typically this is
|
||||
* not necessary to call, as the session middleware does this for you.
|
||||
*
|
||||
* ## Session#cookie
|
||||
*
|
||||
* Each session has a unique cookie object accompany it. This allows
|
||||
* you to alter the session cookie per visitor. For example we can
|
||||
* set `req.session.cookie.expires` to `false` to enable the cookie
|
||||
* to remain for only the duration of the user-agent.
|
||||
*
|
||||
* ## Session#maxAge
|
||||
*
|
||||
* Alternatively `req.session.cookie.maxAge` will return the time
|
||||
* remaining in milliseconds, which we may also re-assign a new value
|
||||
* to adjust the `.expires` property appropriately. The following
|
||||
* are essentially equivalent
|
||||
*
|
||||
* var hour = 3600000;
|
||||
* req.session.cookie.expires = new Date(Date.now() + hour);
|
||||
* req.session.cookie.maxAge = hour;
|
||||
*
|
||||
* For example when `maxAge` is set to `60000` (one minute), and 30 seconds
|
||||
* has elapsed it will return `30000` until the current request has completed,
|
||||
* at which time `req.session.touch()` is called to update `req.session.lastAccess`,
|
||||
* and reset `req.session.maxAge` to its original value.
|
||||
*
|
||||
* req.session.cookie.maxAge;
|
||||
* // => 30000
|
||||
*
|
||||
* Session Store Implementation:
|
||||
*
|
||||
* Every session store _must_ implement the following methods
|
||||
*
|
||||
* - `.get(sid, callback)`
|
||||
* - `.set(sid, session, callback)`
|
||||
* - `.destroy(sid, callback)`
|
||||
*
|
||||
* Recommended methods include, but are not limited to:
|
||||
*
|
||||
* - `.length(callback)`
|
||||
* - `.clear(callback)`
|
||||
*
|
||||
* For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function session(options){
|
||||
var options = options || {}
|
||||
, key = options.key || 'connect.sid'
|
||||
, secret = options.secret
|
||||
, store = options.store || new MemoryStore
|
||||
, fingerprint = options.fingerprint || defaultFingerprint
|
||||
, cookie = options.cookie;
|
||||
|
||||
// notify user that this store is not
|
||||
// meant for a production environment
|
||||
if ('production' == env && store instanceof MemoryStore) {
|
||||
console.warn(warning);
|
||||
}
|
||||
|
||||
// ensure secret is present
|
||||
if (!secret) {
|
||||
throw new Error('connect.session({ secret: "string" }) required for security');
|
||||
}
|
||||
|
||||
// session hashing function
|
||||
store.hash = function(req, base) {
|
||||
return crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(base + fingerprint(req))
|
||||
.digest('base64')
|
||||
.replace(/=*$/, '');
|
||||
};
|
||||
|
||||
// generates the new session
|
||||
store.generate = function(req){
|
||||
var base = utils.uid(24);
|
||||
var sessionID = base + '.' + store.hash(req, base);
|
||||
req.sessionID = sessionID;
|
||||
req.session = new Session(req);
|
||||
req.session.cookie = new Cookie(cookie);
|
||||
};
|
||||
|
||||
return function session(req, res, next) {
|
||||
// self-awareness
|
||||
if (req.session) return next();
|
||||
|
||||
// parse url
|
||||
var url = parse(req.url)
|
||||
, path = url.pathname;
|
||||
|
||||
// ignorable paths
|
||||
if (~exports.ignore.indexOf(path)) return next();
|
||||
|
||||
// expose store
|
||||
req.sessionStore = store;
|
||||
|
||||
// proxy writeHead() to Set-Cookie
|
||||
var writeHead = res.writeHead;
|
||||
res.writeHead = function(status, headers){
|
||||
if (req.session) {
|
||||
var cookie = req.session.cookie;
|
||||
// only send secure session cookies when there is a secure connection.
|
||||
// proxySecure is a custom attribute to allow for a reverse proxy
|
||||
// to handle SSL connections and to communicate to connect over HTTP that
|
||||
// the incoming connection is secure.
|
||||
var secured = cookie.secure && (req.connection.encrypted || req.connection.proxySecure);
|
||||
if (secured || !cookie.secure) {
|
||||
res.setHeader('Set-Cookie', cookie.serialize(key, req.sessionID));
|
||||
}
|
||||
}
|
||||
|
||||
res.writeHead = writeHead;
|
||||
return res.writeHead(status, headers);
|
||||
};
|
||||
|
||||
// proxy end() to commit the session
|
||||
var end = res.end;
|
||||
res.end = function(data, encoding){
|
||||
res.end = end;
|
||||
if (req.session) {
|
||||
// HACK: ensure Set-Cookie for implicit writeHead()
|
||||
if (!res._header) res._implicitHeader();
|
||||
req.session.resetMaxAge();
|
||||
req.session.save(function(){
|
||||
res.end(data, encoding);
|
||||
});
|
||||
} else {
|
||||
res.end(data, encoding);
|
||||
}
|
||||
};
|
||||
|
||||
// session hashing
|
||||
function hash(base) {
|
||||
return store.hash(req, base);
|
||||
}
|
||||
|
||||
// generate the session
|
||||
function generate() {
|
||||
store.generate(req);
|
||||
}
|
||||
|
||||
// get the sessionID from the cookie
|
||||
req.sessionID = req.cookies[key];
|
||||
|
||||
// make a new session if the browser doesn't send a sessionID
|
||||
if (!req.sessionID) {
|
||||
generate();
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// check the fingerprint
|
||||
var parts = req.sessionID.split('.');
|
||||
if (parts[1] != hash(parts[0])) {
|
||||
generate();
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// generate the session object
|
||||
var pause = utils.pause(req);
|
||||
store.get(req.sessionID, function(err, sess){
|
||||
// proxy to resume() events
|
||||
var _next = next;
|
||||
next = function(err){
|
||||
_next(err);
|
||||
pause.resume();
|
||||
}
|
||||
|
||||
// error handling
|
||||
if (err) {
|
||||
if ('ENOENT' == err.code) {
|
||||
generate();
|
||||
next();
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
// no session
|
||||
} else if (!sess) {
|
||||
generate();
|
||||
next();
|
||||
// populate req.session
|
||||
} else {
|
||||
store.createSession(req, sess);
|
||||
next();
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
126
node_modules/connect/lib/middleware/session/cookie.js
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
|
||||
/*!
|
||||
* Connect - session - Cookie
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../../utils');
|
||||
|
||||
/**
|
||||
* Initialize a new `Cookie` with the given `options`.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Cookie = module.exports = function Cookie(options) {
|
||||
this.path = '/';
|
||||
this.httpOnly = true;
|
||||
this.maxAge = 14400000;
|
||||
if (options) utils.merge(this, options);
|
||||
this.originalMaxAge = undefined == this.originalMaxAge
|
||||
? this.maxAge
|
||||
: this.originalMaxAge;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prototype.
|
||||
*/
|
||||
|
||||
Cookie.prototype = {
|
||||
|
||||
/**
|
||||
* Set expires `date`.
|
||||
*
|
||||
* @param {Date} date
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set expires(date) {
|
||||
this._expires = date;
|
||||
this.originalMaxAge = this.maxAge;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get expires `date`.
|
||||
*
|
||||
* @return {Date}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get expires() {
|
||||
return this._expires;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set expires via max-age in `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set maxAge(ms) {
|
||||
this.expires = 'number' == typeof ms
|
||||
? new Date(Date.now() + ms)
|
||||
: ms;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get expires max-age in `ms`.
|
||||
*
|
||||
* @return {Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get maxAge() {
|
||||
return this.expires instanceof Date
|
||||
? this.expires.valueOf() - Date.now()
|
||||
: this.expires;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return cookie data object.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
get data() {
|
||||
return {
|
||||
originalMaxAge: this.originalMaxAge
|
||||
, expires: this._expires
|
||||
, secure: this.secure
|
||||
, httpOnly: this.httpOnly
|
||||
, domain: this.domain
|
||||
, path: this.path
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a serialized cookie string.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
serialize: function(name, val){
|
||||
return utils.serializeCookie(name, val, this.data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return JSON representation of this cookie.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
toJSON: function(){
|
||||
return this.data;
|
||||
}
|
||||
};
|
131
node_modules/connect/lib/middleware/session/memory.js
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
/*!
|
||||
* Connect - session - MemoryStore
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Store = require('./store')
|
||||
, utils = require('../../utils')
|
||||
, Session = require('./session');
|
||||
|
||||
/**
|
||||
* Initialize a new `MemoryStore`.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var MemoryStore = module.exports = function MemoryStore() {
|
||||
this.sessions = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `Store.prototype`.
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.__proto__ = Store.prototype;
|
||||
|
||||
/**
|
||||
* Attempt to fetch session by the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.get = function(sid, fn){
|
||||
var self = this;
|
||||
process.nextTick(function(){
|
||||
var expires
|
||||
, sess = self.sessions[sid];
|
||||
if (sess) {
|
||||
sess = JSON.parse(sess);
|
||||
expires = 'string' == typeof sess.cookie.expires
|
||||
? new Date(sess.cookie.expires)
|
||||
: sess.cookie.expires;
|
||||
if (!expires || new Date < expires) {
|
||||
fn(null, sess);
|
||||
} else {
|
||||
self.destroy(sid, fn);
|
||||
}
|
||||
} 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){
|
||||
var self = this;
|
||||
process.nextTick(function(){
|
||||
self.sessions[sid] = JSON.stringify(sess);
|
||||
fn && fn();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the session associated with the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.destroy = function(sid, fn){
|
||||
var self = this;
|
||||
process.nextTick(function(){
|
||||
delete self.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);
|
||||
};
|
137
node_modules/connect/lib/middleware/session/session.js
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
|
||||
/*!
|
||||
* Connect - session - Session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../../utils')
|
||||
, Cookie = require('./cookie');
|
||||
|
||||
/**
|
||||
* Create a new `Session` with the given request and `data`.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @param {Object} data
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Session = module.exports = function Session(req, data) {
|
||||
Object.defineProperty(this, 'req', { value: req });
|
||||
Object.defineProperty(this, 'id', { value: req.sessionID });
|
||||
if ('object' == typeof data) {
|
||||
utils.merge(this, data);
|
||||
} else {
|
||||
this.lastAccess = Date.now();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update `.lastAccess` timestamp,
|
||||
* and reset `.cookie.maxAge` to prevent
|
||||
* the cookie from expiring when the
|
||||
* session is still active.
|
||||
*
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Session.prototype.touch = function(){
|
||||
return this
|
||||
.resetLastAccess()
|
||||
.resetMaxAge();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update `.lastAccess` timestamp.
|
||||
*
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Session.prototype.resetLastAccess = function(){
|
||||
this.lastAccess = Date.now();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset `.maxAge` to `.originalMaxAge`.
|
||||
*
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Session.prototype.resetMaxAge = function(){
|
||||
this.cookie.maxAge = this.cookie.originalMaxAge;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the session data with optional callback `fn(err)`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Session.prototype.save = function(fn){
|
||||
this.req.sessionStore.set(this.id, this, fn || function(){});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Re-loads the session data _without_ altering
|
||||
* the maxAge or lastAccess properties. Invokes the
|
||||
* callback `fn(err)`, after which time if no exception
|
||||
* has occurred the `req.session` property will be
|
||||
* a new `Session` object, although representing the
|
||||
* same session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Session.prototype.reload = function(fn){
|
||||
var req = this.req
|
||||
, store = this.req.sessionStore;
|
||||
store.get(this.id, function(err, sess){
|
||||
if (err) return fn(err);
|
||||
if (!sess) return fn(new Error('failed to load session'));
|
||||
store.createSession(req, sess);
|
||||
fn();
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy `this` session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Session.prototype.destroy = function(fn){
|
||||
delete this.req.session;
|
||||
this.req.sessionStore.destroy(this.id, fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Regenerate this request's session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Session.prototype.regenerate = function(fn){
|
||||
this.req.sessionStore.regenerate(this.req, fn);
|
||||
return this;
|
||||
};
|
87
node_modules/connect/lib/middleware/session/store.js
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
/*!
|
||||
* Connect - session - Store
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter
|
||||
, Session = require('./session')
|
||||
, Cookie = require('./cookie')
|
||||
, utils = require('../../utils');
|
||||
|
||||
/**
|
||||
* Initialize abstract `Store`.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Store = module.exports = function Store(options){};
|
||||
|
||||
/**
|
||||
* Inherit from `EventEmitter.prototype`.
|
||||
*/
|
||||
|
||||
Store.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
/**
|
||||
* Re-generate the given requests's session.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @return {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Store.prototype.regenerate = function(req, fn){
|
||||
var self = this;
|
||||
this.destroy(req.sessionID, function(err){
|
||||
self.generate(req);
|
||||
fn(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a `Session` instance via the given `sid`
|
||||
* and invoke the callback `fn(err, sess)`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Store.prototype.load = function(sid, fn){
|
||||
var self = this;
|
||||
this.get(sid, function(err, sess){
|
||||
if (err) return fn(err);
|
||||
if (!sess) return fn();
|
||||
var req = { sessionID: sid, sessionStore: self };
|
||||
sess = self.createSession(req, sess, false);
|
||||
fn(null, sess);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create session from JSON `sess` data.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @param {Object} sess
|
||||
* @return {Session}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Store.prototype.createSession = function(req, sess, update){
|
||||
var expires = sess.cookie.expires
|
||||
, orig = sess.cookie.originalMaxAge
|
||||
, update = null == update ? true : false;
|
||||
sess.cookie = new Cookie(sess.cookie);
|
||||
if ('string' == typeof expires) sess.cookie.expires = new Date(expires);
|
||||
sess.cookie.originalMaxAge = orig;
|
||||
req.session = new Session(req, sess);
|
||||
if (update) req.session.resetLastAccess();
|
||||
return req.session;
|
||||
};
|
225
node_modules/connect/lib/middleware/static.js
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
|
||||
/*!
|
||||
* Connect - staticProvider
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, path = require('path')
|
||||
, join = path.join
|
||||
, basename = path.basename
|
||||
, normalize = path.normalize
|
||||
, utils = require('../utils')
|
||||
, Buffer = require('buffer').Buffer
|
||||
, parse = require('url').parse
|
||||
, mime = require('mime');
|
||||
|
||||
/**
|
||||
* Static file server with the given `root` path.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* var oneDay = 86400000;
|
||||
*
|
||||
* connect(
|
||||
* connect.static(__dirname + '/public')
|
||||
* ).listen(3000);
|
||||
*
|
||||
* connect(
|
||||
* connect.static(__dirname + '/public', { maxAge: oneDay })
|
||||
* ).listen(3000);
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` Browser cache maxAge in milliseconds. defaults to 0
|
||||
* - `hidden` Allow transfer of hidden files. defaults to false
|
||||
* - `redirect` Redirect to trailing "/" when the pathname is a dir
|
||||
*
|
||||
* @param {String} root
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function static(root, options){
|
||||
options = options || {};
|
||||
|
||||
// root required
|
||||
if (!root) throw new Error('static() root path required');
|
||||
options.root = root;
|
||||
|
||||
return function static(req, res, next) {
|
||||
options.path = req.url;
|
||||
options.getOnly = true;
|
||||
send(req, res, next, options);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose mime module.
|
||||
*/
|
||||
|
||||
exports.mime = mime;
|
||||
|
||||
/**
|
||||
* Respond with 416 "Requested Range Not Satisfiable"
|
||||
*
|
||||
* @param {ServerResponse} res
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function invalidRange(res) {
|
||||
var body = 'Requested Range Not Satisfiable';
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.setHeader('Content-Length', body.length);
|
||||
res.statusCode = 416;
|
||||
res.end(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to tranfer the requseted file to `res`.
|
||||
*
|
||||
* @param {ServerRequest}
|
||||
* @param {ServerResponse}
|
||||
* @param {Function} next
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var send = exports.send = function(req, res, next, options){
|
||||
options = options || {};
|
||||
if (!options.path) throw new Error('path required');
|
||||
|
||||
// setup
|
||||
var maxAge = options.maxAge || 0
|
||||
, ranges = req.headers.range
|
||||
, head = 'HEAD' == req.method
|
||||
, get = 'GET' == req.method
|
||||
, root = options.root ? normalize(options.root) : null
|
||||
, redirect = false === options.redirect ? false : true
|
||||
, getOnly = options.getOnly
|
||||
, fn = options.callback
|
||||
, hidden = options.hidden
|
||||
, done;
|
||||
|
||||
// replace next() with callback when available
|
||||
if (fn) next = fn;
|
||||
|
||||
// ignore non-GET requests
|
||||
if (getOnly && !get && !head) return next();
|
||||
|
||||
// parse url
|
||||
var url = parse(options.path)
|
||||
, path = decodeURIComponent(url.pathname)
|
||||
, type;
|
||||
|
||||
// null byte(s)
|
||||
if (~path.indexOf('\0')) return utils.badRequest(res);
|
||||
|
||||
// when root is not given, consider .. malicious
|
||||
if (!root && ~path.indexOf('..')) return utils.forbidden(res);
|
||||
|
||||
// join / normalize from optional root dir
|
||||
path = normalize(join(root, path));
|
||||
|
||||
// malicious path
|
||||
if (root && 0 != path.indexOf(root)) return fn
|
||||
? fn(new Error('Forbidden'))
|
||||
: utils.forbidden(res);
|
||||
|
||||
// index.html support
|
||||
if (normalize('/') == path[path.length - 1]) path += 'index.html';
|
||||
|
||||
// "hidden" file
|
||||
if (!hidden && '.' == basename(path)[0]) return next();
|
||||
|
||||
fs.stat(path, function(err, stat){
|
||||
// mime type
|
||||
type = mime.lookup(path);
|
||||
|
||||
// ignore ENOENT
|
||||
if (err) {
|
||||
if (fn) return fn(err);
|
||||
return 'ENOENT' == err.code
|
||||
? next()
|
||||
: next(err);
|
||||
// redirect directory in case index.html is present
|
||||
} else if (stat.isDirectory()) {
|
||||
if (!redirect) return next();
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', url.pathname + '/');
|
||||
res.end('Redirecting to ' + url.pathname + '/');
|
||||
return;
|
||||
}
|
||||
|
||||
// header fields
|
||||
if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString());
|
||||
if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (maxAge / 1000));
|
||||
if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString());
|
||||
if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat));
|
||||
if (!res.getHeader('content-type')) {
|
||||
var charset = mime.charsets.lookup(type);
|
||||
res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : ''));
|
||||
}
|
||||
res.setHeader('Accept-Ranges', 'bytes');
|
||||
|
||||
// conditional GET support
|
||||
if (utils.conditionalGET(req)) {
|
||||
if (!utils.modified(req, res)) {
|
||||
req.emit('static');
|
||||
return utils.notModified(res);
|
||||
}
|
||||
}
|
||||
|
||||
var opts = {};
|
||||
var chunkSize = stat.size;
|
||||
|
||||
// we have a Range request
|
||||
if (ranges) {
|
||||
ranges = utils.parseRange(stat.size, ranges);
|
||||
// valid
|
||||
if (ranges) {
|
||||
// TODO: stream options
|
||||
// TODO: multiple support
|
||||
opts.start = ranges[0].start;
|
||||
opts.end = ranges[0].end;
|
||||
chunkSize = opts.end - opts.start + 1;
|
||||
res.statusCode = 206;
|
||||
res.setHeader('Content-Range', 'bytes '
|
||||
+ opts.start
|
||||
+ '-'
|
||||
+ opts.end
|
||||
+ '/'
|
||||
+ stat.size);
|
||||
// invalid
|
||||
} else {
|
||||
return fn
|
||||
? fn(new Error('Requested Range Not Satisfiable'))
|
||||
: invalidRange(res);
|
||||
}
|
||||
}
|
||||
|
||||
res.setHeader('Content-Length', chunkSize);
|
||||
|
||||
// transfer
|
||||
if (head) return res.end();
|
||||
|
||||
// stream
|
||||
var stream = fs.createReadStream(path, opts);
|
||||
req.emit('static', stream);
|
||||
stream.pipe(res);
|
||||
|
||||
// callback
|
||||
if (fn) {
|
||||
function callback(err) { done || fn(err); done = true }
|
||||
req.on('close', callback);
|
||||
stream.on('end', callback);
|
||||
}
|
||||
});
|
||||
};
|
175
node_modules/connect/lib/middleware/staticCache.js
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
/*!
|
||||
* Connect - staticCache
|
||||
* Copyright(c) 2011 Sencha Inc.
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, utils = require('../utils')
|
||||
, Cache = require('../cache')
|
||||
, url = require('url')
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Enables a memory cache layer on top of
|
||||
* the `static()` middleware, serving popular
|
||||
* static files.
|
||||
*
|
||||
* By default a maximum of 128 objects are
|
||||
* held in cache, with a max of 256k each,
|
||||
* totalling ~32mb.
|
||||
*
|
||||
* A Least-Recently-Used (LRU) cache algo
|
||||
* is implemented through the `Cache` object,
|
||||
* simply rotating cache objects as they are
|
||||
* hit. This means that increasingly popular
|
||||
* objects maintain their positions while
|
||||
* others get shoved out of the stack and
|
||||
* garbage collected.
|
||||
*
|
||||
* Benchmarks:
|
||||
*
|
||||
* static(): 2700 rps
|
||||
* node-static: 5300 rps
|
||||
* static() + staticCache(): 7500 rps
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxObjects` max cache objects [128]
|
||||
* - `maxLength` max cache object length 256kb
|
||||
*
|
||||
* @param {Type} name
|
||||
* @return {Type}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function staticCache(options){
|
||||
var options = options || {}
|
||||
, cache = new Cache(options.maxObjects || 128)
|
||||
, maxlen = options.maxLength || 1024 * 256;
|
||||
|
||||
return function staticCache(req, res, next){
|
||||
var path = url.parse(req.url).pathname
|
||||
, ranges = req.headers.range
|
||||
, hit = cache.get(path)
|
||||
, hitCC
|
||||
, uaCC
|
||||
, header
|
||||
, age;
|
||||
|
||||
// cache static
|
||||
req.on('static', function(stream){
|
||||
var headers = res._headers
|
||||
, cc = utils.parseCacheControl(headers['cache-control'] || '')
|
||||
, contentLength = headers['content-length']
|
||||
, hit;
|
||||
|
||||
// ignore larger files
|
||||
if (!contentLength || contentLength > maxlen) return;
|
||||
|
||||
// dont cache items we shouldn't be
|
||||
if ( cc['no-cache']
|
||||
|| cc['no-store']
|
||||
|| cc['private']
|
||||
|| cc['must-revalidate']) return;
|
||||
|
||||
// if already in cache then validate
|
||||
if (hit = cache.get(path)){
|
||||
if (headers.etag == hit[0].etag) {
|
||||
hit[0].date = new Date;
|
||||
return;
|
||||
} else {
|
||||
cache.remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
// validation notifiactions don't contain a steam
|
||||
if (null == stream) return;
|
||||
|
||||
// add the cache object
|
||||
var arr = cache.add(path);
|
||||
arr.push(headers);
|
||||
|
||||
// store the chunks
|
||||
stream.on('data', function(chunk){
|
||||
arr.push(chunk);
|
||||
});
|
||||
|
||||
// flag it as complete
|
||||
stream.on('end', function(){
|
||||
arr.complete = true;
|
||||
});
|
||||
});
|
||||
|
||||
// cache hit, doesnt support range requests
|
||||
if (hit && hit.complete && !ranges) {
|
||||
header = utils.merge({}, hit[0]);
|
||||
header.Age = age = (new Date - new Date(header.date)) / 1000 | 0;
|
||||
header.date = new Date().toUTCString();
|
||||
|
||||
// parse cache-controls
|
||||
hitCC = utils.parseCacheControl(header['cache-control'] || '');
|
||||
uaCC = utils.parseCacheControl(req.headers['cache-control'] || '');
|
||||
|
||||
// check if we must revalidate(bypass)
|
||||
if (hitCC['no-cache'] || uaCC['no-cache']) return next();
|
||||
|
||||
// check freshness of entity
|
||||
if (isStale(hitCC, age) || isStale(uaCC, age)) return next();
|
||||
|
||||
// conditional GET support
|
||||
if (utils.conditionalGET(req)) {
|
||||
if (!utils.modified(req, res, header)) {
|
||||
header['content-length'] = 0;
|
||||
res.writeHead(304, header);
|
||||
return res.end();
|
||||
}
|
||||
}
|
||||
|
||||
// HEAD support
|
||||
if ('HEAD' == req.method) {
|
||||
header['content-length'] = 0;
|
||||
res.writeHead(200, header);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
// respond with cache
|
||||
res.writeHead(200, header);
|
||||
|
||||
// backpressure
|
||||
function write(i) {
|
||||
var buf = hit[i];
|
||||
if (!buf) return res.end();
|
||||
if (false === res.write(buf)) {
|
||||
res.once('drain', function(){
|
||||
write(++i);
|
||||
});
|
||||
} else {
|
||||
write(++i);
|
||||
}
|
||||
}
|
||||
|
||||
return write(1);
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if cache item is stale
|
||||
*
|
||||
* @param {Object} cc
|
||||
* @param {Number} age
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function isStale(cc, age) {
|
||||
return cc['max-age'] && cc['max-age'] <= age;
|
||||
}
|
44
node_modules/connect/lib/middleware/vhost.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
/*!
|
||||
* Connect - vhost
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Setup vhost for the given `hostname` and `server`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* connect(
|
||||
* 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, '(.*?)') + '$');
|
||||
if (server.onvhost) server.onvhost(hostname);
|
||||
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[0].split('.').slice(0, -1);
|
||||
server.emit("request", req, res);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
};
|
79
node_modules/connect/lib/patch.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
/*!
|
||||
* Connect
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, res = http.OutgoingMessage.prototype;
|
||||
|
||||
// original setHeader()
|
||||
|
||||
var setHeader = res.setHeader;
|
||||
|
||||
// original _renderHeaders()
|
||||
|
||||
var _renderHeaders = res._renderHeaders;
|
||||
|
||||
if (res._hasConnectPatch) return;
|
||||
|
||||
/**
|
||||
* Provide a public "header sent" flag
|
||||
* until node does.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.__defineGetter__('headerSent', function(){
|
||||
return this._headerSent;
|
||||
});
|
||||
|
||||
/**
|
||||
* Set header `field` to `val`, special-casing
|
||||
* the `Set-Cookie` field for multiple support.
|
||||
*
|
||||
* @param {String} field
|
||||
* @param {String} val
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.setHeader = function(field, val){
|
||||
var key = field.toLowerCase()
|
||||
, prev;
|
||||
|
||||
// special-case Set-Cookie
|
||||
if (this._headers && 'set-cookie' == key) {
|
||||
if (prev = this.getHeader(field)) {
|
||||
val = Array.isArray(prev)
|
||||
? prev.concat(val)
|
||||
: [prev, val];
|
||||
}
|
||||
// charset
|
||||
} else if ('content-type' == key && this.charset) {
|
||||
val += '; charset=' + this.charset;
|
||||
}
|
||||
|
||||
return setHeader.call(this, field, val);
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy `res.end()` to expose a 'header' event,
|
||||
* allowing arbitrary augmentation before the header
|
||||
* fields are written to the socket.
|
||||
*
|
||||
* NOTE: this _only_ supports node's progressive header
|
||||
* field API aka `res.setHeader()`.
|
||||
*/
|
||||
|
||||
res._renderHeaders = function(){
|
||||
this.emit('header');
|
||||
return _renderHeaders.call(this);
|
||||
};
|
||||
|
||||
res._hasConnectPatch = true;
|
75
node_modules/connect/lib/public/directory.html
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>listing directory {directory}</title>
|
||||
<style>{style}</style>
|
||||
<script>
|
||||
function $(id){
|
||||
var el = 'string' == typeof id
|
||||
? document.getElementById(id)
|
||||
: id;
|
||||
|
||||
el.on = function(event, fn){
|
||||
if ('content loaded' == event) event = 'DOMContentLoaded';
|
||||
el.addEventListener(event, fn, false);
|
||||
};
|
||||
|
||||
el.all = function(selector){
|
||||
return $(el.querySelectorAll(selector));
|
||||
};
|
||||
|
||||
el.each = function(fn){
|
||||
for (var i = 0, len = el.length; i < len; ++i) {
|
||||
fn($(el[i]), i);
|
||||
}
|
||||
};
|
||||
|
||||
el.getClasses = function(){
|
||||
return this.getAttribute('class').split(/\s+/);
|
||||
};
|
||||
|
||||
el.addClass = function(name){
|
||||
var classes = this.getAttribute('class');
|
||||
el.setAttribute('class', classes
|
||||
? classes + ' ' + name
|
||||
: name);
|
||||
};
|
||||
|
||||
el.removeClass = function(name){
|
||||
var classes = this.getClasses().filter(function(curr){
|
||||
return curr != name;
|
||||
});
|
||||
this.setAttribute('class', classes);
|
||||
};
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function search() {
|
||||
var str = $('search').value
|
||||
, links = $('files').all('a');
|
||||
|
||||
links.each(function(link){
|
||||
var text = link.textContent;
|
||||
|
||||
if ('..' == text) return;
|
||||
if (str.length && ~text.indexOf(str)) {
|
||||
link.addClass('highlight');
|
||||
} else {
|
||||
link.removeClass('highlight');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(window).on('content loaded', function(){
|
||||
$('search').on('keyup', search);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body class="directory">
|
||||
<input id="search" type="text" placeholder="Search" autocomplete="off" />
|
||||
<div id="wrapper">
|
||||
<h1>{linked-path}</h1>
|
||||
{files}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
13
node_modules/connect/lib/public/error.html
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{error}</title>
|
||||
<style>{style}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<h1>{title}</h1>
|
||||
<h2><em>500</em> {error}</h2>
|
||||
<ul id="stacktrace">{stack}</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
node_modules/connect/lib/public/favicon.ico
generated
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
node_modules/connect/lib/public/icons/page.png
generated
vendored
Executable file
After Width: | Height: | Size: 635 B |
BIN
node_modules/connect/lib/public/icons/page_add.png
generated
vendored
Executable file
After Width: | Height: | Size: 739 B |
BIN
node_modules/connect/lib/public/icons/page_attach.png
generated
vendored
Executable file
After Width: | Height: | Size: 794 B |
BIN
node_modules/connect/lib/public/icons/page_code.png
generated
vendored
Executable file
After Width: | Height: | Size: 818 B |
BIN
node_modules/connect/lib/public/icons/page_copy.png
generated
vendored
Executable file
After Width: | Height: | Size: 663 B |
BIN
node_modules/connect/lib/public/icons/page_delete.png
generated
vendored
Executable file
After Width: | Height: | Size: 740 B |
BIN
node_modules/connect/lib/public/icons/page_edit.png
generated
vendored
Executable file
After Width: | Height: | Size: 807 B |
BIN
node_modules/connect/lib/public/icons/page_error.png
generated
vendored
Executable file
After Width: | Height: | Size: 793 B |
BIN
node_modules/connect/lib/public/icons/page_excel.png
generated
vendored
Executable file
After Width: | Height: | Size: 817 B |
BIN
node_modules/connect/lib/public/icons/page_find.png
generated
vendored
Executable file
After Width: | Height: | Size: 879 B |
BIN
node_modules/connect/lib/public/icons/page_gear.png
generated
vendored
Executable file
After Width: | Height: | Size: 833 B |
BIN
node_modules/connect/lib/public/icons/page_go.png
generated
vendored
Executable file
After Width: | Height: | Size: 779 B |
BIN
node_modules/connect/lib/public/icons/page_green.png
generated
vendored
Executable file
After Width: | Height: | Size: 621 B |
BIN
node_modules/connect/lib/public/icons/page_key.png
generated
vendored
Executable file
After Width: | Height: | Size: 801 B |
BIN
node_modules/connect/lib/public/icons/page_lightning.png
generated
vendored
Executable file
After Width: | Height: | Size: 839 B |
BIN
node_modules/connect/lib/public/icons/page_link.png
generated
vendored
Executable file
After Width: | Height: | Size: 830 B |
BIN
node_modules/connect/lib/public/icons/page_paintbrush.png
generated
vendored
Executable file
After Width: | Height: | Size: 813 B |
BIN
node_modules/connect/lib/public/icons/page_paste.png
generated
vendored
Executable file
After Width: | Height: | Size: 703 B |
BIN
node_modules/connect/lib/public/icons/page_red.png
generated
vendored
Executable file
After Width: | Height: | Size: 641 B |
BIN
node_modules/connect/lib/public/icons/page_refresh.png
generated
vendored
Executable file
After Width: | Height: | Size: 858 B |
BIN
node_modules/connect/lib/public/icons/page_save.png
generated
vendored
Executable file
After Width: | Height: | Size: 774 B |
BIN
node_modules/connect/lib/public/icons/page_white.png
generated
vendored
Executable file
After Width: | Height: | Size: 294 B |
BIN
node_modules/connect/lib/public/icons/page_white_acrobat.png
generated
vendored
Executable file
After Width: | Height: | Size: 591 B |
BIN
node_modules/connect/lib/public/icons/page_white_actionscript.png
generated
vendored
Executable file
After Width: | Height: | Size: 664 B |
BIN
node_modules/connect/lib/public/icons/page_white_add.png
generated
vendored
Executable file
After Width: | Height: | Size: 512 B |
BIN
node_modules/connect/lib/public/icons/page_white_c.png
generated
vendored
Executable file
After Width: | Height: | Size: 587 B |
BIN
node_modules/connect/lib/public/icons/page_white_camera.png
generated
vendored
Executable file
After Width: | Height: | Size: 656 B |
BIN
node_modules/connect/lib/public/icons/page_white_cd.png
generated
vendored
Executable file
After Width: | Height: | Size: 666 B |
BIN
node_modules/connect/lib/public/icons/page_white_code.png
generated
vendored
Executable file
After Width: | Height: | Size: 603 B |
BIN
node_modules/connect/lib/public/icons/page_white_code_red.png
generated
vendored
Executable file
After Width: | Height: | Size: 587 B |
BIN
node_modules/connect/lib/public/icons/page_white_coldfusion.png
generated
vendored
Executable file
After Width: | Height: | Size: 592 B |
BIN
node_modules/connect/lib/public/icons/page_white_compressed.png
generated
vendored
Executable file
After Width: | Height: | Size: 724 B |
BIN
node_modules/connect/lib/public/icons/page_white_copy.png
generated
vendored
Executable file
After Width: | Height: | Size: 309 B |
BIN
node_modules/connect/lib/public/icons/page_white_cplusplus.png
generated
vendored
Executable file
After Width: | Height: | Size: 621 B |
BIN
node_modules/connect/lib/public/icons/page_white_csharp.png
generated
vendored
Executable file
After Width: | Height: | Size: 700 B |
BIN
node_modules/connect/lib/public/icons/page_white_cup.png
generated
vendored
Executable file
After Width: | Height: | Size: 639 B |
BIN
node_modules/connect/lib/public/icons/page_white_database.png
generated
vendored
Executable file
After Width: | Height: | Size: 579 B |
BIN
node_modules/connect/lib/public/icons/page_white_delete.png
generated
vendored
Executable file
After Width: | Height: | Size: 536 B |
BIN
node_modules/connect/lib/public/icons/page_white_dvd.png
generated
vendored
Executable file
After Width: | Height: | Size: 638 B |
BIN
node_modules/connect/lib/public/icons/page_white_edit.png
generated
vendored
Executable file
After Width: | Height: | Size: 618 B |
BIN
node_modules/connect/lib/public/icons/page_white_error.png
generated
vendored
Executable file
After Width: | Height: | Size: 623 B |
BIN
node_modules/connect/lib/public/icons/page_white_excel.png
generated
vendored
Executable file
After Width: | Height: | Size: 663 B |
BIN
node_modules/connect/lib/public/icons/page_white_find.png
generated
vendored
Executable file
After Width: | Height: | Size: 676 B |
BIN
node_modules/connect/lib/public/icons/page_white_flash.png
generated
vendored
Executable file
After Width: | Height: | Size: 582 B |
BIN
node_modules/connect/lib/public/icons/page_white_freehand.png
generated
vendored
Executable file
After Width: | Height: | Size: 639 B |
BIN
node_modules/connect/lib/public/icons/page_white_gear.png
generated
vendored
Executable file
After Width: | Height: | Size: 402 B |
BIN
node_modules/connect/lib/public/icons/page_white_get.png
generated
vendored
Executable file
After Width: | Height: | Size: 516 B |
BIN
node_modules/connect/lib/public/icons/page_white_go.png
generated
vendored
Executable file
After Width: | Height: | Size: 612 B |
BIN
node_modules/connect/lib/public/icons/page_white_h.png
generated
vendored
Executable file
After Width: | Height: | Size: 603 B |
BIN
node_modules/connect/lib/public/icons/page_white_horizontal.png
generated
vendored
Executable file
After Width: | Height: | Size: 296 B |
BIN
node_modules/connect/lib/public/icons/page_white_key.png
generated
vendored
Executable file
After Width: | Height: | Size: 616 B |
BIN
node_modules/connect/lib/public/icons/page_white_lightning.png
generated
vendored
Executable file
After Width: | Height: | Size: 669 B |