1 Commits

Author SHA1 Message Date
John Crepezzi
d1e0644a4e Added node modules 2012-02-06 14:09:01 -05:00
614 changed files with 78350 additions and 3117 deletions

View File

@@ -1,8 +0,0 @@
Dockerfile
.git
npm-debug.log
node_modules
*.swp
*.swo
data
*.DS_Store

View File

@@ -1,2 +0,0 @@
**/*.min.js
config.js

View File

@@ -1,25 +0,0 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

2
.gitignore vendored
View File

@@ -1,7 +1,5 @@
npm-debug.log npm-debug.log
node_modules
*.swp *.swp
*.swo *.swo
data data
*.DS_Store *.DS_Store
config.json

View File

@@ -1,68 +0,0 @@
FROM node:14.8.0-stretch
RUN mkdir -p /usr/src/app && \
chown node:node /usr/src/app
USER node:node
WORKDIR /usr/src/app
COPY --chown=node:node . .
RUN npm install && \
npm install redis@0.8.1 && \
npm install pg@4.1.1 && \
npm install memcached@2.2.2 && \
npm install aws-sdk@2.738.0 && \
npm install rethinkdbdash@2.3.31
ENV STORAGE_TYPE=memcached \
STORAGE_HOST=127.0.0.1 \
STORAGE_PORT=11211\
STORAGE_EXPIRE_SECONDS=2592000\
STORAGE_DB=2 \
STORAGE_AWS_BUCKET= \
STORAGE_AWS_REGION= \
STORAGE_USENAME= \
STORAGE_PASSWORD= \
STORAGE_FILEPATH=
ENV LOGGING_LEVEL=verbose \
LOGGING_TYPE=Console \
LOGGING_COLORIZE=true
ENV HOST=0.0.0.0\
PORT=7777\
KEY_LENGTH=10\
MAX_LENGTH=400000\
STATIC_MAX_AGE=86400\
RECOMPRESS_STATIC_ASSETS=true
ENV KEYGENERATOR_TYPE=phonetic \
KEYGENERATOR_KEYSPACE=
ENV RATELIMITS_NORMAL_TOTAL_REQUESTS=500\
RATELIMITS_NORMAL_EVERY_MILLISECONDS=60000 \
RATELIMITS_WHITELIST_TOTAL_REQUESTS= \
RATELIMITS_WHITELIST_EVERY_MILLISECONDS= \
# comma separated list for the whitelisted \
RATELIMITS_WHITELIST=example1.whitelist,example2.whitelist \
\
RATELIMITS_BLACKLIST_TOTAL_REQUESTS= \
RATELIMITS_BLACKLIST_EVERY_MILLISECONDS= \
# comma separated list for the blacklisted \
RATELIMITS_BLACKLIST=example1.blacklist,example2.blacklist
ENV DOCUMENTS=about=./about.md
EXPOSE ${PORT}
STOPSIGNAL SIGINT
ENTRYPOINT [ "bash", "docker-entrypoint.sh" ]
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s \
--retries=3 CMD [ "sh", "-c", "echo -n 'curl localhost:7777... '; \
(\
curl -sf localhost:7777 > /dev/null\
) && echo OK || (\
echo Fail && exit 2\
)"]
CMD ["npm", "start"]

View File

@@ -1 +0,0 @@
web: node server.js

235
README.md
View File

@@ -31,31 +31,21 @@ STDOUT. Check the README there for more details and usages.
1. Download the package, and expand it 1. Download the package, and expand it
2. Explore the settings inside of config.js, but the defaults should be good 2. Explore the settings inside of config.js, but the defaults should be good
3. `npm install` 3. `npm install`
4. `npm start` (you may specify an optional `<config-path>` as well) 4. `npm start`
## Settings ## Settings
* `host` - the host the server runs on (default localhost) * `host` - the host the server runs on (default localhost)
* `port` - the port the server runs on (default 7777) * `port` - the port the server runs on (default 7777)
* `keyLength` - the length of the keys to user (default 10) * `keyLength` - the length of the keys to user (default 10)
* `maxLength` - maximum length of a paste (default 400000) * `maxLength` - maximum length of a paste (default none)
* `staticMaxAge` - max age for static assets (86400) * `staticMaxAge` - max age for static assets (86400)
* `recompressStaticAssets` - whether or not to compile static js assets (true) * `recompressStatisAssets` - whether or not to compile static js assets (true)
* `documents` - static documents to serve (ex: http://hastebin.com/about.com) * `documents` - static documents to serve (ex: http://hastebin.com/about.com)
in addition to static assets. These will never expire. in addition to static assets. These will never expire.
* `storage` - storage options (see below) * `storage` - storage options (see below)
* `logging` - logging preferences * `logging` - logging preferences
* `keyGenerator` - key generator options (see below) * `keyGenerator` - key generator options (see below)
* `rateLimits` - settings for rate limiting (see below)
## Rate Limiting
When present, the `rateLimits` option enables built-in rate limiting courtesy
of `connect-ratelimit`. Any of the options supported by that library can be
used and set in `config.js`.
See the README for [connect-ratelimit](https://github.com/dharmafly/connect-ratelimit)
for more information!
## Key Generation ## Key Generation
@@ -97,14 +87,11 @@ something like:
} }
``` ```
where `path` represents where you want the files stored. Where `path` represents where you want the files stored
File storage currently does not support paste expiration, you can follow [#191](https://github.com/seejohnrun/haste-server/issues/191) for status updates.
### Redis ### Redis
To use redis storage you must install the `redis` package in npm, and have To use redis storage you must install the redis package in npm
`redis-server` running on the machine.
`npm install redis` `npm install redis`
@@ -125,62 +112,11 @@ or post.
All of which are optional except `type` with very logical default values. All of which are optional except `type` with very logical default values.
If your Redis server is configured for password authentification, use the `password` field.
### Postgres
To use postgres storage you must install the `pg` package in npm
`npm install pg`
Once you've done that, your config section should look like:
``` json
{
"type": "postgres",
"connectionUrl": "postgres://user:password@host:5432/database"
}
```
You can also just set the environment variable for `DATABASE_URL` to your database connection url.
You will have to manually add a table to your postgres database:
`create table entries (id serial primary key, key varchar(255) not null, value text not null, expiration int, unique(key));`
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.
### MongoDB
To use mongodb storage you must install the 'mongodb' package in npm
`npm install mongodb`
Once you've done that, your config section should look like:
``` json
{
"type": "mongo",
"connectionUrl": "mongodb://localhost:27017/database"
}
```
You can also just set the environment variable for `DATABASE_URL` to your database connection url.
Unlike with postgres you do NOT have to create the table in your mongo database prior to running.
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.
### Memcached ### Memcached
To use memcache storage you must install the `memcached` package via npm To use memcached storage you must install the `memcache` package via npm
`npm install memcached` `npm install memcache`
Once you've done that, your config section should look like: Once you've done that, your config section should look like:
@@ -198,161 +134,6 @@ forward on GETs.
All of which are optional except `type` with very logical default values. All of which are optional except `type` with very logical default values.
### RethinkDB
To use the RethinkDB storage system, you must install the `rethinkdbdash` package via npm
`npm install rethinkdbdash`
Once you've done that, your config section should look like this:
``` json
{
"type": "rethinkdb",
"host": "127.0.0.1",
"port": 28015,
"db": "haste"
}
```
In order for this to work, the database must be pre-created before the script is ran.
Also, you must create an `uploads` table, which will store all the data for uploads.
You can optionally add the `user` and `password` properties to use a user system.
### Google Datastore
To use the Google Datastore storage system, you must install the `@google-cloud/datastore` package via npm
`npm install @google-cloud/datastore`
Once you've done that, your config section should look like this:
``` json
{
"type": "google-datastore"
}
```
Authentication is handled automatically by [Google Cloud service account credentials](https://cloud.google.com/docs/authentication/getting-started), by providing authentication details to the GOOGLE_APPLICATION_CREDENTIALS environmental variable.
### Amazon S3
To use [Amazon S3](https://aws.amazon.com/s3/) as a storage system, you must
install the `aws-sdk` package via npm:
`npm install aws-sdk`
Once you've done that, your config section should look like this:
```json
{
"type": "amazon-s3",
"bucket": "your-bucket-name",
"region": "us-east-1"
}
```
Authentication is handled automatically by the client. Check
[Amazon's documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html)
for more information. You will need to grant your role these permissions to
your bucket:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::your-bucket-name-goes-here/*"
}
]
}
```
## Docker
### Build image
```bash
docker build --tag haste-server .
```
### Run container
For this example we will run haste-server, and connect it to a redis server
```bash
docker run --name haste-server-container --env STORAGE_TYPE=redis --env STORAGE_HOST=redis-server --env STORAGE_PORT=6379 haste-server
```
### Use docker-compose example
There is an example `docker-compose.yml` which runs haste-server together with memcached
```bash
docker-compose up
```
### Configuration
The docker image is configured using environmental variables as you can see in the example above.
Here is a list of all the environment variables
### Storage
| Name | Default value | Description |
| :--------------------: | :-----------: | :-----------------------------------------------------------------------------------------------------------: |
| STORAGE_TYPE | memcached | Type of storage . Accepted values: "memcached","redis","postgres","rethinkdb", "amazon-s3", and "file" |
| STORAGE_HOST | 127.0.0.1 | Storage host. Applicable for types: memcached, redis, postgres, and rethinkdb |
| STORAGE_PORT | 11211 | Port on the storage host. Applicable for types: memcached, redis, postgres, and rethinkdb |
| STORAGE_EXPIRE_SECONDS | 2592000 | Number of seconds to expire keys in. Applicable for types. Redis, postgres, memcached. `expire` option to the |
| STORAGE_DB | 2 | The name of the database. Applicable for redis, postgres, and rethinkdb |
| STORAGE_PASSWORD | | Password for database. Applicable for redis, postges, rethinkdb . |
| STORAGE_USERNAME | | Database username. Applicable for postgres, and rethinkdb |
| STORAGE_AWS_BUCKET | | Applicable for amazon-s3. This is the name of the S3 bucket |
| STORAGE_AWS_REGION | | Applicable for amazon-s3. The region in which the bucket is located |
| STORAGE_FILEPATH | | Path to file to save data to. Applicable for type file |
### Logging
| Name | Default value | Description |
| :---------------: | :-----------: | :---------: |
| LOGGING_LEVEL | verbose | |
| LOGGING_TYPE= | Console |
| LOGGING_COLORIZE= | true |
### Basics
| Name | Default value | Description |
| :----------------------: | :--------------: | :---------------------------------------------------------------------------------------: |
| HOST | 0.0.0.0 | The hostname which the server answers on |
| PORT | 7777 | The port on which the server is running |
| KEY_LENGTH | 10 | the length of the keys to user |
| MAX_LENGTH | 400000 | maximum length of a paste |
| STATIC_MAX_AGE | 86400 | max age for static assets |
| RECOMPRESS_STATIC_ASSETS | true | whether or not to compile static js assets |
| KEYGENERATOR_TYPE | phonetic | Type of key generator. Acceptable values: "phonetic", or "random" |
| KEYGENERATOR_KEYSPACE | | keySpace argument is a string of acceptable characters |
| DOCUMENTS | about=./about.md | Comma separated list of static documents to serve. ex: \n about=./about.md,home=./home.md |
### Rate limits
| Name | Default value | Description |
| :----------------------------------: | :-----------------------------------: | :--------------------------------------------------------------------------------------: |
| RATELIMITS_NORMAL_TOTAL_REQUESTS | 500 | By default anyone uncategorized will be subject to 500 requests in the defined timespan. |
| RATELIMITS_NORMAL_EVERY_MILLISECONDS | 60000 | The timespan to allow the total requests for uncategorized users |
| RATELIMITS_WHITELIST_TOTAL_REQUESTS | | By default client names in the whitelist will not have their requests limited. |
| RATELIMITS_WHITELIST_EVERY_SECONDS | | By default client names in the whitelist will not have their requests limited. |
| RATELIMITS_WHITELIST | example1.whitelist,example2.whitelist | Comma separated list of the clients which are in the whitelist pool |
| RATELIMITS_BLACKLIST_TOTAL_REQUESTS | | By default client names in the blacklist will be subject to 0 requests per hours. |
| RATELIMITS_BLACKLIST_EVERY_SECONDS | | By default client names in the blacklist will be subject to 0 requests per hours |
| RATELIMITS_BLACKLIST | example1.blacklist,example2.blacklist | Comma separated list of the clients which are in the blacklistpool. |
## Author ## Author
@@ -362,7 +143,7 @@ John Crepezzi <john.crepezzi@gmail.com>
(The MIT License) (The MIT License)
Copyright © 2011-2012 John Crepezzi Copyright © 2011 John Crepezzi
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the Software), to deal in

View File

@@ -15,19 +15,44 @@ To make a new entry, click "New" (or type 'control + n')
## From the Console ## From the Console
[bin-client](git.webionite.com/ceda_ei/bin-client) 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.
Add the following to your bashrc/zshrc `cat something | haste` # http://hastebin.com/1238193
```
export MKR_BIN='https://bin.webionite.com/' You can even take this a step further, and cut out the last step of copying the
export HASTEBIN=1 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.
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
## Duration
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.
## Open Source ## Open Source
Haste can easily be installed behind your network, and it's all open source! Haste can easily be installed behind your network, and its all open source!
* [haste-server](https://git.webionite.com/Webionite/haste-server) * [haste-client](https://github.com/seejohnrun/haste-client)
* [haste-server](https://github.com/seejohnrun/haste-server)
## Author ## Author

View File

@@ -1,6 +1,6 @@
{ {
"host": "0.0.0.0", "host": "localhost",
"port": 7777, "port": 7777,
"keyLength": 10, "keyLength": 10,
@@ -23,17 +23,9 @@
"type": "phonetic" "type": "phonetic"
}, },
"rateLimits": {
"categories": {
"normal": {
"totalRequests": 500,
"every": 60000
}
}
},
"storage": { "storage": {
"type": "file" "type": "file",
"path": "./data"
}, },
"documents": { "documents": {

View File

@@ -1,19 +0,0 @@
version: '3.0'
services:
haste-server:
build: .
networks:
- db-network
environment:
- STORAGE_TYPE=memcached
- STORAGE_HOST=memcached
- STORAGE_PORT=11211
ports:
- 7777:7777
memcached:
image: memcached:latest
networks:
- db-network
networks:
db-network:

View File

@@ -1,108 +0,0 @@
const {
HOST,
PORT,
KEY_LENGTH,
MAX_LENGTH,
STATIC_MAX_AGE,
RECOMPRESS_STATIC_ASSETS,
STORAGE_TYPE,
STORAGE_HOST,
STORAGE_PORT,
STORAGE_EXPIRE_SECONDS,
STORAGE_DB,
STORAGE_AWS_BUCKET,
STORAGE_AWS_REGION,
STORAGE_PASSWORD,
STORAGE_USERNAME,
STORAGE_FILEPATH,
LOGGING_LEVEL,
LOGGING_TYPE,
LOGGING_COLORIZE,
KEYGENERATOR_TYPE,
KEY_GENERATOR_KEYSPACE,
RATE_LIMITS_NORMAL_TOTAL_REQUESTS,
RATE_LIMITS_NORMAL_EVERY_MILLISECONDS,
RATE_LIMITS_WHITELIST_TOTAL_REQUESTS,
RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS,
RATE_LIMITS_WHITELIST,
RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS,
RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS,
RATE_LIMITS_BLACKLIST,
DOCUMENTS,
} = process.env;
const config = {
host: HOST,
port: PORT,
keyLength: KEY_LENGTH,
maxLength: MAX_LENGTH,
staticMaxAge: STATIC_MAX_AGE,
recompressStaticAssets: RECOMPRESS_STATIC_ASSETS,
logging: [
{
level: LOGGING_LEVEL,
type: LOGGING_TYPE,
colorize: LOGGING_COLORIZE,
},
],
keyGenerator: {
type: KEYGENERATOR_TYPE,
keyspace: KEY_GENERATOR_KEYSPACE,
},
rateLimits: {
whitelist: RATE_LIMITS_WHITELIST ? RATE_LIMITS_WHITELIST.split(",") : [],
blacklist: RATE_LIMITS_BLACKLIST ? RATE_LIMITS_BLACKLIST.split(",") : [],
categories: {
normal: {
totalRequests: RATE_LIMITS_NORMAL_TOTAL_REQUESTS,
every: RATE_LIMITS_NORMAL_EVERY_MILLISECONDS,
},
whitelist:
RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS ||
RATE_LIMITS_WHITELIST_TOTAL_REQUESTS
? {
totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS,
every: RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS,
}
: null,
blacklist:
RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS ||
RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS
? {
totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS,
every: RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS,
}
: null,
},
},
storage: {
type: STORAGE_TYPE,
host: STORAGE_HOST,
port: STORAGE_PORT,
expire: STORAGE_EXPIRE_SECONDS,
bucket: STORAGE_AWS_BUCKET,
region: STORAGE_AWS_REGION,
connectionUrl: `postgres://${STORAGE_USERNAME}:${STORAGE_PASSWORD}@${STORAGE_HOST}:${STORAGE_PORT}/${STORAGE_DB}`,
db: STORAGE_DB,
user: STORAGE_USERNAME,
password: STORAGE_PASSWORD,
path: STORAGE_FILEPATH,
},
documents: DOCUMENTS
? DOCUMENTS.split(",").reduce((acc, item) => {
const keyAndValueArray = item.replace(/\s/g, "").split("=");
return { ...acc, [keyAndValueArray[0]]: keyAndValueArray[1] };
}, {})
: null,
};
console.log(JSON.stringify(config));

View File

@@ -1,9 +0,0 @@
#!/bin/bash
# We use this file to translate environmental variables to .env files used by the application
set -e
node ./docker-entrypoint.js > ./config.js
exec "$@"

View File

@@ -1,5 +1,4 @@
var winston = require('winston'); var winston = require('winston');
var Busboy = require('busboy');
// For handling serving stored documents // For handling serving stored documents
@@ -16,56 +15,34 @@ var DocumentHandler = function(options) {
DocumentHandler.defaultKeyLength = 10; DocumentHandler.defaultKeyLength = 10;
// Handle retrieving a document // Handle retrieving a document
DocumentHandler.prototype.handleGet = function(request, response, config) { DocumentHandler.prototype.handleGet = function(key, response, skipExpire) {
const key = request.params.id.split('.')[0];
const skipExpire = !!config.documents[key];
this.store.get(key, function(ret) { this.store.get(key, function(ret) {
if (ret) { if (ret) {
winston.verbose('retrieved document', { key: key }); winston.verbose('retrieved document', { key: key });
response.writeHead(200, { 'content-type': 'application/json' }); response.writeHead(200, { 'content-type': 'application/json' });
if (request.method === 'HEAD') {
response.end();
} else {
response.end(JSON.stringify({ data: ret, key: key })); response.end(JSON.stringify({ data: ret, key: key }));
} }
}
else { else {
winston.warn('document not found', { key: key }); winston.warn('document not found', { key: key });
response.writeHead(404, { 'content-type': 'application/json' }); response.writeHead(404, { 'content-type': 'application/json' });
if (request.method === 'HEAD') {
response.end();
} else {
response.end(JSON.stringify({ message: 'Document not found.' })); response.end(JSON.stringify({ message: 'Document not found.' }));
} }
}
}, skipExpire); }, skipExpire);
}; };
// Handle retrieving the raw version of a document // Handle retrieving the raw version of a document
DocumentHandler.prototype.handleRawGet = function(request, response, config) { DocumentHandler.prototype.handleRawGet = function(key, response, skipExpire) {
const key = request.params.id.split('.')[0];
const skipExpire = !!config.documents[key];
this.store.get(key, function(ret) { this.store.get(key, function(ret) {
if (ret) { if (ret) {
winston.verbose('retrieved raw document', { key: key }); winston.verbose('retrieved raw document', { key: key });
response.writeHead(200, { 'content-type': 'text/plain; charset=UTF-8' }); response.writeHead(200, { 'content-type': 'text/plain' });
if (request.method === 'HEAD') {
response.end();
} else {
response.end(ret); response.end(ret);
} }
}
else { else {
winston.warn('raw document not found', { key: key }); winston.warn('raw document not found', { key: key });
response.writeHead(404, { 'content-type': 'application/json' }); response.writeHead(404, { 'content-type': 'application/json' });
if (request.method === 'HEAD') {
response.end();
} else {
response.end(JSON.stringify({ message: 'Document not found.' })); response.end(JSON.stringify({ message: 'Document not found.' }));
} }
}
}, skipExpire); }, skipExpire);
}; };
@@ -74,10 +51,11 @@ DocumentHandler.prototype.handlePost = function (request, response) {
var _this = this; var _this = this;
var buffer = ''; var buffer = '';
var cancelled = false; var cancelled = false;
request.on('data', function(data) {
// What to do when done if (!buffer) {
var onSuccess = function () { response.writeHead(200, { 'content-type': 'application/json' });
// Check length }
buffer += data.toString();
if (_this.maxLength && buffer.length > _this.maxLength) { if (_this.maxLength && buffer.length > _this.maxLength) {
cancelled = true; cancelled = true;
winston.warn('document >maxLength', { maxLength: _this.maxLength }); winston.warn('document >maxLength', { maxLength: _this.maxLength });
@@ -85,14 +63,14 @@ DocumentHandler.prototype.handlePost = function (request, response) {
response.end( response.end(
JSON.stringify({ message: 'Document exceeds maximum length.' }) JSON.stringify({ message: 'Document exceeds maximum length.' })
); );
return;
} }
// And then save if we should });
request.on('end', function(end) {
if (cancelled) return;
_this.chooseKey(function(key) { _this.chooseKey(function(key) {
_this.store.set(key, buffer, function(res) { _this.store.set(key, buffer, function(res) {
if (res) { if (res) {
winston.verbose('added document', { key: key }); winston.verbose('added document', { key: key });
response.writeHead(200, { 'content-type': 'application/json' });
response.end(JSON.stringify({ key: key })); response.end(JSON.stringify({ key: key }));
} }
else { else {
@@ -102,37 +80,12 @@ DocumentHandler.prototype.handlePost = function (request, response) {
} }
}); });
}); });
};
// If we should, parse a form to grab the data
var ct = request.headers['content-type'];
if (ct && ct.split(';')[0] === 'multipart/form-data') {
var busboy = new Busboy({ headers: request.headers });
busboy.on('field', function (fieldname, val) {
if (fieldname === 'data') {
buffer = val;
}
});
busboy.on('finish', function () {
onSuccess();
});
request.pipe(busboy);
// Otherwise, use our own and just grab flat data from POST body
} else {
request.on('data', function (data) {
buffer += data.toString();
});
request.on('end', function () {
if (cancelled) { return; }
onSuccess();
}); });
request.on('error', function(error) { request.on('error', function(error) {
winston.error('connection error: ' + error.message); winston.error('connection error: ' + error.message);
response.writeHead(500, { 'content-type': 'application/json' }); response.writeHead(500, { 'content-type': 'application/json' });
response.end(JSON.stringify({ message: 'Connection error.' })); response.end(JSON.stringify({ message: 'Connection error.' }));
cancelled = true;
}); });
}
}; };
// Keep choosing keys until one isn't taken // Keep choosing keys until one isn't taken
@@ -145,7 +98,7 @@ DocumentHandler.prototype.chooseKey = function(callback) {
} else { } else {
callback(key); callback(key);
} }
}, true); // Don't bump expirations when key searching });
}; };
DocumentHandler.prototype.acceptableKey = function() { DocumentHandler.prototype.acceptableKey = function() {

View File

@@ -1,56 +0,0 @@
/*global require,module,process*/
var AWS = require('aws-sdk');
var winston = require('winston');
var AmazonS3DocumentStore = function(options) {
this.expire = options.expire;
this.bucket = options.bucket;
this.client = new AWS.S3({region: options.region});
};
AmazonS3DocumentStore.prototype.get = function(key, callback, skipExpire) {
var _this = this;
var req = {
Bucket: _this.bucket,
Key: key
};
_this.client.getObject(req, function(err, data) {
if(err) {
callback(false);
}
else {
callback(data.Body.toString('utf-8'));
if (_this.expire && !skipExpire) {
winston.warn('amazon s3 store cannot set expirations on keys');
}
}
});
}
AmazonS3DocumentStore.prototype.set = function(key, data, callback, skipExpire) {
var _this = this;
var req = {
Bucket: _this.bucket,
Key: key,
Body: data,
ContentType: 'text/plain'
};
_this.client.putObject(req, function(err, data) {
if (err) {
callback(false);
}
else {
callback(true);
if (_this.expire && !skipExpire) {
winston.warn('amazon s3 store cannot set expirations on keys');
}
}
});
}
module.exports = AmazonS3DocumentStore;

