Skip to content

Commit 1beb34a

Browse files
committed
lib: redo everything
1 parent 4cbc040 commit 1beb34a

File tree

9 files changed

+300
-113
lines changed

9 files changed

+300
-113
lines changed

.jscsrc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"disallowKeywordsOnNewLine": [ "else" ],
3+
"disallowMixedSpacesAndTabs": true,
4+
"disallowMultipleLineStrings": true,
5+
"disallowMultipleVarDecl": true,
6+
"disallowNewlineBeforeBlockStatements": true,
7+
"disallowQuotedKeysInObjects": true,
8+
"disallowSpaceAfterObjectKeys": true,
9+
"disallowSpaceAfterPrefixUnaryOperators": true,
10+
"disallowSpaceBeforePostfixUnaryOperators": true,
11+
"disallowSpacesInCallExpression": true,
12+
"disallowTrailingComma": true,
13+
"disallowTrailingWhitespace": true,
14+
"disallowYodaConditions": true,
15+
16+
"requireCommaBeforeLineBreak": true,
17+
"requireOperatorBeforeLineBreak": true,
18+
"requireSpaceAfterBinaryOperators": true,
19+
"requireSpaceAfterKeywords": [ "if", "for", "while", "else", "try", "catch" ],
20+
"requireSpaceAfterLineComment": true,
21+
"requireSpaceBeforeBinaryOperators": true,
22+
"requireSpaceBeforeBlockStatements": true,
23+
"requireSpaceBeforeKeywords": [ "else", "catch" ],
24+
"requireSpaceBeforeObjectValues": true,
25+
"requireSpaceBetweenArguments": true,
26+
"requireSpacesInAnonymousFunctionExpression": {
27+
"beforeOpeningCurlyBrace": true
28+
},
29+
"requireSpacesInFunctionDeclaration": {
30+
"beforeOpeningCurlyBrace": true
31+
},
32+
"requireSpacesInFunctionExpression": {
33+
"beforeOpeningCurlyBrace": true
34+
},
35+
"requireSpacesInConditionalExpression": true,
36+
"requireSpacesInForStatement": true,
37+
"requireSpacesInsideArrayBrackets": "all",
38+
"requireSpacesInsideObjectBrackets": "all",
39+
"requireDotNotation": true,
40+
41+
"maximumLineLength": 80,
42+
"validateIndentation": 2,
43+
"validateLineBreaks": "LF",
44+
"validateParameterSeparator": ", ",
45+
"validateQuoteMarks": "'"
46+
}

.jshintrc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
// JSHint Default Configuration File (as on JSHint website)
3+
// See http://jshint.com/docs/ for more details
4+
5+
"maxerr" : 50, // {int} Maximum error before stopping
6+
7+
// Enforcing
8+
"bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.)
9+
"camelcase" : false, // true: Identifiers must be in camelCase
10+
"curly" : false, // true: Require {} for every new block or scope
11+
"eqeqeq" : true, // true: Require triple equals (===) for comparison
12+
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
13+
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
14+
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
15+
"indent" : 2, // {int} Number of spaces to use for indentation
16+
"latedef" : true, // true: Require variables/functions to be defined before being used
17+
"newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()`
18+
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
19+
"noempty" : false, // true: Prohibit use of empty blocks
20+
"nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
21+
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
22+
"plusplus" : false, // true: Prohibit use of `++` & `--`
23+
"quotmark" : "single", // Quotation mark consistency:
24+
// false : do nothing (default)
25+
// true : ensure whatever is used is consistent
26+
// "single" : require single quotes
27+
// "double" : require double quotes
28+
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
29+
"unused" : true, // true: Require all defined variables be used
30+
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
31+
"maxparams" : false, // {int} Max number of formal params allowed per function
32+
"maxdepth" : 3, // {int} Max depth of nested blocks (within functions)
33+
"maxstatements" : false, // {int} Max number statements per function
34+
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
35+
"maxlen" : false, // {int} Max number of characters per line
36+
37+
// Relaxing
38+
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
39+
"boss" : false, // true: Tolerate assignments where comparisons would be expected
40+
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
41+
"eqnull" : false, // true: Tolerate use of `== null`
42+
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
43+
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
44+
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
45+
// (ex: `for each`, multiple try/catch, function expression…)
46+
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
47+
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
48+
"funcscope" : false, // true: Tolerate defining variables inside control statements
49+
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
50+
"iterator" : false, // true: Tolerate using the `__iterator__` property
51+
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
52+
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
53+
"laxcomma" : false, // true: Tolerate comma-first style coding
54+
"loopfunc" : false, // true: Tolerate functions being defined in loops
55+
"multistr" : false, // true: Tolerate multi-line strings
56+
"noyield" : false, // true: Tolerate generator functions with no yield statement in them.
57+
"notypeof" : false, // true: Tolerate invalid typeof operator values
58+
"proto" : false, // true: Tolerate using the `__proto__` property
59+
"scripturl" : false, // true: Tolerate script-targeted URLs
60+
"shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
61+
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
62+
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
63+
"validthis" : false, // true: Tolerate using this in a non-constructor function
64+
65+
// Environments
66+
"browser" : true, // Web Browser (window, document, etc)
67+
"browserify" : true, // Browserify (node.js code in the browser)
68+
"couch" : false, // CouchDB
69+
"devel" : true, // Development/debugging (alert, confirm, etc)
70+
"dojo" : false, // Dojo Toolkit
71+
"jasmine" : false, // Jasmine
72+
"jquery" : false, // jQuery
73+
"mocha" : true, // Mocha
74+
"mootools" : false, // MooTools
75+
"node" : true, // Node.js
76+
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
77+
"prototypejs" : false, // Prototype and Scriptaculous
78+
"qunit" : false, // QUnit
79+
"rhino" : false, // Rhino
80+
"shelljs" : false, // ShellJS
81+
"worker" : false, // Web Workers
82+
"wsh" : false, // Windows Scripting Host
83+
"yui" : false, // Yahoo User Interface
84+
85+
// Custom Globals
86+
"globals" : {
87+
"module": true
88+
} // additional predefined global variables
89+
}

