유희정

calendar events

Showing 741 changed files with 4005 additions and 2 deletions
...@@ -3,10 +3,22 @@ var express = require('express'); ...@@ -3,10 +3,22 @@ var express = require('express');
3 var path = require('path'); 3 var path = require('path');
4 var cookieParser = require('cookie-parser'); 4 var cookieParser = require('cookie-parser');
5 var logger = require('morgan'); 5 var logger = require('morgan');
6 - 6 +var passport = require('passport');
7 +var session = require('express-session');
7 var index = require('./routes/index'); 8 var index = require('./routes/index');
8 var users = require('./routes/users'); 9 var users = require('./routes/users');
9 var calendar = require('./routes/calendar'); 10 var calendar = require('./routes/calendar');
11 +var login = require('./routes/login');
12 +
13 +passport.serializeUser(function(user,done) {
14 + console.log('serialized');
15 + done(null,user);
16 +});
17 +passport.deserializeUser(function(user,done){
18 + console.log('deserialized');
19 + done(null,user);
20 +})
21 +
10 var app = express(); 22 var app = express();
11 23
12 // view engine setup (뷰 폴더 경로 설정) 24 // view engine setup (뷰 폴더 경로 설정)
...@@ -19,9 +31,19 @@ app.use(express.urlencoded({ extended: false })); ...@@ -19,9 +31,19 @@ app.use(express.urlencoded({ extended: false }));
19 app.use(cookieParser()); 31 app.use(cookieParser());
20 app.use(express.static(path.join(__dirname, 'public'))); 32 app.use(express.static(path.join(__dirname, 'public')));
21 33
34 +app.use(session({
35 + secret: 'secrettexthere',
36 + saveUninitialized: true,
37 + resave: true
38 +}));
39 +
40 +
41 +app.use(passport.initialize());
42 +app.use(passport.session());
22 app.use('/', index); 43 app.use('/', index);
23 app.use('/users', users); 44 app.use('/users', users);
24 app.use('/calendar',calendar); 45 app.use('/calendar',calendar);
46 +app.use('/login',login);
25 47
26 // catch 404 and forward to error handler 48 // catch 404 and forward to error handler
27 app.use(function(req, res, next) { 49 app.use(function(req, res, next) {
......
1 -{"web":{"client_id":"978869138601-u3euf0c04sbdor68r30m599gilvjn91e.apps.googleusercontent.com","project_id":"reminder-talk","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"N7Uh_oVQZt-almzA4DkM4bm_","redirect_uris":["http://localhost:3000"],"javascript_origins":["http://localhost:3000"]}}
...\ No newline at end of file ...\ No newline at end of file
1 +{"web":{"client_id":"978869138601-u3euf0c04sbdor68r30m599gilvjn91e.apps.googleusercontent.com","project_id":"reminder-talk","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"N7Uh_oVQZt-almzA4DkM4bm_","redirect_uris":["http://localhost:3000","http://localhost:3000/auth/success","http://localhost:3000/auth","http://localhost/auth/callback"],"javascript_origins":["http://localhost:3000"]}}
...\ No newline at end of file ...\ No newline at end of file
......
1 +#!/bin/sh
2 +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3 +
4 +case `uname` in
5 + *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
6 +esac
7 +
8 +if [ -x "$basedir/node" ]; then
9 + "$basedir/node" "$basedir/../needle/bin/needle" "$@"
10 + ret=$?
11 +else
12 + node "$basedir/../needle/bin/needle" "$@"
13 + ret=$?
14 +fi
15 +exit $ret
1 +@ECHO off
2 +SETLOCAL
3 +CALL :find_dp0
4 +
5 +IF EXIST "%dp0%\node.exe" (
6 + SET "_prog=%dp0%\node.exe"
7 +) ELSE (
8 + SET "_prog=node"
9 + SET PATHEXT=%PATHEXT:;.JS;=;%
10 +)
11 +
12 +"%_prog%" "%dp0%\..\needle\bin\needle" %*
13 +ENDLOCAL
14 +EXIT /b %errorlevel%
15 +:find_dp0
16 +SET dp0=%~dp0
17 +EXIT /b
1 +#!/usr/bin/env pwsh
2 +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
3 +
4 +$exe=""
5 +if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
6 + # Fix case when both the Windows and Linux builds of Node
7 + # are installed in the same directory
8 + $exe=".exe"
9 +}
10 +$ret=0
11 +if (Test-Path "$basedir/node$exe") {
12 + & "$basedir/node$exe" "$basedir/../needle/bin/needle" $args
13 + $ret=$LASTEXITCODE
14 +} else {
15 + & "node$exe" "$basedir/../needle/bin/needle" $args
16 + $ret=$LASTEXITCODE
17 +}
18 +exit $ret
1 +node_modules
...\ No newline at end of file ...\ No newline at end of file
1 +language: node_js
2 +node_js:
3 + - 0.6
4 + - 0.8
5 +notifications:
6 + email:
7 + recipients:
8 + - brianloveswords@gmail.com
...\ No newline at end of file ...\ No newline at end of file
1 +# buffer-crc32
2 +
3 +[![Build Status](https://secure.travis-ci.org/brianloveswords/buffer-crc32.png?branch=master)](http://travis-ci.org/brianloveswords/buffer-crc32)
4 +
5 +crc32 that works with binary data and fancy character sets, outputs
6 +buffer, signed or unsigned data and has tests.
7 +
8 +Derived from the sample CRC implementation in the PNG specification: http://www.w3.org/TR/PNG/#D-CRCAppendix
9 +
10 +# install
11 +```
12 +npm install buffer-crc32
13 +```
14 +
15 +# example
16 +```js
17 +var crc32 = require('buffer-crc32');
18 +// works with buffers
19 +var buf = Buffer([0x00, 0x73, 0x75, 0x70, 0x20, 0x62, 0x72, 0x6f, 0x00])
20 +crc32(buf) // -> <Buffer 94 5a ab 4a>
21 +
22 +// has convenience methods for getting signed or unsigned ints
23 +crc32.signed(buf) // -> -1805997238
24 +crc32.unsigned(buf) // -> 2488970058
25 +
26 +// will cast to buffer if given a string, so you can
27 +// directly use foreign characters safely
28 +crc32('自動販売機') // -> <Buffer cb 03 1a c5>
29 +
30 +// and works in append mode too
31 +var partialCrc = crc32('hey');
32 +var partialCrc = crc32(' ', partialCrc);
33 +var partialCrc = crc32('sup', partialCrc);
34 +var partialCrc = crc32(' ', partialCrc);
35 +var finalCrc = crc32('bros', partialCrc); // -> <Buffer 47 fa 55 70>
36 +```
37 +
38 +# tests
39 +This was tested against the output of zlib's crc32 method. You can run
40 +the tests with`npm test` (requires tap)
41 +
42 +# see also
43 +https://github.com/alexgorbatchev/node-crc, `crc.buffer.crc32` also
44 +supports buffer inputs and return unsigned ints (thanks @tjholowaychuk).
45 +
46 +# license
47 +MIT/X11
1 +var Buffer = require('buffer').Buffer;
2 +
3 +var CRC_TABLE = [
4 + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
5 + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
6 + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
7 + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
8 + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
9 + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
10 + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
11 + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
12 + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
13 + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
14 + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
15 + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
16 + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
17 + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
18 + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
19 + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
20 + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
21 + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
22 + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
23 + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
24 + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
25 + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
26 + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
27 + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
28 + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
29 + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
30 + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
31 + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
32 + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
33 + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
34 + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
35 + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
36 + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
37 + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
38 + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
39 + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
40 + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
41 + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
42 + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
43 + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
44 + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
45 + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
46 + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
47 + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
48 + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
49 + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
50 + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
51 + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
52 + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
53 + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
54 + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
55 + 0x2d02ef8d
56 +];
57 +
58 +function bufferizeInt(num) {
59 + var tmp = Buffer(4);
60 + tmp.writeInt32BE(num, 0);
61 + return tmp;
62 +}
63 +
64 +function _crc32(buf, previous) {
65 + if (!Buffer.isBuffer(buf)) {
66 + buf = Buffer(buf);
67 + }
68 + if (Buffer.isBuffer(previous)) {
69 + previous = previous.readUInt32BE(0);
70 + }
71 + var crc = ~~previous ^ -1;
72 + for (var n = 0; n < buf.length; n++) {
73 + crc = CRC_TABLE[(crc ^ buf[n]) & 0xff] ^ (crc >>> 8);
74 + }
75 + return (crc ^ -1);
76 +}
77 +
78 +function crc32() {
79 + return bufferizeInt(_crc32.apply(null, arguments));
80 +}
81 +crc32.signed = function () {
82 + return _crc32.apply(null, arguments);
83 +};
84 +crc32.unsigned = function () {
85 + return _crc32.apply(null, arguments) >>> 0;
86 +};
87 +
88 +module.exports = crc32;
1 +{
2 + "_from": "buffer-crc32@0.2.1",
3 + "_id": "buffer-crc32@0.2.1",
4 + "_inBundle": false,
5 + "_integrity": "sha1-vj5TgvwCttYySVasGvmKqYsIU0w=",
6 + "_location": "/buffer-crc32",
7 + "_phantomChildren": {},
8 + "_requested": {
9 + "type": "version",
10 + "registry": true,
11 + "raw": "buffer-crc32@0.2.1",
12 + "name": "buffer-crc32",
13 + "escapedName": "buffer-crc32",
14 + "rawSpec": "0.2.1",
15 + "saveSpec": null,
16 + "fetchSpec": "0.2.1"
17 + },
18 + "_requiredBy": [
19 + "/connect",
20 + "/google-calendar/express"
21 + ],
22 + "_resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz",
23 + "_shasum": "be3e5382fc02b6d6324956ac1af98aa98b08534c",
24 + "_spec": "buffer-crc32@0.2.1",
25 + "_where": "C:\\Users\\LG\\Desktop\\4-1\\Reminder-Talk\\node_modules\\google-calendar\\node_modules\\express",
26 + "author": {
27 + "name": "Brian J. Brennan",
28 + "email": "brianloveswords@gmail.com",
29 + "url": "http://bjb.io"
30 + },
31 + "bugs": {
32 + "url": "https://github.com/brianloveswords/buffer-crc32/issues"
33 + },
34 + "bundleDependencies": false,
35 + "contributors": [
36 + {
37 + "name": "Vladimir Kuznetsov"
38 + }
39 + ],
40 + "dependencies": {},
41 + "deprecated": false,
42 + "description": "A pure javascript CRC32 algorithm that plays nice with binary data",
43 + "devDependencies": {
44 + "tap": "~0.2.5"
45 + },
46 + "engines": {
47 + "node": "*"
48 + },
49 + "homepage": "https://github.com/brianloveswords/buffer-crc32",
50 + "main": "index.js",
51 + "name": "buffer-crc32",
52 + "optionalDependencies": {},
53 + "repository": {
54 + "type": "git",
55 + "url": "git://github.com/brianloveswords/buffer-crc32.git"
56 + },
57 + "scripts": {
58 + "test": "tap tests/*.test.js"
59 + },
60 + "version": "0.2.1"
61 +}
1 +var crc32 = require('..');
2 +var test = require('tap').test;
3 +
4 +test('simple crc32 is no problem', function (t) {
5 + var input = Buffer('hey sup bros');
6 + var expected = Buffer([0x47, 0xfa, 0x55, 0x70]);
7 + t.same(crc32(input), expected);
8 + t.end();
9 +});
10 +
11 +test('another simple one', function (t) {
12 + var input = Buffer('IEND');
13 + var expected = Buffer([0xae, 0x42, 0x60, 0x82]);
14 + t.same(crc32(input), expected);
15 + t.end();
16 +});
17 +
18 +test('slightly more complex', function (t) {
19 + var input = Buffer([0x00, 0x00, 0x00]);
20 + var expected = Buffer([0xff, 0x41, 0xd9, 0x12]);
21 + t.same(crc32(input), expected);
22 + t.end();
23 +});
24 +
25 +test('complex crc32 gets calculated like a champ', function (t) {
26 + var input = Buffer('शीर्षक');
27 + var expected = Buffer([0x17, 0xb8, 0xaf, 0xf1]);
28 + t.same(crc32(input), expected);
29 + t.end();
30 +});
31 +
32 +test('casts to buffer if necessary', function (t) {
33 + var input = 'शीर्षक';
34 + var expected = Buffer([0x17, 0xb8, 0xaf, 0xf1]);
35 + t.same(crc32(input), expected);
36 + t.end();
37 +});
38 +
39 +test('can do signed', function (t) {
40 + var input = 'ham sandwich';
41 + var expected = -1891873021;
42 + t.same(crc32.signed(input), expected);
43 + t.end();
44 +});
45 +
46 +test('can do unsigned', function (t) {
47 + var input = 'bear sandwich';
48 + var expected = 3711466352;
49 + t.same(crc32.unsigned(input), expected);
50 + t.end();
51 +});
52 +
53 +
54 +test('simple crc32 in append mode', function (t) {
55 + var input = [Buffer('hey'), Buffer(' '), Buffer('sup'), Buffer(' '), Buffer('bros')];
56 + var expected = Buffer([0x47, 0xfa, 0x55, 0x70]);
57 + for (var crc = 0, i = 0; i < input.length; i++) {
58 + crc = crc32(input[i], crc);
59 + }
60 + t.same(crc, expected);
61 + t.end();
62 +});
63 +
64 +
65 +test('can do signed in append mode', function (t) {
66 + var input1 = 'ham';
67 + var input2 = ' ';
68 + var input3 = 'sandwich';
69 + var expected = -1891873021;
70 +
71 + var crc = crc32.signed(input1);
72 + crc = crc32.signed(input2, crc);
73 + crc = crc32.signed(input3, crc);
74 +
75 + t.same(crc, expected);
76 + t.end();
77 +});
78 +
79 +test('can do unsigned in append mode', function (t) {
80 + var input1 = 'bear san';
81 + var input2 = 'dwich';
82 + var expected = 3711466352;
83 +
84 + var crc = crc32.unsigned(input1);
85 + crc = crc32.unsigned(input2, crc);
86 + t.same(crc, expected);
87 + t.end();
88 +});
89 +
1 +*.markdown
2 +*.md
3 +.git*
4 +Makefile
5 +benchmarks/
6 +docs/
7 +examples/
8 +install.sh
9 +support/
10 +test/
11 +.DS_Store
12 +coverage.html
1 +language: node_js
2 +node_js:
3 + - "0.8"
4 + - "0.10"
...\ No newline at end of file ...\ No newline at end of file
1 +(The MIT License)
2 +
3 +Copyright (c) 2010 Sencha Inc.
4 +Copyright (c) 2011 LearnBoost
5 +Copyright (c) 2011 TJ Holowaychuk
6 +
7 +Permission is hereby granted, free of charge, to any person obtaining
8 +a copy of this software and associated documentation files (the
9 +'Software'), to deal in the Software without restriction, including
10 +without limitation the rights to use, copy, modify, merge, publish,
11 +distribute, sublicense, and/or sell copies of the Software, and to
12 +permit persons to whom the Software is furnished to do so, subject to
13 +the following conditions:
14 +
15 +The above copyright notice and this permission notice shall be
16 +included in all copies or substantial portions of the Software.
17 +
18 +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
19 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
1 +[![build status](https://secure.travis-ci.org/senchalabs/connect.png)](http://travis-ci.org/senchalabs/connect)
2 +# Connect
3 +
4 + Connect is an extensible HTTP server framework for [node](http://nodejs.org), providing high performance "plugins" known as _middleware_.
5 +
6 + Connect is bundled with over _20_ commonly used middleware, including
7 + a logger, session support, cookie parser, and [more](http://senchalabs.github.com/connect). Be sure to view the 2.x [documentation](http://senchalabs.github.com/connect/).
8 +
9 +```js
10 +var connect = require('connect')
11 + , http = require('http');
12 +
13 +var app = connect()
14 + .use(connect.favicon())
15 + .use(connect.logger('dev'))
16 + .use(connect.static('public'))
17 + .use(connect.directory('public'))
18 + .use(connect.cookieParser())
19 + .use(connect.session({ secret: 'my secret here' }))
20 + .use(function(req, res){
21 + res.end('Hello from Connect!\n');
22 + });
23 +
24 +http.createServer(app).listen(3000);
25 +```
26 +
27 +## Middleware
28 +
29 + - [csrf](http://www.senchalabs.org/connect/csrf.html)
30 + - [basicAuth](http://www.senchalabs.org/connect/basicAuth.html)
31 + - [bodyParser](http://www.senchalabs.org/connect/bodyParser.html)
32 + - [json](http://www.senchalabs.org/connect/json.html)
33 + - [multipart](http://www.senchalabs.org/connect/multipart.html)
34 + - [urlencoded](http://www.senchalabs.org/connect/urlencoded.html)
35 + - [cookieParser](http://www.senchalabs.org/connect/cookieParser.html)
36 + - [directory](http://www.senchalabs.org/connect/directory.html)
37 + - [compress](http://www.senchalabs.org/connect/compress.html)
38 + - [errorHandler](http://www.senchalabs.org/connect/errorHandler.html)
39 + - [favicon](http://www.senchalabs.org/connect/favicon.html)
40 + - [limit](http://www.senchalabs.org/connect/limit.html)
41 + - [logger](http://www.senchalabs.org/connect/logger.html)
42 + - [methodOverride](http://www.senchalabs.org/connect/methodOverride.html)
43 + - [query](http://www.senchalabs.org/connect/query.html)
44 + - [responseTime](http://www.senchalabs.org/connect/responseTime.html)
45 + - [session](http://www.senchalabs.org/connect/session.html)
46 + - [static](http://www.senchalabs.org/connect/static.html)
47 + - [staticCache](http://www.senchalabs.org/connect/staticCache.html)
48 + - [vhost](http://www.senchalabs.org/connect/vhost.html)
49 + - [subdomains](http://www.senchalabs.org/connect/subdomains.html)
50 + - [cookieSession](http://www.senchalabs.org/connect/cookieSession.html)
51 +
52 +## Running Tests
53 +
54 +first:
55 +
56 + $ npm install -d
57 +
58 +then:
59 +
60 + $ make test
61 +
62 +## Authors
63 +
64 + Below is the output from [git-summary](http://github.com/visionmedia/git-extras).
65 +
66 +
67 + project: connect
68 + commits: 2033
69 + active : 301 days
70 + files : 171
71 + authors:
72 + 1414 Tj Holowaychuk 69.6%
73 + 298 visionmedia 14.7%
74 + 191 Tim Caswell 9.4%
75 + 51 TJ Holowaychuk 2.5%
76 + 10 Ryan Olds 0.5%
77 + 8 Astro 0.4%
78 + 5 Nathan Rajlich 0.2%
79 + 5 Jakub Nešetřil 0.2%
80 + 3 Daniel Dickison 0.1%
81 + 3 David Rio Deiros 0.1%
82 + 3 Alexander Simmerl 0.1%
83 + 3 Andreas Lind Petersen 0.1%
84 + 2 Aaron Heckmann 0.1%
85 + 2 Jacques Crocker 0.1%
86 + 2 Fabian Jakobs 0.1%
87 + 2 Brian J Brennan 0.1%
88 + 2 Adam Malcontenti-Wilson 0.1%
89 + 2 Glen Mailer 0.1%
90 + 2 James Campos 0.1%
91 + 1 Trent Mick 0.0%
92 + 1 Troy Kruthoff 0.0%
93 + 1 Wei Zhu 0.0%
94 + 1 comerc 0.0%
95 + 1 darobin 0.0%
96 + 1 nateps 0.0%
97 + 1 Marco Sanson 0.0%
98 + 1 Arthur Taylor 0.0%
99 + 1 Aseem Kishore 0.0%
100 + 1 Bart Teeuwisse 0.0%
101 + 1 Cameron Howey 0.0%
102 + 1 Chad Weider 0.0%
103 + 1 Craig Barnes 0.0%
104 + 1 Eran Hammer-Lahav 0.0%
105 + 1 Gregory McWhirter 0.0%
106 + 1 Guillermo Rauch 0.0%
107 + 1 Jae Kwon 0.0%
108 + 1 Jakub Nesetril 0.0%
109 + 1 Joshua Peek 0.0%
110 + 1 Jxck 0.0%
111 + 1 AJ ONeal 0.0%
112 + 1 Michael Hemesath 0.0%
113 + 1 Morten Siebuhr 0.0%
114 + 1 Samori Gorse 0.0%
115 + 1 Tom Jensen 0.0%
116 +
117 +## Node Compatibility
118 +
119 + Connect `< 1.x` is compatible with node 0.2.x
120 +
121 +
122 + Connect `1.x` is compatible with node 0.4.x
123 +
124 +
125 + Connect (_master_) `2.x` is compatible with node 0.6.x
126 +
127 +## CLA
128 +
129 + [http://sencha.com/cla](http://sencha.com/cla)
130 +
131 +## License
132 +
133 +View the [LICENSE](https://github.com/senchalabs/connect/blob/master/LICENSE) file. The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons used by the `directory` middleware created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).
1 +
2 +module.exports = process.env.CONNECT_COV
3 + ? require('./lib-cov/connect')
4 + : require('./lib/connect');
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +/*!
3 + * Connect - Cache
4 + * Copyright(c) 2011 Sencha Inc.
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Expose `Cache`.
10 + */
11 +
12 +module.exports = Cache;
13 +
14 +/**
15 + * LRU cache store.
16 + *
17 + * @param {Number} limit
18 + * @api private
19 + */
20 +
21 +function Cache(limit) {
22 + this.store = {};
23 + this.keys = [];
24 + this.limit = limit;
25 +}
26 +
27 +/**
28 + * Touch `key`, promoting the object.
29 + *
30 + * @param {String} key
31 + * @param {Number} i
32 + * @api private
33 + */
34 +
35 +Cache.prototype.touch = function(key, i){
36 + this.keys.splice(i,1);
37 + this.keys.push(key);
38 +};
39 +
40 +/**
41 + * Remove `key`.
42 + *
43 + * @param {String} key
44 + * @api private
45 + */
46 +
47 +Cache.prototype.remove = function(key){
48 + delete this.store[key];
49 +};
50 +
51 +/**
52 + * Get the object stored for `key`.
53 + *
54 + * @param {String} key
55 + * @return {Array}
56 + * @api private
57 + */
58 +
59 +Cache.prototype.get = function(key){
60 + return this.store[key];
61 +};
62 +
63 +/**
64 + * Add a cache `key`.
65 + *
66 + * @param {String} key
67 + * @return {Array}
68 + * @api private
69 + */
70 +
71 +Cache.prototype.add = function(key){
72 + // initialize store
73 + var len = this.keys.push(key);
74 +
75 + // limit reached, invalidate LRU
76 + if (len > this.limit) this.remove(this.keys.shift());
77 +
78 + var arr = this.store[key] = [];
79 + arr.createdAt = new Date;
80 + return arr;
81 +};
1 +/*!
2 + * Connect
3 + * Copyright(c) 2010 Sencha Inc.
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var EventEmitter = require('events').EventEmitter
13 + , proto = require('./proto')
14 + , utils = require('./utils')
15 + , path = require('path')
16 + , basename = path.basename
17 + , fs = require('fs');
18 +
19 +// node patches
20 +
21 +require('./patch');
22 +
23 +// expose createServer() as the module
24 +
25 +exports = module.exports = createServer;
26 +
27 +/**
28 + * Framework version.
29 + */
30 +
31 +exports.version = '2.7.11';
32 +
33 +/**
34 + * Expose mime module.
35 + */
36 +
37 +exports.mime = require('./middleware/static').mime;
38 +
39 +/**
40 + * Expose the prototype.
41 + */
42 +
43 +exports.proto = proto;
44 +
45 +/**
46 + * Auto-load middleware getters.
47 + */
48 +
49 +exports.middleware = {};
50 +
51 +/**
52 + * Expose utilities.
53 + */
54 +
55 +exports.utils = utils;
56 +
57 +/**
58 + * Create a new connect server.
59 + *
60 + * @return {Function}
61 + * @api public
62 + */
63 +
64 +function createServer() {
65 + function app(req, res, next){ app.handle(req, res, next); }
66 + utils.merge(app, proto);
67 + utils.merge(app, EventEmitter.prototype);
68 + app.route = '/';
69 + app.stack = [];
70 + for (var i = 0; i < arguments.length; ++i) {
71 + app.use(arguments[i]);
72 + }
73 + return app;
74 +};
75 +
76 +/**
77 + * Support old `.createServer()` method.
78 + */
79 +
80 +createServer.createServer = createServer;
81 +
82 +/**
83 + * Auto-load bundled middleware with getters.
84 + */
85 +
86 +fs.readdirSync(__dirname + '/middleware').forEach(function(filename){
87 + if (!/\.js$/.test(filename)) return;
88 + var name = basename(filename, '.js');
89 + function load(){ return require('./middleware/' + name); }
90 + exports.middleware.__defineGetter__(name, load);
91 + exports.__defineGetter__(name, load);
92 +});
1 +
2 +/**
3 + * Connect is a middleware framework for node,
4 + * shipping with over 18 bundled middleware and a rich selection of
5 + * 3rd-party middleware.
6 + *
7 + * var app = connect()
8 + * .use(connect.logger('dev'))
9 + * .use(connect.static('public'))
10 + * .use(function(req, res){
11 + * res.end('hello world\n');
12 + * })
13 + * .listen(3000);
14 + *
15 + * Installation:
16 + *
17 + * $ npm install connect
18 + *
19 + * Middleware:
20 + *
21 + * - [logger](logger.html) request logger with custom format support
22 + * - [csrf](csrf.html) Cross-site request forgery protection
23 + * - [compress](compress.html) Gzip compression middleware
24 + * - [basicAuth](basicAuth.html) basic http authentication
25 + * - [bodyParser](bodyParser.html) extensible request body parser
26 + * - [json](json.html) application/json parser
27 + * - [urlencoded](urlencoded.html) application/x-www-form-urlencoded parser
28 + * - [multipart](multipart.html) multipart/form-data parser
29 + * - [timeout](timeout.html) request timeouts
30 + * - [cookieParser](cookieParser.html) cookie parser
31 + * - [session](session.html) session management support with bundled MemoryStore
32 + * - [cookieSession](cookieSession.html) cookie-based session support
33 + * - [methodOverride](methodOverride.html) faux HTTP method support
34 + * - [responseTime](responseTime.html) calculates response-time and exposes via X-Response-Time
35 + * - [staticCache](staticCache.html) memory cache layer for the static() middleware
36 + * - [static](static.html) streaming static file server supporting `Range` and more
37 + * - [directory](directory.html) directory listing middleware
38 + * - [vhost](vhost.html) virtual host sub-domain mapping middleware
39 + * - [favicon](favicon.html) efficient favicon server (with default icon)
40 + * - [limit](limit.html) limit the bytesize of request bodies
41 + * - [query](query.html) automatic querystring parser, populating `req.query`
42 + * - [errorHandler](errorHandler.html) flexible error handler
43 + *
44 + * Links:
45 + *
46 + * - list of [3rd-party](https://github.com/senchalabs/connect/wiki) middleware
47 + * - GitHub [repository](http://github.com/senchalabs/connect)
48 + * - [test documentation](https://github.com/senchalabs/connect/blob/gh-pages/tests.md)
49 + *
50 + */
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +/*!
3 + * Connect - basicAuth
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var utils = require('../utils')
14 + , unauthorized = utils.unauthorized;
15 +
16 +/**
17 + * Basic Auth:
18 + *
19 + * Enfore basic authentication by providing a `callback(user, pass)`,
20 + * which must return `true` in order to gain access. Alternatively an async
21 + * method is provided as well, invoking `callback(user, pass, callback)`. Populates
22 + * `req.user`. The final alternative is simply passing username / password
23 + * strings.
24 + *
25 + * Simple username and password
26 + *
27 + * connect(connect.basicAuth('username', 'password'));
28 + *
29 + * Callback verification
30 + *
31 + * connect()
32 + * .use(connect.basicAuth(function(user, pass){
33 + * return 'tj' == user & 'wahoo' == pass;
34 + * }))
35 + *
36 + * Async callback verification, accepting `fn(err, user)`.
37 + *
38 + * connect()
39 + * .use(connect.basicAuth(function(user, pass, fn){
40 + * User.authenticate({ user: user, pass: pass }, fn);
41 + * }))
42 + *
43 + * @param {Function|String} callback or username
44 + * @param {String} realm
45 + * @api public
46 + */
47 +
48 +module.exports = function basicAuth(callback, realm) {
49 + var username, password;
50 +
51 + // user / pass strings
52 + if ('string' == typeof callback) {
53 + username = callback;
54 + password = realm;
55 + if ('string' != typeof password) throw new Error('password argument required');
56 + realm = arguments[2];
57 + callback = function(user, pass){
58 + return user == username && pass == password;
59 + }
60 + }
61 +
62 + realm = realm || 'Authorization Required';
63 +
64 + return function(req, res, next) {
65 + var authorization = req.headers.authorization;
66 +
67 + if (req.user) return next();
68 + if (!authorization) return unauthorized(res, realm);
69 +
70 + var parts = authorization.split(' ');
71 +
72 + if (parts.length !== 2) return next(utils.error(400));
73 +
74 + var scheme = parts[0]
75 + , credentials = new Buffer(parts[1], 'base64').toString()
76 + , index = credentials.indexOf(':');
77 +
78 + if ('Basic' != scheme || index < 0) return next(utils.error(400));
79 +
80 + var user = credentials.slice(0, index)
81 + , pass = credentials.slice(index + 1);
82 +
83 + // async
84 + if (callback.length >= 3) {
85 + var pause = utils.pause(req);
86 + callback(user, pass, function(err, user){
87 + if (err || !user) return unauthorized(res, realm);
88 + req.user = req.remoteUser = user;
89 + next();
90 + pause.resume();
91 + });
92 + // sync
93 + } else {
94 + if (callback(user, pass)) {
95 + req.user = req.remoteUser = user;
96 + next();
97 + } else {
98 + unauthorized(res, realm);
99 + }
100 + }
101 + }
102 +};
103 +
1 +
2 +/*!
3 + * Connect - bodyParser
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var multipart = require('./multipart')
14 + , urlencoded = require('./urlencoded')
15 + , json = require('./json');
16 +
17 +/**
18 + * Body parser:
19 + *
20 + * Parse request bodies, supports _application/json_,
21 + * _application/x-www-form-urlencoded_, and _multipart/form-data_.
22 + *
23 + * This is equivalent to:
24 + *
25 + * app.use(connect.json());
26 + * app.use(connect.urlencoded());
27 + * app.use(connect.multipart());
28 + *
29 + * Examples:
30 + *
31 + * connect()
32 + * .use(connect.bodyParser())
33 + * .use(function(req, res) {
34 + * res.end('viewing user ' + req.body.user.name);
35 + * });
36 + *
37 + * $ curl -d 'user[name]=tj' http://local/
38 + * $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://local/
39 + *
40 + * View [json](json.html), [urlencoded](urlencoded.html), and [multipart](multipart.html) for more info.
41 + *
42 + * @param {Object} options
43 + * @return {Function}
44 + * @api public
45 + */
46 +
47 +exports = module.exports = function bodyParser(options){
48 + var _urlencoded = urlencoded(options)
49 + , _multipart = multipart(options)
50 + , _json = json(options);
51 +
52 + return function bodyParser(req, res, next) {
53 + _json(req, res, function(err){
54 + if (err) return next(err);
55 + _urlencoded(req, res, function(err){
56 + if (err) return next(err);
57 + _multipart(req, res, next);
58 + });
59 + });
60 + }
61 +};
...\ No newline at end of file ...\ No newline at end of file
1 +/*!
2 + * Connect - compress
3 + * Copyright(c) 2010 Sencha Inc.
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var zlib = require('zlib');
13 +var utils = require('../utils');
14 +
15 +/**
16 + * Supported content-encoding methods.
17 + */
18 +
19 +exports.methods = {
20 + gzip: zlib.createGzip
21 + , deflate: zlib.createDeflate
22 +};
23 +
24 +/**
25 + * Default filter function.
26 + */
27 +
28 +exports.filter = function(req, res){
29 + return /json|text|javascript|dart|image\/svg\+xml|application\/x-font-ttf|application\/vnd\.ms-opentype|application\/vnd\.ms-fontobject/.test(res.getHeader('Content-Type'));
30 +};
31 +
32 +/**
33 + * Compress:
34 + *
35 + * Compress response data with gzip/deflate.
36 + *
37 + * Filter:
38 + *
39 + * A `filter` callback function may be passed to
40 + * replace the default logic of:
41 + *
42 + * exports.filter = function(req, res){
43 + * return /json|text|javascript/.test(res.getHeader('Content-Type'));
44 + * };
45 + *
46 + * Threshold:
47 + *
48 + * Only compress the response if the byte size is at or above a threshold.
49 + * Always compress while streaming.
50 + *
51 + * - `threshold` - string representation of size or bytes as an integer.
52 + *
53 + * Options:
54 + *
55 + * All remaining options are passed to the gzip/deflate
56 + * creation functions. Consult node's docs for additional details.
57 + *
58 + * - `chunkSize` (default: 16*1024)
59 + * - `windowBits`
60 + * - `level`: 0-9 where 0 is no compression, and 9 is slow but best compression
61 + * - `memLevel`: 1-9 low is slower but uses less memory, high is fast but uses more
62 + * - `strategy`: compression strategy
63 + *
64 + * @param {Object} options
65 + * @return {Function}
66 + * @api public
67 + */
68 +
69 +module.exports = function compress(options) {
70 + options = options || {};
71 + var names = Object.keys(exports.methods)
72 + , filter = options.filter || exports.filter
73 + , threshold;
74 +
75 + if (false === options.threshold || 0 === options.threshold) {
76 + threshold = 0
77 + } else if ('string' === typeof options.threshold) {
78 + threshold = utils.parseBytes(options.threshold)
79 + } else {
80 + threshold = options.threshold || 1024
81 + }
82 +
83 + return function compress(req, res, next){
84 + var accept = req.headers['accept-encoding']
85 + , vary = res.getHeader('Vary')
86 + , write = res.write
87 + , end = res.end
88 + , compress = true
89 + , stream
90 + , method;
91 +
92 + // vary
93 + if (!vary) {
94 + res.setHeader('Vary', 'Accept-Encoding');
95 + } else if (!~vary.indexOf('Accept-Encoding')) {
96 + res.setHeader('Vary', vary + ', Accept-Encoding');
97 + }
98 +
99 + // see #724
100 + req.on('close', function(){
101 + res.write = res.end = function(){};
102 + });
103 +
104 + // proxy
105 +
106 + res.write = function(chunk, encoding){
107 + if (!this.headerSent) this._implicitHeader();
108 + return stream
109 + ? stream.write(new Buffer(chunk, encoding))
110 + : write.call(res, chunk, encoding);
111 + };
112 +
113 + res.end = function(chunk, encoding){
114 + if (chunk) {
115 + if (!this.headerSent && getSize(chunk) < threshold) compress = false;
116 + this.write(chunk, encoding);
117 + } else if (!this.headerSent) {
118 + // response size === 0
119 + compress = false;
120 + }
121 + return stream
122 + ? stream.end()
123 + : end.call(res);
124 + };
125 +
126 + res.on('header', function(){
127 + if (!compress) return;
128 +
129 + var encoding = res.getHeader('Content-Encoding') || 'identity';
130 +
131 + // already encoded
132 + if ('identity' != encoding) return;
133 +
134 + // default request filter
135 + if (!filter(req, res)) return;
136 +
137 + // SHOULD use identity
138 + if (!accept) return;
139 +
140 + // head
141 + if ('HEAD' == req.method) return;
142 +
143 + // default to gzip
144 + if ('*' == accept.trim()) method = 'gzip';
145 +
146 + // compression method
147 + if (!method) {
148 + for (var i = 0, len = names.length; i < len; ++i) {
149 + if (~accept.indexOf(names[i])) {
150 + method = names[i];
151 + break;
152 + }
153 + }
154 + }
155 +
156 + // compression method
157 + if (!method) return;
158 +
159 + // compression stream
160 + stream = exports.methods[method](options);
161 +
162 + // header fields
163 + res.setHeader('Content-Encoding', method);
164 + res.removeHeader('Content-Length');
165 +
166 + // compression
167 +
168 + stream.on('data', function(chunk){
169 + write.call(res, chunk);
170 + });
171 +
172 + stream.on('end', function(){
173 + end.call(res);
174 + });
175 +
176 + stream.on('drain', function() {
177 + res.emit('drain');
178 + });
179 + });
180 +
181 + next();
182 + };
183 +};
184 +
185 +function getSize(chunk) {
186 + return Buffer.isBuffer(chunk)
187 + ? chunk.length
188 + : Buffer.byteLength(chunk);
189 +}
1 +
2 +/*!
3 + * Connect - cookieParser
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var utils = require('./../utils')
14 + , cookie = require('cookie');
15 +
16 +/**
17 + * Cookie parser:
18 + *
19 + * Parse _Cookie_ header and populate `req.cookies`
20 + * with an object keyed by the cookie names. Optionally
21 + * you may enabled signed cookie support by passing
22 + * a `secret` string, which assigns `req.secret` so
23 + * it may be used by other middleware.
24 + *
25 + * Examples:
26 + *
27 + * connect()
28 + * .use(connect.cookieParser('optional secret string'))
29 + * .use(function(req, res, next){
30 + * res.end(JSON.stringify(req.cookies));
31 + * })
32 + *
33 + * @param {String} secret
34 + * @return {Function}
35 + * @api public
36 + */
37 +
38 +module.exports = function cookieParser(secret){
39 + return function cookieParser(req, res, next) {
40 + if (req.cookies) return next();
41 + var cookies = req.headers.cookie;
42 +
43 + req.secret = secret;
44 + req.cookies = {};
45 + req.signedCookies = {};
46 +
47 + if (cookies) {
48 + try {
49 + req.cookies = cookie.parse(cookies);
50 + if (secret) {
51 + req.signedCookies = utils.parseSignedCookies(req.cookies, secret);
52 + req.signedCookies = utils.parseJSONCookies(req.signedCookies);
53 + }
54 + req.cookies = utils.parseJSONCookies(req.cookies);
55 + } catch (err) {
56 + err.status = 400;
57 + return next(err);
58 + }
59 + }
60 + next();
61 + };
62 +};
1 +/*!
2 + * Connect - cookieSession
3 + * Copyright(c) 2011 Sencha Inc.
4 + * MIT Licensed
5 + */
6 +
7 +/**
8 + * Module dependencies.
9 + */
10 +
11 +var utils = require('./../utils')
12 + , Cookie = require('./session/cookie')
13 + , debug = require('debug')('connect:cookieSession')
14 + , signature = require('cookie-signature')
15 + , crc32 = require('buffer-crc32');
16 +
17 +/**
18 + * Cookie Session:
19 + *
20 + * Cookie session middleware.
21 + *
22 + * var app = connect();
23 + * app.use(connect.cookieParser());
24 + * app.use(connect.cookieSession({ secret: 'tobo!', cookie: { maxAge: 60 * 60 * 1000 }}));
25 + *
26 + * Options:
27 + *
28 + * - `key` cookie name defaulting to `connect.sess`
29 + * - `secret` prevents cookie tampering
30 + * - `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }`
31 + * - `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto")
32 + *
33 + * Clearing sessions:
34 + *
35 + * To clear the session simply set its value to `null`,
36 + * `cookieSession()` will then respond with a 1970 Set-Cookie.
37 + *
38 + * req.session = null;
39 + *
40 + * @param {Object} options
41 + * @return {Function}
42 + * @api public
43 + */
44 +
45 +module.exports = function cookieSession(options){
46 + // TODO: utilize Session/Cookie to unify API
47 + options = options || {};
48 + var key = options.key || 'connect.sess'
49 + , trustProxy = options.proxy;
50 +
51 + return function cookieSession(req, res, next) {
52 +
53 + // req.secret is for backwards compatibility
54 + var secret = options.secret || req.secret;
55 + if (!secret) throw new Error('`secret` option required for cookie sessions');
56 +
57 + // default session
58 + req.session = {};
59 + var cookie = req.session.cookie = new Cookie(options.cookie);
60 +
61 + // pathname mismatch
62 + if (0 != req.originalUrl.indexOf(cookie.path)) return next();
63 +
64 + // cookieParser secret
65 + if (!options.secret && req.secret) {
66 + req.session = req.signedCookies[key] || {};
67 + req.session.cookie = cookie;
68 + } else {
69 + // TODO: refactor
70 + var rawCookie = req.cookies[key];
71 + if (rawCookie) {
72 + var unsigned = utils.parseSignedCookie(rawCookie, secret);
73 + if (unsigned) {
74 + var originalHash = crc32.signed(unsigned);
75 + req.session = utils.parseJSONCookie(unsigned) || {};
76 + req.session.cookie = cookie;
77 + }
78 + }
79 + }
80 +
81 + res.on('header', function(){
82 + // removed
83 + if (!req.session) {
84 + debug('clear session');
85 + cookie.expires = new Date(0);
86 + res.setHeader('Set-Cookie', cookie.serialize(key, ''));
87 + return;
88 + }
89 +
90 + delete req.session.cookie;
91 +
92 + // check security
93 + var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase()
94 + , tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0]);
95 +
96 + // only send secure cookies via https
97 + if (cookie.secure && !tls) return debug('not secured');
98 +
99 + // serialize
100 + debug('serializing %j', req.session);
101 + var val = 'j:' + JSON.stringify(req.session);
102 +
103 + // compare hashes, no need to set-cookie if unchanged
104 + if (originalHash == crc32.signed(val)) return debug('unmodified session');
105 +
106 + // set-cookie
107 + val = 's:' + signature.sign(val, secret);
108 + val = cookie.serialize(key, val);
109 + debug('set-cookie %j', cookie);
110 + res.setHeader('Set-Cookie', val);
111 + });
112 +
113 + next();
114 + };
115 +};
1 +/*!
2 + * Connect - csrf
3 + * Copyright(c) 2011 Sencha Inc.
4 + * MIT Licensed
5 + */
6 +
7 +/**
8 + * Module dependencies.
9 + */
10 +
11 +var utils = require('../utils');
12 +var uid = require('uid2');
13 +var crypto = require('crypto');
14 +
15 +/**
16 + * Anti CSRF:
17 + *
18 + * CSRF protection middleware.
19 + *
20 + * This middleware adds a `req.csrfToken()` function to make a token
21 + * which should be added to requests which mutate
22 + * state, within a hidden form field, query-string etc. This
23 + * token is validated against the visitor's session.
24 + *
25 + * The default `value` function checks `req.body` generated
26 + * by the `bodyParser()` middleware, `req.query` generated
27 + * by `query()`, and the "X-CSRF-Token" header field.
28 + *
29 + * This middleware requires session support, thus should be added
30 + * somewhere _below_ `session()` and `cookieParser()`.
31 + *
32 + * Options:
33 + *
34 + * - `value` a function accepting the request, returning the token
35 + *
36 + * @param {Object} options
37 + * @api public
38 + */
39 +
40 +module.exports = function csrf(options) {
41 + options = options || {};
42 + var value = options.value || defaultValue;
43 +
44 + return function(req, res, next){
45 +
46 + // already have one
47 + var secret = req.session._csrfSecret;
48 + if (secret) return createToken(secret);
49 +
50 + // generate secret
51 + uid(24, function(err, secret){
52 + if (err) return next(err);
53 + req.session._csrfSecret = secret;
54 + createToken(secret);
55 + });
56 +
57 + // generate the token
58 + function createToken(secret) {
59 + var token;
60 +
61 + // lazy-load token
62 + req.csrfToken = function csrfToken() {
63 + return token || (token = saltedToken(secret));
64 + };
65 +
66 + // compatibility with old middleware
67 + Object.defineProperty(req.session, '_csrf', {
68 + configurable: true,
69 + get: function() {
70 + console.warn('req.session._csrf is deprecated, use req.csrfToken([callback]) instead');
71 + return req.csrfToken();
72 + }
73 + });
74 +
75 + // ignore these methods
76 + if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next();
77 +
78 + // determine user-submitted value
79 + var val = value(req);
80 +
81 + // check
82 + if (!checkToken(val, secret)) return next(utils.error(403));
83 +
84 + next();
85 + }
86 + }
87 +};
88 +
89 +/**
90 + * Default value function, checking the `req.body`
91 + * and `req.query` for the CSRF token.
92 + *
93 + * @param {IncomingMessage} req
94 + * @return {String}
95 + * @api private
96 + */
97 +
98 +function defaultValue(req) {
99 + return (req.body && req.body._csrf)
100 + || (req.query && req.query._csrf)
101 + || (req.headers['x-csrf-token'])
102 + || (req.headers['x-xsrf-token']);
103 +}
104 +
105 +/**
106 + * Return salted token.
107 + *
108 + * @param {String} secret
109 + * @return {String}
110 + * @api private
111 + */
112 +
113 +function saltedToken(secret) {
114 + return createToken(generateSalt(10), secret);
115 +}
116 +
117 +/**
118 + * Creates a CSRF token from a given salt and secret.
119 + *
120 + * @param {String} salt (should be 10 characters)
121 + * @param {String} secret
122 + * @return {String}
123 + * @api private
124 + */
125 +
126 +function createToken(salt, secret) {
127 + return salt + crypto
128 + .createHash('sha1')
129 + .update(salt + secret)
130 + .digest('base64');
131 +}
132 +
133 +/**
134 + * Checks if a given CSRF token matches the given secret.
135 + *
136 + * @param {String} token
137 + * @param {String} secret
138 + * @return {Boolean}
139 + * @api private
140 + */
141 +
142 +function checkToken(token, secret) {
143 + if ('string' != typeof token) return false;
144 + return token === createToken(token.slice(0, 10), secret);
145 +}
146 +
147 +/**
148 + * Generates a random salt, using a fast non-blocking PRNG (Math.random()).
149 + *
150 + * @param {Number} length
151 + * @return {String}
152 + * @api private
153 + */
154 +
155 +function generateSalt(length) {
156 + var i, r = [];
157 + for (i = 0; i < length; ++i) {
158 + r.push(SALTCHARS[Math.floor(Math.random() * SALTCHARS.length)]);
159 + }
160 + return r.join('');
161 +}
162 +
163 +var SALTCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
1 +
2 +/*!
3 + * Connect - directory
4 + * Copyright(c) 2011 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +// TODO: icon / style for directories
10 +// TODO: arrow key navigation
11 +// TODO: make icons extensible
12 +
13 +/**
14 + * Module dependencies.
15 + */
16 +
17 +var fs = require('fs')
18 + , parse = require('url').parse
19 + , utils = require('../utils')
20 + , path = require('path')
21 + , normalize = path.normalize
22 + , extname = path.extname
23 + , join = path.join;
24 +
25 +/*!
26 + * Icon cache.
27 + */
28 +
29 +var cache = {};
30 +
31 +/**
32 + * Directory:
33 + *
34 + * Serve directory listings with the given `root` path.
35 + *
36 + * Options:
37 + *
38 + * - `hidden` display hidden (dot) files. Defaults to false.
39 + * - `icons` display icons. Defaults to false.
40 + * - `filter` Apply this filter function to files. Defaults to false.
41 + *
42 + * @param {String} root
43 + * @param {Object} options
44 + * @return {Function}
45 + * @api public
46 + */
47 +
48 +exports = module.exports = function directory(root, options){
49 + options = options || {};
50 +
51 + // root required
52 + if (!root) throw new Error('directory() root path required');
53 + var hidden = options.hidden
54 + , icons = options.icons
55 + , filter = options.filter
56 + , root = normalize(root);
57 +
58 + return function directory(req, res, next) {
59 + if ('GET' != req.method && 'HEAD' != req.method) return next();
60 +
61 + var accept = req.headers.accept || 'text/plain'
62 + , url = parse(req.url)
63 + , dir = decodeURIComponent(url.pathname)
64 + , path = normalize(join(root, dir))
65 + , originalUrl = parse(req.originalUrl)
66 + , originalDir = decodeURIComponent(originalUrl.pathname)
67 + , showUp = path != root && path != root + '/';
68 +
69 + // null byte(s), bad request
70 + if (~path.indexOf('\0')) return next(utils.error(400));
71 +
72 + // malicious path, forbidden
73 + if (0 != path.indexOf(root)) return next(utils.error(403));
74 +
75 + // check if we have a directory
76 + fs.stat(path, function(err, stat){
77 + if (err) return 'ENOENT' == err.code
78 + ? next()
79 + : next(err);
80 +
81 + if (!stat.isDirectory()) return next();
82 +
83 + // fetch files
84 + fs.readdir(path, function(err, files){
85 + if (err) return next(err);
86 + if (!hidden) files = removeHidden(files);
87 + if (filter) files = files.filter(filter);
88 + files.sort();
89 +
90 + // content-negotiation
91 + for (var key in exports) {
92 + if (~accept.indexOf(key) || ~accept.indexOf('*/*')) {
93 + exports[key](req, res, files, next, originalDir, showUp, icons);
94 + return;
95 + }
96 + }
97 +
98 + // not acceptable
99 + next(utils.error(406));
100 + });
101 + });
102 + };
103 +};
104 +
105 +/**
106 + * Respond with text/html.
107 + */
108 +
109 +exports.html = function(req, res, files, next, dir, showUp, icons){
110 + fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){
111 + if (err) return next(err);
112 + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){
113 + if (err) return next(err);
114 + if (showUp) files.unshift('..');
115 + str = str
116 + .replace('{style}', style)
117 + .replace('{files}', html(files, dir, icons))
118 + .replace('{directory}', dir)
119 + .replace('{linked-path}', htmlPath(dir));
120 + res.setHeader('Content-Type', 'text/html');
121 + res.setHeader('Content-Length', str.length);
122 + res.end(str);
123 + });
124 + });
125 +};
126 +
127 +/**
128 + * Respond with application/json.
129 + */
130 +
131 +exports.json = function(req, res, files){
132 + files = JSON.stringify(files);
133 + res.setHeader('Content-Type', 'application/json');
134 + res.setHeader('Content-Length', files.length);
135 + res.end(files);
136 +};
137 +
138 +/**
139 + * Respond with text/plain.
140 + */
141 +
142 +exports.plain = function(req, res, files){
143 + files = files.join('\n') + '\n';
144 + res.setHeader('Content-Type', 'text/plain');
145 + res.setHeader('Content-Length', files.length);
146 + res.end(files);
147 +};
148 +
149 +/**
150 + * Map html `dir`, returning a linked path.
151 + */
152 +
153 +function htmlPath(dir) {
154 + var curr = [];
155 + return dir.split('/').map(function(part){
156 + curr.push(part);
157 + return '<a href="' + curr.join('/') + '">' + part + '</a>';
158 + }).join(' / ');
159 +}
160 +
161 +/**
162 + * Map html `files`, returning an html unordered list.
163 + */
164 +
165 +function html(files, dir, useIcons) {
166 + return '<ul id="files">' + files.map(function(file){
167 + var icon = ''
168 + , classes = [];
169 +
170 + if (useIcons && '..' != file) {
171 + icon = icons[extname(file)] || icons.default;
172 + icon = '<img src="data:image/png;base64,' + load(icon) + '" />';
173 + classes.push('icon');
174 + }
175 +
176 + return '<li><a href="'
177 + + join(dir, file)
178 + + '" class="'
179 + + classes.join(' ') + '"'
180 + + ' title="' + file + '">'
181 + + icon + file + '</a></li>';
182 +
183 + }).join('\n') + '</ul>';
184 +}
185 +
186 +/**
187 + * Load and cache the given `icon`.
188 + *
189 + * @param {String} icon
190 + * @return {String}
191 + * @api private
192 + */
193 +
194 +function load(icon) {
195 + if (cache[icon]) return cache[icon];
196 + return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64');
197 +}
198 +
199 +/**
200 + * Filter "hidden" `files`, aka files
201 + * beginning with a `.`.
202 + *
203 + * @param {Array} files
204 + * @return {Array}
205 + * @api private
206 + */
207 +
208 +function removeHidden(files) {
209 + return files.filter(function(file){
210 + return '.' != file[0];
211 + });
212 +}
213 +
214 +/**
215 + * Icon map.
216 + */
217 +
218 +var icons = {
219 + '.js': 'page_white_code_red.png'
220 + , '.c': 'page_white_c.png'
221 + , '.h': 'page_white_h.png'
222 + , '.cc': 'page_white_cplusplus.png'
223 + , '.php': 'page_white_php.png'
224 + , '.rb': 'page_white_ruby.png'
225 + , '.cpp': 'page_white_cplusplus.png'
226 + , '.swf': 'page_white_flash.png'
227 + , '.pdf': 'page_white_acrobat.png'
228 + , 'default': 'page_white.png'
229 +};
1 +/*!
2 + * Connect - errorHandler
3 + * Copyright(c) 2010 Sencha Inc.
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var utils = require('../utils')
13 + , fs = require('fs');
14 +
15 +// environment
16 +
17 +var env = process.env.NODE_ENV || 'development';
18 +
19 +/**
20 + * Error handler:
21 + *
22 + * Development error handler, providing stack traces
23 + * and error message responses for requests accepting text, html,
24 + * or json.
25 + *
26 + * Text:
27 + *
28 + * By default, and when _text/plain_ is accepted a simple stack trace
29 + * or error message will be returned.
30 + *
31 + * JSON:
32 + *
33 + * When _application/json_ is accepted, connect will respond with
34 + * an object in the form of `{ "error": error }`.
35 + *
36 + * HTML:
37 + *
38 + * When accepted connect will output a nice html stack trace.
39 + *
40 + * @return {Function}
41 + * @api public
42 + */
43 +
44 +exports = module.exports = function errorHandler(){
45 + return function errorHandler(err, req, res, next){
46 + if (err.status) res.statusCode = err.status;
47 + if (res.statusCode < 400) res.statusCode = 500;
48 + if ('test' != env) console.error(err.stack);
49 + var accept = req.headers.accept || '';
50 + // html
51 + if (~accept.indexOf('html')) {
52 + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){
53 + fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){
54 + var stack = (err.stack || '')
55 + .split('\n').slice(1)
56 + .map(function(v){ return '<li>' + v + '</li>'; }).join('');
57 + html = html
58 + .replace('{style}', style)
59 + .replace('{stack}', stack)
60 + .replace('{title}', exports.title)
61 + .replace('{statusCode}', res.statusCode)
62 + .replace(/\{error\}/g, utils.escape(err.toString()));
63 + res.setHeader('Content-Type', 'text/html; charset=utf-8');
64 + res.end(html);
65 + });
66 + });
67 + // json
68 + } else if (~accept.indexOf('json')) {
69 + var error = { message: err.message, stack: err.stack };
70 + for (var prop in err) error[prop] = err[prop];
71 + var json = JSON.stringify({ error: error });
72 + res.setHeader('Content-Type', 'application/json');
73 + res.end(json);
74 + // plain text
75 + } else {
76 + res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
77 + res.end(err.stack);
78 + }
79 + };
80 +};
81 +
82 +/**
83 + * Template title, framework authors may override this value.
84 + */
85 +
86 +exports.title = 'Connect';
1 +/*!
2 + * Connect - favicon
3 + * Copyright(c) 2010 Sencha Inc.
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var fs = require('fs')
13 + , utils = require('../utils');
14 +
15 +/**
16 + * Favicon:
17 + *
18 + * By default serves the connect favicon, or the favicon
19 + * located by the given `path`.
20 + *
21 + * Options:
22 + *
23 + * - `maxAge` cache-control max-age directive, defaulting to 1 day
24 + *
25 + * Examples:
26 + *
27 + * Serve default favicon:
28 + *
29 + * connect()
30 + * .use(connect.favicon())
31 + *
32 + * Serve favicon before logging for brevity:
33 + *
34 + * connect()
35 + * .use(connect.favicon())
36 + * .use(connect.logger('dev'))
37 + *
38 + * Serve custom favicon:
39 + *
40 + * connect()
41 + * .use(connect.favicon('public/favicon.ico'))
42 + *
43 + * @param {String} path
44 + * @param {Object} options
45 + * @return {Function}
46 + * @api public
47 + */
48 +
49 +module.exports = function favicon(path, options){
50 + var options = options || {}
51 + , path = path || __dirname + '/../public/favicon.ico'
52 + , maxAge = options.maxAge || 86400000
53 + , icon; // favicon cache
54 +
55 + return function favicon(req, res, next){
56 + if ('/favicon.ico' == req.url) {
57 + if (icon) {
58 + res.writeHead(200, icon.headers);
59 + res.end(icon.body);
60 + } else {
61 + fs.readFile(path, function(err, buf){
62 + if (err) return next(err);
63 + icon = {
64 + headers: {
65 + 'Content-Type': 'image/x-icon'
66 + , 'Content-Length': buf.length
67 + , 'ETag': '"' + utils.md5(buf) + '"'
68 + , 'Cache-Control': 'public, max-age=' + (maxAge / 1000)
69 + },
70 + body: buf
71 + };
72 + res.writeHead(200, icon.headers);
73 + res.end(icon.body);
74 + });
75 + }
76 + } else {
77 + next();
78 + }
79 + };
80 +};
1 +
2 +/*!
3 + * Connect - json
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var utils = require('../utils')
14 + , _limit = require('./limit');
15 +
16 +/**
17 + * noop middleware.
18 + */
19 +
20 +function noop(req, res, next) {
21 + next();
22 +}
23 +
24 +/**
25 + * JSON:
26 + *
27 + * Parse JSON request bodies, providing the
28 + * parsed object as `req.body`.
29 + *
30 + * Options:
31 + *
32 + * - `strict` when `false` anything `JSON.parse()` accepts will be parsed
33 + * - `reviver` used as the second "reviver" argument for JSON.parse
34 + * - `limit` byte limit disabled by default
35 + *
36 + * @param {Object} options
37 + * @return {Function}
38 + * @api public
39 + */
40 +
41 +exports = module.exports = function(options){
42 + var options = options || {}
43 + , strict = options.strict !== false;
44 +
45 + var limit = options.limit
46 + ? _limit(options.limit)
47 + : noop;
48 +
49 + return function json(req, res, next) {
50 + if (req._body) return next();
51 + req.body = req.body || {};
52 +
53 + if (!utils.hasBody(req)) return next();
54 +
55 + // check Content-Type
56 + if (!exports.regexp.test(utils.mime(req))) return next();
57 +
58 + // flag as parsed
59 + req._body = true;
60 +
61 + // parse
62 + limit(req, res, function(err){
63 + if (err) return next(err);
64 + var buf = '';
65 + req.setEncoding('utf8');
66 + req.on('data', function(chunk){ buf += chunk });
67 + req.on('end', function(){
68 + var first = buf.trim()[0];
69 +
70 + if (0 == buf.length) {
71 + return next(utils.error(400, 'invalid json, empty body'));
72 + }
73 +
74 + if (strict && '{' != first && '[' != first) return next(utils.error(400, 'invalid json'));
75 + try {
76 + req.body = JSON.parse(buf, options.reviver);
77 + } catch (err){
78 + err.body = buf;
79 + err.status = 400;
80 + return next(err);
81 + }
82 + next();
83 + });
84 + });
85 + };
86 +};
87 +
88 +exports.regexp = /^application\/([\w!#\$%&\*`\-\.\^~]*\+)?json$/i;
89 +
1 +
2 +/*!
3 + * Connect - limit
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var utils = require('../utils'),
13 + brokenPause = utils.brokenPause;
14 +
15 +/**
16 + * Limit:
17 + *
18 + * Limit request bodies to the given size in `bytes`.
19 + *
20 + * A string representation of the bytesize may also be passed,
21 + * for example "5mb", "200kb", "1gb", etc.
22 + *
23 + * connect()
24 + * .use(connect.limit('5.5mb'))
25 + * .use(handleImageUpload)
26 + *
27 + * @param {Number|String} bytes
28 + * @return {Function}
29 + * @api public
30 + */
31 +
32 +module.exports = function limit(bytes){
33 + if ('string' == typeof bytes) bytes = utils.parseBytes(bytes);
34 + if ('number' != typeof bytes) throw new Error('limit() bytes required');
35 + return function limit(req, res, next){
36 + var received = 0
37 + , len = req.headers['content-length']
38 + ? parseInt(req.headers['content-length'], 10)
39 + : null;
40 +
41 + // self-awareness
42 + if (req._limit) return next();
43 + req._limit = true;
44 +
45 + // limit by content-length
46 + if (len && len > bytes) return next(utils.error(413));
47 +
48 + // limit
49 + if (brokenPause) {
50 + listen();
51 + } else {
52 + req.on('newListener', function handler(event) {
53 + if (event !== 'data') return;
54 +
55 + req.removeListener('newListener', handler);
56 + // Start listening at the end of the current loop
57 + // otherwise the request will be consumed too early.
58 + // Sideaffect is `limit` will miss the first chunk,
59 + // but that's not a big deal.
60 + // Unfortunately, the tests don't have large enough
61 + // request bodies to test this.
62 + process.nextTick(listen);
63 + });
64 + };
65 +
66 + next();
67 +
68 + function listen() {
69 + req.on('data', function(chunk) {
70 + received += Buffer.isBuffer(chunk)
71 + ? chunk.length :
72 + Buffer.byteLength(chunk);
73 +
74 + if (received > bytes) req.destroy();
75 + });
76 + };
77 + };
78 +};
...\ No newline at end of file ...\ No newline at end of file
1 +/*!
2 + * Connect - logger
3 + * Copyright(c) 2010 Sencha Inc.
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var bytes = require('bytes');
13 +
14 +/*!
15 + * Log buffer.
16 + */
17 +
18 +var buf = [];
19 +
20 +/*!
21 + * Default log buffer duration.
22 + */
23 +
24 +var defaultBufferDuration = 1000;
25 +
26 +/**
27 + * Logger:
28 + *
29 + * Log requests with the given `options` or a `format` string.
30 + *
31 + * Options:
32 + *
33 + * - `format` Format string, see below for tokens
34 + * - `stream` Output stream, defaults to _stdout_
35 + * - `buffer` Buffer duration, defaults to 1000ms when _true_
36 + * - `immediate` Write log line on request instead of response (for response times)
37 + *
38 + * Tokens:
39 + *
40 + * - `:req[header]` ex: `:req[Accept]`
41 + * - `:res[header]` ex: `:res[Content-Length]`
42 + * - `:http-version`
43 + * - `:response-time`
44 + * - `:remote-addr`
45 + * - `:date`
46 + * - `:method`
47 + * - `:url`
48 + * - `:referrer`
49 + * - `:user-agent`
50 + * - `:status`
51 + *
52 + * Formats:
53 + *
54 + * Pre-defined formats that ship with connect:
55 + *
56 + * - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'
57 + * - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'
58 + * - `tiny` ':method :url :status :res[content-length] - :response-time ms'
59 + * - `dev` concise output colored by response status for development use
60 + *
61 + * Examples:
62 + *
63 + * connect.logger() // default
64 + * connect.logger('short')
65 + * connect.logger('tiny')
66 + * connect.logger({ immediate: true, format: 'dev' })
67 + * connect.logger(':method :url - :referrer')
68 + * connect.logger(':req[content-type] -> :res[content-type]')
69 + * connect.logger(function(tokens, req, res){ return 'some format string' })
70 + *
71 + * Defining Tokens:
72 + *
73 + * To define a token, simply invoke `connect.logger.token()` with the
74 + * name and a callback function. The value returned is then available
75 + * as ":type" in this case.
76 + *
77 + * connect.logger.token('type', function(req, res){ return req.headers['content-type']; })
78 + *
79 + * Defining Formats:
80 + *
81 + * All default formats are defined this way, however it's public API as well:
82 + *
83 + * connect.logger.format('name', 'string or function')
84 + *
85 + * @param {String|Function|Object} format or options
86 + * @return {Function}
87 + * @api public
88 + */
89 +
90 +exports = module.exports = function logger(options) {
91 + if ('object' == typeof options) {
92 + options = options || {};
93 + } else if (options) {
94 + options = { format: options };
95 + } else {
96 + options = {};
97 + }
98 +
99 + // output on request instead of response
100 + var immediate = options.immediate;
101 +
102 + // format name
103 + var fmt = exports[options.format] || options.format || exports.default;
104 +
105 + // compile format
106 + if ('function' != typeof fmt) fmt = compile(fmt);
107 +
108 + // options
109 + var stream = options.stream || process.stdout
110 + , buffer = options.buffer;
111 +
112 + // buffering support
113 + if (buffer) {
114 + var realStream = stream
115 + , interval = 'number' == typeof buffer
116 + ? buffer
117 + : defaultBufferDuration;
118 +
119 + // flush interval
120 + setInterval(function(){
121 + if (buf.length) {
122 + realStream.write(buf.join(''));
123 + buf.length = 0;
124 + }
125 + }, interval);
126 +
127 + // swap the stream
128 + stream = {
129 + write: function(str){
130 + buf.push(str);
131 + }
132 + };
133 + }
134 +
135 + return function logger(req, res, next) {
136 + req._startTime = new Date;
137 +
138 + // immediate
139 + if (immediate) {
140 + var line = fmt(exports, req, res);
141 + if (null == line) return;
142 + stream.write(line + '\n');
143 + // proxy end to output logging
144 + } else {
145 + var end = res.end;
146 + res.end = function(chunk, encoding){
147 + res.end = end;
148 + res.end(chunk, encoding);
149 + var line = fmt(exports, req, res);
150 + if (null == line) return;
151 + stream.write(line + '\n');
152 + };
153 + }
154 +
155 +
156 + next();
157 + };
158 +};
159 +
160 +/**
161 + * Compile `fmt` into a function.
162 + *
163 + * @param {String} fmt
164 + * @return {Function}
165 + * @api private
166 + */
167 +
168 +function compile(fmt) {
169 + fmt = fmt.replace(/"/g, '\\"');
170 + var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){
171 + return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
172 + }) + '";'
173 + return new Function('tokens, req, res', js);
174 +};
175 +
176 +/**
177 + * Define a token function with the given `name`,
178 + * and callback `fn(req, res)`.
179 + *
180 + * @param {String} name
181 + * @param {Function} fn
182 + * @return {Object} exports for chaining
183 + * @api public
184 + */
185 +
186 +exports.token = function(name, fn) {
187 + exports[name] = fn;
188 + return this;
189 +};
190 +
191 +/**
192 + * Define a `fmt` with the given `name`.
193 + *
194 + * @param {String} name
195 + * @param {String|Function} fmt
196 + * @return {Object} exports for chaining
197 + * @api public
198 + */
199 +
200 +exports.format = function(name, str){
201 + exports[name] = str;
202 + return this;
203 +};
204 +
205 +/**
206 + * Default format.
207 + */
208 +
209 +exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"');
210 +
211 +/**
212 + * Short format.
213 + */
214 +
215 +exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms');
216 +
217 +/**
218 + * Tiny format.
219 + */
220 +
221 +exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms');
222 +
223 +/**
224 + * dev (colored)
225 + */
226 +
227 +exports.format('dev', function(tokens, req, res){
228 + var status = res.statusCode
229 + , len = parseInt(res.getHeader('Content-Length'), 10)
230 + , color = 32;
231 +
232 + if (status >= 500) color = 31
233 + else if (status >= 400) color = 33
234 + else if (status >= 300) color = 36;
235 +
236 + len = isNaN(len)
237 + ? ''
238 + : len = ' - ' + bytes(len);
239 +
240 + return '\x1b[90m' + req.method
241 + + ' ' + req.originalUrl + ' '
242 + + '\x1b[' + color + 'm' + res.statusCode
243 + + ' \x1b[90m'
244 + + (new Date - req._startTime)
245 + + 'ms' + len
246 + + '\x1b[0m';
247 +});
248 +
249 +/**
250 + * request url
251 + */
252 +
253 +exports.token('url', function(req){
254 + return req.originalUrl || req.url;
255 +});
256 +
257 +/**
258 + * request method
259 + */
260 +
261 +exports.token('method', function(req){
262 + return req.method;
263 +});
264 +
265 +/**
266 + * response time in milliseconds
267 + */
268 +
269 +exports.token('response-time', function(req){
270 + return new Date - req._startTime;
271 +});
272 +
273 +/**
274 + * UTC date
275 + */
276 +
277 +exports.token('date', function(){
278 + return new Date().toUTCString();
279 +});
280 +
281 +/**
282 + * response status code
283 + */
284 +
285 +exports.token('status', function(req, res){
286 + return res.statusCode;
287 +});
288 +
289 +/**
290 + * normalized referrer
291 + */
292 +
293 +exports.token('referrer', function(req){
294 + return req.headers['referer'] || req.headers['referrer'];
295 +});
296 +
297 +/**
298 + * remote address
299 + */
300 +
301 +exports.token('remote-addr', function(req){
302 + if (req.ip) return req.ip;
303 + var sock = req.socket;
304 + if (sock.socket) return sock.socket.remoteAddress;
305 + return sock.remoteAddress;
306 +});
307 +
308 +/**
309 + * HTTP version
310 + */
311 +
312 +exports.token('http-version', function(req){
313 + return req.httpVersionMajor + '.' + req.httpVersionMinor;
314 +});
315 +
316 +/**
317 + * UA string
318 + */
319 +
320 +exports.token('user-agent', function(req){
321 + return req.headers['user-agent'];
322 +});
323 +
324 +/**
325 + * request header
326 + */
327 +
328 +exports.token('req', function(req, res, field){
329 + return req.headers[field.toLowerCase()];
330 +});
331 +
332 +/**
333 + * response header
334 + */
335 +
336 +exports.token('res', function(req, res, field){
337 + return (res._headers || {})[field.toLowerCase()];
338 +});
339 +
1 +
2 +/*!
3 + * Connect - methodOverride
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var methods = require('methods');
14 +
15 +/**
16 + * Method Override:
17 + *
18 + * Provides faux HTTP method support.
19 + *
20 + * Pass an optional `key` to use when checking for
21 + * a method override, othewise defaults to _\_method_.
22 + * The original method is available via `req.originalMethod`.
23 + *
24 + * @param {String} key
25 + * @return {Function}
26 + * @api public
27 + */
28 +
29 +module.exports = function methodOverride(key){
30 + key = key || "_method";
31 + return function methodOverride(req, res, next) {
32 + var method;
33 + req.originalMethod = req.originalMethod || req.method;
34 +
35 + // req.body
36 + if (req.body && key in req.body) {
37 + method = req.body[key].toLowerCase();
38 + delete req.body[key];
39 + }
40 +
41 + // check X-HTTP-Method-Override
42 + if (req.headers['x-http-method-override']) {
43 + method = req.headers['x-http-method-override'].toLowerCase();
44 + }
45 +
46 + // replace
47 + if (supports(method)) req.method = method.toUpperCase();
48 +
49 + next();
50 + };
51 +};
52 +
53 +/**
54 + * Check if node supports `method`.
55 + */
56 +
57 +function supports(method) {
58 + return ~methods.indexOf(method);
59 +}
1 +/*!
2 + * Connect - multipart
3 + * Copyright(c) 2010 Sencha Inc.
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var formidable = require('formidable')
13 + , _limit = require('./limit')
14 + , utils = require('../utils')
15 + , qs = require('qs');
16 +
17 +/**
18 + * noop middleware.
19 + */
20 +
21 +function noop(req, res, next) {
22 + next();
23 +}
24 +
25 +/**
26 + * Multipart:
27 + *
28 + * Parse multipart/form-data request bodies,
29 + * providing the parsed object as `req.body`
30 + * and `req.files`.
31 + *
32 + * Configuration:
33 + *
34 + * The options passed are merged with [formidable](https://github.com/felixge/node-formidable)'s
35 + * `IncomingForm` object, allowing you to configure the upload directory,
36 + * size limits, etc. For example if you wish to change the upload dir do the following.
37 + *
38 + * app.use(connect.multipart({ uploadDir: path }));
39 + *
40 + * Options:
41 + *
42 + * - `limit` byte limit defaulting to none
43 + * - `defer` defers processing and exposes the Formidable form object as `req.form`.
44 + * `next()` is called without waiting for the form's "end" event.
45 + * This option is useful if you need to bind to the "progress" event, for example.
46 + *
47 + * @param {Object} options
48 + * @return {Function}
49 + * @api public
50 + */
51 +
52 +exports = module.exports = function(options){
53 + options = options || {};
54 +
55 + var limit = options.limit
56 + ? _limit(options.limit)
57 + : noop;
58 +
59 + return function multipart(req, res, next) {
60 + if (req._body) return next();
61 + req.body = req.body || {};
62 + req.files = req.files || {};
63 +
64 + if (!utils.hasBody(req)) return next();
65 +
66 + // ignore GET
67 + if ('GET' == req.method || 'HEAD' == req.method) return next();
68 +
69 + // check Content-Type
70 + if ('multipart/form-data' != utils.mime(req)) return next();
71 +
72 + // flag as parsed
73 + req._body = true;
74 +
75 + // parse
76 + limit(req, res, function(err){
77 + if (err) return next(err);
78 +
79 + var form = new formidable.IncomingForm
80 + , data = {}
81 + , files = {}
82 + , done;
83 +
84 + Object.keys(options).forEach(function(key){
85 + form[key] = options[key];
86 + });
87 +
88 + function ondata(name, val, data){
89 + if (Array.isArray(data[name])) {
90 + data[name].push(val);
91 + } else if (data[name]) {
92 + data[name] = [data[name], val];
93 + } else {
94 + data[name] = val;
95 + }
96 + }
97 +
98 + form.on('field', function(name, val){
99 + ondata(name, val, data);
100 + });
101 +
102 + form.on('file', function(name, val){
103 + ondata(name, val, files);
104 + });
105 +
106 + form.on('error', function(err){
107 + if (!options.defer) {
108 + err.status = 400;
109 + next(err);
110 + }
111 + done = true;
112 + });
113 +
114 + form.on('end', function(){
115 + if (done) return;
116 + try {
117 + req.body = qs.parse(data);
118 + req.files = qs.parse(files);
119 + } catch (err) {
120 + form.emit('error', err);
121 + return;
122 + }
123 + if (!options.defer) next();
124 + });
125 +
126 + form.parse(req);
127 +
128 + if (options.defer) {
129 + req.form = form;
130 + next();
131 + }
132 + });
133 + }
134 +};
1 +/*!
2 + * Connect - query
3 + * Copyright(c) 2011 TJ Holowaychuk
4 + * Copyright(c) 2011 Sencha Inc.
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var qs = require('qs')
13 + , parse = require('../utils').parseUrl;
14 +
15 +/**
16 + * Query:
17 + *
18 + * Automatically parse the query-string when available,
19 + * populating the `req.query` object.
20 + *
21 + * Examples:
22 + *
23 + * connect()
24 + * .use(connect.query())
25 + * .use(function(req, res){
26 + * res.end(JSON.stringify(req.query));
27 + * });
28 + *
29 + * The `options` passed are provided to qs.parse function.
30 + *
31 + * @param {Object} options
32 + * @return {Function}
33 + * @api public
34 + */
35 +
36 +module.exports = function query(options){
37 + return function query(req, res, next){
38 + if (!req.query) {
39 + req.query = ~req.url.indexOf('?')
40 + ? qs.parse(parse(req).query, options)
41 + : {};
42 + }
43 +
44 + next();
45 + };
46 +};
1 +
2 +/*!
3 + * Connect - responseTime
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Reponse time:
10 + *
11 + * Adds the `X-Response-Time` header displaying the response
12 + * duration in milliseconds.
13 + *
14 + * @return {Function}
15 + * @api public
16 + */
17 +
18 +module.exports = function responseTime(){
19 + return function(req, res, next){
20 + var start = new Date;
21 +
22 + if (res._responseTime) return next();
23 + res._responseTime = true;
24 +
25 + res.on('header', function(){
26 + var duration = new Date - start;
27 + res.setHeader('X-Response-Time', duration + 'ms');
28 + });
29 +
30 + next();
31 + };
32 +};
This diff is collapsed. Click to expand it.
1 +
2 +/*!
3 + * Connect - session - Cookie
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var utils = require('../../utils')
14 + , cookie = require('cookie');
15 +
16 +/**
17 + * Initialize a new `Cookie` with the given `options`.
18 + *
19 + * @param {IncomingMessage} req
20 + * @param {Object} options
21 + * @api private
22 + */
23 +
24 +var Cookie = module.exports = function Cookie(options) {
25 + this.path = '/';
26 + this.maxAge = null;
27 + this.httpOnly = true;
28 + if (options) utils.merge(this, options);
29 + this.originalMaxAge = undefined == this.originalMaxAge
30 + ? this.maxAge
31 + : this.originalMaxAge;
32 +};
33 +
34 +/*!
35 + * Prototype.
36 + */
37 +
38 +Cookie.prototype = {
39 +
40 + /**
41 + * Set expires `date`.
42 + *
43 + * @param {Date} date
44 + * @api public
45 + */
46 +
47 + set expires(date) {
48 + this._expires = date;
49 + this.originalMaxAge = this.maxAge;
50 + },
51 +
52 + /**
53 + * Get expires `date`.
54 + *
55 + * @return {Date}
56 + * @api public
57 + */
58 +
59 + get expires() {
60 + return this._expires;
61 + },
62 +
63 + /**
64 + * Set expires via max-age in `ms`.
65 + *
66 + * @param {Number} ms
67 + * @api public
68 + */
69 +
70 + set maxAge(ms) {
71 + this.expires = 'number' == typeof ms
72 + ? new Date(Date.now() + ms)
73 + : ms;
74 + },
75 +
76 + /**
77 + * Get expires max-age in `ms`.
78 + *
79 + * @return {Number}
80 + * @api public
81 + */
82 +
83 + get maxAge() {
84 + return this.expires instanceof Date
85 + ? this.expires.valueOf() - Date.now()
86 + : this.expires;
87 + },
88 +
89 + /**
90 + * Return cookie data object.
91 + *
92 + * @return {Object}
93 + * @api private
94 + */
95 +
96 + get data() {
97 + return {
98 + originalMaxAge: this.originalMaxAge
99 + , expires: this._expires
100 + , secure: this.secure
101 + , httpOnly: this.httpOnly
102 + , domain: this.domain
103 + , path: this.path
104 + }
105 + },
106 +
107 + /**
108 + * Check if the cookie has a reasonably large max-age.
109 + *
110 + * @return {Boolean}
111 + * @api private
112 + */
113 +
114 + get hasLongExpires() {
115 + var week = 604800000;
116 + return this.maxAge > (4 * week);
117 + },
118 +
119 + /**
120 + * Return a serialized cookie string.
121 + *
122 + * @return {String}
123 + * @api public
124 + */
125 +
126 + serialize: function(name, val){
127 + return cookie.serialize(name, val, this.data);
128 + },
129 +
130 + /**
131 + * Return JSON representation of this cookie.
132 + *
133 + * @return {Object}
134 + * @api private
135 + */
136 +
137 + toJSON: function(){
138 + return this.data;
139 + }
140 +};
1 +
2 +/*!
3 + * Connect - session - MemoryStore
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var Store = require('./store');
14 +
15 +/**
16 + * Initialize a new `MemoryStore`.
17 + *
18 + * @api public
19 + */
20 +
21 +var MemoryStore = module.exports = function MemoryStore() {
22 + this.sessions = {};
23 +};
24 +
25 +/**
26 + * Inherit from `Store.prototype`.
27 + */
28 +
29 +MemoryStore.prototype.__proto__ = Store.prototype;
30 +
31 +/**
32 + * Attempt to fetch session by the given `sid`.
33 + *
34 + * @param {String} sid
35 + * @param {Function} fn
36 + * @api public
37 + */
38 +
39 +MemoryStore.prototype.get = function(sid, fn){
40 + var self = this;
41 + process.nextTick(function(){
42 + var expires
43 + , sess = self.sessions[sid];
44 + if (sess) {
45 + sess = JSON.parse(sess);
46 + expires = 'string' == typeof sess.cookie.expires
47 + ? new Date(sess.cookie.expires)
48 + : sess.cookie.expires;
49 + if (!expires || new Date < expires) {
50 + fn(null, sess);
51 + } else {
52 + self.destroy(sid, fn);
53 + }
54 + } else {
55 + fn();
56 + }
57 + });
58 +};
59 +
60 +/**
61 + * Commit the given `sess` object associated with the given `sid`.
62 + *
63 + * @param {String} sid
64 + * @param {Session} sess
65 + * @param {Function} fn
66 + * @api public
67 + */
68 +
69 +MemoryStore.prototype.set = function(sid, sess, fn){
70 + var self = this;
71 + process.nextTick(function(){
72 + self.sessions[sid] = JSON.stringify(sess);
73 + fn && fn();
74 + });
75 +};
76 +
77 +/**
78 + * Destroy the session associated with the given `sid`.
79 + *
80 + * @param {String} sid
81 + * @api public
82 + */
83 +
84 +MemoryStore.prototype.destroy = function(sid, fn){
85 + var self = this;
86 + process.nextTick(function(){
87 + delete self.sessions[sid];
88 + fn && fn();
89 + });
90 +};
91 +
92 +/**
93 + * Invoke the given callback `fn` with all active sessions.
94 + *
95 + * @param {Function} fn
96 + * @api public
97 + */
98 +
99 +MemoryStore.prototype.all = function(fn){
100 + var arr = []
101 + , keys = Object.keys(this.sessions);
102 + for (var i = 0, len = keys.length; i < len; ++i) {
103 + arr.push(this.sessions[keys[i]]);
104 + }
105 + fn(null, arr);
106 +};
107 +
108 +/**
109 + * Clear all sessions.
110 + *
111 + * @param {Function} fn
112 + * @api public
113 + */
114 +
115 +MemoryStore.prototype.clear = function(fn){
116 + this.sessions = {};
117 + fn && fn();
118 +};
119 +
120 +/**
121 + * Fetch number of sessions.
122 + *
123 + * @param {Function} fn
124 + * @api public
125 + */
126 +
127 +MemoryStore.prototype.length = function(fn){
128 + fn(null, Object.keys(this.sessions).length);
129 +};
1 +
2 +/*!
3 + * Connect - session - Session
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var utils = require('../../utils');
14 +
15 +/**
16 + * Create a new `Session` with the given request and `data`.
17 + *
18 + * @param {IncomingRequest} req
19 + * @param {Object} data
20 + * @api private
21 + */
22 +
23 +var Session = module.exports = function Session(req, data) {
24 + Object.defineProperty(this, 'req', { value: req });
25 + Object.defineProperty(this, 'id', { value: req.sessionID });
26 + if ('object' == typeof data) utils.merge(this, data);
27 +};
28 +
29 +/**
30 + * Update reset `.cookie.maxAge` to prevent
31 + * the cookie from expiring when the
32 + * session is still active.
33 + *
34 + * @return {Session} for chaining
35 + * @api public
36 + */
37 +
38 +Session.prototype.touch = function(){
39 + return this.resetMaxAge();
40 +};
41 +
42 +/**
43 + * Reset `.maxAge` to `.originalMaxAge`.
44 + *
45 + * @return {Session} for chaining
46 + * @api public
47 + */
48 +
49 +Session.prototype.resetMaxAge = function(){
50 + this.cookie.maxAge = this.cookie.originalMaxAge;
51 + return this;
52 +};
53 +
54 +/**
55 + * Save the session data with optional callback `fn(err)`.
56 + *
57 + * @param {Function} fn
58 + * @return {Session} for chaining
59 + * @api public
60 + */
61 +
62 +Session.prototype.save = function(fn){
63 + this.req.sessionStore.set(this.id, this, fn || function(){});
64 + return this;
65 +};
66 +
67 +/**
68 + * Re-loads the session data _without_ altering
69 + * the maxAge properties. Invokes the callback `fn(err)`,
70 + * after which time if no exception has occurred the
71 + * `req.session` property will be a new `Session` object,
72 + * although representing the same session.
73 + *
74 + * @param {Function} fn
75 + * @return {Session} for chaining
76 + * @api public
77 + */
78 +
79 +Session.prototype.reload = function(fn){
80 + var req = this.req
81 + , store = this.req.sessionStore;
82 + store.get(this.id, function(err, sess){
83 + if (err) return fn(err);
84 + if (!sess) return fn(new Error('failed to load session'));
85 + store.createSession(req, sess);
86 + fn();
87 + });
88 + return this;
89 +};
90 +
91 +/**
92 + * Destroy `this` session.
93 + *
94 + * @param {Function} fn
95 + * @return {Session} for chaining
96 + * @api public
97 + */
98 +
99 +Session.prototype.destroy = function(fn){
100 + delete this.req.session;
101 + this.req.sessionStore.destroy(this.id, fn);
102 + return this;
103 +};
104 +
105 +/**
106 + * Regenerate this request's session.
107 + *
108 + * @param {Function} fn
109 + * @return {Session} for chaining
110 + * @api public
111 + */
112 +
113 +Session.prototype.regenerate = function(fn){
114 + this.req.sessionStore.regenerate(this.req, fn);
115 + return this;
116 +};
1 +
2 +/*!
3 + * Connect - session - Store
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var EventEmitter = require('events').EventEmitter
14 + , Session = require('./session')
15 + , Cookie = require('./cookie');
16 +
17 +/**
18 + * Initialize abstract `Store`.
19 + *
20 + * @api private
21 + */
22 +
23 +var Store = module.exports = function Store(options){};
24 +
25 +/**
26 + * Inherit from `EventEmitter.prototype`.
27 + */
28 +
29 +Store.prototype.__proto__ = EventEmitter.prototype;
30 +
31 +/**
32 + * Re-generate the given requests's session.
33 + *
34 + * @param {IncomingRequest} req
35 + * @return {Function} fn
36 + * @api public
37 + */
38 +
39 +Store.prototype.regenerate = function(req, fn){
40 + var self = this;
41 + this.destroy(req.sessionID, function(err){
42 + self.generate(req);
43 + fn(err);
44 + });
45 +};
46 +
47 +/**
48 + * Load a `Session` instance via the given `sid`
49 + * and invoke the callback `fn(err, sess)`.
50 + *
51 + * @param {String} sid
52 + * @param {Function} fn
53 + * @api public
54 + */
55 +
56 +Store.prototype.load = function(sid, fn){
57 + var self = this;
58 + this.get(sid, function(err, sess){
59 + if (err) return fn(err);
60 + if (!sess) return fn();
61 + var req = { sessionID: sid, sessionStore: self };
62 + sess = self.createSession(req, sess);
63 + fn(null, sess);
64 + });
65 +};
66 +
67 +/**
68 + * Create session from JSON `sess` data.
69 + *
70 + * @param {IncomingRequest} req
71 + * @param {Object} sess
72 + * @return {Session}
73 + * @api private
74 + */
75 +
76 +Store.prototype.createSession = function(req, sess){
77 + var expires = sess.cookie.expires
78 + , orig = sess.cookie.originalMaxAge;
79 + sess.cookie = new Cookie(sess.cookie);
80 + if ('string' == typeof expires) sess.cookie.expires = new Date(expires);
81 + sess.cookie.originalMaxAge = orig;
82 + req.session = new Session(req, sess);
83 + return req.session;
84 +};
1 +/*!
2 + * Connect - static
3 + * Copyright(c) 2010 Sencha Inc.
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var send = require('send')
13 + , utils = require('../utils')
14 + , parse = utils.parseUrl
15 + , url = require('url');
16 +
17 +/**
18 + * Static:
19 + *
20 + * Static file server with the given `root` path.
21 + *
22 + * Examples:
23 + *
24 + * var oneDay = 86400000;
25 + *
26 + * connect()
27 + * .use(connect.static(__dirname + '/public'))
28 + *
29 + * connect()
30 + * .use(connect.static(__dirname + '/public', { maxAge: oneDay }))
31 + *
32 + * Options:
33 + *
34 + * - `maxAge` Browser cache maxAge in milliseconds. defaults to 0
35 + * - `hidden` Allow transfer of hidden files. defaults to false
36 + * - `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to true
37 + * - `index` Default file name, defaults to 'index.html'
38 + *
39 + * @param {String} root
40 + * @param {Object} options
41 + * @return {Function}
42 + * @api public
43 + */
44 +
45 +exports = module.exports = function(root, options){
46 + options = options || {};
47 +
48 + // root required
49 + if (!root) throw new Error('static() root path required');
50 +
51 + // default redirect
52 + var redirect = false !== options.redirect;
53 +
54 + return function staticMiddleware(req, res, next) {
55 + if ('GET' != req.method && 'HEAD' != req.method) return next();
56 + var path = parse(req).pathname;
57 + var pause = utils.pause(req);
58 +
59 + function resume() {
60 + next();
61 + pause.resume();
62 + }
63 +
64 + function directory() {
65 + if (!redirect) return resume();
66 + var pathname = url.parse(req.originalUrl).pathname;
67 + res.statusCode = 303;
68 + res.setHeader('Location', pathname + '/');
69 + res.end('Redirecting to ' + utils.escape(pathname) + '/');
70 + }
71 +
72 + function error(err) {
73 + if (404 == err.status) return resume();
74 + next(err);
75 + }
76 +
77 + send(req, path)
78 + .maxage(options.maxAge || 0)
79 + .root(root)
80 + .index(options.index || 'index.html')
81 + .hidden(options.hidden)
82 + .on('error', error)
83 + .on('directory', directory)
84 + .pipe(res);
85 + };
86 +};
87 +
88 +/**
89 + * Expose mime module.
90 + *
91 + * If you wish to extend the mime table use this
92 + * reference to the "mime" module in the npm registry.
93 + */
94 +
95 +exports.mime = send.mime;
1 +
2 +/*!
3 + * Connect - staticCache
4 + * Copyright(c) 2011 Sencha Inc.
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var utils = require('../utils')
13 + , Cache = require('../cache')
14 + , fresh = require('fresh');
15 +
16 +/**
17 + * Static cache:
18 + *
19 + * Enables a memory cache layer on top of
20 + * the `static()` middleware, serving popular
21 + * static files.
22 + *
23 + * By default a maximum of 128 objects are
24 + * held in cache, with a max of 256k each,
25 + * totalling ~32mb.
26 + *
27 + * A Least-Recently-Used (LRU) cache algo
28 + * is implemented through the `Cache` object,
29 + * simply rotating cache objects as they are
30 + * hit. This means that increasingly popular
31 + * objects maintain their positions while
32 + * others get shoved out of the stack and
33 + * garbage collected.
34 + *
35 + * Benchmarks:
36 + *
37 + * static(): 2700 rps
38 + * node-static: 5300 rps
39 + * static() + staticCache(): 7500 rps
40 + *
41 + * Options:
42 + *
43 + * - `maxObjects` max cache objects [128]
44 + * - `maxLength` max cache object length 256kb
45 + *
46 + * @param {Object} options
47 + * @return {Function}
48 + * @api public
49 + */
50 +
51 +module.exports = function staticCache(options){
52 + var options = options || {}
53 + , cache = new Cache(options.maxObjects || 128)
54 + , maxlen = options.maxLength || 1024 * 256;
55 +
56 + console.warn('connect.staticCache() is deprecated and will be removed in 3.0');
57 + console.warn('use varnish or similar reverse proxy caches.');
58 +
59 + return function staticCache(req, res, next){
60 + var key = cacheKey(req)
61 + , ranges = req.headers.range
62 + , hasCookies = req.headers.cookie
63 + , hit = cache.get(key);
64 +
65 + // cache static
66 + // TODO: change from staticCache() -> cache()
67 + // and make this work for any request
68 + req.on('static', function(stream){
69 + var headers = res._headers
70 + , cc = utils.parseCacheControl(headers['cache-control'] || '')
71 + , contentLength = headers['content-length']
72 + , hit;
73 +
74 + // dont cache set-cookie responses
75 + if (headers['set-cookie']) return hasCookies = true;
76 +
77 + // dont cache when cookies are present
78 + if (hasCookies) return;
79 +
80 + // ignore larger files
81 + if (!contentLength || contentLength > maxlen) return;
82 +
83 + // don't cache partial files
84 + if (headers['content-range']) return;
85 +
86 + // dont cache items we shouldn't be
87 + // TODO: real support for must-revalidate / no-cache
88 + if ( cc['no-cache']
89 + || cc['no-store']
90 + || cc['private']
91 + || cc['must-revalidate']) return;
92 +
93 + // if already in cache then validate
94 + if (hit = cache.get(key)){
95 + if (headers.etag == hit[0].etag) {
96 + hit[0].date = new Date;
97 + return;
98 + } else {
99 + cache.remove(key);
100 + }
101 + }
102 +
103 + // validation notifiactions don't contain a steam
104 + if (null == stream) return;
105 +
106 + // add the cache object
107 + var arr = [];
108 +
109 + // store the chunks
110 + stream.on('data', function(chunk){
111 + arr.push(chunk);
112 + });
113 +
114 + // flag it as complete
115 + stream.on('end', function(){
116 + var cacheEntry = cache.add(key);
117 + delete headers['x-cache']; // Clean up (TODO: others)
118 + cacheEntry.push(200);
119 + cacheEntry.push(headers);
120 + cacheEntry.push.apply(cacheEntry, arr);
121 + });
122 + });
123 +
124 + if (req.method == 'GET' || req.method == 'HEAD') {
125 + if (ranges) {
126 + next();
127 + } else if (!hasCookies && hit && !mustRevalidate(req, hit)) {
128 + res.setHeader('X-Cache', 'HIT');
129 + respondFromCache(req, res, hit);
130 + } else {
131 + res.setHeader('X-Cache', 'MISS');
132 + next();
133 + }
134 + } else {
135 + next();
136 + }
137 + }
138 +};
139 +
140 +/**
141 + * Respond with the provided cached value.
142 + * TODO: Assume 200 code, that's iffy.
143 + *
144 + * @param {Object} req
145 + * @param {Object} res
146 + * @param {Object} cacheEntry
147 + * @return {String}
148 + * @api private
149 + */
150 +
151 +function respondFromCache(req, res, cacheEntry) {
152 + var status = cacheEntry[0]
153 + , headers = utils.merge({}, cacheEntry[1])
154 + , content = cacheEntry.slice(2);
155 +
156 + headers.age = (new Date - new Date(headers.date)) / 1000 || 0;
157 +
158 + switch (req.method) {
159 + case 'HEAD':
160 + res.writeHead(status, headers);
161 + res.end();
162 + break;
163 + case 'GET':
164 + if (utils.conditionalGET(req) && fresh(req.headers, headers)) {
165 + headers['content-length'] = 0;
166 + res.writeHead(304, headers);
167 + res.end();
168 + } else {
169 + res.writeHead(status, headers);
170 +
171 + function write() {
172 + while (content.length) {
173 + if (false === res.write(content.shift())) {
174 + res.once('drain', write);
175 + return;
176 + }
177 + }
178 + res.end();
179 + }
180 +
181 + write();
182 + }
183 + break;
184 + default:
185 + // This should never happen.
186 + res.writeHead(500, '');
187 + res.end();
188 + }
189 +}
190 +
191 +/**
192 + * Determine whether or not a cached value must be revalidated.
193 + *
194 + * @param {Object} req
195 + * @param {Object} cacheEntry
196 + * @return {String}
197 + * @api private
198 + */
199 +
200 +function mustRevalidate(req, cacheEntry) {
201 + var cacheHeaders = cacheEntry[1]
202 + , reqCC = utils.parseCacheControl(req.headers['cache-control'] || '')
203 + , cacheCC = utils.parseCacheControl(cacheHeaders['cache-control'] || '')
204 + , cacheAge = (new Date - new Date(cacheHeaders.date)) / 1000 || 0;
205 +
206 + if ( cacheCC['no-cache']
207 + || cacheCC['must-revalidate']
208 + || cacheCC['proxy-revalidate']) return true;
209 +
210 + if (reqCC['no-cache']) return true;
211 +
212 + if (null != reqCC['max-age']) return reqCC['max-age'] < cacheAge;
213 +
214 + if (null != cacheCC['max-age']) return cacheCC['max-age'] < cacheAge;
215 +
216 + return false;
217 +}
218 +
219 +/**
220 + * The key to use in the cache. For now, this is the URL path and query.
221 + *
222 + * 'http://example.com?key=value' -> '/?key=value'
223 + *
224 + * @param {Object} req
225 + * @return {String}
226 + * @api private
227 + */
228 +
229 +function cacheKey(req) {
230 + return utils.parseUrl(req).path;
231 +}
1 +/*!
2 + * Connect - timeout
3 + * Ported from https://github.com/LearnBoost/connect-timeout
4 + * MIT Licensed
5 + */
6 +
7 +/**
8 + * Module dependencies.
9 + */
10 +
11 +var debug = require('debug')('connect:timeout');
12 +
13 +/**
14 + * Timeout:
15 + *
16 + * Times out the request in `ms`, defaulting to `5000`. The
17 + * method `req.clearTimeout()` is added to revert this behaviour
18 + * programmatically within your application's middleware, routes, etc.
19 + *
20 + * The timeout error is passed to `next()` so that you may customize
21 + * the response behaviour. This error has the `.timeout` property as
22 + * well as `.status == 503`.
23 + *
24 + * @param {Number} ms
25 + * @return {Function}
26 + * @api public
27 + */
28 +
29 +module.exports = function timeout(ms) {
30 + ms = ms || 5000;
31 +
32 + return function(req, res, next) {
33 + var id = setTimeout(function(){
34 + req.emit('timeout', ms);
35 + }, ms);
36 +
37 + req.on('timeout', function(){
38 + if (res.headerSent) return debug('response started, cannot timeout');
39 + var err = new Error('Response timeout');
40 + err.timeout = ms;
41 + err.status = 503;
42 + next(err);
43 + });
44 +
45 + req.clearTimeout = function(){
46 + clearTimeout(id);
47 + };
48 +
49 + res.on('header', function(){
50 + clearTimeout(id);
51 + });
52 +
53 + next();
54 + };
55 +};
1 +
2 +/*!
3 + * Connect - urlencoded
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var utils = require('../utils')
14 + , _limit = require('./limit')
15 + , qs = require('qs');
16 +
17 +/**
18 + * noop middleware.
19 + */
20 +
21 +function noop(req, res, next) {
22 + next();
23 +}
24 +
25 +/**
26 + * Urlencoded:
27 + *
28 + * Parse x-ww-form-urlencoded request bodies,
29 + * providing the parsed object as `req.body`.
30 + *
31 + * Options:
32 + *
33 + * - `limit` byte limit disabled by default
34 + *
35 + * @param {Object} options
36 + * @return {Function}
37 + * @api public
38 + */
39 +
40 +exports = module.exports = function(options){
41 + options = options || {};
42 +
43 + var limit = options.limit
44 + ? _limit(options.limit)
45 + : noop;
46 +
47 + return function urlencoded(req, res, next) {
48 + if (req._body) return next();
49 + req.body = req.body || {};
50 +
51 + if (!utils.hasBody(req)) return next();
52 +
53 + // check Content-Type
54 + if ('application/x-www-form-urlencoded' != utils.mime(req)) return next();
55 +
56 + // flag as parsed
57 + req._body = true;
58 +
59 + // parse
60 + limit(req, res, function(err){
61 + if (err) return next(err);
62 + var buf = '';
63 + req.setEncoding('utf8');
64 + req.on('data', function(chunk){ buf += chunk });
65 + req.on('end', function(){
66 + try {
67 + req.body = buf.length
68 + ? qs.parse(buf, options)
69 + : {};
70 + } catch (err){
71 + err.body = buf;
72 + return next(err);
73 + }
74 + next();
75 + });
76 + });
77 + }
78 +};
1 +
2 +/*!
3 + * Connect - vhost
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Vhost:
11 + *
12 + * Setup vhost for the given `hostname` and `server`.
13 + *
14 + * connect()
15 + * .use(connect.vhost('foo.com', fooApp))
16 + * .use(connect.vhost('bar.com', barApp))
17 + * .use(connect.vhost('*.com', mainApp))
18 + *
19 + * The `server` may be a Connect server or
20 + * a regular Node `http.Server`.
21 + *
22 + * @param {String} hostname
23 + * @param {Server} server
24 + * @return {Function}
25 + * @api public
26 + */
27 +
28 +module.exports = function vhost(hostname, server){
29 + if (!hostname) throw new Error('vhost hostname required');
30 + if (!server) throw new Error('vhost server required');
31 + var regexp = new RegExp('^' + hostname.replace(/[^*\w]/g, '\\$&').replace(/[*]/g, '(?:.*?)') + '$', 'i');
32 + if (server.onvhost) server.onvhost(hostname);
33 + return function vhost(req, res, next){
34 + if (!req.headers.host) return next();
35 + var host = req.headers.host.split(':')[0];
36 + if (!regexp.test(host)) return next();
37 + if ('function' == typeof server) return server(req, res, next);
38 + server.emit('request', req, res);
39 + };
40 +};
1 +
2 +/*!
3 + * Connect
4 + * Copyright(c) 2011 TJ Holowaychuk
5 + * MIT Licensed
6 + */
7 +
8 +/**
9 + * Module dependencies.
10 + */
11 +
12 +var http = require('http')
13 + , res = http.ServerResponse.prototype
14 + , setHeader = res.setHeader
15 + , _renderHeaders = res._renderHeaders
16 + , writeHead = res.writeHead;
17 +
18 +// apply only once
19 +
20 +if (!res._hasConnectPatch) {
21 +
22 + /**
23 + * Provide a public "header sent" flag
24 + * until node does.
25 + *
26 + * @return {Boolean}
27 + * @api public
28 + */
29 +
30 + res.__defineGetter__('headerSent', function(){
31 + return this._header;
32 + });
33 +
34 + /**
35 + * Set header `field` to `val`, special-casing
36 + * the `Set-Cookie` field for multiple support.
37 + *
38 + * @param {String} field
39 + * @param {String} val
40 + * @api public
41 + */
42 +
43 + res.setHeader = function(field, val){
44 + var key = field.toLowerCase()
45 + , prev;
46 +
47 + // special-case Set-Cookie
48 + if (this._headers && 'set-cookie' == key) {
49 + if (prev = this.getHeader(field)) {
50 + val = Array.isArray(prev)
51 + ? prev.concat(val)
52 + : [prev, val];
53 + }
54 + // charset
55 + } else if ('content-type' == key && this.charset) {
56 + val += '; charset=' + this.charset;
57 + }
58 +
59 + return setHeader.call(this, field, val);
60 + };
61 +
62 + /**
63 + * Proxy to emit "header" event.
64 + */
65 +
66 + res._renderHeaders = function(){
67 + if (!this._emittedHeader) this.emit('header');
68 + this._emittedHeader = true;
69 + return _renderHeaders.call(this);
70 + };
71 +
72 + res.writeHead = function(){
73 + if (!this._emittedHeader) this.emit('header');
74 + this._emittedHeader = true;
75 + return writeHead.apply(this, arguments);
76 + };
77 +
78 + res._hasConnectPatch = true;
79 +}
1 +
2 +/*!
3 + * Connect - HTTPServer
4 + * Copyright(c) 2010 Sencha Inc.
5 + * Copyright(c) 2011 TJ Holowaychuk
6 + * MIT Licensed
7 + */
8 +
9 +/**
10 + * Module dependencies.
11 + */
12 +
13 +var http = require('http')
14 + , utils = require('./utils')
15 + , debug = require('debug')('connect:dispatcher');
16 +
17 +// prototype
18 +
19 +var app = module.exports = {};
20 +
21 +// environment
22 +
23 +var env = process.env.NODE_ENV || 'development';
24 +
25 +/**
26 + * Utilize the given middleware `handle` to the given `route`,
27 + * defaulting to _/_. This "route" is the mount-point for the
28 + * middleware, when given a value other than _/_ the middleware
29 + * is only effective when that segment is present in the request's
30 + * pathname.
31 + *
32 + * For example if we were to mount a function at _/admin_, it would
33 + * be invoked on _/admin_, and _/admin/settings_, however it would
34 + * not be invoked for _/_, or _/posts_.
35 + *
36 + * Examples:
37 + *
38 + * var app = connect();
39 + * app.use(connect.favicon());
40 + * app.use(connect.logger());
41 + * app.use(connect.static(__dirname + '/public'));
42 + *
43 + * If we wanted to prefix static files with _/public_, we could
44 + * "mount" the `static()` middleware:
45 + *
46 + * app.use('/public', connect.static(__dirname + '/public'));
47 + *
48 + * This api is chainable, so the following is valid:
49 + *
50 + * connect()
51 + * .use(connect.favicon())
52 + * .use(connect.logger())
53 + * .use(connect.static(__dirname + '/public'))
54 + * .listen(3000);
55 + *
56 + * @param {String|Function|Server} route, callback or server
57 + * @param {Function|Server} callback or server
58 + * @return {Server} for chaining
59 + * @api public
60 + */
61 +
62 +app.use = function(route, fn){
63 + // default route to '/'
64 + if ('string' != typeof route) {
65 + fn = route;
66 + route = '/';
67 + }
68 +
69 + // wrap sub-apps
70 + if ('function' == typeof fn.handle) {
71 + var server = fn;
72 + fn.route = route;
73 + fn = function(req, res, next){
74 + server.handle(req, res, next);
75 + };
76 + }
77 +
78 + // wrap vanilla http.Servers
79 + if (fn instanceof http.Server) {
80 + fn = fn.listeners('request')[0];
81 + }
82 +
83 + // strip trailing slash
84 + if ('/' == route[route.length - 1]) {
85 + route = route.slice(0, -1);
86 + }
87 +
88 + // add the middleware
89 + debug('use %s %s', route || '/', fn.name || 'anonymous');
90 + this.stack.push({ route: route, handle: fn });
91 +
92 + return this;
93 +};
94 +
95 +/**
96 + * Handle server requests, punting them down
97 + * the middleware stack.
98 + *
99 + * @api private
100 + */
101 +
102 +app.handle = function(req, res, out) {
103 + var stack = this.stack
104 + , fqdn = ~req.url.indexOf('://')
105 + , removed = ''
106 + , slashAdded = false
107 + , index = 0;
108 +
109 + function next(err) {
110 + var layer, path, status, c;
111 +
112 + if (slashAdded) {
113 + req.url = req.url.substr(1);
114 + slashAdded = false;
115 + }
116 +
117 + req.url = removed + req.url;
118 + req.originalUrl = req.originalUrl || req.url;
119 + removed = '';
120 +
121 + // next callback
122 + layer = stack[index++];
123 +
124 + // all done
125 + if (!layer || res.headerSent) {
126 + // delegate to parent
127 + if (out) return out(err);
128 +
129 + // unhandled error
130 + if (err) {
131 + // default to 500
132 + if (res.statusCode < 400) res.statusCode = 500;
133 + debug('default %s', res.statusCode);
134 +
135 + // respect err.status
136 + if (err.status) res.statusCode = err.status;
137 +
138 + // production gets a basic error message
139 + var msg = 'production' == env
140 + ? http.STATUS_CODES[res.statusCode]
141 + : err.stack || err.toString();
142 +
143 + // log to stderr in a non-test env
144 + if ('test' != env) console.error(err.stack || err.toString());
145 + if (res.headerSent) return req.socket.destroy();
146 + res.setHeader('Content-Type', 'text/plain');
147 + res.setHeader('Content-Length', Buffer.byteLength(msg));
148 + if ('HEAD' == req.method) return res.end();
149 + res.end(msg);
150 + } else {
151 + debug('default 404');
152 + res.statusCode = 404;
153 + res.setHeader('Content-Type', 'text/plain');
154 + if ('HEAD' == req.method) return res.end();
155 + res.end('Cannot ' + utils.escape(req.method) + ' ' + utils.escape(req.originalUrl));
156 + }
157 + return;
158 + }
159 +
160 + try {
161 + path = utils.parseUrl(req).pathname;
162 + if (undefined == path) path = '/';
163 +
164 + // skip this layer if the route doesn't match.
165 + if (0 != path.toLowerCase().indexOf(layer.route.toLowerCase())) return next(err);
166 +
167 + c = path[layer.route.length];
168 + if (c && '/' != c && '.' != c) return next(err);
169 +
170 + // Call the layer handler
171 + // Trim off the part of the url that matches the route
172 + removed = layer.route;
173 + req.url = req.url.substr(removed.length);
174 +
175 + // Ensure leading slash
176 + if (!fqdn && '/' != req.url[0]) {
177 + req.url = '/' + req.url;
178 + slashAdded = true;
179 + }
180 +
181 + debug('%s %s : %s', layer.handle.name || 'anonymous', layer.route, req.originalUrl);
182 + var arity = layer.handle.length;
183 + if (err) {
184 + if (arity === 4) {
185 + layer.handle(err, req, res, next);
186 + } else {
187 + next(err);
188 + }
189 + } else if (arity < 4) {
190 + layer.handle(req, res, next);
191 + } else {
192 + next();
193 + }
194 + } catch (e) {
195 + next(e);
196 + }
197 + }
198 + next();
199 +};
200 +
201 +/**
202 + * Listen for connections.
203 + *
204 + * This method takes the same arguments
205 + * as node's `http.Server#listen()`.
206 + *
207 + * HTTP and HTTPS:
208 + *
209 + * If you run your application both as HTTP
210 + * and HTTPS you may wrap them individually,
211 + * since your Connect "server" is really just
212 + * a JavaScript `Function`.
213 + *
214 + * var connect = require('connect')
215 + * , http = require('http')
216 + * , https = require('https');
217 + *
218 + * var app = connect();
219 + *
220 + * http.createServer(app).listen(80);
221 + * https.createServer(options, app).listen(443);
222 + *
223 + * @return {http.Server}
224 + * @api public
225 + */
226 +
227 +app.listen = function(){
228 + var server = http.createServer(this);
229 + return server.listen.apply(server, arguments);
230 +};
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset='utf-8'>
5 + <title>listing directory {directory}</title>
6 + <style>{style}</style>
7 + <script>
8 + function $(id){
9 + var el = 'string' == typeof id
10 + ? document.getElementById(id)
11 + : id;
12 +
13 + el.on = function(event, fn){
14 + if ('content loaded' == event) {
15 + event = window.attachEvent ? "load" : "DOMContentLoaded";
16 + }
17 + el.addEventListener
18 + ? el.addEventListener(event, fn, false)
19 + : el.attachEvent("on" + event, fn);
20 + };
21 +
22 + el.all = function(selector){
23 + return $(el.querySelectorAll(selector));
24 + };
25 +
26 + el.each = function(fn){
27 + for (var i = 0, len = el.length; i < len; ++i) {
28 + fn($(el[i]), i);
29 + }
30 + };
31 +
32 + el.getClasses = function(){
33 + return this.getAttribute('class').split(/\s+/);
34 + };
35 +
36 + el.addClass = function(name){
37 + var classes = this.getAttribute('class');
38 + el.setAttribute('class', classes
39 + ? classes + ' ' + name
40 + : name);
41 + };
42 +
43 + el.removeClass = function(name){
44 + var classes = this.getClasses().filter(function(curr){
45 + return curr != name;
46 + });
47 + this.setAttribute('class', classes);
48 + };
49 +
50 + return el;
51 + }
52 +
53 + function search() {
54 + var str = $('search').value
55 + , links = $('files').all('a');
56 +
57 + links.each(function(link){
58 + var text = link.textContent;
59 +
60 + if ('..' == text) return;
61 + if (str.length && ~text.indexOf(str)) {
62 + link.addClass('highlight');
63 + } else {
64 + link.removeClass('highlight');
65 + }
66 + });
67 + }
68 +
69 + $(window).on('content loaded', function(){
70 + $('search').on('keyup', search);
71 + });
72 + </script>
73 + </head>
74 + <body class="directory">
75 + <input id="search" type="text" placeholder="Search" autocomplete="off" />
76 + <div id="wrapper">
77 + <h1>{linked-path}</h1>
78 + {files}
79 + </div>
80 + </body>
81 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +<html>
2 + <head>
3 + <meta charset='utf-8'>
4 + <title>{error}</title>
5 + <style>{style}</style>
6 + </head>
7 + <body>
8 + <div id="wrapper">
9 + <h1>{title}</h1>
10 + <h2><em>{statusCode}</em> {error}</h2>
11 + <ul id="stacktrace">{stack}</ul>
12 + </div>
13 + </body>
14 +</html>
No preview for this file type
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.