View File

@@ -1,89 +0,0 @@
/*global require,module,process*/
const Datastore = require('@google-cloud/datastore');
const winston = require('winston');
class GoogleDatastoreDocumentStore {
// Create a new store with options
constructor(options) {
this.kind = "Haste";
this.expire = options.expire;
this.datastore = new Datastore();
}
// Save file in a key
set(key, data, callback, skipExpire) {
var expireTime = (skipExpire || this.expire === undefined) ? null : new Date(Date.now() + this.expire * 1000);
var taskKey = this.datastore.key([this.kind, key])
var task = {
key: taskKey,
data: [
{
name: 'value',
value: data,
excludeFromIndexes: true
},
{
name: 'expiration',
value: expireTime
}
]
};
this.datastore.insert(task).then(() => {
callback(true);
})
.catch(err => {
callback(false);
});
}
// Get a file from a key
get(key, callback, skipExpire) {
var taskKey = this.datastore.key([this.kind, key])
this.datastore.get(taskKey).then((entity) => {
if (skipExpire || entity[0]["expiration"] == null) {
callback(entity[0]["value"]);
}
else {
// check for expiry
if (entity[0]["expiration"] < new Date()) {
winston.info("document expired", {key: key, expiration: entity[0]["expiration"], check: new Date(null)});
callback(false);
}
else {
// update expiry
var task = {
key: taskKey,
data: [
{
name: 'value',
value: entity[0]["value"],
excludeFromIndexes: true
},
{
name: 'expiration',
value: new Date(Date.now() + this.expire * 1000)
}
]
};
this.datastore.update(task).then(() => {
})
.catch(err => {
winston.error("failed to update expiration", {error: err});
});
callback(entity[0]["value"]);
}
}
})
.catch(err => {
winston.error("Error retrieving value from Google Datastore", {error: err});
callback(false);
});
}
}
module.exports = GoogleDatastoreDocumentStore;