README.md

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,20 @@ npm install sticky-session
1414
```javascript
1515
var sticky = require('sticky-session');
1616

17-
sticky(require('http').createServer(function(req, res) {
17+
var server = require('http').createServer(function(req, res) {
1818
res.end('worker: ' + process.env.NODE_WORKER_ID);
19-
})).listen(3000, function() {
20-
console.log('server started on 3000 port');
2119
});
22-
```
23-
Simple
24-
25-
```javascript
26-
var sticky = require('sticky-session');
2720

28-
sticky(function() {
29-
// This code will be executed only in slave workers
30-
31-
var http = require('http'),
32-
io = require('socket.io');
33-
34-
var server = http.createServer(function(req, res) {
35-
// ....
21+
if (sticky.listen(server, 3000)) {
22+
// Master code
23+
server.once('listening', function() {
24+
console.log('server started on 3000 port');
3625
});
37-
io.listen(server);
38-
39-
return server;
40-
}).listen(3000, function() {
41-
console.log('server started on 3000 port');
42-
});
26+
} else {
27+
// Worker code
28+
}
4329
```
44-
Socket.io
30+
Simple
4531

4632
## Reasoning
4733

@@ -57,7 +43,7 @@ expected, but on multiple processes!
5743

5844
This software is licensed under the MIT License.
5945

60-
Copyright Fedor Indutny, 2012.
46+
Copyright Fedor Indutny, 2015.
6147

