From 8bfe7d91eb181b68deb3ead22e5171396f51b52d Mon Sep 17 00:00:00 2001 From: Jerome Etienne Date: Tue, 1 Mar 2011 10:15:47 +0100 Subject: [PATCH] node/server: added experimental support for cluster.js --- node/server/cluster.js | 15 + node/server/node_modules/cluster/._History.md | Bin 0 -> 186 bytes node/server/node_modules/cluster/._Readme.md | Bin 0 -> 186 bytes .../node_modules/cluster/._package.json | Bin 0 -> 186 bytes node/server/node_modules/cluster/._test.js | Bin 0 -> 185 bytes node/server/node_modules/cluster/.gitignore | 3 + node/server/node_modules/cluster/.gitmodules | 3 + node/server/node_modules/cluster/.npmignore | 6 + node/server/node_modules/cluster/History.md | 112 +++ node/server/node_modules/cluster/LICENSE | 22 + node/server/node_modules/cluster/Makefile | 8 + node/server/node_modules/cluster/Readme.md | 98 +++ .../server/node_modules/cluster/docs/._api.md | Bin 0 -> 188 bytes .../server/node_modules/cluster/docs/._cli.md | Bin 0 -> 187 bytes .../node_modules/cluster/docs/._debug.md | Bin 0 -> 184 bytes .../node_modules/cluster/docs/._logger.md | Bin 0 -> 186 bytes .../node_modules/cluster/docs/._pidfiles.md | Bin 0 -> 187 bytes .../node_modules/cluster/docs/._reload.md | Bin 0 -> 187 bytes .../node_modules/cluster/docs/._repl.md | Bin 0 -> 187 bytes .../node_modules/cluster/docs/._stats.md | Bin 0 -> 184 bytes node/server/node_modules/cluster/docs/api.md | 198 +++++ node/server/node_modules/cluster/docs/cli.md | 59 ++ .../server/node_modules/cluster/docs/debug.md | 27 + .../node_modules/cluster/docs/logger.md | 33 + .../node_modules/cluster/docs/pidfiles.md | 41 + .../node_modules/cluster/docs/reload.md | 39 + node/server/node_modules/cluster/docs/repl.md | 164 ++++ .../server/node_modules/cluster/docs/stats.md | 45 ++ node/server/node_modules/cluster/index.js | 2 + .../node_modules/cluster/lib/._cluster.js | Bin 0 -> 185 bytes .../node_modules/cluster/lib/._master.js | Bin 0 -> 187 bytes .../node_modules/cluster/lib/._worker.js | Bin 0 -> 186 bytes .../node_modules/cluster/lib/cluster.js | 48 ++ .../server/node_modules/cluster/lib/master.js | 755 ++++++++++++++++++ .../cluster/lib/mixins/receiver.js | 53 ++ .../node_modules/cluster/lib/plugins/cli.js | 214 +++++ .../node_modules/cluster/lib/plugins/debug.js | 108 +++ .../cluster/lib/plugins/logger.js | 146 ++++ .../cluster/lib/plugins/pidfiles.js | 64 ++ .../cluster/lib/plugins/reload.js | 120 +++ .../node_modules/cluster/lib/plugins/repl.js | 189 +++++ .../node_modules/cluster/lib/plugins/stats.js | 113 +++ node/server/node_modules/cluster/lib/utils.js | 51 ++ .../server/node_modules/cluster/lib/worker.js | 199 +++++ .../cluster/node_modules/log/._History.md | Bin 0 -> 186 bytes .../cluster/node_modules/log/._Makefile | Bin 0 -> 187 bytes .../cluster/node_modules/log/._Readme.md | Bin 0 -> 186 bytes .../cluster/node_modules/log/._index.js | Bin 0 -> 186 bytes .../cluster/node_modules/log/._package.json | Bin 0 -> 186 bytes .../cluster/node_modules/log/History.md | 12 + .../cluster/node_modules/log/Makefile | 14 + .../cluster/node_modules/log/Readme.md | 95 +++ .../node_modules/log/examples/._file.js | Bin 0 -> 186 bytes .../node_modules/log/examples/._file.log | Bin 0 -> 186 bytes .../node_modules/log/examples/._reader.js | Bin 0 -> 186 bytes .../node_modules/log/examples/._stdout.js | Bin 0 -> 186 bytes .../cluster/node_modules/log/examples/file.js | 18 + .../node_modules/log/examples/file.log | 8 + .../node_modules/log/examples/reader.js | 15 + .../node_modules/log/examples/stdout.js | 16 + .../cluster/node_modules/log/index.html | 329 ++++++++ .../cluster/node_modules/log/index.js | 2 + .../cluster/node_modules/log/lib/log.js | 250 ++++++ .../cluster/node_modules/log/package.json | 1 + node/server/node_modules/cluster/package.json | 1 + .../cluster/support/should/.gitmodules | 3 + .../cluster/support/should/History.md | 22 + .../cluster/support/should/Makefile | 6 + .../cluster/support/should/Readme.md | 248 ++++++ .../cluster/support/should/index.js | 2 + .../cluster/support/should/lib/eql.js | 91 +++ .../cluster/support/should/lib/should.js | 548 +++++++++++++ .../cluster/support/should/package.json | 8 + node/server/server.js | 7 +- 74 files changed, 4630 insertions(+), 1 deletion(-) create mode 100644 node/server/cluster.js create mode 100644 node/server/node_modules/cluster/._History.md create mode 100644 node/server/node_modules/cluster/._Readme.md create mode 100644 node/server/node_modules/cluster/._package.json create mode 100644 node/server/node_modules/cluster/._test.js create mode 100644 node/server/node_modules/cluster/.gitignore create mode 100644 node/server/node_modules/cluster/.gitmodules create mode 100644 node/server/node_modules/cluster/.npmignore create mode 100644 node/server/node_modules/cluster/History.md create mode 100644 node/server/node_modules/cluster/LICENSE create mode 100644 node/server/node_modules/cluster/Makefile create mode 100644 node/server/node_modules/cluster/Readme.md create mode 100644 node/server/node_modules/cluster/docs/._api.md create mode 100644 node/server/node_modules/cluster/docs/._cli.md create mode 100644 node/server/node_modules/cluster/docs/._debug.md create mode 100644 node/server/node_modules/cluster/docs/._logger.md create mode 100644 node/server/node_modules/cluster/docs/._pidfiles.md create mode 100644 node/server/node_modules/cluster/docs/._reload.md create mode 100644 node/server/node_modules/cluster/docs/._repl.md create mode 100644 node/server/node_modules/cluster/docs/._stats.md create mode 100644 node/server/node_modules/cluster/docs/api.md create mode 100644 node/server/node_modules/cluster/docs/cli.md create mode 100644 node/server/node_modules/cluster/docs/debug.md create mode 100644 node/server/node_modules/cluster/docs/logger.md create mode 100644 node/server/node_modules/cluster/docs/pidfiles.md create mode 100644 node/server/node_modules/cluster/docs/reload.md create mode 100644 node/server/node_modules/cluster/docs/repl.md create mode 100644 node/server/node_modules/cluster/docs/stats.md create mode 100644 node/server/node_modules/cluster/index.js create mode 100644 node/server/node_modules/cluster/lib/._cluster.js create mode 100644 node/server/node_modules/cluster/lib/._master.js create mode 100644 node/server/node_modules/cluster/lib/._worker.js create mode 100644 node/server/node_modules/cluster/lib/cluster.js create mode 100644 node/server/node_modules/cluster/lib/master.js create mode 100644 node/server/node_modules/cluster/lib/mixins/receiver.js create mode 100644 node/server/node_modules/cluster/lib/plugins/cli.js create mode 100644 node/server/node_modules/cluster/lib/plugins/debug.js create mode 100644 node/server/node_modules/cluster/lib/plugins/logger.js create mode 100644 node/server/node_modules/cluster/lib/plugins/pidfiles.js create mode 100644 node/server/node_modules/cluster/lib/plugins/reload.js create mode 100644 node/server/node_modules/cluster/lib/plugins/repl.js create mode 100644 node/server/node_modules/cluster/lib/plugins/stats.js create mode 100644 node/server/node_modules/cluster/lib/utils.js create mode 100644 node/server/node_modules/cluster/lib/worker.js create mode 100644 node/server/node_modules/cluster/node_modules/log/._History.md create mode 100644 node/server/node_modules/cluster/node_modules/log/._Makefile create mode 100644 node/server/node_modules/cluster/node_modules/log/._Readme.md create mode 100644 node/server/node_modules/cluster/node_modules/log/._index.js create mode 100644 node/server/node_modules/cluster/node_modules/log/._package.json create mode 100644 node/server/node_modules/cluster/node_modules/log/History.md create mode 100644 node/server/node_modules/cluster/node_modules/log/Makefile create mode 100644 node/server/node_modules/cluster/node_modules/log/Readme.md create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/._file.js create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/._file.log create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/._reader.js create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/._stdout.js create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/file.js create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/file.log create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/reader.js create mode 100644 node/server/node_modules/cluster/node_modules/log/examples/stdout.js create mode 100644 node/server/node_modules/cluster/node_modules/log/index.html create mode 100644 node/server/node_modules/cluster/node_modules/log/index.js create mode 100644 node/server/node_modules/cluster/node_modules/log/lib/log.js create mode 100644 node/server/node_modules/cluster/node_modules/log/package.json create mode 100644 node/server/node_modules/cluster/package.json create mode 100644 node/server/node_modules/cluster/support/should/.gitmodules create mode 100644 node/server/node_modules/cluster/support/should/History.md create mode 100644 node/server/node_modules/cluster/support/should/Makefile create mode 100644 node/server/node_modules/cluster/support/should/Readme.md create mode 100644 node/server/node_modules/cluster/support/should/index.js create mode 100644 node/server/node_modules/cluster/support/should/lib/eql.js create mode 100644 node/server/node_modules/cluster/support/should/lib/should.js create mode 100644 node/server/node_modules/cluster/support/should/package.json diff --git a/node/server/cluster.js b/node/server/cluster.js new file mode 100644 index 0000000..582cf76 --- /dev/null +++ b/node/server/cluster.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node +/** + * cluster script - https://github.com/learnboost/cluster +*/ + +var cluster = require('cluster'); + +cluster('./server.js') + .set('workers', 1) // only 1 workers + .use(cluster.logger('logs')) + .use(cluster.stats()) + .use(cluster.pidfiles('pids')) + .use(cluster.cli()) + .use(cluster.repl(8888)) + .listen(8667); diff --git a/node/server/node_modules/cluster/._History.md b/node/server/node_modules/cluster/._History.md new file mode 100644 index 0000000000000000000000000000000000000000..42e4783db22982771bdc7173d76a4c29f1642690 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62!cf$rh*-q+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*$k4Hn4B%u5Aw I&8)d<0kAk3k^lez literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/._Readme.md b/node/server/node_modules/cluster/._Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..30c79ebfe05872ab67cbcfdde40e3af975241f60 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62!cf$qlF!q+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*Ra=I3PQr2=`T IhSprQ0JGH@m;e9( literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/._package.json b/node/server/node_modules/cluster/._package.json new file mode 100644 index 0000000000000000000000000000000000000000..795638e5b46dc9f24f83824b9027feeddfbe4436 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62!cf$pxqsq+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*qp4Hn4B%u5Aw IO{}?U0kcIJo&W#< literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/._test.js b/node/server/node_modules/cluster/._test.js new file mode 100644 index 0000000000000000000000000000000000000000..1e29b9984add148bfe71eaef18e337ae34cab178 GIT binary patch literal 185 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aXS!$_&b4^2;dkJ62!cf$rGp)q+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*Ra=I3PQr2=`D H)?Bp!nUEQ2 literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/.gitignore b/node/server/node_modules/cluster/.gitignore new file mode 100644 index 0000000..c021100 --- /dev/null +++ b/node/server/node_modules/cluster/.gitignore @@ -0,0 +1,3 @@ +testing +test.js +nohup.out diff --git a/node/server/node_modules/cluster/.gitmodules b/node/server/node_modules/cluster/.gitmodules new file mode 100644 index 0000000..01e2298 --- /dev/null +++ b/node/server/node_modules/cluster/.gitmodules @@ -0,0 +1,3 @@ +[submodule "support/should"] + path = support/should + url = git://github.com/visionmedia/should.js.git diff --git a/node/server/node_modules/cluster/.npmignore b/node/server/node_modules/cluster/.npmignore new file mode 100644 index 0000000..3c6b643 --- /dev/null +++ b/node/server/node_modules/cluster/.npmignore @@ -0,0 +1,6 @@ +test +testing +examples +*.sock +*.pid +*.log diff --git a/node/server/node_modules/cluster/History.md b/node/server/node_modules/cluster/History.md new file mode 100644 index 0000000..a103f1f --- /dev/null +++ b/node/server/node_modules/cluster/History.md @@ -0,0 +1,112 @@ + +0.3.1 / 2011-02-28 +================== + + * Added `cluster(filename)` support. Closes #45 + This is highly recommended, view the API docs + on the site for more info. + +0.3.0 / 2011-02-28 +================== + + * Added "worker exception" event. Closes #41 + * Added `listen()` host dns resolution. Closes #35 + * Added `pidfiles()` helper `master.pidof(name)` + * Added; `reload()` ignoring _node_modules_ and similar dirs. Closes #31 + * Fixed master __PPID__ reference. Closes #38 + * Fixed restart __SIGQUIT__ default + * Fixed; using `-g` for graceful shutdown instead of duplicate `-s`. Closes #39 + +0.2.4 / 2011-02-25 +================== + + * Added `Master#preventDefault` support to clean `cli()`. + Plugins can now tell master to "prevent its default behaviour", aka + listening for connections. + + * Fixed bug preventing consistent envs. Closes #37 [reported by sambarnes] + This caused `require.paths` to be altered. + + * Fixed; throw `pidfiles()` related errors, instead of ignoring + +0.2.3 / 2011-02-21 +================== + + * Fixed `reload()` plugin; protect against cyclic restarts. + +0.2.2 / 2011-02-21 +================== + + * Added __SIGCHLD__ trap to notify master of killed worker. + This means that master can now recover a child that + is __KILL__ed. + * Removed `Master#workerKilled()` call from worker + +0.2.1 / 2011-02-21 +================== + + * Added `Master#do()` + +0.2.0 / 2011-02-21 +================== + + * Added; maintaining worker count on __SIGCHLD__. Closes #28 + * Added; defaulting `reload()` to the servers root dir + * Changed; `reload()` filtering out non-js files. Closes #30 + * Removed __SIGHUP__ trap from worker + +0.1.1 / 2011-02-18 +================== + + * Added vhost example + * Added restarts stat + * Added `'all'` env support, `in('all')` executing regardless + of the environment. Useful when `listen()`ing on the same port + regardless. + + * Changed; `working directory` setting defaulting to the script directory (POLS) + +0.1.0 / 2011-02-18 +================== + + * Added TCP echo server example + * Added REPL `shutdown()` function + * Added REPL `stop()` function + * Added master spawning strategy + On restart, master now spawns a new master to accept + connections while the previous works (and master) finish + and die off. + * Added `Master#in()` for environment based usage. Closes #22 + For example: + cluster(server) + .in('development') + .use(cluster.debug()) + .use(cluster.repl()) + .listen(3000) + .in('production') + .use(cluster.logger()) + .listen(80); + + * Fixed some test race-conditions + * Fixed event leak. Closes #18 + +0.0.4 / 2011-02-17 +================== + + * Fixed `stats()` / `repl()` breakage when used with 0.2.x due to os mod. Closes #16 + * Changed; close _REPL_ connections on shutdown + +0.0.3 / 2011-02-16 +================== + + * Added log dependency to _package.json_. Closes #14 + +0.0.2 / 2011-02-15 +================== + + * Fixed `process.setuid()` typo + +0.0.1 / 2011-02-15 +================== + + * Initial commit \ No newline at end of file diff --git a/node/server/node_modules/cluster/LICENSE b/node/server/node_modules/cluster/LICENSE new file mode 100644 index 0000000..3709ae4 --- /dev/null +++ b/node/server/node_modules/cluster/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2011 LearnBoost + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node/server/node_modules/cluster/Makefile b/node/server/node_modules/cluster/Makefile new file mode 100644 index 0000000..0c721ac --- /dev/null +++ b/node/server/node_modules/cluster/Makefile @@ -0,0 +1,8 @@ + +test: + @./test/run + +test-debug: + @./test/run debug + +.PHONY: test test-debug \ No newline at end of file diff --git a/node/server/node_modules/cluster/Readme.md b/node/server/node_modules/cluster/Readme.md new file mode 100644 index 0000000..9463895 --- /dev/null +++ b/node/server/node_modules/cluster/Readme.md @@ -0,0 +1,98 @@ + +# Cluster + + [Cluster](http://learnboost.github.com/cluster) is an extensible multi-core server manager for [node.js](http://nodejs.org). + +## Installation + + $ npm install cluster + +## Features + + - zero-downtime restart + - hard shutdown support + - graceful shutdown support + - resuscitates workers + - maintains worker count, even if worker was _SIGKILL_ed. + - workers commit suicide when master dies + - spawns one worker per cpu (by default) + - extensible via plugins + - bundled plugins + - [cli](http://learnboost.github.com/cluster/docs/cli.html): provides a command-line interface for your cluster + - [debug](http://learnboost.github.com/cluster/docs/debug.html): verbose debugging information + - [logger](http://learnboost.github.com/cluster/docs/logger.html): master / worker logs + - [pidfiles](http://learnboost.github.com/cluster/docs/pidfiles.html): writes master / worker pidfiles + - [reload](http://learnboost.github.com/cluster/docs/reload.html): reloads workers when files change + - [repl](http://learnboost.github.com/cluster/docs/repl.html): perform real-time administration + - [stats](http://learnboost.github.com/cluster/docs/stats.html): adds real-time statistics to the `repl` plugin + - supports node 0.2.x + - supports node 0.4.x + - supports TCP servers + +## Example + + var cluster = require('cluster') + , http = require('http'); + + var server = http.createServer(function(req, res){ + console.log('%s %s', req.method, req.url); + var body = 'Hello World'; + res.writeHead(200, { 'Content-Length': body.length }); + res.end(body); + }); + + cluster(server) + .use(cluster.logger('logs')) + .use(cluster.stats()) + .use(cluster.pidfiles('pids')) + .use(cluster.cli()) + .use(cluster.repl(8888)) + .listen(3000); + +# Screencasts + + - Cluster [Introduction](http://screenr.com/X8v) + +## Running Tests + +First: + + $ git submodule update --init + +Then: + + $ make test + +Actively tested with node: + + - 0.2.6 + - 0.4.1 + +## Authors + + * TJ Holowaychuk + +## License + +(The MIT License) + +Copyright (c) 2011 LearnBoost <dev@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node/server/node_modules/cluster/docs/._api.md b/node/server/node_modules/cluster/docs/._api.md new file mode 100644 index 0000000000000000000000000000000000000000..bae67655eb6ba0cea691c2316f2c876d72b5b59b GIT binary patch literal 188 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aTgGS_CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*edY7G|2$;?Xy J@y)HdY619f8a4m` literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/._cli.md b/node/server/node_modules/cluster/docs/._cli.md new file mode 100644 index 0000000000000000000000000000000000000000..924c18e90ff7875063aceb6ba87a8dc5a2ca65c4 GIT binary patch literal 187 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aVHRi_`89a2;dkJ62!cfDGR6+q+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*$s4Hn4B%u5Av IO{}?U0o=?P_5c6? literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/._debug.md b/node/server/node_modules/cluster/docs/._debug.md new file mode 100644 index 0000000000000000000000000000000000000000..ca5bea7831f4432348929c3296559a4d9c672459 GIT binary patch literal 184 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aT^eW_&b1@2;dkJ62!cfDF>(&q+c;w_hgP#PbkF3F1pxau9ozr_ literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/._logger.md b/node/server/node_modules/cluster/docs/._logger.md new file mode 100644 index 0000000000000000000000000000000000000000..0f6cd4987af5686bd6ce9cf10bd07e06f63af00e GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62!cfDG#U=q+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*Ra=I3PQr2=`z HKyEDn!k-zv literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/._pidfiles.md b/node/server/node_modules/cluster/docs/._pidfiles.md new file mode 100644 index 0000000000000000000000000000000000000000..6050d7ce4fbf65c64805599b93b5b039da4c305f GIT binary patch literal 187 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aVHRi_`89a2;dkJ62!cfsQ{=Hq+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*ed4Hn4B%u5Aw Ije*=+0N;%o1^@s6 literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/._reload.md b/node/server/node_modules/cluster/docs/._reload.md new file mode 100644 index 0000000000000000000000000000000000000000..bcfbcf041f647b36d6eae8f07ccd3fb8c427a930 GIT binary patch literal 187 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aVHRi_`89a2;dkJ62!cfsR*bPq+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*$t4Hn4B%u5Aw J4b7~%Y60M48U_FW literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/._repl.md b/node/server/node_modules/cluster/docs/._repl.md new file mode 100644 index 0000000000000000000000000000000000000000..acdb8f67d57abab1ea2c9abd3fae90c9537c906f GIT binary patch literal 187 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aVHRi_`89a2;dkJ62!cfsRXDLq+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*Ym4Hn4B%u5Aw J4UMe1Y60N18UFwP literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/._stats.md b/node/server/node_modules/cluster/docs/._stats.md new file mode 100644 index 0000000000000000000000000000000000000000..1b8e4d8c19f37bf55d14285a7fbc585fb37dbb9e GIT binary patch literal 184 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aT^eW_&b1@2;dkJ62!cfsSKzTq+c;w_hgP#PbkF3F1pxlp9p(T4 literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/docs/api.md b/node/server/node_modules/cluster/docs/api.md new file mode 100644 index 0000000..3bcdc0a --- /dev/null +++ b/node/server/node_modules/cluster/docs/api.md @@ -0,0 +1,198 @@ + +## API + + The Cluster API at its core is extremely simple, all we need to do is pass + our tcp or http `server` to `cluster()`, then call `listen()` as we would on the `http.Server` itself. + + + var cluster = require('../') + , http = require('http'); + + var server = http.createServer(function(req, res){ + res.writeHead(200); + res.end('Hello World'); + }); + + cluster(server) + .listen(3000); + + Alternatively (and recommended) is to export your server instance via `module.exports`, and supply a path to `cluster()`. For example _app.js_: + + module.exports = http.createServer(....); + + and _server.js_ with our cluster logic, allowing our server to be `require()`ed within tests, and preventing potential issues by having open database connections etc within the master processes, as only the workers need access to the `server` instance. + + cluster('app') + .listen(3000); + + A good example if this, is a long-lived database connection. Our _app.js_ may have this initialized at the top, which although will work fine stand-alone, may cause cluster's master processes to hang when restarting or closing due to the connection remaining active in the event loop. + + var db = redis.createClient(); + +### Plugins + + A plugin simple a function that accepts the `master` process. Most plugin functions _return_ another anonymous function, allowing them to accept options, for example: + + function myPlugin(path){ + return function(master) { + // do stuff + } + } + + To use them, all we need to do is pass it to the `use()` method: + + cluster(server) + .use(myPlugin('/some/path')) + .listen(3000); + + To use a plugin that is bundled with Cluster simply grab it from the `cluster` object: + + cluster(server) + .use(cluster.logger()) + .listen(3000); + +### Settings + + Below are the settings available: + + - `workers` Number of workers to spawn, defaults to the number of CPUs or `1` + - `working directory` Working directory defaulting to the script's dir + - `backlog` Connection backlog, defaulting to 128 + - `socket path` Master socket path defaulting to `./` + - `timeout` Worker shutdown timeout in milliseconds, defaulting to `60000` + - `user` User id / name + - `group` Group id / name + + We can take what we have now, and go on to apply settings using the `set(option, value)` method. For example: + + cluster(server) + .set('working directory', '/') + .set('workers', 5) + .listen(3000); + +### Signals + + Cluster performs the following actions when handling signals: + + - `SIGINT` hard shutdown + - `SIGTERM` hard shutdown + - `SIGQUIT` graceful shutdown + - `SIGUSR2` restart workers + +### Events + + The following events are emitted, useful for plugins or general purpose logging etc. + + - `start`. When the server is starting (pre-spawn) + - `worker`. When a worker is spawned, passing the `worker` + - `listening`. When the server is listening for connections (post-spawn) + - `closing`. When master is gracefully shutting down + - `close`. When master has completed shutting down + - `worker killed`. When a worker has died + - `worker exception`. Worker uncaughtException. Receives the worker and exception object + - `kill`. When a `signal` is being sent to all workers + - `restarting`. Restart requested by REPL or signal. Receives an object + which can be patched in order to preserve plugin state. + - `restart`. Restart complete, new master established, previous killed. + Receives an object with state preserved by the `restarting` even, + patched in the previous master. + +### Master#state + + Current state of the master process, one of: + + - `active` + - `hard shutdown` + - `graceful shutdown` + +### Master#isWorker + + `true` when the script is executed as a worker. + + cluster = cluster(server).listen(3000); + + if (cluster.isWorker) { + // do something + } + +### Master#isMaster + +`true` when the script is executed as master. + + cluster = cluster(server).listen(3000); + + if (cluster.isMaster) { + // do something + } + +### Master#set(option, value) + + Set `option` to `value`. + +### Master#use(plugin) + + Register a `plugin` for use. + +### Master#in(env) + + Conditionally perform the following action, if + __NODE_ENV__ matches `env`. + + cluster(server) + .in('development').use(cluster.debug()) + .in('development').listen(3000) + .in('production').listen(80); + + The environment conditionals may be applied to several calls: + + cluster(server) + .set('working directory', '/') + .in('development') + .set('workers', 1) + .use(cluster.logger('logs', 'debug')) + .use(cluster.debug()) + .listen(3000) + .in('production') + .set('workers', 4) + .use(cluster.logger()) + .use(cluster.pidfiles()) + .listen(80); + + If we perform the same action for environments, set them before + the first `in()` call, or use `in('all')`. + + cluster(server) + .set('working directory', '/') + .do(function(){ + console.log('some arbitrary action'); + }) + .in('development') + .set('workers', 1) + .use(cluster.logger('logs', 'debug')) + .use(cluster.debug()) + .in('production') + .set('workers', 4) + .use(cluster.logger()) + .use(cluster.pidfiles()) + .in('all') + .listen(80); + +### Master#spawn(n) + + Spawn `n` additional workers. + +### Master#close() + + Graceful shutdown, waits for all workers to reply before exiting. + +### Master#destroy() + + Hard shutdown, immediately kill all workers. + +### Master#restart([signal]) + + Defaults to a graceful restart, spawning a new master process, and sending __SIGQUIT__ to the previous master process. Alternatively a custom `signal` may be passed. + +### Master#kill([signal]) + + Sends __SIGTERM__ or `signal` to all worker processes. This method is used by `Master#restart()`, `Master#close()` etc. \ No newline at end of file diff --git a/node/server/node_modules/cluster/docs/cli.md b/node/server/node_modules/cluster/docs/cli.md new file mode 100644 index 0000000..97f16e6 --- /dev/null +++ b/node/server/node_modules/cluster/docs/cli.md @@ -0,0 +1,59 @@ + +## CLI + + Adds a command-line interface to your cluster. + +### Usage + +This plugin requires that you use `pidfiles()` +above `cli()`, so that the pidfile directory +is exposed. + + cluster(server) + .use(cluster.pidfiles()) + .use(cluster.cli()) + .listen(3000); + +Once set up our server script serves as both +the master, and the CLI. For example we may +still launch the server(s) as shown below. + + $ nohup node server.js & + +However now we may also utilize commands +provided by this plugin. + + $ node server.js status + + master 3281 alive + worker 0 3282 dead + worker 1 3283 alive + worker 2 3284 alive + worker 3 3285 alive + + $ node server.js restart + $ node server.js shutdown + +For more command information use `--help`. + + $ node server.js --help + +### Defining CLI Commands + + Plugins may define additional commands, simply by invoking `cluster.cli.define()` passing the flag(s), a callback function, + and a description. Below is the implementation of `--help` for reference: + + var cli = require('cluster').cli; + + cli.define('-h, --help, help', function(master){ + console.log('\n Usage: node \n'); + commands.forEach(function(command){ + console.log(' ' + + command.flags.join(', ') + + '\n ' + + '\033[90m' + command.desc + '\033[0m' + + '\n'); + }); + console.log(); + process.exit(); + }, 'Show help information'); diff --git a/node/server/node_modules/cluster/docs/debug.md b/node/server/node_modules/cluster/docs/debug.md new file mode 100644 index 0000000..e45c916 --- /dev/null +++ b/node/server/node_modules/cluster/docs/debug.md @@ -0,0 +1,27 @@ + +## Debug + +Outputs verbose debugging information to _stderr_. + + info - master started + info - worker 0 spawned + info - worker 1 spawned + info - worker 2 spawned + info - worker 3 spawned + info - worker 2 connected + info - worker 0 connected + info - worker 3 connected + info - worker 1 connected + info - listening for connections + ^C info - shutting down + warning - kill(SIGKILL) + info - shutdown complete + warning - worker 2 died + warning - worker 0 died + warning - worker 3 died + +## Usage + + cluster(server) + .use(cluster.debug()) + .listen(3000); \ No newline at end of file diff --git a/node/server/node_modules/cluster/docs/logger.md b/node/server/node_modules/cluster/docs/logger.md new file mode 100644 index 0000000..f505532 --- /dev/null +++ b/node/server/node_modules/cluster/docs/logger.md @@ -0,0 +1,33 @@ + +## Logger + + File-based logging of both the _master_ and _worker_ processes. + +### Usage + +The `logger([path[, level]])` plugin accepts an optional `path`, and optional `level` to control the verbosity of the master process logs. By default the log level is _info_. + +Outputting to `./logs`: + + cluster(server) + .use(cluster.logger()) + .listen(3000); + + +Outputting to `./tmp/logs`: + + cluster(server) + .use(cluster.logger('tmp/logs')) + .listen(3000); + +Outputting to `/var/log/node` with a log level of `debug`: + + cluster(server) + .use(cluster.logger('/var/log/node', 'debug')) + .listen(3000); + +Generated files: + + master.log + workers.access.log + workers.error.log \ No newline at end of file diff --git a/node/server/node_modules/cluster/docs/pidfiles.md b/node/server/node_modules/cluster/docs/pidfiles.md new file mode 100644 index 0000000..201ce82 --- /dev/null +++ b/node/server/node_modules/cluster/docs/pidfiles.md @@ -0,0 +1,41 @@ + +## PID Files + + Saves out PID files, for example: + + master.pid + worker.0.pid + worker.1.pid + worker.2.pid + worker.3.pid + +### Usage + +The `pidfiles([path])` plugin saves pid (process-id) files to the given `path` or `./pids`. + +save to `./pids`: + + cluster(server) + .use(cluster.pidfiles()) + .listen(3000); + +save to `/var/run/node`: + + cluster(server) + .use(cluster.logger('/var/run/node')) + .listen(3000); + +### master.pidfiles + + The pidfiles directory. + +### master.pidof(name) + + Return a __PID__ for the given `name`. + + master.pidof('master') + // => 5978 + + master.pidof('worker.0') + // => 5979 + \ No newline at end of file diff --git a/node/server/node_modules/cluster/docs/reload.md b/node/server/node_modules/cluster/docs/reload.md new file mode 100644 index 0000000..766c2ca --- /dev/null +++ b/node/server/node_modules/cluster/docs/reload.md @@ -0,0 +1,39 @@ + +## Reload + + Restart the server the given js `files` have changed. + `files` may be several directories, filenames, etc, defaulting + to the script's directory. + +### Options + + - `signal` Signal to send, defaults to __SIGTERM__ + - `interval` Watcher interval, defaulting to `100` + +### Usage + + The `reload(paths[, signal])` plugin accepts a single path, or an array of paths, watching for __mtime__ changes, and re-loading the workers when a change has been made. By default the __SIGTERM__ signal is sent, killing the workers immediately, however we may pass a `signal` for graceful termination as well. + + Reload when files in `./` (`__dirname`) change: + + cluster(server) + .use(cluster.reload()) + .listen(3000); + + Reload when files in `./lib` change: + + cluster(server) + .use(cluster.reload('lib')) + .listen(3000); + + Reload when files in `./lib`, `./tests`, or the `./index.js` file change: + + cluster(server) + .use(cluster.reload(['lib', 'tests', 'index.js'])) + .listen(3000); + + Graceful shutdown: + + cluster(server) + .use(cluster.reload('lib', { signal: 'SIGQUIT' })) + .listen(3000); \ No newline at end of file diff --git a/node/server/node_modules/cluster/docs/repl.md b/node/server/node_modules/cluster/docs/repl.md new file mode 100644 index 0000000..69f3187 --- /dev/null +++ b/node/server/node_modules/cluster/docs/repl.md @@ -0,0 +1,164 @@ + +## REPL + + Provides live administration tools for inspecting state, spawning and killing workers, and more. The __REPL__ plugin itself is extensible, for example the `stats()` plugin provides a __REPL__ function named `stats()`. + +### Usage + + The `repl([port | path])` accepts a `port` or unix domain socket `path`, after which you may telnet to at any time. + +Launch the __REPL__ on port _8888_: + + cluster(server) + .use(cluster.repl(8888, '127.0.0.1')) + .listen(3000); + +Start a telnet session: + + $ telnet localhost 8888 + + cluster> help() + + Commands + help(): Display help information + spawn(n): Spawn one or more additional workers + pids(): Output process ids + kill(id, signal): Send signal or SIGTERM to the given worker + shutdown(): Gracefully shutdown server + stop(): Hard shutdown + restart(): Gracefully restart all workers + echo(msg): echo the given message + stats(): Display server statistics + + +### pids() + + Outputs the master / worker process ids. + + cluster> pids() + + pids + master: 1799 + worker #0: 1801 + worker #1: 1802 + worker #2: 1803 + worker #3: 1804 + +### spawn() + + Spawn an additional worker. + + cluster> spawn() + spawning 1 worker + cluster> pids() + + pids + master: 1799 + worker #0: 1801 + worker #1: 1802 + worker #2: 1803 + worker #3: 1804 + worker #4: 1809 + +### spawn(n) + + Spawn `n` workers: + + cluster> spawn(4) + spawning 4 workers + cluster> pids() + + pids + master: 1817 + worker #0: 1818 + worker #1: 1819 + worker #2: 1820 + worker #3: 1821 + worker #4: 1825 + worker #5: 1826 + worker #6: 1827 + worker #7: 1828 + +### kill(id[, signal]) + +Kill worker `id` with the given `signal` or __SIGTERM__. For graceful termination use __SIGQUIT__. + + cluster> pids() + + pids + master: 1835 + worker #0: 1837 + worker #1: 1838 + worker #2: 1839 + worker #3: 1840 + + cluster> kill(2) + sent SIGTERM to worker #2 + cluster> kill(3) + sent SIGTERM to worker #3 + cluster> pids() + + pids + master: 1835 + worker #0: 1837 + worker #1: 1838 + worker #2: 1843 + worker #3: 1844 + +### restart() + + Gracefully restart all workers. + + cluster> pids() + + pids + master: 1835 + worker #0: 1837 + worker #1: 1838 + worker #2: 1843 + worker #3: 1844 + + cluster> restart() + restarting 4 workers + cluster> pids() + + pids + master: 1835 + worker #0: 1845 + worker #1: 1849 + worker #2: 1848 + worker #3: 1847 + +### Defining REPL Functions + + To define a function accessible to the __REPL__, all we need to do is call `cluster.repl.define()`, passing the function, as well as a description string. + + Below we define the `echo()` function, simply printing the input `msg` given. As you can see our function receivers the `Master` instance, the __REPL__ `sock`, and any arguments that were passed. For example `echo("test")` would pass the `msg` as `"test"`, and `echo("foo", "bar")` would pass `msg` as `"foo"`, and `arguments[3]` as `"bar"`. + + repl.define('echo', function(master, sock, msg){ + sock.write(msg + '\n'); + }, 'echo the given message'); + + Shown below is a more complete example. + + var cluster = require('../') + , repl = cluster.repl + , http = require('http'); + + var server = http.createServer(function(req, res){ + var body = 'Hello World'; + res.writeHead(200, { 'Content-Length': body.length }); + res.end(body); + }); + + // custom repl function + + repl.define('echo', function(master, sock, msg){ + sock.write(msg + '\n'); + }, 'echo the given message'); + + // $ telnet localhots 8888 + + cluster(server) + .use(repl(8888)) + .listen(3000); \ No newline at end of file diff --git a/node/server/node_modules/cluster/docs/stats.md b/node/server/node_modules/cluster/docs/stats.md new file mode 100644 index 0000000..b367144 --- /dev/null +++ b/node/server/node_modules/cluster/docs/stats.md @@ -0,0 +1,45 @@ + +## Stats + + The stats plugin collects statistics from the events emitter by the master process, and exposes a `stats()` __REPL__ function. + + +### Usage + +To utilize simply `use()` both the `stats()` and `repl()` plugins. + + cluster(server) + .use(cluster.stats()) + .use(cluster.repl(8888)) + .listen(3000); + +Telnet to the repl: + + $ telnet localhost 8888 + +### stats() + + After manually killing two workers, the stats below show information regarding system load average, uptime, total workers spawned, deaths, worker-specific stats and more. + + cluster> stats() + + Master + os: Darwin 10.5.0 + state: active + started: Fri, 11 Feb 2011 16:58:48 GMT + uptime: 2 minutes + workers: 4 + deaths: 2 + + Resources + load average: 0.35 0.23 0.15 + cores utilized: 4 / 4 + memory at boot (free / total): 2.18gb / 4.00gb + memory now (free / total): 2.08gb / 4.00gb + + Workers + uptime #0: 2 minutes + uptime #1: 2 minutes + uptime #2: 1 minute + uptime #3: 22 seconds + diff --git a/node/server/node_modules/cluster/index.js b/node/server/node_modules/cluster/index.js new file mode 100644 index 0000000..1f03c3b --- /dev/null +++ b/node/server/node_modules/cluster/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/cluster'); \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/._cluster.js b/node/server/node_modules/cluster/lib/._cluster.js new file mode 100644 index 0000000000000000000000000000000000000000..3be7ef13c8ae742470fac0085281e358afa0fe4c GIT binary patch literal 185 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aXS!$_&b4^2;dkJ62!cfDIBO2q+CMFl<=O&h<7V9M^7NwRjRLoiZEQq1$$)7ow?%1$KvnmT`Z*cHS Pi_@AC&CM`Jn4=Q_8`d73 literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/lib/._master.js b/node/server/node_modules/cluster/lib/._master.js new file mode 100644 index 0000000000000000000000000000000000000000..959a7042b0f4b9d1eb6dd90462641ff0497aba9a GIT binary patch literal 187 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aVHRi_`89a2;dkJ62!cfDH5m@q+CMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*qi4Hn4B%u5Aw JEsU(WY5~@c8R-B3 literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/lib/._worker.js b/node/server/node_modules/cluster/lib/._worker.js new file mode 100644 index 0000000000000000000000000000000000000000..97b7dd0e0d4de9496eb3b1ef44d0c98817b95e36 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62!cfDGsOCMFl<=O&h<7V9M^7NwRjRC9qGlANDYnwzI!t6*Ra=I3PQr2=^- I#@1Z50KC8%u>b%7 literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/lib/cluster.js b/node/server/node_modules/cluster/lib/cluster.js new file mode 100644 index 0000000..04b883c --- /dev/null +++ b/node/server/node_modules/cluster/lib/cluster.js @@ -0,0 +1,48 @@ + +/*! + * Cluster + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Master = require('./master') + , fs = require('fs'); + +/** + * Export `start` as the module. + */ + +exports = module.exports = start; + +/** + * Library version. + */ + +exports.version = '0.3.1'; + +/** + * Start a new `Master` with the given `server`. + * + * @param {http.Server} server + * @return {Master} + * @api public + */ + +function start(server) { + return new Master(server); +} + +/** + * Expose middleware via lazy-requires. + */ + +fs.readdirSync(__dirname + '/plugins').forEach(function(plugin){ + plugin = plugin.replace('.js', ''); + exports.__defineGetter__(plugin, function(){ + return require('./plugins/' + plugin); + }); +}); \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/master.js b/node/server/node_modules/cluster/lib/master.js new file mode 100644 index 0000000..93527aa --- /dev/null +++ b/node/server/node_modules/cluster/lib/master.js @@ -0,0 +1,755 @@ + +/*! + * Cluster - Master + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Worker = require('./worker') + , EventEmitter = require('events').EventEmitter + , binding = process.binding('net') + , dirname = require('path').dirname + , spawn = require('child_process').spawn + , ENOENT = process.version >= 'v0.4.0' + ? require('constants').ENOENT + : process.ENOENT + , net = require('net') + , fs = require('fs') + , os; + +// COMPAT: + +try { + os = require('os'); +} catch (err) { + // ignore +} + +// COMPAT: + +net.Socket = net.Stream; + +// COMPAT: + +if (!net.isIP) net.isIP = binding.isIP; + +/** + * Node binary. + */ + +var node = process.execPath + +/** + * Start a new `Master` with the given `server` or filename to + * a node module exporting a server. + * + * Options: + * + * - `workers` Number of workers to spawn, defaults to the number of CPUs + * - 'working directory` Working directory defaulting to the script's dir + * - 'backlog` Connection backlog, defaulting to 128 + * - 'socket path` Master socket path defaulting to `./` + * - 'timeout` Worker shutdown timeout in milliseconds, defaulting to 60,000 + * - 'user` User id / name + * - 'group` Group id / name + * + * Events: + * + * - `start`. When the server is starting (pre-spawn) + * - `worker`. When a worker is spawned, passing the `worker` + * - `listening`. When the server is listening for connections (post-spawn) + * - `closing`. When master is gracefully shutting down + * - `close`. When master has completed shutting down + * - `worker killed`. When a worker has died + * - `worker exception`. Worker uncaughtException. Receives the worker / exception + * - `kill`. When a `signal` is being sent to all workers + * - `restarting`. Restart requested by REPL or signal. Receives an object + * which can be patched in order to preserve plugin state. + * - `restart`. Restart complete, new master established, previous died. + * Receives an object with state preserved by the `restarting` event. + * + * Signals: + * + * - `SIGINT` hard shutdown + * - `SIGTERM` hard shutdown + * - `SIGQUIT` graceful shutdown + * - `SIGUSR2` graceful restart + * - `SIGHUP` graceful restart + * + * @param {net.Server|String} server + * @return {Master} + * @api public + */ + +var Master = module.exports = function Master(server) { + var self = this; + this.server = server; + this.plugins = []; + this.children = []; + this.state = 'active'; + + // grab server root + this.cmd = process.argv.slice(1); + this.dir = dirname(this.cmd[0]); + + // defaults + this.options = { + 'backlog': 128 + , 'working directory': this.dir + , 'socket path': this.dir + , 'timeout': 60000 + }; + + // parent master pid + this.ppid = process.env.CLUSTER_PARENT_PID + ? parseInt(process.env.CLUSTER_PARENT_PID, 10) + : null; + + // environment + this.env = process.env.NODE_ENV || 'development'; + + // process is a worker + this.isWorker = !! process.env.CLUSTER_MASTER_PID; + + // process is a child (worker or master replacement) + this.isChild = this.isWorker || !! process.env.CLUSTER_REPLACEMENT_MASTER; + + // process is master + this.isMaster = ! this.isWorker; + + // process id + this.pid = process.pid; + if (this.isMaster) process.env.CLUSTER_MASTER_PID = this.pid; + + // custom worker fds, defaults to std{out,err} + this.customFds = [1, 2]; + + // tcp server for IPC + this._server = net.createServer(function(sock){ + sock.setEncoding('ascii'); + sock.on('data', self.frame.bind(self)); + }); +}; + +/** + * Interit from `EventEmitter.prototype`. + */ + +Master.prototype.__proto__ = EventEmitter.prototype; + +/** + * Worker is a receiver. + */ + +require('./mixins/receiver')(Master.prototype); + +/** + * Resolve `path` relative to the server file being executed. + * + * @param {String} path + * @return {String} + * @api public + */ + +Master.prototype.resolve = function(path){ + return '/' == path[0] + ? path + : this.dir + '/' + path; +}; + +/** + * Return the path of the unix domain socket used for IPC. + * + * @return {String} + * @api public + */ + +Master.prototype.__defineGetter__('socketPath', function(){ + var pid = process.env.CLUSTER_MASTER_PID || this.pid; + return this.options['socket path'] + '/cluster.' + pid + '.sock'; +}); + +/** + * Return `true` when the environment set by `Master#in()` + * matches __NODE_ENV__. + * + * @return {Boolean} + * @api private + */ + +Master.prototype.__defineGetter__('environmentMatches', function(){ + if (this._env) + return this.env == this._env || 'all' == this._env; + return true; +}); + +/** + * Invoke masters's `method` with worker `id`. (called from Worker) + * + * @param {Number} id + * @param {String} method + * @param {...} args + * @api private + */ + +Master.prototype.call = function(id, method){ + var args = Array.prototype.slice.call(arguments) + , id = args.shift() + , method = args.shift(); + this.sock.write(JSON.stringify({ + method: method + , args: args + , id: id + }), 'ascii'); +}; + +/** + * Defer `http.Server#listen()` call. + * + * @param {Number|String} port or unix domain socket path + * @param {String|Function} host or callback + * @param {Function} callback + * @return {Master} for chaining + * @api public + */ + +Master.prototype.listen = function(port, host, callback){ + var self = this; + if (this.preventDefault) return; + if (this.environmentMatches) { + if (this.isWorker) { + var worker = new Worker(this); + this.sock = net.createConnection(this.socketPath); + this.sock.on('connect', function(){ + worker.start(); + }); + } else { + this.port = port; + if ('function' == typeof host) callback = host, host = null; + this.host = host; + this.callback = callback; + if (this.isChild) { + this.acceptFd(); + } else { + this.createSocket(function(err, fd){ + if (err) throw err; + self.fd = fd; + self.start(); + }); + } + } + } + + return this; +}; + +/** + * Conditionally perform the following action, if + * __NODE_ENV__ matches `env`. + * + * Examples: + * + * cluster(server) + * .in('development').use(cluster.debug()) + * .in('development').listen(3000) + * .in('production').listen(80); + * + * @param {String} env + * @return {Master} self or stubs + * @api public + */ + +Master.prototype.in = function(env){ + this._env = env; + return this; +}; + +/** + * Set option `key` to `val`. + * + * @param {String} key + * @param {Mixed} val + * @return {Master} for chaining + * @api public + */ + +Master.prototype.set = function(key, val){ + if (this.environmentMatches) this.options[key] = val; + return this; +}; + +/** + * Invoke `fn(master)`. + * + * @param {Function} fn + * @api public + */ + +Master.prototype.do = function(fn){ + if (this.environmentMatches) fn.call(this, this); + return this; +}; + +/** + * Check if `option` has been set. + * + * @param {String} option + * @return {Boolean} + * @api public + */ + +Master.prototype.has = function(option){ + return !! this.options[option]; +}; + +/** + * Use the given `plugin`. + * + * @param {Function} plugin + * @return {Master} for chaining + * @api public + */ + +Master.prototype.use = function(plugin){ + if (this.environmentMatches) { + this.plugins.push(plugin); + if (!this.isWorker) plugin(this); + } + return this; +}; + +/** + * Create listening socket and callback `fn(err, fd)`. + * + * @return {Function} fn + * @api private + */ + +Master.prototype.createSocket = function(fn){ + var self = this + , ipv; + + // explicit host + if (this.host) { + // ip + if (ipv = net.isIP(this.host)) { + fn(null, binding.socket('tcp' + ipv)); + // lookup + } else { + require('dns').lookup(this.host, function(err, ip, ipv){ + if (err) return fn(err); + self.host = ip; + fn(null, binding.socket('tcp' + ipv)); + }); + } + // local socket + } else if ('string' == typeof this.port) { + fn(null, binding.socket('unix')); + // only port + } else if (this.port) { + fn(null, binding.socket('tcp4')); + } +}; + +/** + * Start master process. + * + * - defaults workers to the number of CPUs available + * - creates listening socket + * + * @api private + */ + +Master.prototype.start = function(){ + var self = this; + + // signal handlers + process.on('SIGINT', this.destroy.bind(this)); + process.on('SIGTERM', this.destroy.bind(this)); + process.on('SIGQUIT', this.close.bind(this)); + process.on('SIGUSR2', this.restart.bind(this)); + process.on('SIGHUP', this.restart.bind(this)); + process.on('SIGCHLD', this.maintainWorkerCount.bind(this)); + + // Default worker to the # of cpus + if (!this.has('workers')) { + this.set('workers', os + ? os.cpus().length + : 1); + } + + // TCP server for IPC + this._server.listen(this.socketPath, function(){ + self.emit('start'); + + // change working dir + process.chdir(self.options['working directory']); + + // user + var user = self.options.user; + if (user) process.setuid(user); + + // group + var group = self.options.group; + if (group) process.setgid(group); + + // spawn workers + self.spawn(self.options.workers); + + // set # of pending workers + self.starting = true; + self.pendingWorkers = self.options.workers; + }); +}; + +/** + * Maintain worker count, re-spawning if necessary. + * + * @api private + */ + +Master.prototype.maintainWorkerCount = function(){ + this.children.forEach(function(worker){ + var pid = worker.proc.pid; + if (!pid) this.workerKilled(worker); + }, this); +}; + +/** + * Spawn `n` workers. + * + * @param {Number} n + * @api private + */ + +Master.prototype.spawn = function(n){ + while (n--) this.spawnWorker(); +}; + +/** + * Spawn a worker with optional `id`. + * + * @param {Number} id + * @return {Worker} + * @api private + */ + +Master.prototype.spawnWorker = function(id){ + var worker = new Worker(this).spawn(); + + // id given + if ('number' == typeof id) { + this.children[id] = worker; + worker.id = id; + // generate an id + } else { + var len = this.children.push(worker); + worker.id = len - 1; + } + + // TODO: refactor + var json = JSON.stringify({ + method: 'connect' + , args: [worker.id, this.options.timeout] + }); + worker.sock.write(json, 'ascii', this.fd); + + // emit + this.emit('worker', worker); + + return worker; +}; + +/** + * Graceful shutdown, wait for all workers + * to reply before exiting. + * + * @api public + */ + +Master.prototype.close = function(){ + this.state = 'graceful shutdown'; + this.emit('closing'); + this.kill('SIGQUIT'); + this.pendingDeaths = this.children.length; +}; + +/** + * Hard shutdwn, immediately kill all workers. + * + * @api public + */ + +Master.prototype.destroy = function(){ + this.state = 'hard shutdown'; + this.emit('closing'); + this.kill('SIGKILL'); + this._destroy(); +}; + + +/** + * Restart all workers, by sending __SIGQUIT__ + * or `sig` to them, enabling master to re-spawn. + * + * @param {String} sig + * @return {ChildProcess} replacement master process + * @api public + */ + +Master.prototype.restart = function(sig){ + var data = {} + , proc = this.spawnMaster(); + + // pass object to plugins, allowing them + // to patch it, and utilize the data in + // the new Master + this.emit('restarting', data); + proc.sock.write(JSON.stringify({ + method: 'connectMaster' + , args: [sig || 'SIGQUIT'] + }), 'ascii', this.fd); + + // TODO: refactor + this.on('close', function(){ + proc.sock.write(JSON.stringify({ + method: 'masterKilled' + , args: [data] + }), 'ascii'); + }); + + return proc; +}; + +/** + * Spawn a new master process. + * + * @return {ChildProcess} + * @api private + */ + +Master.prototype.spawnMaster = function(){ + var fds = binding.socketpair() + , customFds = [fds[0], 1, 2] + , env = {}; + + // merge current env + for (var key in process.env) { + env[key] = process.env[key]; + } + + delete env.CLUSTER_MASTER_PID; + env.CLUSTER_REPLACEMENT_MASTER = 1; + env.CLUSTER_PARENT_PID = this.pid; + + // spawn new master process + var proc = spawn(node, this.cmd, { + customFds: customFds + , env: env + }); + + proc.stdin = new net.Socket(fds[0]); + + // unix domain socket for ICP + fd passing + proc.sock = new net.Socket(fds[1], 'unix');; + return proc; +}; + +/** + * Master replacement connected. + * + * @param {String} sig + * @api private + */ + +Master.prototype.connectMaster = function(sig){ + var self = this; + this.on('listening', function(){ + process.kill(self.ppid, sig); + }); +}; + +/** + * Original master has died aka 'retired', + * we now fire the 'restart' event. + * + * @param {Object} data + * @api private + */ + +Master.prototype.masterKilled = function(data){ + this.emit('restart', data); +}; + +/** + * Accept fd from parent master, then `start()`. + * + * @api private + */ + +Master.prototype.acceptFd = function(){ + var self = this + , stdin = new net.Socket(0, 'unix'); + + // set fd and start master + stdin.setEncoding('ascii'); + stdin.on('fd', function(fd){ + self.fd = fd; + self.start(); + }); + + // frame commands from the parent master + stdin.on('data', this.frame.bind(this)); + stdin.resume(); +}; + +/** + * Immediate shutdown, used by `Master#close()` + * and `Master#destroy()` when all workers have shutdown. + * + * @api private + */ + +Master.prototype._destroy = function(){ + var self = this; + self._server.on('close', function(){ + self.emit('close'); + process.nextTick(process.exit.bind(process)); + }).close(); +}; + +/** + * Worker is connected, on boot + * wait for all workers to connect, + * then emit the 'listening' event. + * + * @param {Worker} worker + * @api private + */ + +Master.prototype.connect = function(worker){ + var self = this; + this.emit('worker connected', worker); + if (this.starting && !--this.pendingWorkers) { + this.startListening(!this.isChild); + } +}; + +/** + * Start listening, when `bind` is `true` the socket + * will be bound, and will start listening for connections. + * + * @param {Boolean} bind + * @api private + */ + +Master.prototype.startListening = function(bind){ + var self = this; + + // remove unix domain socket + if ('string' == typeof this.port && bind) { + fs.unlink(this.port, function(err){ + if (ENOENT != err.errno) throw err; + listen(); + }); + } else { + listen(); + } + + // bind / listen + function listen() { + if (bind) { + binding.bind(self.fd, self.port, self.host); + binding.listen(self.fd, self.options.backlog); + } + self.callback && self.callback(); + self.emit('listening'); + } +}; + +/** + * The given `worker` has been killed. + * Emit the "worker killed" event, remove + * the worker, and re-spawn depending on + * the master state. + * + * @api private + */ + +Master.prototype.workerKilled = function(worker){ + this.emit('worker killed', worker); + + // always remove worker + this.removeWorker(worker.id); + + // state specifics + switch (this.state) { + case 'hard shutdown': + break; + case 'graceful shutdown': + --this.pendingDeaths || this._destroy(); + break; + default: + this.spawnWorker(worker.id); + } +}; + +/** + * `worker` received exception `err`. + * + * @api private + */ + +Master.prototype.workerException = function(worker, err){ + this.emit('worker exception', worker, err); +}; + +/** + * Received worker timeout. + * + * @api private + */ + +Master.prototype.workerTimeout = function(worker, timeout){ + this.emit('worker timeout', worker, timeout); +}; + +/** + * Worker waiting on `connections` to close. + * + * @api private + */ + +Master.prototype.workerWaiting = function(worker, connections){ + this.emit('worker waiting', worker, connections); +}; + +/** + * Remove worker `id`. + * + * @param {Number} id + * @api public + */ + +Master.prototype.removeWorker = function(id){ + delete this.children[id]; +}; + +/** + * Send `sig` to all worker processes, defaults to __SIGTERM__. + * + * @param {String} sig + * @api public + */ + +Master.prototype.kill = function(sig){ + var self = this; + this.emit('kill', sig); + this.children.forEach(function(worker){ + worker.proc.kill(sig); + }); +}; \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/mixins/receiver.js b/node/server/node_modules/cluster/lib/mixins/receiver.js new file mode 100644 index 0000000..186c4b8 --- /dev/null +++ b/node/server/node_modules/cluster/lib/mixins/receiver.js @@ -0,0 +1,53 @@ + +/*! + * Cluster - receiver mixin + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +module.exports = function(obj){ + + /** + * Frame incoming command, buffering the given `chunk` + * until a frame is complete. + * + * @param {Socket} sock + * @param {String} chunk + * @api private + */ + + obj.frame = function(chunk){ + this._buf = this._buf || ''; + this.braces = this.braces || 0; + + for (var i = 0, len = chunk.length; i < len; ++i) { + if ('{' == chunk[i]) ++this.braces; + if ('}' == chunk[i]) --this.braces; + this._buf += chunk[i]; + if (0 == this.braces) { + var worker + , obj = JSON.parse(this._buf); + this._buf = ''; + if ('number' == typeof obj.id) worker = this.children[obj.id]; + this.invoke(obj.method, obj.args, worker); + } + } + }; + + /** + * Invoke `method` with the given `args`. + * + * @param {String} method + * @param {Mixed} args + * @param {Worker} worker + * @api private + */ + + obj.invoke = function(method, args, worker){ + if (!method) return; + if (!Array.isArray(args)) args = [args]; + if (worker) args.unshift(worker); + if (!this[method]) throw new Error('method ' + method + '() does not exist'); + this[method].apply(this, args); + }; +}; \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/plugins/cli.js b/node/server/node_modules/cluster/lib/plugins/cli.js new file mode 100644 index 0000000..c1c3e03 --- /dev/null +++ b/node/server/node_modules/cluster/lib/plugins/cli.js @@ -0,0 +1,214 @@ + +/*! + * Cluster - cli + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , Log = require('log') + , ESRCH = process.version >= 'v0.4.0' + ? require('constants').ESRCH + : process.ESRCH + +/** + * Commands. + */ + +var commands = []; + +/** + * Adds a command-line interface to your cluster. + * + * This plugin requires that you use `pidfiles()` + * above `cli()`, so that the pidfile directory + * is exposed. + * + * Examples: + * + * cluster(server) + * .use(cluster.pidfiles()) + * .use(cluster.cli()) + * .listen(3000); + * + * Once set up our server script serves as both + * the master, and the CLI. For example we may + * still launch the server(s) as shown below. + * + * $ nohup node server.js & + * + * However now we may also utilize commands + * provided by this plugin. + * + * $ node server.js status + * + * master 3281 dead + * worker 0 3282 dead + * + * For more command information use `--help`. + * + * $ node server.js --help + * + * @return {Function} + * @api public + */ + +exports = module.exports = function(){ + return function(master){ + requirePIDs(master); + + var args = process.argv.slice(2) + , len = commands.length + , command + , arg; + + // parse arguments + while (args.length) { + arg = args.shift(); + for (var i = 0; i < len; ++i) { + command = commands[i]; + if (~command.flags.indexOf(arg)) { + master.preventDefault = true; + command.callback(master); + } + } + } + } +}; + +/** + * Define command `name` with the given callback `fn(master)` + * and a short `desc`. + * + * @param {String} name + * @param {Function} fn + * @param {String} desc + * @return {Object} exports for chaining + * @api public + */ + +var define = exports.define = function(name, fn, desc){ + commands.push({ + flags: name.split(/ *, */) + , desc: desc + , callback: fn + }); + return exports; +}; + +/** + * Report master / worker status based on + * the PID files saved by the pidfiles() + * plugin. + */ + +define('-s, --status, status', function(master){ + var dir = master.pidfiles + , files = fs.readdirSync(dir); + + // null signal failed previous + // to this release + if (process.version < 'v0.4.1') { + console.log('status will not work with node < 0.4.1'); + console.log('due to SIGTERM globbering the null signal'); + process.exit(1); + } + + console.log(); + + // only pids + files.filter(function(file){ + return file.match(/\.pid$/); + // check status + }).forEach(function(file){ + var name = file.replace('.pid', '') + , pid = master.pidof(name) + , name = name.replace('.', ' ') + , color + , status; + + try { + process.kill(pid, 0); + status = 'alive'; + color = '36'; + } catch (err) { + if (ESRCH == err.errno) { + color = '31'; + status = 'dead'; + } else { + throw err; + } + } + + console.log(' %s\033[90m %d\033[0m \033[' + color + 'm%s\033[0m' + , name + , pid + , status); + }); + + console.log(); +}, 'Output cluster status'); + +/** + * Restart workers. + */ + +define('-r, --restart, restart', function(master){ + process.kill(master.pidof('master'), 'SIGUSR2'); +}, 'Restart master by sending the SIGUSR2 signal'); + +/** + * Graceful shutdown. + */ + +define('-g, --shutdown, shutdown', function(master){ + process.kill(master.pidof('master'), 'SIGQUIT'); +}, 'Graceful shutdown by sending the SIGQUIT signal'); + +/** + * Hard shutdown. + */ + +define('-S, --stop, stop', function(master){ + process.kill(master.pidof('master'), 'SIGTERM'); +}, 'Hard shutdown by sending the SIGTERM signal'); + +/** + * Display help information. + */ + +define('-h, --help, help', function(master){ + console.log('\n Usage: node \n'); + commands.forEach(function(command){ + console.log(' ' + + command.flags.join(', ') + + '\n ' + + '\033[90m' + command.desc + '\033[0m' + + '\n'); + }); + console.log(); +}, 'Show help information'); + +/** + * Output cluster version. + */ + +define('-v, --version', function(master){ + console.log(require('../cluster').version); +}, 'Output cluster version'); + +/** + * Require `pidfiles()` plugin. + * + * @param {Master} master + * @api private + */ + +function requirePIDs(master) { + if (master.pidfiles) return; + throw new Error('cli() plugin requires pidfiles(), please add pidfiles() before cli()'); +} \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/plugins/debug.js b/node/server/node_modules/cluster/lib/plugins/debug.js new file mode 100644 index 0000000..a7bbc98 --- /dev/null +++ b/node/server/node_modules/cluster/lib/plugins/debug.js @@ -0,0 +1,108 @@ + +/*! + * Cluster - debug + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Logger. + */ + +var log = { + debug: function(str){ + console.error(' \033[90mdebug - %s\033[0m', str); + }, + info: function(str){ + console.error(' info \033[90m- %s\033[0m', str); + }, + warning: function(str){ + console.error(' \033[33mwarning\033[0m \033[90m- %s\033[0m', str); + }, + error: function(str){ + console.error(' \033[31merror\033[0m \033[90m- %s\033[0m', str); + } +}; + +/** + * Enable verbose debugging output. + * + * @return {Function} + * @api public + */ + +module.exports = function(){ + return function(master){ + + // start + master.on('start', function(){ + log.info('master started'); + }); + + // closing + master.on('closing', function(){ + log.info('shutting down'); + }); + + // close + master.on('close', function(){ + log.info('shutdown complete'); + }); + + // killing workers + master.on('kill', function(sig){ + log.warning('kill(' + (sig || 'SIGTERM') + ')'); + }); + + // worker died + master.on('worker killed', function(worker){ + if ('restarting' == master.state) return; + log.warning('worker ' + worker.id + ' died'); + }); + + // worker exception + master.on('worker exception', function(worker, err){ + log.error('worker ' + worker.id + ' uncaught exception ' + err.message); + }); + + // worker is waiting on connections to be closed + master.on('worker waiting', function(worker, connections){ + log.warning('worker ' + worker.id + ' waiting on ' + connections + ' connections'); + }); + + // worker has timed out + master.on('worker timeout', function(worker, timeout){ + log.warning('worker ' + worker.id + ' timed out after ' + timeout + 'ms'); + }); + + // connection + master.on('worker connected', function(worker){ + log.info('worker ' + worker.id + ' connected'); + }); + + // worker + master.on('worker', function(worker){ + log.info('worker ' + worker.id + ' spawned'); + }); + + // listening + master.on('listening', function(){ + log.info('listening for connections'); + }); + + // restart requested + master.on('restarting', function(){ + log.info('restart requested'); + }); + + // restart complete + master.on('restart', function(){ + log.info('restart complete'); + }); + + // exit + process.on('exit', function(){ + log.debug('exit'); + }); + } +}; \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/plugins/logger.js b/node/server/node_modules/cluster/lib/plugins/logger.js new file mode 100644 index 0000000..60d07df --- /dev/null +++ b/node/server/node_modules/cluster/lib/plugins/logger.js @@ -0,0 +1,146 @@ + +/*! + * Cluster - logger + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , Log = require('log'); + +/** + * Enable stdout / stderr logs for both the master + * process, as well as workers. + * + * These output to the given `dir`, or `./logs` + * relative to the server's file. + * + * Examples: + * + * // log to ./logs + * engine(server) + * .use(engine.logger()) + * .listen(3000); + * + * // log to ./app/logs + * engine(server) + * .use(engine.logger('./app/logs')) + * .listen(3000); + * + * // log to /var/log/node + * engine(server) + * .use(engine.logger('/var/log/node')) + * .listen(3000); + * + * @param {String} dir + * @param {Number} level + * @return {Function} + * @api public + */ + +module.exports = function(dir, level){ + return function(master){ + dir = master.resolve(dir || 'logs'); + + // master log + var stream = fs.createWriteStream(dir + '/master.log', { flags: 'a' }); + var log = master.log = new Log(level || Log.INFO, stream); + + // master events + master.on('start', function(){ + log.info('master started'); + }); + + // master is shutting down + master.on('closing', function(){ + log.warning('shutting down master'); + }); + + // master has closed and performed cleanup + master.on('close', function(){ + log.info('shutdown complete'); + }); + + // sending signal to all workers + master.on('kill', function(sig){ + log.warning('sent kill(' + sig + ') to all workers'); + }); + + // worker was killed + master.on('worker killed', function(worker){ + if ('restarting' == master.state) return; + log.error('worker ' + worker.id + ' died'); + }); + + // worker exception + master.on('worker exception', function(worker, err){ + log.error('worker ' + worker.id + ' uncaught exception ' + err.message); + }); + + // worker is waiting on connections to be closed + master.on('worker waiting', function(worker, connections){ + log.info('worker ' + worker.id + ' waiting on ' + connections + ' connections'); + }); + + // worker has timed out + master.on('worker timeout', function(worker, timeout){ + log.warning('worker ' + worker.id + ' timed out after ' + timeout + 'ms'); + }); + + // worker connected to master + master.on('worker connected', function(worker){ + log.debug('worker ' + worker.id + ' connected'); + }); + + // restart requested + master.on('restarting', function(){ + log.info('restart requested'); + }); + + // restart complete + master.on('restart', function(){ + log.info('restart complete'); + }); + + // repl socket connection established + master.on('repl socket', function(sock){ + var from = sock.remoteAddress + ? 'from ' + sock.remoteAddress + : ''; + sock.on('connect', function(){ + log.info('repl connection ' + from); + }); + sock.on('close', function(){ + log.info('repl disconnect ' + from); + }); + }); + + // override fds + master.customFds = [-1, -1]; + + // children + master.on('worker', function(worker){ + var proc = worker.proc; + + log.info('spawned worker ' + worker.id); + + // worker log streams + var access = fs.createWriteStream(dir + '/workers.access.log', { flags: 'a' }) + , error = fs.createWriteStream(dir + '/workers.error.log', { flags: 'a' }); + + // redirect stdout / stderr + // COMPAT: + if (proc.stdout.pipe) { + proc.stdout.pipe(access); + proc.stderr.pipe(error); + } else { + proc.stdout.on('data', function(chunk){ access.write(chunk); }); + proc.stderr.on('data', function(chunk){ error.write(chunk); }); + } + }); + } +}; \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/plugins/pidfiles.js b/node/server/node_modules/cluster/lib/plugins/pidfiles.js new file mode 100644 index 0000000..8578bcc --- /dev/null +++ b/node/server/node_modules/cluster/lib/plugins/pidfiles.js @@ -0,0 +1,64 @@ + +/*! + * Cluster - pidfiles + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs'); + +/** + * Save pidfiles to the given `dir` or `./pids`. + * + * Examples: + * + * // save to ./pids + * cluster(server) + * .use(cluster.pidfiles()) + * .listen(3000); + * + * // save to /tmp + * cluster(server) + * .use(cluster.pidfiles('/tmp')) + * .listen(3000); + * + * // save to /var/run/node + * cluster(server) + * .use(cluster.logger('/var/run/node')) + * .listen(3000); + * + * @param {String} dir + * @return {Function} + * @api public + */ + +module.exports = function(dir){ + return function(master){ + dir = master.pidfiles = master.resolve(dir || 'pids'); + function fn(err){ if (err) throw err; } + + // augment master + // augment master + master.pidof = function(name){ + var dir = master.pidfiles + , path = dir + '/' + name + '.pid' + , pid = fs.readFileSync(path, 'ascii'); + return parseInt(pid, 10); + }; + + // save worker pids + master.on('worker', function(worker){ + var path = dir + '/worker.' + worker.id + '.pid'; + fs.writeFile(path, worker.proc.pid.toString(), 'ascii', fn); + }); + + master.on('listening', function(){ + // save master pid + fs.writeFile(dir + '/master.pid', process.pid.toString(), 'ascii', fn); + }); + } +}; \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/plugins/reload.js b/node/server/node_modules/cluster/lib/plugins/reload.js new file mode 100644 index 0000000..0a50bae --- /dev/null +++ b/node/server/node_modules/cluster/lib/plugins/reload.js @@ -0,0 +1,120 @@ + +/*! + * Cluster - reload + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , basename = require('path').basename; + +/** + * Restart the server the given js `files` have changed. + * `files` may be several directories, filenames, etc, + * defaulting to the server's root directory. + * + * Options: + * + * - `signal` Signal to send, defaults to __SIGTERM__ + * - `interval` Watcher interval, defaulting to `100` + * + * Examples: + * + * cluster(server) + * .use(cluster.reload()) + * .listen(3000); + * + * cluster(server) + * .use(cluster.reload('lib')) + * .listen(3000); + * + * cluster(server) + * .use(cluster.reload(['lib', 'tests', 'index.js'])) + * .listen(3000); + * + * cluster(server) + * .use(cluster.reload('lib', { signal: 'SIGQUIT', interval: 60000 })) + * .listen(3000); + * + * Ignore Directories: + * + * By default `reload()` will ignore the following directories: + * + * - node_modules + * - support + * - examples + * - test + * - bin + * + * Alter with `reload.ignoreDirectories` + * + * cluster.reload.ignoreDirectories.push('src'); + * + * @param {String|Array} files + * @param {Options} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(files, options){ + options = options || {}; + + // defaults + var sig = options.sig || 'SIGTERM' + , interval = options.interval || 100; + + return function(master){ + var restarting; + + if (!files) files = master.dir; + if (!Array.isArray(files)) files = [files]; + files.forEach(traverse); + + // traverse file if it is a directory + // otherwise setup the watcher + function traverse(file) { + file = master.resolve(file); + fs.stat(file, function(err, stat){ + if (!err) { + if (stat.isDirectory()) { + if (~exports.ignoreDirectories.indexOf(basename(file))) return; + fs.readdir(file, function(err, files){ + files.map(function(f){ + return file + '/' + f; + }).forEach(traverse); + }); + } else { + watch(file); + } + } + }); + } + + // watch file for changes + function watch(file) { + // js-only for now + if (!~file.indexOf('.js')) return; + fs.watchFile(file, { interval: interval }, function(curr, prev){ + if (restarting) return; + if (curr.mtime > prev.mtime) { + console.log(' \033[36mchanged\033[0m \033[90m- %s\033[0m', file); + master.restart(sig); + } + }); + } + + master.on('restarting', function(){ + restarting = true; + }); + } +}; + +/** + * Directories to ignore. + */ + +exports.ignoreDirectories = ['node_modules', 'support', 'examples', 'test', 'bin']; \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/plugins/repl.js b/node/server/node_modules/cluster/lib/plugins/repl.js new file mode 100644 index 0000000..f7d7880 --- /dev/null +++ b/node/server/node_modules/cluster/lib/plugins/repl.js @@ -0,0 +1,189 @@ + +/*! + * Cluster - repl + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var net = require('net') + , repl = require('repl'); + +/** + * Enable REPL with all arguments passed to `net.Server#listen()`. + * + * Examples: + * + * cluster(server) + * .use(cluster.stats()) + * .use(cluster.repl('/var/run/cluster')) + * .listen(); + * + * In the terminal: + * + * $ sudo telnet /var/run/cluster + * + * @return {Function} + * @api public + */ + +exports = module.exports = function(){ + var args = arguments; + if (!args.length) throw new Error('repl() plugin requires port/host or path'); + return function(master){ + var server + , sockets = []; + + + // start repl + function start(){ + // TCP or unix-domain socket repl + server = net.createServer(function(sock){ + sockets.push(sock); + var ctx = repl.start('cluster> ', sock).context; + master.emit('repl socket', sock); + + // augment socket to provide some formatting methods + sock.title = function(str){ this.write('\n \033[36m' + str + '\033[0m\n'); } + sock.row = function(key, val){ this.write(' \033[90m' + key + ':\033[0m ' + val + '\n'); } + + // merge commands into context + // executing in context of master + Object.keys(exports).forEach(function(cmd){ + ctx[cmd] = function(){ + var args = Array.prototype.slice.call(arguments); + args.unshift(master, sock); + return exports[cmd].apply(master, args); + }; + }); + }); + + // Apply all arguments given + server.listen.apply(server, args); + } + + // initial master starts immediately + // replacements starts when the previous + // has closed + master.on(master.isChild + ? 'restart' + : 'start', start); + + // restart notification + master.on('restarting', function(){ + sockets.forEach(function(sock){ + if (sock.fd) { + sock.write('\n\033[33mrestarting\033[0m - closing connection soon\n'); + } + }); + }); + + // close + master.on('close', function(){ + sockets.forEach(function(sock){ + sock.fd && sock.end(); + }); + if (server.fd) server.close(); + }); + } +}; + +/** + * Define function `name`, with the given callback + * `fn(master, sock, ...)` and `description`. + * + * @param {String} name + * @param {Function} fn + * @param {String} desc + * @return {Object} exports for chaining + * @api public + */ + +var define = exports.define = function(name, fn, desc){ + (exports[name] = fn).description = desc; + return exports; +}; + +/** + * Display commmand help. + */ + +define('help', function(master, sock){ + sock.title('Commands'); + Object.keys(exports).forEach(function(cmd){ + if ('define' == cmd) return; + + var fn = exports[cmd] + , params = fn.toString().match(/^function +\((.*?)\)/)[1] + , params = params.split(/ *, */).slice(2); + + sock.row( + cmd + '(' + params.join(', ') + ')' + , fn.description); + }); + sock.write('\n'); +}, 'Display help information'); + +/** + * Spawn `n` additional workers. + */ + +define('spawn', function(master, sock, n){ + n = n || 1; + sock.write('spawning ' + n + ' worker' + (n > 1 ? 's' : '') + '\n'); + master.spawn(n); +}, 'Spawn one or more additional workers'); + +/** + * Output process ids. + */ + +define('pids', function(master, sock){ + sock.title('pids'); + sock.row('master', process.pid); + master.children.forEach(function(worker){ + sock.row('worker #' + worker.id, worker.proc.pid); + }); + sock.write('\n'); +}, 'Output process ids'); + +/** + * Kill the given worker by `id` and `signal`. + */ + +define('kill', function(master, sock, id, signal){ + var worker = master.children[id]; + if (worker) { + worker.proc.kill(signal); + sock.write('sent \033[36m' + (signal || 'SIGTERM') + '\033[0m to worker #' + id + '\n'); + } else { + sock.write('invalid worker id\n'); + } +}, 'Send signal or SIGTERM to the given worker'); + +/** + * Gracefully shutdown. + */ + +define('shutdown', function(master, sock){ + master.close(); +}, 'Gracefully shutdown server'); + +/** + * Hard shutdown. + */ + +define('stop', function(master, sock){ + master.destroy(); +}, 'Hard shutdown'); + +/** + * Gracefully restart all workers. + */ + +define('restart', function(master, sock){ + master.restart(); +}, 'Gracefully restart all workers'); \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/plugins/stats.js b/node/server/node_modules/cluster/lib/plugins/stats.js new file mode 100644 index 0000000..2d0d5ef --- /dev/null +++ b/node/server/node_modules/cluster/lib/plugins/stats.js @@ -0,0 +1,113 @@ + +/*! + * Cluster - stats + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , Log = require('log') + , repl = require('./repl') + , utils = require('../utils') + , os; + +// COMPAT: +try { + os = require('os'); +} catch (err) { + // ignore +} + +/** + * Enable stat tracking. + * + * @return {Function} + * @api public + */ + +module.exports = function(){ + return function(master){ + master.stats = { + start: new Date + , restarts: 0 + , workersSpawned: 0 + , workersKilled: 0 + }; + + // 0.4.x + if (os) { + master.stats.totalmem = os.totalmem(); + master.stats.freemem = os.freemem(); + } + + // total workers spawned + master.on('worker', function(worker){ + ++master.stats.workersSpawned; + worker.stats = { start: new Date }; + }); + + // total worker deaths + master.on('worker killed', function(worker){ + ++master.stats.workersKilled; + }); + + // restarting + master.on('restarting', function(data){ + ++master.stats.restarts; + data.stats = master.stats; + }); + + // restart + master.on('restart', function(data){ + master.stats = data.stats; + master.stats.start = new Date(master.stats.start); + }); + } +}; + +/** + * REPL statistics command. + */ + +repl.define('stats', function(master, sock){ + var active = master.children.length + , total = master.stats.workersSpawned + , deaths = master.stats.workersKilled + , restarts = master.stats.restarts; + + // master stats + sock.title('Master'); + if (os) sock.row('os', os.type() + ' ' + os.release()); + sock.row('state', master.state); + sock.row('started', master.stats.start.toUTCString()); + sock.row('uptime', utils.formatDateRange(new Date, master.stats.start)); + sock.row('restarts', restarts); + sock.row('workers', active); + sock.row('deaths', deaths); + + // resources + if (os) { + sock.title('Resources'); + sock.row('load average', os.loadavg().map(function(n){ return n.toFixed(2); }).join(' ')); + sock.row('cores utilized', active + ' / ' + os.cpus().length); + var free = utils.formatBytes(master.stats.freemem); + var total = utils.formatBytes(master.stats.totalmem); + sock.row('memory at boot (free / total)', free + ' / ' + total); + var free = utils.formatBytes(os.freemem()); + var total = utils.formatBytes(os.totalmem()); + sock.row('memory now (free / total)', free + ' / ' + total); + } + + // worker stats + sock.title('Workers'); + master.children.forEach(function(worker){ + sock.row( + 'uptime #' + worker.id + , utils.formatDateRange(new Date, worker.stats.start)); + }); + sock.write('\n'); +}, 'Display server statistics'); diff --git a/node/server/node_modules/cluster/lib/utils.js b/node/server/node_modules/cluster/lib/utils.js new file mode 100644 index 0000000..36fb906 --- /dev/null +++ b/node/server/node_modules/cluster/lib/utils.js @@ -0,0 +1,51 @@ + +/*! + * Cluster - utils + * Copyright (c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Format byte-size. + * + * @param {Number} bytes + * @return {String} + * @api private + */ + +exports.formatBytes = function(bytes) { + var kb = 1024 + , mb = 1024 * kb + , gb = 1024 * mb; + if (bytes < kb) return bytes + 'b'; + if (bytes < mb) return (bytes / kb).toFixed(2) + 'kb'; + if (bytes < gb) return (bytes / mb).toFixed(2) + 'mb'; + return (bytes / gb).toFixed(2) + 'gb'; +}; + +/** + * Format date difference between `a` and `b`. + * + * @param {Date} a + * @param {Date} b + * @return {String} + * @api private + */ + +exports.formatDateRange = function(a, b) { + var diff = a > b ? a - b : b - a + , second = 1000 + , minute = second * 60 + , hour = minute * 60 + , day = hour * 24; + + function unit(name, n) { + return n + ' ' + name + (1 == n ? '' : 's'); + } + + if (diff < second) return unit('millisecond', diff); + if (diff < minute) return unit('second', (diff / second).toFixed(0)); + if (diff < hour) return unit('minute', (diff / minute).toFixed(0)); + if (diff < day) return unit('hour', (diff / hour).toFixed(0)); + return unit('day', (diff / day).toFixed(1)); +}; \ No newline at end of file diff --git a/node/server/node_modules/cluster/lib/worker.js b/node/server/node_modules/cluster/lib/worker.js new file mode 100644 index 0000000..6cbf794 --- /dev/null +++ b/node/server/node_modules/cluster/lib/worker.js @@ -0,0 +1,199 @@ + +/*! + * Cluster - Worker + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , spawn = require('child_process').spawn + , binding = process.binding('net') + , net = require('net'); + +// COMPAT: + +net.Socket = net.Stream; + +/** + * Node binary. + */ + +var node = process.execPath + +/** + * Initialize a new `Worker` with the given `master`. + * + * Signals: + * + * - `SIGINT` immediately exit + * - `SIGTERM` immediately exit + * - `SIGQUIT` graceful exit + * + * @param {Master} master + * @api private + */ + +var Worker = module.exports = function Worker(master) { + this.master = master; + this.server = master.server; + if ('string' == typeof this.server) { + this.server = require(master.resolve(this.server)); + } +}; + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Worker.prototype.__proto__ = EventEmitter.prototype; + +/** + * Worker is a receiver. + */ + +require('./mixins/receiver')(Worker.prototype); + +/** + * Start worker. + * + * @api private + */ + +Worker.prototype.start = function(){ + var self = this + , call = this.master.call; + + // proxy to provide worker id + this.master.call = function(){ + var args = Array.prototype.slice.call(arguments); + args.unshift(self.id); + return call.apply(this, args); + }; + + // stdin + this.stdin = new net.Socket(0, 'unix'); + this.stdin.setEncoding('ascii'); + this.stdin.on('fd', this.server.listenFD.bind(this.server)); + this.stdin.on('data', this.frame.bind(this)); + this.stdin.resume(); + + // signal handlers + process.on('SIGINT', this.destroy.bind(this)); + process.on('SIGTERM', this.destroy.bind(this)); + process.on('SIGQUIT', this.close.bind(this)); + process.on('uncaughtException', function(err){ + // stderr for logs + console.error(err.stack || err.message); + + // report exception + self.master.call('workerException', err); + + // exit + process.nextTick(function(){ + self.destroy(); + }); + }); +}; + +/** + * Received connect event, set the worker `id` + * and `timeout`. + * + * @param {String} id + * @param {Number} timeout + * @api private + */ + +Worker.prototype.connect = function(id, timeout){ + // worker id + this.id = id; + + // timeout + this.timeout = timeout; + + // notify master of connection + this.master.call('connect'); +}; + +/** + * Immediate shutdown. + * + * @api private + */ + +Worker.prototype.destroy = function(){ + process.exit(); +}; + +/** + * Perform graceful shutdown. + * + * @api private + */ + +Worker.prototype.close = function(){ + var self = this + , server = this.server; + + if (server.connections) { + // stop accepting + server.watcher.stop(); + + // check pending connections + setInterval(function(){ + self.master.call('workerWaiting', server.connections); + server.connections || self.destroy(); + }, 2000); + + // timeout + if (this.timeout) { + setTimeout(function(){ + self.master.call('workerTimeout', self.timeout); + self.destroy(); + }, this.timeout); + } + } else { + this.destroy(); + } +}; + +/** + * Spawn the worker. + * + * @return {Worker} for chaining + * @api private + */ + +Worker.prototype.spawn = function(){ + var fds = binding.socketpair() + , customFds = [fds[0]].concat(this.master.customFds); + + // spawn worker process + this.proc = spawn(node, this.master.cmd, { customFds: customFds }); + this.proc.stdin = new net.Socket(fds[0]); + + // unix domain socket for ICP + fd passing + this.sock = new net.Socket(fds[1], 'unix'); + return this; +}; + +/** + * Invoke worker's `method` (called from Master). + * + * @param {String} method + * @param {...} args + * @api private + */ + +Worker.prototype.call = function(method){ + var args = Array.prototype.slice.call(arguments) + , method = args.shift(); + this.sock.write(JSON.stringify({ + method: method + , args: args + }), 'ascii'); +}; diff --git a/node/server/node_modules/cluster/node_modules/log/._History.md b/node/server/node_modules/cluster/node_modules/log/._History.md new file mode 100644 index 0000000000000000000000000000000000000000..0ea62ff512d60041a8c635776e1e21e573704a96 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OWYd=saNXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>91|8Z3~LnU@OW Jnpkty0ssJx8gKvr literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/._Makefile b/node/server/node_modules/cluster/node_modules/log/._Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6531ecfb5459de9b00889ccb646857fab89b5f40 GIT binary patch literal 187 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aVHRi_`89a2;dkJ62$OW>nKnuNXHBy zRz}mth!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>9ER8Z3~LnU@OU J8d`JJ0stP$8lwOJ literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/._Readme.md b/node/server/node_modules/cluster/node_modules/log/._Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..4702612a583e97e66deeae3572724832c57b64c8 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OW>m*PqNXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>9aB%+JZpO9k=F Hths6d1PmH< literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/._index.js b/node/server/node_modules/cluster/node_modules/log/._index.js new file mode 100644 index 0000000000000000000000000000000000000000..d47e607bd81f812c520691ecb8c70d8b54f095ca GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OW>kv>WNXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>9c98Z3~LnU@OW J8d`JJ0ssMK8f^do literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/._package.json b/node/server/node_modules/cluster/node_modules/log/._package.json new file mode 100644 index 0000000000000000000000000000000000000000..aeed2f84d643bf4a1ae762b388c8f201d80a7bb1 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OW>o`y;NXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>9EH8Z3~LnU@OW J8e4PK0ssRw8gl>u literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/History.md b/node/server/node_modules/cluster/node_modules/log/History.md new file mode 100644 index 0000000..175a551 --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/History.md @@ -0,0 +1,12 @@ + +1.1.1 / 2010-09-26 +================== + + * Fixed `Log()` initialization without giving a stream [bentruyman] + +1.1.0 / 2010-09-26 +================== + + * Added streaming reader capabilities (_pass a readable stream_) + * Added `Log()` log level as string support (_alternative of constants_) + * Added _./index.js_ so people can clone diff --git a/node/server/node_modules/cluster/node_modules/log/Makefile b/node/server/node_modules/cluster/node_modules/log/Makefile new file mode 100644 index 0000000..6c12be5 --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/Makefile @@ -0,0 +1,14 @@ + +docs: index.html + +index.html: lib/log.js + dox \ + --title "Log.js" \ + --desc "Tiny logger for [NodeJS](http://nodejs.org)." \ + --ribbon "http://github.com/visionmedia/log.js" \ + lib/log.js > $@ + +docclean: + rm -f index.html + +.PHONY: docs docclean \ No newline at end of file diff --git a/node/server/node_modules/cluster/node_modules/log/Readme.md b/node/server/node_modules/cluster/node_modules/log/Readme.md new file mode 100644 index 0000000..62ad3da --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/Readme.md @@ -0,0 +1,95 @@ + +# Log.js + + Light-weight logging for [NodeJS](http://nodejs.org), including a + streaming log reader. + +## Installation + + $ npm install log + +## Example + +Log level defaults to __DEBUG__ however here we specify __INFO__, and the stream defaults to _stdout_: + + var Log = require('log') + , log = new Log(Log.INFO); + + log.debug('preparing email'); + log.info('sending email'); + log.error('failed to send email'); + +Specifying a specific stream: + + var fs = require('fs') + , Log = require('log') + , log = new Log(Log.DEBUG, fs.createWriteStream('my.log')); + +Instead of the log level constants, you may also supply a string: + + var Log = require('log') + , log = new Log('warning'); + +## Reader + + To stream a log, simply pass a readable stream instead of a writable: + + var Log = require('log') + , fs = require('fs') + , stream = fs.createReadStream(__dirname + '/file.log') + , log = new Log('debug', stream); + + log.on('line', function(line){ + console.log(line); + }); + +Example stdout: + + { date: Sun, 26 Sep 2010 01:26:14 GMT + , level: 1 + , levelString: 'ALERT' + , msg: 'a alert message' + } + { date: Sun, 26 Sep 2010 01:26:14 GMT + , level: 0 + , levelString: 'EMERGENCY' + , msg: 'a emergency message' + } + +## Log Levels + + Mirror that of syslog: + + - 0 __EMERGENCY__ system is unusable + - 1 __ALERT__ action must be taken immediately + - 2 __CRITICAL__ the system is in critical condition + - 3 __ERROR__ error condition + - 4 __WARNING__ warning condition + - 5 __NOTICE__ a normal but significant condition + - 6 __INFO__ a purely informational message + - 7 __DEBUG__ messages to debug an application + +## License + +(The MIT License) + +Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node/server/node_modules/cluster/node_modules/log/examples/._file.js b/node/server/node_modules/cluster/node_modules/log/examples/._file.js new file mode 100644 index 0000000000000000000000000000000000000000..e207f1b8cc03fb5d22f4c4431954c06b0fe8fc07 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OW>kLpSNXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>9oR8Z3~LnU@OU IT65I`00jaXdH?_b literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/examples/._file.log b/node/server/node_modules/cluster/node_modules/log/examples/._file.log new file mode 100644 index 0000000000000000000000000000000000000000..f9b26ac519daa7fc993eabeaa597a58e8793cc18 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OW>l{!iNXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>8!~8Z3~LnU@OW Jnp$(!0ssZs8hii% literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/examples/._reader.js b/node/server/node_modules/cluster/node_modules/log/examples/._reader.js new file mode 100644 index 0000000000000000000000000000000000000000..2ba174eb68fdf46702e8bcf14450086f9e3fccbe GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OW>jF?INXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>8;`%+JZpO9k=_ Hf!taE26!5D literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/examples/._stdout.js b/node/server/node_modules/cluster/node_modules/log/examples/._stdout.js new file mode 100644 index 0000000000000000000000000000000000000000..6d246eb32ac53ee5a1cfe37446f656f34842a954 GIT binary patch literal 186 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aR(5C_`86Z2;dkJ62$OW>k?2YNXHBy zRzlOph!7V|&d=4$O-wGz&rK{zE!Im;EJ`h5sOADWBso8)G&fJdR>8=~8Z3~LnU@OW Jnp$(!0ssc*8hQW# literal 0 HcmV?d00001 diff --git a/node/server/node_modules/cluster/node_modules/log/examples/file.js b/node/server/node_modules/cluster/node_modules/log/examples/file.js new file mode 100644 index 0000000..03d7d88 --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/examples/file.js @@ -0,0 +1,18 @@ + +/** + * Module dependencies. + */ + +var Log = require('../lib/log') + , fs = require('fs') + , stream = fs.createWriteStream(__dirname + '/file.log', { flags: 'a' }) + , log = new Log('debug', stream); + +log.debug('a debug message'); +log.info('a info message'); +log.notice('a notice message'); +log.warning('a warning message'); +log.error('a error message'); +log.critical('a critical message'); +log.alert('a alert message'); +log.emergency('a emergency message'); \ No newline at end of file diff --git a/node/server/node_modules/cluster/node_modules/log/examples/file.log b/node/server/node_modules/cluster/node_modules/log/examples/file.log new file mode 100644 index 0000000..e555868 --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/examples/file.log @@ -0,0 +1,8 @@ +[Sun, 26 Sep 2010 01:26:14 GMT] DEBUG a debug message +[Sun, 26 Sep 2010 01:26:14 GMT] INFO a info message +[Sun, 26 Sep 2010 01:26:14 GMT] NOTICE a notice message +[Sun, 26 Sep 2010 01:26:14 GMT] WARNING a warning message +[Sun, 26 Sep 2010 01:26:14 GMT] ERROR a error message +[Sun, 26 Sep 2010 01:26:14 GMT] CRITICAL a critical message +[Sun, 26 Sep 2010 01:26:14 GMT] ALERT a alert message +[Sun, 26 Sep 2010 01:26:14 GMT] EMERGENCY a emergency message diff --git a/node/server/node_modules/cluster/node_modules/log/examples/reader.js b/node/server/node_modules/cluster/node_modules/log/examples/reader.js new file mode 100644 index 0000000..2d2aad8 --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/examples/reader.js @@ -0,0 +1,15 @@ + +/** + * Module dependencies. + */ + +var Log = require('../lib/log') + , fs = require('fs') + , stream = fs.createReadStream(__dirname + '/file.log') + , log = new Log('debug', stream); + +log.on('line', function(line){ + console.log(line); +}).on('end', function(){ + console.log('done'); +});; \ No newline at end of file diff --git a/node/server/node_modules/cluster/node_modules/log/examples/stdout.js b/node/server/node_modules/cluster/node_modules/log/examples/stdout.js new file mode 100644 index 0000000..1b0aec7 --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/examples/stdout.js @@ -0,0 +1,16 @@ + +/** + * Module dependencies. + */ + +var Log = require('../lib/log') + , log = new Log('notice'); + +log.debug('a debug message'); +log.info('a info message'); +log.notice('a notice message'); +log.warning('a warning message'); +log.error('a error message'); +log.critical('a critical message'); +log.alert('a alert message'); +log.emergency('a emergency message'); \ No newline at end of file diff --git a/node/server/node_modules/cluster/node_modules/log/index.html b/node/server/node_modules/cluster/node_modules/log/index.html new file mode 100644 index 0000000..1c16bab --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/index.html @@ -0,0 +1,329 @@ +Fork me on GitHub + + Log.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Log.js