View File

@@ -1,54 +1,45 @@
const memcached = require('memcached'); var memcached = require('memcache');
const winston = require('winston'); var winston = require('winston');
class MemcachedDocumentStore {
// Create a new store with options // Create a new store with options
constructor(options) { var MemcachedDocumentStore = function(options) {
this.expire = options.expire; this.expire = options.expire;
if (!MemcachedDocumentStore.client) {
const host = options.host || '127.0.0.1'; MemcachedDocumentStore.connect(options);
const port = options.port || 11211;
const url = `${host}:${port}`;
this.connect(url);
} }
};
// Create a connection // Create a connection
connect(url) { MemcachedDocumentStore.connect = function(options) {
this.client = new memcached(url); var host = options.host || '127.0.0.1';
var port = options.port || 11211;
winston.info(`connecting to memcached on ${url}`); this.client = new memcached.Client(port, host);
this.client.connect();
this.client.on('failure', function(error) { this.client.on('connect', function() {
winston.info('error connecting to memcached', {error}); 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 // Save file in a key
set(key, data, callback, skipExpire) { MemcachedDocumentStore.prototype.set =
this.client.set(key, data, skipExpire ? 0 : this.expire || 0, (error) => { function(key, data, callback, skipExpire) {
callback(!error); MemcachedDocumentStore.client.set(key, data, function(err, reply) {
}); err ? callback(false) : callback(true);
} }, skipExpire ? 0 : this.expire);
};
// Get a file from a key // Get a file from a key
get(key, callback, skipExpire) { MemcachedDocumentStore.prototype.get = function(key, callback, skipExpire) {
this.client.get(key, (error, data) => { var _this = this;
const value = error ? false : data; MemcachedDocumentStore.client.get(key, function(err, reply) {
callback(err ? false : reply);
callback(value); if (_this.expire && !skipExpire) {
winston.warn('store does not currently push forward expirations on GET');
// Update the key so that the expiration is pushed forward
if (value && !skipExpire) {
this.set(key, data, (updateSucceeded) => {
if (!updateSucceeded) {
winston.error('failed to update expiration on GET', {key});
}
}, skipExpire);
} }
}); });
} };
}
module.exports = MemcachedDocumentStore; module.exports = MemcachedDocumentStore;

View File

@@ -1,88 +0,0 @@
var MongoClient = require('mongodb').MongoClient,
winston = require('winston');
var MongoDocumentStore = function (options) {
this.expire = options.expire;
this.connectionUrl = process.env.DATABASE_URl || options.connectionUrl;
};
MongoDocumentStore.prototype.set = function (key, data, callback, skipExpire) {
var now = Math.floor(new Date().getTime() / 1000),
that = this;
this.safeConnect(function (err, db) {
if (err)
return callback(false);
db.collection('entries').update({
'entry_id': key,
$or: [
{ expiration: -1 },
{ expiration: { $gt: now } }
]
}, {
'entry_id': key,
'value': data,
'expiration': that.expire && !skipExpire ? that.expire + now : -1
}, {
upsert: true
}, function (err, existing) {
if (err) {
winston.error('error persisting value to mongodb', { error: err });
return callback(false);
}
callback(true);
});
});
};
MongoDocumentStore.prototype.get = function (key, callback, skipExpire) {
var now = Math.floor(new Date().getTime() / 1000),
that = this;
this.safeConnect(function (err, db) {
if (err)
return callback(false);
db.collection('entries').findOne({
'entry_id': key,
$or: [
{ expiration: -1 },
{ expiration: { $gt: now } }
]
}, function (err, entry) {
if (err) {
winston.error('error persisting value to mongodb', { error: err });
return callback(false);
}
callback(entry === null ? false : entry.value);
if (entry !== null && entry.expiration !== -1 && that.expire && !skipExpire) {
db.collection('entries').update({
'entry_id': key
}, {
$set: {
'expiration': that.expire + now
}
}, function (err, result) { });
}
});
});
};
MongoDocumentStore.prototype.safeConnect = function (callback) {
MongoClient.connect(this.connectionUrl, function (err, db) {
if (err) {
winston.error('error connecting to mongodb', { error: err });
callback(err);
} else {
callback(undefined, db);
}
});
};
module.exports = MongoDocumentStore;

View File

@@ -1,80 +0,0 @@
/*global require,module,process*/
var winston = require('winston');
const {Pool} = require('pg');
// create table entries (id serial primary key, key varchar(255) not null, value text not null, expiration int, unique(key));
// A postgres document store
var PostgresDocumentStore = function (options) {
this.expireJS = options.expire;
const connectionString = process.env.DATABASE_URL || options.connectionUrl;
this.pool = new Pool({connectionString});
};
PostgresDocumentStore.prototype = {
// Set a given key
set: function (key, data, callback, skipExpire) {
var now = Math.floor(new Date().getTime() / 1000);
var that = this;
this.safeConnect(function (err, client, done) {
if (err) { return callback(false); }
client.query('INSERT INTO entries (key, value, expiration) VALUES ($1, $2, $3)', [
key,
data,
that.expireJS && !skipExpire ? that.expireJS + now : null
], function (err) {
if (err) {
winston.error('error persisting value to postgres', { error: err });
return callback(false);
}
callback(true);
done();
});
});
},
// Get a given key's data
get: function (key, callback, skipExpire) {
var now = Math.floor(new Date().getTime() / 1000);
var that = this;
this.safeConnect(function (err, client, done) {
if (err) { return callback(false); }
client.query('SELECT id,value,expiration from entries where KEY = $1 and (expiration IS NULL or expiration > $2)', [key, now], function (err, result) {
if (err) {
winston.error('error retrieving value from postgres', { error: err });
return callback(false);
}
callback(result.rows.length ? result.rows[0].value : false);
if (result.rows.length && that.expireJS && !skipExpire) {
client.query('UPDATE entries SET expiration = $1 WHERE ID = $2', [
that.expireJS + now,
result.rows[0].id
], function (err) {
if (!err) {
done();
}
});
} else {
done();
}
});
});
},
// A connection wrapper
safeConnect: function (callback) {
this.pool.connect((error, client, done) => {
if (error) {
winston.error('error connecting to postgres', {error});
callback(error);
} else {
callback(undefined, client, done);
}
});
}
};
module.exports = PostgresDocumentStore;

View File

@@ -8,13 +8,9 @@ var winston = require('winston');
// options[db] - The db to use (default 0) // options[db] - The db to use (default 0)
// options[expire] - The time to live for each key set (default never) // options[expire] - The time to live for each key set (default never)
var RedisDocumentStore = function(options, client) { var RedisDocumentStore = function(options) {
this.expire = options.expire; this.expire = options.expire;
if (client) { if (!RedisDocumentStore.client) {
winston.info('using predefined redis client');
RedisDocumentStore.client = client;
} else if (!RedisDocumentStore.client) {
winston.info('configuring redis');
RedisDocumentStore.connect(options); RedisDocumentStore.connect(options);
} }
}; };
@@ -25,20 +21,11 @@ RedisDocumentStore.connect = function(options) {
var port = options.port || 6379; var port = options.port || 6379;
var index = options.db || 0; var index = options.db || 0;
RedisDocumentStore.client = redis.createClient(port, host); RedisDocumentStore.client = redis.createClient(port, host);
// authenticate if password is provided RedisDocumentStore.client.select(index, function(err, reply) {
if (options.password) {
RedisDocumentStore.client.auth(options.password);
}
RedisDocumentStore.client.on('error', function(err) {
winston.error('redis disconnected', err);
});
RedisDocumentStore.client.select(index, function(err) {
if (err) { if (err) {
winston.error( winston.error(
'error connecting to redis index ' + index, 'error connecting to redis index ' + index,
{ error: err } { error: err.message }
); );
process.exit(1); process.exit(1);
} }
@@ -51,7 +38,7 @@ RedisDocumentStore.connect = function(options) {
// Save file in a key // Save file in a key
RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) { RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
var _this = this; var _this = this;
RedisDocumentStore.client.set(key, data, function(err) { RedisDocumentStore.client.set(key, data, function(err, reply) {
if (err) { if (err) {
callback(false); callback(false);
} }
@@ -67,7 +54,7 @@ RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
// Expire a key in expire time if set // Expire a key in expire time if set
RedisDocumentStore.prototype.setExpiration = function(key) { RedisDocumentStore.prototype.setExpiration = function(key) {
if (this.expire) { if (this.expire) {
RedisDocumentStore.client.expire(key, this.expire, function(err) { RedisDocumentStore.client.expire(key, this.expire, function(err, reply) {
if (err) { if (err) {
winston.error('failed to set expiry on key: ' + key); winston.error('failed to set expiry on key: ' + key);
} }

View File

@@ -1,46 +0,0 @@
const crypto = require('crypto');
const rethink = require('rethinkdbdash');
const winston = require('winston');
const md5 = (str) => {
const md5sum = crypto.createHash('md5');
md5sum.update(str);
return md5sum.digest('hex');
};
class RethinkDBStore {
constructor(options) {
this.client = rethink({
silent: true,
host: options.host || '127.0.0.1',
port: options.port || 28015,
db: options.db || 'haste',
user: options.user || 'admin',
password: options.password || ''
});
}
set(key, data, callback) {
this.client.table('uploads').insert({ id: md5(key), data: data }).run((error) => {
if (error) {
callback(false);
winston.error('failed to insert to table', error);
return;
}
callback(true);
});
}
get(key, callback) {
this.client.table('uploads').get(md5(key)).run((error, result) => {
if (error || !result) {
callback(false);
if (error) winston.error('failed to insert to table', error);
return;
}
callback(result.data);
});
}
}
module.exports = RethinkDBStore;

View File

@@ -1,32 +0,0 @@
const fs = require('fs');
module.exports = class DictionaryGenerator {
constructor(options, readyCallback) {
// Check options format
if (!options) throw Error('No options passed to generator');
if (!options.path) throw Error('No dictionary path specified in options');
// Load dictionary
fs.readFile(options.path, 'utf8', (err, data) => {
if (err) throw err;
this.dictionary = data.split(/[\n\r]+/);
if (readyCallback) readyCallback();
});
}
// Generates a dictionary-based key, of keyLength words
createKey(keyLength) {
let text = '';
for (let i = 0; i < keyLength; i++) {
const index = Math.floor(Math.random() * this.dictionary.length);
text += this.dictionary[index];
}
return text;
}
};

View File

@@ -1,27 +1,32 @@
// Draws inspiration from pwgen and http://tools.arantius.com/password // Draws inspiration from pwgen and http://tools.arantius.com/password
var PhoneticKeyGenerator = function(options) {
const randOf = (collection) => { // No options
return () => {
return collection[Math.floor(Math.random() * collection.length)];
};
}; };
// Helper methods to get an random vowel or consonant // Generate a phonetic key
const randVowel = randOf('aeiou'); PhoneticKeyGenerator.prototype.createKey = function(keyLength) {
const randConsonant = randOf('bcdfghjklmnpqrstvwxyz'); var text = '';
for (var i = 0; i < keyLength; i++) {
module.exports = class PhoneticKeyGenerator { text += (i % 2 == 0) ? this.randConsonant() : this.randVowel();
// Generate a phonetic key of alternating consonant & vowel
createKey(keyLength) {
let text = '';
const start = Math.round(Math.random());
for (let i = 0; i < keyLength; i++) {
text += (i % 2 == start) ? randConsonant() : randVowel();
} }
return text; return text;
}
}; };
PhoneticKeyGenerator.consonants = 'bcdfghjklmnpqrstvwxy';
PhoneticKeyGenerator.vowels = 'aeiou';
// Get an random vowel
PhoneticKeyGenerator.prototype.randVowel = function() {
return PhoneticKeyGenerator.vowels[
Math.floor(Math.random() * PhoneticKeyGenerator.vowels.length)
];
};
// Get an random consonant
PhoneticKeyGenerator.prototype.randConsonant = function() {
return PhoneticKeyGenerator.consonants[
Math.floor(Math.random() * PhoneticKeyGenerator.consonants.length)
];
};
module.exports = PhoneticKeyGenerator;

View File

@@ -1,20 +1,19 @@
module.exports = class RandomKeyGenerator { var RandomKeyGenerator = function(options) {
if (!options) {
// Initialize a new generator with the given keySpace options = {};
constructor(options = {}) {
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
} }
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
};
// Generate a key of the given length // Generate a random key
createKey(keyLength) { RandomKeyGenerator.prototype.createKey = function(keyLength) {
var text = ''; var text = '';
var index;
for (var i = 0; i < keyLength; i++) { for (var i = 0; i < keyLength; i++) {
const index = Math.floor(Math.random() * this.keyspace.length); index = Math.floor(Math.random() * this.keyspace.length);
text += this.keyspace.charAt(index); text += this.keyspace.charAt(index);
} }
return text; return text;
}
}; };
module.exports = RandomKeyGenerator;

1
node_modules/.bin/_mocha generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../mocha/bin/_mocha

1
node_modules/.bin/mocha generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../mocha/bin/mocha

1
node_modules/.bin/uglifyjs generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../uglify-js/bin/uglifyjs

11
node_modules/connect/.npmignore generated vendored Normal file
View 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
View 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
View File

@@ -0,0 +1,2 @@
module.exports = require('./lib/connect');

81
node_modules/connect/lib/cache.js generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
node_modules/connect/lib/public/icons/page.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

BIN
node_modules/connect/lib/public/icons/page_add.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

BIN
node_modules/connect/lib/public/icons/page_attach.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

BIN
node_modules/connect/lib/public/icons/page_code.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

BIN
node_modules/connect/lib/public/icons/page_copy.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

BIN
node_modules/connect/lib/public/icons/page_delete.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

BIN
node_modules/connect/lib/public/icons/page_edit.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

BIN
node_modules/connect/lib/public/icons/page_error.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

BIN
node_modules/connect/lib/public/icons/page_excel.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

BIN
node_modules/connect/lib/public/icons/page_find.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

BIN
node_modules/connect/lib/public/icons/page_gear.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

BIN
node_modules/connect/lib/public/icons/page_go.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

BIN
node_modules/connect/lib/public/icons/page_green.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

BIN
node_modules/connect/lib/public/icons/page_key.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

BIN
node_modules/connect/lib/public/icons/page_lightning.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
node_modules/connect/lib/public/icons/page_link.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

BIN
node_modules/connect/lib/public/icons/page_paintbrush.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

BIN
node_modules/connect/lib/public/icons/page_paste.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

BIN
node_modules/connect/lib/public/icons/page_red.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

BIN
node_modules/connect/lib/public/icons/page_refresh.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

BIN
node_modules/connect/lib/public/icons/page_save.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

BIN
node_modules/connect/lib/public/icons/page_white.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

BIN
node_modules/connect/lib/public/icons/page_white_acrobat.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

BIN
node_modules/connect/lib/public/icons/page_white_add.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

BIN
node_modules/connect/lib/public/icons/page_white_c.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

BIN
node_modules/connect/lib/public/icons/page_white_camera.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

BIN
node_modules/connect/lib/public/icons/page_white_cd.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

BIN
node_modules/connect/lib/public/icons/page_white_code.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

BIN
node_modules/connect/lib/public/icons/page_white_code_red.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

BIN
node_modules/connect/lib/public/icons/page_white_copy.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

BIN
node_modules/connect/lib/public/icons/page_white_csharp.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

BIN
node_modules/connect/lib/public/icons/page_white_cup.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

BIN
node_modules/connect/lib/public/icons/page_white_database.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

BIN
node_modules/connect/lib/public/icons/page_white_delete.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

BIN
node_modules/connect/lib/public/icons/page_white_dvd.png generated vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Some files were not shown because too many files have changed in this diff Show More