6248
Permission is hereby granted, free of charge, to any person obtaining a
6349
copy of this software and associated documentation files (the

example/server.js

Lines changed: 0 additions & 8 deletions
This file was deleted.

lib/sticky-session.js

Lines changed: 3 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,4 @@
1-
var net = require('net'),
2-
cluster = require('cluster');
1+
'use strict';
32

4-
function hash(ip, seed) {
5-
var hash = ip.reduce(function(r, num) {
6-
r += parseInt(num, 10);
7-
r %= 2147483648;
8-
r += (r << 10)
9-
r %= 2147483648;
10-
r ^= r >> 6;
11-
return r;
12-
}, seed);
13-
14-
hash += hash << 3;
15-
hash %= 2147483648;
16-
hash ^= hash >> 11;
17-
hash += hash << 15;
18-
hash %= 2147483648;
19-
20-
return hash >>> 0;
21-
}
22-
23-
module.exports = function sticky(num, callback) {
24-
var server;
25-
26-
// `num` argument is optional
27-
if (typeof num !== 'number') {
28-
callback = num;
29-
num = require('os').cpus().length;
30-
}
31-
32-
// Master will spawn `num` workers
33-
if (cluster.isMaster) {
34-
var workers = [];
35-
for (var i = 0; i < num; i++) {
36-
!function spawn(i) {
37-
workers[i] = cluster.fork();
38-
// Restart worker on exit
39-
workers[i].on('exit', function() {
40-
console.error('sticky-session: worker died');
41-
spawn(i);
42-
});
43-
}(i);
44-
}
45-
46-
var seed = ~~(Math.random() * 1e9);
47-
server = net.createServer(function(c) {
48-
// Get int31 hash of ip
49-
var worker,
50-
ipHash = hash((c.remoteAddress || '').split(/\./g), seed);
51-
52-
// Pass connection to worker
53-
worker = workers[ipHash % workers.length];
54-
worker.send('sticky-session:connection', c);
55-
});
56-
} else {
57-
server = typeof callback === 'function' ? callback() : callback;
58-
59-
// Worker process
60-
process.on('message', function(msg, socket) {
61-
if (msg !== 'sticky-session:connection') return;
62-
63-
server.emit('connection', socket);
64-
});
65-
66-
if (!server) throw new Error('Worker hasn\'t created server!');
67-
68-
// Monkey patch server to do not bind to port
69-
var oldListen = server.listen;
70-
server.listen = function listen() {
71-
var lastArg = arguments[arguments.length - 1];
72-
73-
if (typeof lastArg === 'function') lastArg();
74-
75-
return oldListen.call(this, null);
76-
};
77-
}
78-
79-
return server;
80-
};
3+
exports.Master = require('./sticky/master');
4+
exports.listen = require('./sticky/api').listen;

lib/sticky/api.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
var cluster = require('cluster');
4+
var os = require('os');
5+
var debug = require('debug')('sticky:worker');
6+
7+
var sticky = require('../sticky-session');
8+
var Master = sticky.Master;
9+
10+
function listen(server, port, options) {
11+
if (!options)
12+
options = {};
13+
14+
if (cluster.isMaster) {
15+
var workerCount = options.workers || os.cpus().length;
16+
17+
var master = new Master(workerCount);
18+
master.listen(port);
19+
return false;
20+
}
21+
22+
process.on('message', function(msg, socket) {
23+
if (msg !== 'sticky:balance' || !socket)
24+
return;
25+
26+
debug('incoming socket');
27+
server.emit('connection', socket);
28+
});
29+
return true;
30+
}
31+
exports.listen = listen;

lib/sticky/master.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict';
2+
3+
var cluster = require('cluster');
4+
var util = require('util');
5+
var net = require('net');
6+
var ip = require('ip');
7+
8+
var debug = require('debug')('sticky:master');
9+
10+
function Master(workerCount) {
11+
net.Server.call(this, {
12+
pauseOnConnect: true
13+
}, this.balance);
14+
15+
this.seed = (Math.random() * 0xffffffff) | 0;
16+
this.workers = [];
17+
18+
debug('master seed=%d', this.seed);
19+
20+
for (var i = 0; i < workerCount; i++)
21+
this.spawnWorker();
22+
23+
this.once('listening', function() {
24+
debug('master listening on %j', this.address());
25+
});
26+
}
27+
util.inherits(Master, net.Server);
28+
module.exports = Master;
29+
30+
Master.prototype.hash = function hash(ip) {
31+
var hash = this.seed;
32+
for (var i = 0; i < ip.length; i++) {
33+
var num = ip[i];
34+
35+
hash += num;
36+
hash %= 2147483648;
37+
hash += (hash << 10);
38+
hash %= 2147483648;
39+
hash ^= hash >> 6;
40+
}
41+
42+
hash += hash << 3;
43+
hash %= 2147483648;
44+
hash ^= hash >> 11;
45+
hash += hash << 15;
46+
hash %= 2147483648;
47+
48+
return hash >>> 0;
49+
};
50+
51+
Master.prototype.spawnWorker = function spawnWorker() {
52+
var worker = cluster.fork();
53+
54+
var self = this;
55+
worker.on('exit', function(code) {
56+
debug('worker=%d died with code=%d', worker.process.pid, code);
57+
self.respawn(worker);
58+
});
59+
60+
debug('worker=%d spawn', worker.process.pid);
61+
this.workers.push(worker);
62+
};
63+
64+
Master.prototype.respawn = function respawn(worker) {
65+
var index = this.workers.indexOf(worker);
66+
if (index !== -1)
67+
this.workers.splice(index, 1);
68+
this.spawnWorker();
69+
};
70+
71+
Master.prototype.balance = function balance(socket) {
72+
var addr = ip.toBuffer(socket.remoteAddress || '127.0.0.1');
73+
var hash = this.hash(addr);
74+
75+
debug('balacing connection %j', addr);
76+
this.workers[hash % this.workers.length].send('sticky:balance', socket);
77+
};

0 commit comments

Comments
 (0)