Tiny logger for NodeJS.

log

lib/log.js
+

Initialize a Loggeer with the given log level defaulting +to DEBUG and stream defaulting to stdout.

+ +

+ +
  • param: Number level

  • param: Object stream

  • api: public

+
+
var Log = exports = module.exports = function Log(level, stream){
+  this.level = level || exports.DEBUG;
+  this.stream = stream || process.stdout;
+};
+
+

System is unusable.

+ +
  • type: Number

+
+
exports.EMERGENCY = 0;
+
+

Action must be taken immediately.

+ +
  • type: Number

+
+
exports.ALERT = 1;
+
+

Critical condition.

+ +
  • type: Number

+
+
exports.CRITICAL = 2;
+
+

Error condition.

+ +
  • type: Number

+
+
exports.ERROR = 3;
+
+

Warning condition.

+ +
  • type: Number

+
+
exports.WARNING = 4;
+
+

Normal but significant condition.

+ +
  • type: Number

+
+
exports.NOTICE = 5;
+
+

Purely informational message.

+ +
  • type: Number

+
+
exports.INFO = 6;
+
+

Application debug messages.

+ +
  • type: Number

+
+
exports.DEBUG = 7;
+
+

prototype. +

+
+
Log.prototype = {
+
+

Log emergency msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
emergency: function(msg){
+    this.log('EMERGENCY', msg);
+  },
+
+

Log alert msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
alert: function(msg){
+    this.log('ALERT', msg);
+  },
+
+

Log critical msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
critical: function(msg){
+    this.log('CRITICAL', msg);
+  },
+
+

Log error msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
error: function(msg){
+    this.log('ERROR', msg);
+  },
+
+

Log warning msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
warning: function(msg){
+    this.log('WARNING', msg);
+  },
+
+

Log notice msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
notice: function(msg){
+    this.log('NOTICE', msg);
+  },
+
+

Log info msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
info: function(msg){
+    this.log('INFO', msg);
+  },
+
+

Log debug msg.

+ +

+ +
  • param: String msg

  • api: public

+
+
debug: function(msg){
+    this.log('DEBUG', msg);
+  }
+};
+
\ No newline at end of file diff --git a/node/server/node_modules/cluster/node_modules/log/index.js b/node/server/node_modules/cluster/node_modules/log/index.js new file mode 100644 index 0000000..c669c17 --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/log'); \ No newline at end of file diff --git a/node/server/node_modules/cluster/node_modules/log/lib/log.js b/node/server/node_modules/cluster/node_modules/log/lib/log.js new file mode 100644 index 0000000..9627e7c --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/lib/log.js @@ -0,0 +1,250 @@ + +/*! + * Log.js + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter; + +/** + * Initialize a `Loggeer` with the given log `level` defaulting + * to __DEBUG__ and `stream` defaulting to _stdout_. + * + * @param {Number} level + * @param {Object} stream + * @api public + */ + +var Log = exports = module.exports = function Log(level, stream){ + if ('string' == typeof level) level = exports[level.toUpperCase()]; + this.level = level || exports.DEBUG; + this.stream = stream || process.stdout; + if (this.stream.readable) this.read(); +}; + +/** + * System is unusable. + * + * @type Number + */ + +exports.EMERGENCY = 0; + +/** + * Action must be taken immediately. + * + * @type Number + */ + +exports.ALERT = 1; + +/** + * Critical condition. + * + * @type Number + */ + +exports.CRITICAL = 2; + +/** + * Error condition. + * + * @type Number + */ + +exports.ERROR = 3; + +/** + * Warning condition. + * + * @type Number + */ + +exports.WARNING = 4; + +/** + * Normal but significant condition. + * + * @type Number + */ + +exports.NOTICE = 5; + +/** + * Purely informational message. + * + * @type Number + */ + +exports.INFO = 6; + +/** + * Application debug messages. + * + * @type Number + */ + +exports.DEBUG = 7; + +/** + * prototype. + */ + +Log.prototype = { + + /** + * Start emitting "line" events. + * + * @api public + */ + + read: function(){ + var buf = '' + , self = this + , stream = this.stream; + + stream.setEncoding('ascii'); + stream.on('data', function(chunk){ + buf += chunk; + if ('\n' != buf[buf.length - 1]) return; + buf.split('\n').map(function(line){ + if (!line.length) return; + try { + var captures = line.match(/^\[([^\]]+)\] (\w+) (.*)/); + var obj = { + date: new Date(captures[1]) + , level: exports[captures[2]] + , levelString: captures[2] + , msg: captures[3] + }; + self.emit('line', obj); + } catch (err) { + // Ignore + } + }); + buf = ''; + }); + + stream.on('end', function(){ + self.emit('end'); + }); + }, + + /** + * Log output message. + * + * @param {String} levelStr + * @param {String} msg + * @api private + */ + + log: function(levelStr, msg) { + if (exports[levelStr] <= this.level) { + this.stream.write( + '[' + new Date().toUTCString() + ']' + + ' ' + levelStr + + ' ' + msg + + '\n' + ); + } + }, + + /** + * Log emergency `msg`. + * + * @param {String} msg + * @api public + */ + + emergency: function(msg){ + this.log('EMERGENCY', msg); + }, + + /** + * Log alert `msg`. + * + * @param {String} msg + * @api public + */ + + alert: function(msg){ + this.log('ALERT', msg); + }, + + /** + * Log critical `msg`. + * + * @param {String} msg + * @api public + */ + + critical: function(msg){ + this.log('CRITICAL', msg); + }, + + /** + * Log error `msg`. + * + * @param {String} msg + * @api public + */ + + error: function(msg){ + this.log('ERROR', msg); + }, + + /** + * Log warning `msg`. + * + * @param {String} msg + * @api public + */ + + warning: function(msg){ + this.log('WARNING', msg); + }, + + /** + * Log notice `msg`. + * + * @param {String} msg + * @api public + */ + + notice: function(msg){ + this.log('NOTICE', msg); + }, + + /** + * Log info `msg`. + * + * @param {String} msg + * @api public + */ + + info: function(msg){ + this.log('INFO', msg); + }, + + /** + * Log debug `msg`. + * + * @param {String} msg + * @api public + */ + + debug: function(msg){ + this.log('DEBUG', msg); + } +}; + +/** + * Inherit from `EventEmitter`. + */ + +Log.prototype.__proto__ = EventEmitter.prototype; \ No newline at end of file diff --git a/node/server/node_modules/cluster/node_modules/log/package.json b/node/server/node_modules/cluster/node_modules/log/package.json new file mode 100644 index 0000000..f10d04d --- /dev/null +++ b/node/server/node_modules/cluster/node_modules/log/package.json @@ -0,0 +1 @@ +{"name":"log","description":"Tiny logger with streaming reader","version":"1.1.1","author":{"name":"TJ Holowaychuk","email":"tj@vision-media.ca"},"keywords":["log","logger"],"main":"./lib/log","engines":{"node":">= 0.2.0"},"_id":"log@1.1.1","_nodeSupported":true,"_npmVersion":"0.2.7-2","_nodeVersion":"v0.3.1-pre","dist":{"tarball":"http://registry.npmjs.org/log/-/log-1.1.1.tgz"},"directories":{}} \ No newline at end of file diff --git a/node/server/node_modules/cluster/package.json b/node/server/node_modules/cluster/package.json new file mode 100644 index 0000000..067631d --- /dev/null +++ b/node/server/node_modules/cluster/package.json @@ -0,0 +1 @@ +{"name":"cluster","description":"extensible multi-core server manager","keywords":["server","spark","fugue","tcp","workers"],"version":"0.3.1","homepage":"http://learnboost.github.com/cluster","repository":"git://github.com/LearnBoost/cluster.git","author":{"name":"TJ Holowaychuk","email":"tj@learnboost.com","url":"http://tjholowaychuk.com"},"main":"index","dependencies":{"log":">= 1.1.1"},"engines":{"node":"*"},"_id":"cluster@0.3.1","_engineSupported":true,"_npmVersion":"0.2.18","_nodeVersion":"v0.4.1","directories":{"lib":"./lib"},"files":[""],"_defaultsLoaded":true,"dist":{"shasum":"bd210242af35772c151b21ac0299cf72b35f2c2f","tarball":"http://registry.npmjs.org/cluster/-/cluster-0.3.1.tgz"}} \ No newline at end of file diff --git a/node/server/node_modules/cluster/support/should/.gitmodules b/node/server/node_modules/cluster/support/should/.gitmodules new file mode 100644 index 0000000..ffe0dcb --- /dev/null +++ b/node/server/node_modules/cluster/support/should/.gitmodules @@ -0,0 +1,3 @@ +[submodule "support/expresso"] + path = support/expresso + url = git://github.com/visionmedia/expresso.git diff --git a/node/server/node_modules/cluster/support/should/History.md b/node/server/node_modules/cluster/support/should/History.md new file mode 100644 index 0000000..cb95b57 --- /dev/null +++ b/node/server/node_modules/cluster/support/should/History.md @@ -0,0 +1,22 @@ + +0.0.4 / 2010-11-24 +================== + + * Added `.ok` to assert truthfulness + * Added `.arguments` + * Fixed double required bug. [thanks dominictarr] + +0.0.3 / 2010-11-19 +================== + + * Added `true` / `false` assertions + +0.0.2 / 2010-11-19 +================== + + * Added chaining support + +0.0.1 / 2010-11-19 +================== + + * Initial release diff --git a/node/server/node_modules/cluster/support/should/Makefile b/node/server/node_modules/cluster/support/should/Makefile new file mode 100644 index 0000000..a2f2a41 --- /dev/null +++ b/node/server/node_modules/cluster/support/should/Makefile @@ -0,0 +1,6 @@ + +test: + @./support/expresso/bin/expresso \ + -I lib + +.PHONY: test \ No newline at end of file diff --git a/node/server/node_modules/cluster/support/should/Readme.md b/node/server/node_modules/cluster/support/should/Readme.md new file mode 100644 index 0000000..7c5c0ac --- /dev/null +++ b/node/server/node_modules/cluster/support/should/Readme.md @@ -0,0 +1,248 @@ + _should_ is an expressive, test framework agnostic, assertion library for [node](http://nodejs.org). + +_should_ literally extends node's _assert_ module, in fact, it is node's assert module, for example `should.equal(str, 'foo')` will work, just as `assert.equal(str, 'foo')` would, and `should.AssertionError` **is** `asset.AssertionError`, meaning any test framework supporting this constructor will function properly with _should_. + +## Example + + var user = { + name: 'tj' + , pets: ['tobi', 'loki', 'jane', 'bandit'] + }; + + user.should.have.property('name', 'tj'); + user.should.have.property('pets').with.lengthOf(4) + +## Installation + + $ npm install should + +## modifiers + + _should_'s assertion chaining provides an expressive way to build up an assertion, along with dummy getters such as _an_, _have_, and _be_, provided are what I am simply calling **modifiers**, which have a meaning effect on the assertion. An example of this is the _not_ getter, which negates the meaning, aka `user.should.not.have.property('name')`. In the previous example note the use of _have_, as we could omit it and still construct a valid assertion. + +Some modifiers such as _include_ only have an effect with specific assertion methods, for example when asserting a substring like so: `str.should.include.string('test')`, we could omit _include_, but it helps express the meaning, however _keys_ has a strict effect, unless the _include_ modifier is used. + +## chaining assertions + +Some assertions can be chained, for example if a property is volatile we can first assert property existence: + + user.should.have.property('pets').with.lengthOf(4) + +which is essentially equivalent to below, however the property may not exist: + + user.pets.should.have.lengthOf(4) + +our dummy getters such as _and_ also help express chaining: + + user.should.be.a('object').and.have.property('name', 'tj') + +## ok + +Assert truthfulness: + + true.should.be.ok + 'yay'.should.be.ok + (1).should.be.ok + +or negated: + + false.should.not.be.ok + ''.should.not.be.ok + (0).should.not.be.ok + +## true + +Assert === true: + + true.should.be.true + '1'.should.not.be.true + +## false + +Assert === false: + + false.should.be.false + (0).should.not.be.false + +## arguments + +Assert `Arguments`: + + var args = (function(){ return arguments; })(1,2,3); + args.should.be.arguments; + [].should.not.be.arguments; + +## empty + +Asserts that length is 0: + + [].should.be.empty + ''.should.be.empty + ({ length: 0 }).should.be.empty + +## eql + +equality: + + ({ foo: 'bar' }).should.eql({ foo: 'bar' }) + [1,2,3].should.eql([1,2,3]) + +## equal + +strict equality: + + should.strictEqual(undefined, value) + should.strictEqual(false, value) + (4).should.equal(4) + 'test'.should.equal('test') + [1,2,3].should.not.equal([1,2,3]) + +## within + +Assert inclusive numeric range: + + user.age.should.be.within(5, 50) + +## a + +Assert __typeof__: + + user.should.be.a('object') + 'test'.should.be.a('string') + +## instanceof + +Assert __instanceof__: + + user.should.be.an.instanceof(User) + [].should.be.an.instanceof(Array) + +## above + +Assert numeric value above the given value: + + user.age.should.be.above(5) + user.age.should.not.be.above(100) + +## below + +Assert numeric value below the given value: + + user.age.should.be.below(100) + user.age.should.not.be.below(5) + +## match + +Assert regexp match: + + username.should.match(/^\w+$/) + +## length + +Assert _length_ property exists and has a value of the given number: + + user.pets.should.have.length(5) + user.pets.should.have.a.lengthOf(5) + +Aliases: _lengthOf_ + +## string + +Substring assertion: + + 'foobar'.should.include.string('foo') + 'foobar'.should.include.string('bar') + 'foobar'.should.not.include.string('baz') + +## property + +Assert property exists and has optional value: + + user.should.have.property('name') + user.should.have.property('age', 15) + user.should.not.have.property('rawr') + user.should.not.have.property('age', 0) + +## ownProperty + +Assert own property (on the immediate object): + + ({ foo: 'bar' }).should.have.ownProperty('foo') + +## contain + +Assert array value: + + [1,2,3].should.contain(3) + [1,2,3].should.contain(2) + [1,2,3].should.not.contain(4) + +## keys + +Assert own object keys, which must match _exactly_, +and will fail if you omit a key or two: + + var obj = { foo: 'bar', baz: 'raz' }; + obj.should.have.keys('foo', 'bar'); + obj.should.have.keys(['foo', 'bar']); + +using the _include_ modifier, we can check inclusion of a key, +but not fail when we omit a few: + + obj.should.include.keys('foo') + obj.should.include.keys('bar') + obj.should.not.include.keys('baz') + +## respondTo + +Assert that the given property is a function: + + user.should.respondTo('email') + +## Express example + +For example you can use should with the [Expresso TDD Framework](http://github.com/visionmedia/expresso) by simply including it: + + var lib = require('mylib') + , should = require('should'); + + module.exports = { + 'test .version': function(){ + lib.version.should.match(/^\d+\.\d+\.\d+$/); + } + }; + +## Running tests + +To run the tests for _should_ simple update your git submodules and run: + + $ make test + +## OMG IT EXTENDS OBJECT???!?!@ + +Yes, yes it does, with a single getter _should_, and no it wont break your code, because it does this **properly** with a non-enumerable property. + +## License + +(The MIT License) + +Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node/server/node_modules/cluster/support/should/index.js b/node/server/node_modules/cluster/support/should/index.js new file mode 100644 index 0000000..6dbdbde --- /dev/null +++ b/node/server/node_modules/cluster/support/should/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/should'); \ No newline at end of file diff --git a/node/server/node_modules/cluster/support/should/lib/eql.js b/node/server/node_modules/cluster/support/should/lib/eql.js new file mode 100644 index 0000000..31135e9 --- /dev/null +++ b/node/server/node_modules/cluster/support/should/lib/eql.js @@ -0,0 +1,91 @@ + +// Taken from node's assert module, because it sucks +// and exposes next to nothing useful. + +module.exports = _deepEqual; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } else if (typeof actual != 'object' && typeof expected != 'object') { + return actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical "prototype" property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isUndefinedOrNull (value) { + return value === null || value === undefined; +} + +function isArguments (object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv (a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical "prototype" property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try{ + var ka = Object.keys(a), + kb = Object.keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} diff --git a/node/server/node_modules/cluster/support/should/lib/should.js b/node/server/node_modules/cluster/support/should/lib/should.js new file mode 100644 index 0000000..f49475a --- /dev/null +++ b/node/server/node_modules/cluster/support/should/lib/should.js @@ -0,0 +1,548 @@ + +/*! + * Should + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var util = require('sys') + , assert = require('assert') + , AssertionError = assert.AssertionError + , eql = require('./eql') + , i = util.inspect; + +/** + * Expose assert as should. + * + * This allows you to do things like below + * without require()ing the assert module. + * + * should.equal(foo.bar, undefined); + * + */ + +exports = module.exports = assert; + +/** + * Library version. + */ + +exports.version = '0.0.4'; + +/** + * Expose api via `Object#should`. + * + * @api public + */ + +Object.defineProperty(Object.prototype, 'should', { + set: function(){}, + get: function(){ + return new Assertion(this); + } +}); + +/** + * Initialize a new `Assertion` with the given _obj_. + * + * @param {Mixed} obj + * @api private + */ + +var Assertion = exports.Assertion = function Assertion(obj) { + this.obj = obj; +}; + +/** + * Prototype. + */ + +Assertion.prototype = { + + /** + * HACK: prevents double require() from failing. + */ + + exports: exports, + + /** + * Assert _expr_ with the given _msg_ and _negatedMsg_. + * + * @param {Boolean} expr + * @param {String} msg + * @param {String} negatedMsg + * @api private + */ + + assert: function(expr, msg, negatedMsg){ + var msg = this.negate ? negatedMsg : msg + , ok = this.negate ? !expr : expr; + if (!ok) { + throw new AssertionError({ + message: msg + , stackStartFunction: this.assert + }); + } + }, + + /** + * Dummy getter. + * + * @api public + */ + + get an() { + return this; + }, + + /** + * Dummy getter. + * + * @api public + */ + + get and() { + return this; + }, + + /** + * Dummy getter. + * + * @api public + */ + + get be() { + return this; + }, + + /** + * Dummy getter. + * + * @api public + */ + + get have() { + return this; + }, + + /** + * Dummy getter. + * + * @api public + */ + + get with() { + return this; + }, + + /** + * Inclusion modifier. + * + * @api public + */ + + get include() { + this.includes = true; + return this; + }, + + /** + * Negation modifier. + * + * @api public + */ + + get not() { + this.negate = true; + return this; + }, + + /** + * Get object inspection string. + * + * @return {String} + * @api private + */ + + get inspect() { + return i(this.obj); + }, + + /** + * Assert instanceof `Arguments`. + * + * @api public + */ + + get arguments() { + this.assert( + '[object Arguments]' == Object.prototype.toString.call(this.obj) + , 'expected ' + this.inspect + ' to be arguments' + , 'expected ' + this.inspect + ' to not be arguments'); + return this; + }, + + /** + * Assert that an object is empty aka length of 0. + * + * @api public + */ + + get empty() { + this.obj.should.have.property('length'); + this.assert( + 0 === this.obj.length + , 'expected ' + this.inspect + ' to be empty' + , 'expected ' + this.inspect + ' not to be empty'); + return this; + }, + + /** + * Assert ok. + * + * @api public + */ + + get ok() { + this.assert( + this.obj + , 'expected ' + this.inspect + ' to be truthy' + , 'expected ' + this.inspect + ' to be falsey'); + return this; + }, + + /** + * Assert true. + * + * @api public + */ + + get true() { + this.assert( + true === this.obj + , 'expected ' + this.inspect + ' to be true' + , 'expected ' + this.inspect + ' not to be true'); + return this; + }, + + /** + * Assert false. + * + * @api public + */ + + get false() { + this.assert( + false === this.obj + , 'expected ' + this.inspect + ' to be false' + , 'expected ' + this.inspect + ' not to be false'); + return this; + }, + + /** + * Assert equal. + * + * @param {Mixed} val + * @api public + */ + + eql: function(val){ + this.assert( + eql(val, this.obj) + , 'expected ' + this.inspect + ' to equal ' + i(val) + , 'expected ' + this.inspect + ' to not equal ' + i(val)); + return this; + }, + + /** + * Assert strict equal. + * + * @param {Mixed} val + * @api public + */ + + equal: function(val){ + this.assert( + val === this.obj + , 'expected ' + this.inspect + ' to equal ' + i(val) + , 'expected ' + this.inspect + ' to not equal ' + i(val)); + return this; + }, + + /** + * Assert within start to finish (inclusive). + * + * @param {Number} start + * @param {Number} finish + * @api public + */ + + within: function(start, finish){ + var range = start + '..' + finish; + this.assert( + this.obj >= start && this.obj <= finish + , 'expected ' + this.inspect + ' to be within ' + range + , 'expected ' + this.inspect + ' to not be within ' + range); + return this; + }, + + /** + * Assert typeof. + * + * @api public + */ + + a: function(type){ + this.assert( + type == typeof this.obj + , 'expected ' + this.inspect + ' to be a ' + type + , 'expected ' + this.inspect + ' not to be a ' + type); + return this; + }, + + /** + * Assert instanceof. + * + * @api public + */ + + instanceof: function(constructor){ + var name = constructor.name; + this.assert( + this.obj instanceof constructor + , 'expected ' + this.inspect + ' to be an instance of ' + name + , 'expected ' + this.inspect + ' not to be an instance of ' + name); + return this; + }, + + /** + * Assert numeric value above _n_. + * + * @param {Number} n + * @api public + */ + + above: function(n){ + this.assert( + this.obj > n + , 'expected ' + this.inspect + ' to be above ' + n + , 'expected ' + this.inspect + ' to be below ' + n); + return this; + }, + + /** + * Assert numeric value below _n_. + * + * @param {Number} n + * @api public + */ + + below: function(n){ + this.assert( + this.obj < n + , 'expected ' + this.inspect + ' to be below ' + n + , 'expected ' + this.inspect + ' to be above ' + n); + return this; + }, + + /** + * Assert string value matches _regexp_. + * + * @param {RegExp} regexp + * @api public + */ + + match: function(regexp){ + this.assert( + regexp.exec(this.obj) + , 'expected ' + this.inspect + ' to match ' + regexp + , 'expected ' + this.inspect + ' not to match ' + regexp); + return this; + }, + + /** + * Assert property "length" exists and has value of _n_. + * + * @param {Number} n + * @api public + */ + + length: function(n){ + this.obj.should.have.property('length'); + var len = this.obj.length; + this.assert( + n == len + , 'expected ' + this.inspect + ' to have a length of ' + n + ' but got ' + len + , 'expected ' + this.inspect + ' to not have a length of ' + len); + return this; + }, + + /** + * Assert substring. + * + * @param {String} str + * @api public + */ + + string: function(str){ + this.obj.should.be.a('string'); + this.assert( + ~this.obj.indexOf(str) + , 'expected ' + this.inspect + ' to include ' + i(str) + , 'expected ' + this.inspect + ' to not include ' + i(str)); + return this; + }, + + /** + * Assert property _name_ exists, with optional _val_. + * + * @param {String} name + * @param {Mixed} val + * @api public + */ + + property: function(name, val){ + if (this.negate && undefined !== val) { + if (undefined === this.obj[name]) { + throw new Error(this.inspect + ' has no property ' + i(name)); + } + } else { + this.assert( + undefined !== this.obj[name] + , 'expected ' + this.inspect + ' to have a property ' + i(name) + , 'expected ' + this.inspect + ' to not have a property ' + i(name)); + } + + if (undefined !== val) { + this.assert( + val === this.obj[name] + , 'expected ' + this.inspect + ' to have a property ' + i(name) + + ' of ' + i(val) + ', but got ' + i(this.obj[name]) + , 'expected ' + this.inspect + ' to not have a property ' + i(name) + ' of ' + i(val)); + } + + this.obj = this.obj[name]; + return this; + }, + + /** + * Assert own property _name_ exists. + * + * @param {String} name + * @api public + */ + + ownProperty: function(name){ + this.assert( + this.obj.hasOwnProperty(name) + , 'expected ' + this.inspect + ' to have own property ' + i(name) + , 'expected ' + this.inspect + ' to not have own property ' + i(name)); + return this; + }, + + /** + * Assert that the array contains _obj_. + * + * @param {Mixed} obj + * @api public + */ + + contain: function(obj){ + this.obj.should.be.an.instanceof(Array); + this.assert( + ~this.obj.indexOf(obj) + , 'expected ' + this.inspect + ' to contain ' + i(obj) + , 'expected ' + this.inspect + ' to not contain ' + i(obj)); + return this; + }, + + /** + * Assert exact keys or inclusion of keys by using + * the `.include` modifier. + * + * @param {Array|String ...} keys + * @api public + */ + + keys: function(keys){ + var str + , ok = true; + + keys = keys instanceof Array + ? keys + : Array.prototype.slice.call(arguments); + + if (!keys.length) throw new Error('keys required'); + + var actual = Object.keys(this.obj) + , len = keys.length; + + // Inclusion + ok = keys.every(function(key){ + return ~actual.indexOf(key); + }); + + // Strict + if (!this.negate && !this.includes) { + ok = ok && keys.length == actual.length; + } + + // Key string + if (len > 1) { + keys = keys.map(function(key){ + return i(key); + }); + var last = keys.pop(); + str = keys.join(', ') + ', and ' + last; + } else { + str = i(keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (this.includes ? 'include ' : 'have ') + str; + + // Assertion + this.assert( + ok + , 'expected ' + this.inspect + ' to ' + str + , 'expected ' + this.inspect + ' to not ' + str); + + return this; + }, + + /** + * Assert that _method_ is a function. + * + * @param {String} method + * @api public + */ + + respondTo: function(method){ + this.assert( + 'function' == typeof this.obj[method] + , 'expected ' + this.inspect + ' to respond to ' + method + '()' + , 'expected ' + this.inspect + ' to not respond to ' + method + '()'); + return this; + } +}; + +/** + * Aliases. + */ + +(function alias(name, as){ + Assertion.prototype[as] = Assertion.prototype[name]; + return alias; +}) +('length', 'lengthOf') +('keys', 'key') +('ownProperty', 'haveOwnProperty') +('above', 'greaterThan') +('below', 'lessThan'); diff --git a/node/server/node_modules/cluster/support/should/package.json b/node/server/node_modules/cluster/support/should/package.json new file mode 100644 index 0000000..413e437 --- /dev/null +++ b/node/server/node_modules/cluster/support/should/package.json @@ -0,0 +1,8 @@ +{ "name": "should" + , "description": "test framework agnostic BDD-style assertions" + , "version": "0.0.4" + , "author": "TJ Holowaychuk " + , "keywords": ["test", "bdd", "assert"] + , "main": "./lib/should.js" + , "engines": { "node": ">= 0.2.0" } +} \ No newline at end of file diff --git a/node/server/server.js b/node/server/server.js index f10674f..7eac9bc 100755 --- a/node/server/server.js +++ b/node/server/server.js @@ -6,7 +6,12 @@ var listenPort = 8667; // start http server var httpSrv = require('http').createServer(function(request, response) {}) // server start listening -httpSrv.listen(listenPort); +if( module === require.main){ + httpSrv.listen(listenPort); +}else{ + // this one is needed for cluster.js + module.exports = httpSrv; +} console.log("listen on port "+listenPort)