This commit is contained in:
libccy 2016-03-29 21:43:15 +08:00
parent a8d37bf22e
commit e2408b42db
42 changed files with 4806 additions and 5 deletions

View File

@ -0,0 +1,7 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /audio/skill/reyingzi1_sunce.mp3 was not found on this server.</p>
</body></html>

View File

@ -0,0 +1,7 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /audio/skill/reyingzi2_sunce.mp3 was not found on this server.</p>
</body></html>

BIN
audio/skill/reyingzi_cunce1.mp3 Executable file

Binary file not shown.

BIN
audio/skill/reyingzi_sunce2.mp3 Executable file

Binary file not shown.

View File

@ -349,6 +349,7 @@ character.refresh={
},
reyingzi:{
audio:2,
audioname:['sunce'],
trigger:{player:'phaseDrawBegin'},
forced:true,
content:function(){

View File

@ -1,5 +1,5 @@
window.noname_asset_list=[
'1.8.1',
'1.8.1.1',
'image/background/huangtian_bg.jpg',
'image/background/shengshi_bg.jpg',
'image/background/xueji_bg.jpg',
@ -1854,6 +1854,8 @@ window.noname_asset_list=[
'audio/skill/reyiji2.mp3',
'audio/skill/reyingzi1.mp3',
'audio/skill/reyingzi2.mp3',
'audio/skill/reyingzi1_sunce.mp3',
'audio/skill/reyingzi2_sunce.mp3',
'audio/skill/roulin1.mp3',
'audio/skill/roulin2.mp3',
'audio/skill/ruoyu1.mp3',
@ -2160,4 +2162,36 @@ window.noname_asset_list=[
'font/xiaozhuan.ttf',
'font/huangcao.ttf',
'font/xinwei.ttf',
'theme/music/grid.png',
'theme/music/style.css',
'theme/music/wood.png',
'theme/music/wood3.png',
'theme/simple/card.png',
'theme/simple/grid.png',
'theme/simple/style.css',
'theme/simple/unknown.png',
'theme/simple/wood.png',
'theme/simple/wood3.png',
'theme/woodden/grid.png',
'theme/woodden/style.css',
'theme/woodden/wood.jpg',
'theme/woodden/wood.png',
'theme/woodden/wood2.jpg',
'theme/woodden/wood2.png',
'theme/style/card/default.css',
'theme/style/card/music.css',
'theme/style/card/simple.css',
'theme/style/card/wood.css',
'theme/style/cardback/default.css',
'theme/style/cardback/music.css',
'theme/style/cardback/official.css',
'theme/style/cardback/wood.css',
'theme/style/cardback/image/official.png',
'theme/style/cardback/image/official2.png',
'theme/style/hp/default.css',
'theme/style/hp/official.css',
'theme/style/hp/image/hp1.png',
'theme/style/hp/image/hp2.png',
'theme/style/hp/image/hp3.png',
'theme/style/hp/image/hp4.png',
]

View File

@ -3291,6 +3291,9 @@
}
});
}
if(lib.config.debug){
require('remote').getCurrentWindow().openDevTools();
}
ui.background=ui.create.div('.background');
ui.background.style.backgroundSize="cover";
if(lib.config.image_background&&lib.config.image_background!='default'&&lib.config.image_background!='custom'){
@ -3737,8 +3740,9 @@
if(path[path.length-1]=='/'){
path=path.slice(0,path.length-1);
}
if(path=='mode'&&lib.config.all.stockmode.indexOf(file)==-1){
if(path==lib.assetURL+'mode'&&lib.config.all.stockmode.indexOf(file)==-1){
lib.init['setMode_'+file]();
onload();
return;
}
if(Array.isArray(file)){
@ -17600,6 +17604,7 @@
text.style.position='absolute';
text.style.left='30px';
text.style.top='12px';
text.style.wordBreak='break-all';
page.appendChild(text);
var caption=ui.create.div('','输入命令',page);
@ -17633,7 +17638,7 @@
else{
try{
var result=eval(text2.value);
if(result){
if(result!==undefined){
game.print(result);
}
}
@ -17941,6 +17946,11 @@
script.remove();
var updates=window.noname_source_list;
delete window.noname_source_list;
for(var i=0;i<updates.length;i++){
if(updates[i].indexOf('theme/')==0){
updates.splice(i--,1);
}
}
if(!ui.arena.classList.contains('menupaused')){
ui.click.configMenu();

30
game/server.js Normal file
View File

@ -0,0 +1,30 @@
'use strict';
{
let WebSocketServer=require('ws').Server;
let wss=new WebSocketServer({port:8080});
wss.on('connection',function(ws){
ws.on('message',function(messagestr){
let message;
try{
message=JSON.parse(messagestr);
if(!Array.isArray(message)||
typeof server.message[message[0]]!=='function'){
throw('err');
}
}
catch(e){
console.log('invalid message: '+messagestr);
return;
}
server.message[message.shift()].apply(ws.player,message);
});
ws.on('close',function(){
if(server.status.gaming){
ws.player.closed=true;
}
else{
server.players.delete(ws.player.id);
}
});
});
}

View File

@ -1,7 +1,9 @@
window.noname_update={
version:'1.8.1.2',
version:'1.8.1.4',
changeLog:[
'自由选将改进',
'命令输入改进'
'命令输入改进',
'修复不能导入模式的bug',
'主题并入素材'
]
}

BIN
image/skin/caocao/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

7
node_modules/options/.npmignore generated vendored Normal file
View File

@ -0,0 +1,7 @@
npm-debug.log
node_modules
.*.swp
.lock-*
build/
test

12
node_modules/options/Makefile generated vendored Normal file
View File

@ -0,0 +1,12 @@
ALL_TESTS = $(shell find test/ -name '*.test.js')
run-tests:
@./node_modules/.bin/mocha \
-t 2000 \
$(TESTFLAGS) \
$(TESTS)
test:
@$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
.PHONY: test

69
node_modules/options/README.md generated vendored Normal file
View File

@ -0,0 +1,69 @@
# options.js #
A very light-weight in-code option parsers for node.js.
## Usage ##
``` js
var Options = require("options");
// Create an Options object
function foo(options) {
var default_options = {
foo : "bar"
};
// Create an option object with default value
var opts = new Options(default_options);
// Merge options
opts = opts.merge(options);
// Reset to default value
opts.reset();
// Copy selected attributes out
var seled_att = opts.copy("foo");
// Read json options from a file.
opts.read("options.file"); // Sync
opts.read("options.file", function(err){ // Async
if(err){ // If error occurs
console.log("File error.");
}else{
// No error
}
});
// Attributes defined or not
opts.isDefinedAndNonNull("foobar");
opts.isDefined("foobar");
}
```
## License ##
(The MIT License)
Copyright (c) 2012 Einar Otto Stangvik &lt;einaros@gmail.com&gt;
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.

86
node_modules/options/lib/options.js generated vendored Normal file
View File

@ -0,0 +1,86 @@
/*!
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var fs = require('fs');
function Options(defaults) {
var internalValues = {};
var values = this.value = {};
Object.keys(defaults).forEach(function(key) {
internalValues[key] = defaults[key];
Object.defineProperty(values, key, {
get: function() { return internalValues[key]; },
configurable: false,
enumerable: true
});
});
this.reset = function() {
Object.keys(defaults).forEach(function(key) {
internalValues[key] = defaults[key];
});
return this;
};
this.merge = function(options, required) {
options = options || {};
if (Object.prototype.toString.call(required) === '[object Array]') {
var missing = [];
for (var i = 0, l = required.length; i < l; ++i) {
var key = required[i];
if (!(key in options)) {
missing.push(key);
}
}
if (missing.length > 0) {
if (missing.length > 1) {
throw new Error('options ' +
missing.slice(0, missing.length - 1).join(', ') + ' and ' +
missing[missing.length - 1] + ' must be defined');
}
else throw new Error('option ' + missing[0] + ' must be defined');
}
}
Object.keys(options).forEach(function(key) {
if (key in internalValues) {
internalValues[key] = options[key];
}
});
return this;
};
this.copy = function(keys) {
var obj = {};
Object.keys(defaults).forEach(function(key) {
if (keys.indexOf(key) !== -1) {
obj[key] = values[key];
}
});
return obj;
};
this.read = function(filename, cb) {
if (typeof cb == 'function') {
var self = this;
fs.readFile(filename, function(error, data) {
if (error) return cb(error);
var conf = JSON.parse(data);
self.merge(conf);
cb();
});
}
else {
var conf = JSON.parse(fs.readFileSync(filename));
this.merge(conf);
}
return this;
};
this.isDefined = function(key) {
return typeof values[key] != 'undefined';
};
this.isDefinedAndNonNull = function(key) {
return typeof values[key] != 'undefined' && values[key] !== null;
};
Object.freeze(values);
Object.freeze(this);
}
module.exports = Options;

51
node_modules/options/package.json generated vendored Normal file
View File

@ -0,0 +1,51 @@
{
"author": {
"name": "Einar Otto Stangvik",
"email": "einaros@gmail.com",
"url": "http://2x.io"
},
"name": "options",
"description": "A very light-weight in-code option parsers for node.js.",
"version": "0.0.6",
"repository": {
"type": "git",
"url": "git://github.com/einaros/options.js.git"
},
"main": "lib/options",
"scripts": {
"test": "make test"
},
"engines": {
"node": ">=0.4.0"
},
"dependencies": {},
"devDependencies": {
"mocha": "latest"
},
"gitHead": "ff53d0a092c897cb95964232a96fe17da65c11af",
"bugs": {
"url": "https://github.com/einaros/options.js/issues"
},
"homepage": "https://github.com/einaros/options.js",
"_id": "options@0.0.6",
"_shasum": "ec22d312806bb53e731773e7cdaefcf1c643128f",
"_from": "options@>=0.0.5",
"_npmVersion": "1.4.21",
"_npmUser": {
"name": "einaros",
"email": "einaros@gmail.com"
},
"maintainers": [
{
"name": "einaros",
"email": "einaros@gmail.com"
}
],
"dist": {
"shasum": "ec22d312806bb53e731773e7cdaefcf1c643128f",
"tarball": "http://registry.npmjs.org/options/-/options-0.0.6.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
"readme": "ERROR: No README data found!"
}

3
node_modules/ultron/.npmignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
coverage
.tern-port

21
node_modules/ultron/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,21 @@
sudo: false
language: node_js
node_js:
- "0.12"
- "0.10"
- "0.8"
- "iojs"
before_install:
- 'if [ "${TRAVIS_NODE_VERSION}" == "0.8" ]; then npm install -g npm@2.11.1; fi'
script:
- "npm run test-travis"
after_script:
- "npm install coveralls@2.11.x && cat coverage/lcov.info | coveralls"
matrix:
fast_finish: true
notifications:
irc:
channels:
- "irc.freenode.org#unshift"
on_success: change
on_failure: change

22
node_modules/ultron/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Unshift.io, Arnout Kazemier, the Contributors.
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.

97
node_modules/ultron/README.md generated vendored Normal file
View File

@ -0,0 +1,97 @@
# Ultron
[![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](http://img.shields.io/npm/v/ultron.svg?style=flat-square)](http://browsenpm.org/package/ultron)[![Build Status](http://img.shields.io/travis/unshiftio/ultron/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/ultron)[![Dependencies](https://img.shields.io/david/unshiftio/ultron.svg?style=flat-square)](https://david-dm.org/unshiftio/ultron)[![Coverage Status](http://img.shields.io/coveralls/unshiftio/ultron/master.svg?style=flat-square)](https://coveralls.io/r/unshiftio/ultron?branch=master)[![IRC channel](http://img.shields.io/badge/IRC-irc.freenode.net%23unshift-00a8ff.svg?style=flat-square)](http://webchat.freenode.net/?channels=unshift)
Ultron is a high-intelligence robot. It gathers intelligence so it can start
improving upon his rudimentary design. It will learn your event emitting
patterns and find ways to exterminate them. Allowing you to remove only the
event emitters that **you** assigned and not the ones that your users or
developers assigned. This can prevent race conditions, memory leaks and even file
descriptor leaks from ever happening as you won't remove clean up processes.
## Installation
The module is designed to be used in browsers using browserify and in Node.js.
You can install the module through the public npm registry by running the
following command in CLI:
```
npm install --save ultron
```
## Usage
In all examples we assume that you've required the library as following:
```js
'use strict';
var Ultron = require('ultron');
```
Now that we've required the library we can construct our first `Ultron` instance.
The constructor requires one argument which should be the `EventEmitter`
instance that we need to operate upon. This can be the `EventEmitter` module
that ships with Node.js or `EventEmitter3` or anything else as long as it
follow the same API and internal structure as these 2. So with that in mind we
can create the instance:
```js
//
// For the sake of this example we're going to construct an empty EventEmitter
//
var EventEmitter = require('events').EventEmitter; // or require('eventmitter3');
var events = new EventEmitter();
var ultron = new Ultron(events);
```
You can now use the following API's from the Ultron instance:
### Ultron.on
Register a new event listener for the given event. It follows the exact same API
as `EventEmitter.on` but it will return itself instead of returning the
EventEmitter instance. If you are using EventEmitter3 it also supports the
context param:
```js
ultron.on('event-name', handler, { custom: 'function context' });
```
### Ultron.once
Exactly the same as the [Ultron.on](#ultronon) but it only allows the execution
once.
### Ultron.remove
This is where all the magic happens and the safe removal starts. This function
accepts different argument styles:
- No arguments, assume that all events need to be removed so it will work as
`removeAllListeners()` API.
- 1 argument, when it's a string it will be split on ` ` and `,` to create a
list of events that need to be cleared.
- Multiple arguments, we assume that they are all names of events that need to
be cleared.
```js
ultron.remove('foo, bar baz'); // Removes foo, bar and baz.
ultron.remove('foo', 'bar', 'baz'); // Removes foo, bar and baz.
ultron.remove(); // Removes everything.
```
If you just want to remove a single event listener using a function reference
you can still use the EventEmitter's `removeListener(event, fn)` API:
```js
function foo() {}
ulton.on('foo', foo);
events.removeListener('foo', foo);
```
## License
MIT

129
node_modules/ultron/index.js generated vendored Normal file
View File

@ -0,0 +1,129 @@
'use strict';
var has = Object.prototype.hasOwnProperty;
/**
* An auto incrementing id which we can use to create "unique" Ultron instances
* so we can track the event emitters that are added through the Ultron
* interface.
*
* @type {Number}
* @private
*/
var id = 0;
/**
* Ultron is high-intelligence robot. It gathers intelligence so it can start improving
* upon his rudimentary design. It will learn from your EventEmitting patterns
* and exterminate them.
*
* @constructor
* @param {EventEmitter} ee EventEmitter instance we need to wrap.
* @api public
*/
function Ultron(ee) {
if (!(this instanceof Ultron)) return new Ultron(ee);
this.id = id++;
this.ee = ee;
}
/**
* Register a new EventListener for the given event.
*
* @param {String} event Name of the event.
* @param {Functon} fn Callback function.
* @param {Mixed} context The context of the function.
* @returns {Ultron}
* @api public
*/
Ultron.prototype.on = function on(event, fn, context) {
fn.__ultron = this.id;
this.ee.on(event, fn, context);
return this;
};
/**
* Add an EventListener that's only called once.
*
* @param {String} event Name of the event.
* @param {Function} fn Callback function.
* @param {Mixed} context The context of the function.
* @returns {Ultron}
* @api public
*/
Ultron.prototype.once = function once(event, fn, context) {
fn.__ultron = this.id;
this.ee.once(event, fn, context);
return this;
};
/**
* Remove the listeners we assigned for the given event.
*
* @returns {Ultron}
* @api public
*/
Ultron.prototype.remove = function remove() {
var args = arguments
, event;
//
// When no event names are provided we assume that we need to clear all the
// events that were assigned through us.
//
if (args.length === 1 && 'string' === typeof args[0]) {
args = args[0].split(/[, ]+/);
} else if (!args.length) {
args = [];
for (event in this.ee._events) {
if (has.call(this.ee._events, event)) args.push(event);
}
}
for (var i = 0; i < args.length; i++) {
var listeners = this.ee.listeners(args[i]);
for (var j = 0; j < listeners.length; j++) {
event = listeners[j];
//
// Once listeners have a `listener` property that stores the real listener
// in the EventEmitter that ships with Node.js.
//
if (event.listener) {
if (event.listener.__ultron !== this.id) continue;
delete event.listener.__ultron;
} else {
if (event.__ultron !== this.id) continue;
delete event.__ultron;
}
this.ee.removeListener(args[i], event);
}
}
return this;
};
/**
* Destroy the Ultron instance, remove all listeners and release all references.
*
* @returns {Boolean}
* @api public
*/
Ultron.prototype.destroy = function destroy() {
if (!this.ee) return false;
this.remove();
this.ee = null;
return true;
};
//
// Expose the module.
//
module.exports = Ultron;

74
node_modules/ultron/package.json generated vendored Normal file
View File

@ -0,0 +1,74 @@
{
"name": "ultron",
"version": "1.0.2",
"description": "Ultron is high-intelligence robot. It gathers intel so it can start improving upon his rudimentary design",
"main": "index.js",
"scripts": {
"100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100",
"test": "mocha test.js",
"watch": "mocha --watch test.js",
"coverage": "istanbul cover ./node_modules/.bin/_mocha -- test.js",
"test-travis": "istanbul cover node_modules/.bin/_mocha --report lcovonly -- test.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/unshiftio/ultron.git"
},
"keywords": [
"Ultron",
"robot",
"gather",
"intelligence",
"event",
"events",
"eventemitter",
"emitter",
"cleanup"
],
"author": {
"name": "Arnout Kazemier"
},
"license": "MIT",
"devDependencies": {
"assume": "1.2.x",
"eventemitter3": "1.1.x",
"istanbul": "0.3.x",
"mocha": "2.2.x",
"pre-commit": "1.0.x"
},
"bugs": {
"url": "https://github.com/unshiftio/ultron/issues"
},
"homepage": "https://github.com/unshiftio/ultron",
"gitHead": "a10482ae98a09120821545456c90c6d60d540f7c",
"_id": "ultron@1.0.2",
"_shasum": "ace116ab557cd197386a4e88f4685378c8b2e4fa",
"_from": "ultron@>=1.0.0 <1.1.0",
"_npmVersion": "2.9.1",
"_nodeVersion": "0.12.3",
"_npmUser": {
"name": "3rdeden",
"email": "npm@3rd-Eden.com"
},
"maintainers": [
{
"name": "unshift",
"email": "npm@unshift.io"
},
{
"name": "v1",
"email": "info@3rd-Eden.com"
},
{
"name": "3rdeden",
"email": "npm@3rd-Eden.com"
}
],
"dist": {
"shasum": "ace116ab557cd197386a4e88f4685378c8b2e4fa",
"tarball": "http://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
"readme": "ERROR: No README data found!"
}

327
node_modules/ultron/test.js generated vendored Normal file
View File

@ -0,0 +1,327 @@
/* istanbul ignore next */
describe('Ultron', function () {
'use strict';
var EventEmitter = require('eventemitter3')
, EE = require('events').EventEmitter
, assume = require('assume')
, Ultron = require('./')
, ultron
, ee;
beforeEach(function () {
ee = new EventEmitter();
ultron = new Ultron(ee);
});
afterEach(function () {
ultron.destroy();
ee.removeAllListeners();
});
it('is exposed as a function', function () {
assume(Ultron).is.a('function');
});
it('can be initialized without the new keyword', function () {
assume(Ultron(ee)).is.instanceOf(Ultron);
});
it('assigns a unique id to every instance', function () {
for (var i = 0; i < 100; i++) {
assume(ultron.id).does.not.equal((new Ultron()).id);
}
});
it('allows removal through the event emitter', function () {
function foo() {}
function bar() {}
ultron.on('foo', foo);
ultron.once('foo', bar);
assume(foo.__ultron).equals(ultron.id);
assume(bar.__ultron).equals(ultron.id);
assume(ee.listeners('foo').length).equals(2);
ee.removeListener('foo', foo);
assume(ee.listeners('foo').length).equals(1);
ee.removeListener('foo', bar);
assume(ee.listeners('foo').length).equals(0);
});
describe('#on', function () {
it('assigns a listener', function () {
assume(ee.listeners('foo').length).equals(0);
function foo() {}
ultron.on('foo', foo);
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('foo')[0]).equals(foo);
});
it('tags the assigned function', function () {
assume(ee.listeners('foo').length).equals(0);
ultron.on('foo', function () {});
assume(ee.listeners('foo')[0].__ultron).equals(ultron.id);
});
it('also passes in the context', function (next) {
var context = 1313;
ultron.on('foo', function (a, b, c) {
assume(a).equals('a');
assume(b).equals('b');
assume(c).equals('c');
assume(this).equals(context);
next();
}, context);
ee.emit('foo', 'a', 'b', 'c');
});
it('works with regular eventemitters as well', function (next) {
var ee = new EE()
, ultron = new Ultron(ee);
ultron.on('foo', function (a, b, c) {
assume(a).equals('a');
assume(b).equals('b');
assume(c).equals('c');
next();
});
ee.emit('foo', 'a', 'b', 'c');
});
});
describe('#once', function () {
it('assigns a listener', function () {
assume(ee.listeners('foo').length).equals(0);
function foo() {}
ultron.once('foo', foo);
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('foo')[0]).equals(foo);
});
it('tags the assigned function', function () {
assume(ee.listeners('foo').length).equals(0);
ultron.once('foo', function () {});
assume(ee.listeners('foo')[0].__ultron).equals(ultron.id);
});
it('also passes in the context', function (next) {
var context = 1313;
ultron.once('foo', function (a, b, c) {
assume(a).equals('a');
assume(b).equals('b');
assume(c).equals('c');
assume(this).equals(context);
next();
}, context);
ee.emit('foo', 'a', 'b', 'c');
ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute
});
it('works with regular eventemitters as well', function (next) {
var ee = new EE()
, ultron = new Ultron(ee);
ultron.once('foo', function (a, b, c) {
assume(a).equals('a');
assume(b).equals('b');
assume(c).equals('c');
next();
});
ee.emit('foo', 'a', 'b', 'c');
ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute
});
});
describe('#remove', function () {
it('removes only our assigned `on` listeners', function () {
function foo() {}
function bar() {}
ee.on('foo', foo);
ultron.on('foo', bar);
assume(ee.listeners('foo').length).equals(2);
ultron.remove('foo');
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('foo')[0]).equals(foo);
});
it('removes our private __ultron references', function () {
function once() {}
function on() {}
assume('__ultron' in once).is.false();
assume('__ultron' in on).is.false();
ultron.on('foo', on);
ultron.once('bar', once);
assume('__ultron' in once).is.true();
assume('__ultron' in on).is.true();
ultron.remove('foo, bar');
assume('__ultron' in once).is.false();
assume('__ultron' in on).is.false();
ultron.destroy();
ee = new EE();
ultron = new Ultron(ee);
assume('__ultron' in once).is.false();
assume('__ultron' in on).is.false();
ultron.on('foo', on);
ultron.once('bar', once);
assume('__ultron' in once).is.true();
assume('__ultron' in on).is.true();
ultron.remove('foo, bar');
assume('__ultron' in once).is.false();
assume('__ultron' in on).is.false();
});
it('removes only our assigned `once` listeners', function () {
function foo() {}
function bar() {}
ee.once('foo', foo);
ultron.once('foo', bar);
assume(ee.listeners('foo').length).equals(2);
ultron.remove('foo');
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('foo')[0]).equals(foo);
});
it('removes only our assigned `once` listeners from regular EE', function () {
var ee = new EE()
, ultron = new Ultron(ee);
function foo() {}
function bar() {}
ee.once('foo', foo);
ultron.once('foo', bar);
assume(ee.listeners('foo').length).equals(2);
ultron.remove('foo');
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('foo')[0].listener).equals(foo);
});
it('removes all assigned events if called without args', function () {
function foo() {}
function bar() {}
ultron.on('foo', foo);
ultron.on('bar', bar);
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('bar').length).equals(1);
ultron.remove();
assume(ee.listeners('foo').length).equals(0);
assume(ee.listeners('bar').length).equals(0);
});
it('removes multiple listeners based on args', function () {
function foo() {}
function bar() {}
function baz() {}
ultron.on('foo', foo);
ultron.on('bar', bar);
ultron.on('baz', baz);
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('bar').length).equals(1);
assume(ee.listeners('baz').length).equals(1);
ultron.remove('foo', 'bar');
assume(ee.listeners('foo').length).equals(0);
assume(ee.listeners('bar').length).equals(0);
assume(ee.listeners('baz').length).equals(1);
});
it('removes multiple listeners if first arg is seperated string', function () {
function foo() {}
function bar() {}
function baz() {}
ultron.on('foo', foo);
ultron.on('bar', bar);
ultron.on('baz', baz);
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('bar').length).equals(1);
assume(ee.listeners('baz').length).equals(1);
ultron.remove('foo, bar');
assume(ee.listeners('foo').length).equals(0);
assume(ee.listeners('bar').length).equals(0);
assume(ee.listeners('baz').length).equals(1);
});
});
describe('#destroy', function () {
it('removes all listeners', function () {
function foo() {}
function bar() {}
function baz() {}
ultron.on('foo', foo);
ultron.on('bar', bar);
ultron.on('baz', baz);
assume(ee.listeners('foo').length).equals(1);
assume(ee.listeners('bar').length).equals(1);
assume(ee.listeners('baz').length).equals(1);
ultron.destroy();
assume(ee.listeners('foo').length).equals(0);
assume(ee.listeners('bar').length).equals(0);
assume(ee.listeners('baz').length).equals(0);
});
it('removes the .ee reference', function () {
assume(ultron.ee).equals(ee);
ultron.destroy();
assume(ultron.ee).equals(null);
});
it('returns booleans for state indication', function () {
assume(ultron.destroy()).is.true();
assume(ultron.destroy()).is.false();
assume(ultron.destroy()).is.false();
assume(ultron.destroy()).is.false();
});
});
});

11
node_modules/ws/.npmignore generated vendored Normal file
View File

@ -0,0 +1,11 @@
npm-debug.log
node_modules
.*.swp
.lock-*
build
bench
doc
examples
test

15
node_modules/ws/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,15 @@
language: node_js
sudo: false
node_js:
- "5"
- "4"
- "0.12"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-4.9
- g++-4.9
before_install:
- export CC="gcc-4.9" CXX="g++-4.9"

40
node_modules/ws/Makefile generated vendored Normal file
View File

@ -0,0 +1,40 @@
ALL_TESTS = $(shell find test/ -name '*.test.js')
ALL_INTEGRATION = $(shell find test/ -name '*.integration.js')
all:
node-gyp configure build
clean:
node-gyp clean
run-tests:
@./node_modules/.bin/mocha \
-t 5000 \
-s 2400 \
$(TESTFLAGS) \
$(TESTS)
run-integrationtests:
@./node_modules/.bin/mocha \
-t 5000 \
-s 6000 \
$(TESTFLAGS) \
$(TESTS)
test:
@$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
integrationtest:
@$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_INTEGRATION)" run-integrationtests
benchmark:
@node bench/sender.benchmark.js
@node bench/parser.benchmark.js
autobahn:
@NODE_PATH=lib node test/autobahn.js
autobahn-server:
@NODE_PATH=lib node test/autobahn-server.js
.PHONY: test

242
node_modules/ws/README.md generated vendored Normal file
View File

@ -0,0 +1,242 @@
# ws: a node.js websocket library
[![Build Status](https://travis-ci.org/websockets/ws.svg?branch=master)](https://travis-ci.org/websockets/ws)
`ws` is a simple to use WebSocket implementation, up-to-date against RFC-6455,
and [probably the fastest WebSocket library for node.js][archive].
Passes the quite extensive Autobahn test suite. See http://websockets.github.com/ws
for the full reports.
## Protocol support
* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera.
Added to ws version 0.4.2, but server only. Can be disabled by setting the
`disableHixie` option to true.)
* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`)
### Installing
```
npm install --save ws
```
### Opt-in for performance
There are 2 optional modules that can be installed along side with the `ws`
module. These modules are binary addons which improve certain operations, but as
they are binary addons they require compilation which can fail if no c++
compiler is installed on the host system.
- `npm install --save bufferutil`: Improves internal buffer operations which
allows for faster processing of masked WebSocket frames and general buffer
operations.
- `npm install --save utf-8-validate`: The specification requires validation of
invalid UTF-8 chars, some of these validations could not be done in JavaScript
hence the need for a binary addon. In most cases you will already be
validating the input that you receive for security purposes leading to double
validation. But if you want to be 100% spec conform and fast validation of UTF-8
then this module is a must.
### Sending and receiving text data
```js
var WebSocket = require('ws');
var ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
ws.send('something');
});
ws.on('message', function(data, flags) {
// flags.binary will be set if a binary data is received.
// flags.masked will be set if the data was masked.
});
```
### Sending binary data
```js
var WebSocket = require('ws');
var ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
var array = new Float32Array(5);
for (var i = 0; i < array.length; ++i) {
array[i] = i / 2;
}
ws.send(array, { binary: true, mask: true });
});
```
Setting `mask`, as done for the send options above, will cause the data to be
masked according to the WebSocket protocol. The same option applies for text
data.
### Server example
```js
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
```
### ExpressJS example
```js
var server = require('http').createServer()
, url = require('url')
, WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ server: server })
, express = require('express')
, app = express()
, port = 4080;
app.use(function (req, res) {
res.send({ msg: "hello" });
});
wss.on('connection', function connection(ws) {
var location = url.parse(ws.upgradeReq.url, true);
// you might use location.query.access_token to authenticate or share sessions
// or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
server.on('request', app);
server.listen(port, function () { console.log('Listening on ' + server.address().port) });
```
### Server sending broadcast data
```js
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
client.send(data);
});
};
```
### Error handling best practices
```js
// If the WebSocket is closed before the following send is attempted
ws.send('something');
// Errors (both immediate and async write errors) can be detected in an optional
// callback. The callback is also the only way of being notified that data has
// actually been sent.
ws.send('something', function ack(error) {
// if error is not defined, the send has been completed,
// otherwise the error object will indicate what failed.
});
// Immediate errors can also be handled with try/catch-blocks, but **note** that
// since sends are inherently asynchronous, socket write failures will *not* be
// captured when this technique is used.
try { ws.send('something'); }
catch (e) { /* handle error */ }
```
### echo.websocket.org demo
```js
var WebSocket = require('ws');
var ws = new WebSocket('ws://echo.websocket.org/', {
protocolVersion: 8,
origin: 'http://websocket.org'
});
ws.on('open', function open() {
console.log('connected');
ws.send(Date.now().toString(), {mask: true});
});
ws.on('close', function close() {
console.log('disconnected');
});
ws.on('message', function message(data, flags) {
console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);
setTimeout(function timeout() {
ws.send(Date.now().toString(), {mask: true});
}, 500);
});
```
### Browserify users
When including ws via a browserify bundle, ws returns global.WebSocket which has slightly different API.
You should use the standard WebSockets API instead.
https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications#Availability_of_WebSockets
### Other examples
For a full example with a browser client communicating with a ws server, see the
examples folder.
Note that the usage together with Express 3.0 is quite different from Express
2.x. The difference is expressed in the two different serverstats-examples.
Otherwise, see the test cases.
### Running the tests
```
make test
```
## API Docs
See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md) for Node.js-like docs for the ws classes.
## Changelog
We're using the GitHub [`releases`](https://github.com/websockets/ws/releases) for changelog entries.
## License
(The MIT License)
Copyright (c) 2011 Einar Otto Stangvik &lt;einaros@gmail.com&gt;
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.
[archive]: http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs

49
node_modules/ws/index.js generated vendored Normal file
View File

@ -0,0 +1,49 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var WS = module.exports = require('./lib/WebSocket');
WS.Server = require('./lib/WebSocketServer');
WS.Sender = require('./lib/Sender');
WS.Receiver = require('./lib/Receiver');
/**
* Create a new WebSocket server.
*
* @param {Object} options Server options
* @param {Function} fn Optional connection listener.
* @returns {WS.Server}
* @api public
*/
WS.createServer = function createServer(options, fn) {
var server = new WS.Server(options);
if (typeof fn === 'function') {
server.on('connection', fn);
}
return server;
};
/**
* Create a new WebSocket connection.
*
* @param {String} address The URL/address we need to connect to.
* @param {Function} fn Open listener.
* @returns {WS}
* @api public
*/
WS.connect = WS.createConnection = function connect(address, fn) {
var client = new WS(address);
if (typeof fn === 'function') {
client.on('open', fn);
}
return client;
};

63
node_modules/ws/lib/BufferPool.js generated vendored Normal file
View File

@ -0,0 +1,63 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util');
function BufferPool(initialSize, growStrategy, shrinkStrategy) {
if (this instanceof BufferPool === false) {
throw new TypeError("Classes can't be function-called");
}
if (typeof initialSize === 'function') {
shrinkStrategy = growStrategy;
growStrategy = initialSize;
initialSize = 0;
}
else if (typeof initialSize === 'undefined') {
initialSize = 0;
}
this._growStrategy = (growStrategy || function(db, size) {
return db.used + size;
}).bind(null, this);
this._shrinkStrategy = (shrinkStrategy || function(db) {
return initialSize;
}).bind(null, this);
this._buffer = initialSize ? new Buffer(initialSize) : null;
this._offset = 0;
this._used = 0;
this._changeFactor = 0;
this.__defineGetter__('size', function(){
return this._buffer == null ? 0 : this._buffer.length;
});
this.__defineGetter__('used', function(){
return this._used;
});
}
BufferPool.prototype.get = function(length) {
if (this._buffer == null || this._offset + length > this._buffer.length) {
var newBuf = new Buffer(this._growStrategy(length));
this._buffer = newBuf;
this._offset = 0;
}
this._used += length;
var buf = this._buffer.slice(this._offset, this._offset + length);
this._offset += length;
return buf;
}
BufferPool.prototype.reset = function(forceNewBuffer) {
var len = this._shrinkStrategy();
if (len < this.size) this._changeFactor -= 1;
if (forceNewBuffer || this._changeFactor < -2) {
this._changeFactor = 0;
this._buffer = len ? new Buffer(len) : null;
}
this._offset = 0;
this._used = 0;
}
module.exports = BufferPool;

47
node_modules/ws/lib/BufferUtil.fallback.js generated vendored Normal file
View File

@ -0,0 +1,47 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
module.exports.BufferUtil = {
merge: function(mergedBuffer, buffers) {
var offset = 0;
for (var i = 0, l = buffers.length; i < l; ++i) {
var buf = buffers[i];
buf.copy(mergedBuffer, offset);
offset += buf.length;
}
},
mask: function(source, mask, output, offset, length) {
var maskNum = mask.readUInt32LE(0, true);
var i = 0;
for (; i < length - 3; i += 4) {
var num = maskNum ^ source.readUInt32LE(i, true);
if (num < 0) num = 4294967296 + num;
output.writeUInt32LE(num, offset + i, true);
}
switch (length % 4) {
case 3: output[offset + i + 2] = source[i + 2] ^ mask[2];
case 2: output[offset + i + 1] = source[i + 1] ^ mask[1];
case 1: output[offset + i] = source[i] ^ mask[0];
case 0:;
}
},
unmask: function(data, mask) {
var maskNum = mask.readUInt32LE(0, true);
var length = data.length;
var i = 0;
for (; i < length - 3; i += 4) {
var num = maskNum ^ data.readUInt32LE(i, true);
if (num < 0) num = 4294967296 + num;
data.writeUInt32LE(num, i, true);
}
switch (length % 4) {
case 3: data[i + 2] = data[i + 2] ^ mask[2];
case 2: data[i + 1] = data[i + 1] ^ mask[1];
case 1: data[i] = data[i] ^ mask[0];
case 0:;
}
}
}

13
node_modules/ws/lib/BufferUtil.js generated vendored Normal file
View File

@ -0,0 +1,13 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
try {
module.exports = require('bufferutil');
} catch (e) {
module.exports = require('./BufferUtil.fallback');
}

24
node_modules/ws/lib/ErrorCodes.js generated vendored Normal file
View File

@ -0,0 +1,24 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
module.exports = {
isValidErrorCode: function(code) {
return (code >= 1000 && code <= 1011 && code != 1004 && code != 1005 && code != 1006) ||
(code >= 3000 && code <= 4999);
},
1000: 'normal',
1001: 'going away',
1002: 'protocol error',
1003: 'unsupported data',
1004: 'reserved',
1005: 'reserved for extensions',
1006: 'reserved for extensions',
1007: 'inconsistent or invalid data',
1008: 'policy violation',
1009: 'message too big',
1010: 'extension handshake missing',
1011: 'an unexpected condition prevented the request from being fulfilled',
};

70
node_modules/ws/lib/Extensions.js generated vendored Normal file
View File

@ -0,0 +1,70 @@
var util = require('util');
/**
* Module exports.
*/
exports.parse = parse;
exports.format = format;
/**
* Parse extensions header value
*/
function parse(value) {
value = value || '';
var extensions = {};
value.split(',').forEach(function(v) {
var params = v.split(';');
var token = params.shift().trim();
var paramsList = extensions[token] = extensions[token] || [];
var parsedParams = {};
params.forEach(function(param) {
var parts = param.trim().split('=');
var key = parts[0];
var value = parts[1];
if (typeof value === 'undefined') {
value = true;
} else {
// unquote value
if (value[0] === '"') {
value = value.slice(1);
}
if (value[value.length - 1] === '"') {
value = value.slice(0, value.length - 1);
}
}
(parsedParams[key] = parsedParams[key] || []).push(value);
});
paramsList.push(parsedParams);
});
return extensions;
}
/**
* Format extensions header value
*/
function format(value) {
return Object.keys(value).map(function(token) {
var paramsList = value[token];
if (!util.isArray(paramsList)) {
paramsList = [paramsList];
}
return paramsList.map(function(params) {
return [token].concat(Object.keys(params).map(function(k) {
var p = params[k];
if (!util.isArray(p)) p = [p];
return p.map(function(v) {
return v === true ? k : k + '=' + v;
}).join('; ');
})).join('; ');
}).join(', ');
}).join(', ');
}

325
node_modules/ws/lib/PerMessageDeflate.js generated vendored Normal file
View File

@ -0,0 +1,325 @@
var zlib = require('zlib');
var AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15];
var DEFAULT_WINDOW_BITS = 15;
var DEFAULT_MEM_LEVEL = 8;
PerMessageDeflate.extensionName = 'permessage-deflate';
/**
* Per-message Compression Extensions implementation
*/
function PerMessageDeflate(options, isServer) {
if (this instanceof PerMessageDeflate === false) {
throw new TypeError("Classes can't be function-called");
}
this._options = options || {};
this._isServer = !!isServer;
this._inflate = null;
this._deflate = null;
this.params = null;
}
/**
* Create extension parameters offer
*
* @api public
*/
PerMessageDeflate.prototype.offer = function() {
var params = {};
if (this._options.serverNoContextTakeover) {
params.server_no_context_takeover = true;
}
if (this._options.clientNoContextTakeover) {
params.client_no_context_takeover = true;
}
if (this._options.serverMaxWindowBits) {
params.server_max_window_bits = this._options.serverMaxWindowBits;
}
if (this._options.clientMaxWindowBits) {
params.client_max_window_bits = this._options.clientMaxWindowBits;
} else if (this._options.clientMaxWindowBits == null) {
params.client_max_window_bits = true;
}
return params;
};
/**
* Accept extension offer
*
* @api public
*/
PerMessageDeflate.prototype.accept = function(paramsList) {
paramsList = this.normalizeParams(paramsList);
var params;
if (this._isServer) {
params = this.acceptAsServer(paramsList);
} else {
params = this.acceptAsClient(paramsList);
}
this.params = params;
return params;
};
/**
* Releases all resources used by the extension
*
* @api public
*/
PerMessageDeflate.prototype.cleanup = function() {
if (this._inflate) {
if (this._inflate.writeInProgress) {
this._inflate.pendingClose = true;
} else {
if (this._inflate.close) this._inflate.close();
this._inflate = null;
}
}
if (this._deflate) {
if (this._deflate.writeInProgress) {
this._deflate.pendingClose = true;
} else {
if (this._deflate.close) this._deflate.close();
this._deflate = null;
}
}
};
/**
* Accept extension offer from client
*
* @api private
*/
PerMessageDeflate.prototype.acceptAsServer = function(paramsList) {
var accepted = {};
var result = paramsList.some(function(params) {
accepted = {};
if (this._options.serverNoContextTakeover === false && params.server_no_context_takeover) {
return;
}
if (this._options.serverMaxWindowBits === false && params.server_max_window_bits) {
return;
}
if (typeof this._options.serverMaxWindowBits === 'number' &&
typeof params.server_max_window_bits === 'number' &&
this._options.serverMaxWindowBits > params.server_max_window_bits) {
return;
}
if (typeof this._options.clientMaxWindowBits === 'number' && !params.client_max_window_bits) {
return;
}
if (this._options.serverNoContextTakeover || params.server_no_context_takeover) {
accepted.server_no_context_takeover = true;
}
if (this._options.clientNoContextTakeover) {
accepted.client_no_context_takeover = true;
}
if (this._options.clientNoContextTakeover !== false && params.client_no_context_takeover) {
accepted.client_no_context_takeover = true;
}
if (typeof this._options.serverMaxWindowBits === 'number') {
accepted.server_max_window_bits = this._options.serverMaxWindowBits;
} else if (typeof params.server_max_window_bits === 'number') {
accepted.server_max_window_bits = params.server_max_window_bits;
}
if (typeof this._options.clientMaxWindowBits === 'number') {
accepted.client_max_window_bits = this._options.clientMaxWindowBits;
} else if (this._options.clientMaxWindowBits !== false && typeof params.client_max_window_bits === 'number') {
accepted.client_max_window_bits = params.client_max_window_bits;
}
return true;
}, this);
if (!result) {
throw new Error('Doesn\'t support the offered configuration');
}
return accepted;
};
/**
* Accept extension response from server
*
* @api privaye
*/
PerMessageDeflate.prototype.acceptAsClient = function(paramsList) {
var params = paramsList[0];
if (this._options.clientNoContextTakeover != null) {
if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) {
throw new Error('Invalid value for "client_no_context_takeover"');
}
}
if (this._options.clientMaxWindowBits != null) {
if (this._options.clientMaxWindowBits === false && params.client_max_window_bits) {
throw new Error('Invalid value for "client_max_window_bits"');
}
if (typeof this._options.clientMaxWindowBits === 'number' &&
(!params.client_max_window_bits || params.client_max_window_bits > this._options.clientMaxWindowBits)) {
throw new Error('Invalid value for "client_max_window_bits"');
}
}
return params;
};
/**
* Normalize extensions parameters
*
* @api private
*/
PerMessageDeflate.prototype.normalizeParams = function(paramsList) {
return paramsList.map(function(params) {
Object.keys(params).forEach(function(key) {
var value = params[key];
if (value.length > 1) {
throw new Error('Multiple extension parameters for ' + key);
}
value = value[0];
switch (key) {
case 'server_no_context_takeover':
case 'client_no_context_takeover':
if (value !== true) {
throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
}
params[key] = true;
break;
case 'server_max_window_bits':
case 'client_max_window_bits':
if (typeof value === 'string') {
value = parseInt(value, 10);
if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) {
throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
}
}
if (!this._isServer && value === true) {
throw new Error('Missing extension parameter value for ' + key);
}
params[key] = value;
break;
default:
throw new Error('Not defined extension parameter (' + key + ')');
}
}, this);
return params;
}, this);
};
/**
* Decompress message
*
* @api public
*/
PerMessageDeflate.prototype.decompress = function (data, fin, callback) {
var endpoint = this._isServer ? 'client' : 'server';
if (!this._inflate) {
var maxWindowBits = this.params[endpoint + '_max_window_bits'];
this._inflate = zlib.createInflateRaw({
windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS
});
}
this._inflate.writeInProgress = true;
var self = this;
var buffers = [];
this._inflate.on('error', onError).on('data', onData);
this._inflate.write(data);
if (fin) {
this._inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff]));
}
this._inflate.flush(function() {
cleanup();
callback(null, Buffer.concat(buffers));
});
function onError(err) {
cleanup();
callback(err);
}
function onData(data) {
buffers.push(data);
}
function cleanup() {
if (!self._inflate) return;
self._inflate.removeListener('error', onError);
self._inflate.removeListener('data', onData);
self._inflate.writeInProgress = false;
if ((fin && self.params[endpoint + '_no_context_takeover']) || self._inflate.pendingClose) {
if (self._inflate.close) self._inflate.close();
self._inflate = null;
}
}
};
/**
* Compress message
*
* @api public
*/
PerMessageDeflate.prototype.compress = function (data, fin, callback) {
var endpoint = this._isServer ? 'server' : 'client';
if (!this._deflate) {
var maxWindowBits = this.params[endpoint + '_max_window_bits'];
this._deflate = zlib.createDeflateRaw({
flush: zlib.Z_SYNC_FLUSH,
windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS,
memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL
});
}
this._deflate.writeInProgress = true;
var self = this;
var buffers = [];
this._deflate.on('error', onError).on('data', onData);
this._deflate.write(data);
this._deflate.flush(function() {
cleanup();
var data = Buffer.concat(buffers);
if (fin) {
data = data.slice(0, data.length - 4);
}
callback(null, data);
});
function onError(err) {
cleanup();
callback(err);
}
function onData(data) {
buffers.push(data);
}
function cleanup() {
if (!self._deflate) return;
self._deflate.removeListener('error', onError);
self._deflate.removeListener('data', onData);
self._deflate.writeInProgress = false;
if ((fin && self.params[endpoint + '_no_context_takeover']) || self._deflate.pendingClose) {
if (self._deflate.close) self._deflate.close();
self._deflate = null;
}
}
};
module.exports = PerMessageDeflate;

184
node_modules/ws/lib/Receiver.hixie.js generated vendored Normal file
View File

@ -0,0 +1,184 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util');
/**
* State constants
*/
var EMPTY = 0
, BODY = 1;
var BINARYLENGTH = 2
, BINARYBODY = 3;
/**
* Hixie Receiver implementation
*/
function Receiver () {
if (this instanceof Receiver === false) {
throw new TypeError("Classes can't be function-called");
}
this.state = EMPTY;
this.buffers = [];
this.messageEnd = -1;
this.spanLength = 0;
this.dead = false;
this.onerror = function() {};
this.ontext = function() {};
this.onbinary = function() {};
this.onclose = function() {};
this.onping = function() {};
this.onpong = function() {};
}
module.exports = Receiver;
/**
* Add new data to the parser.
*
* @api public
*/
Receiver.prototype.add = function(data) {
var self = this;
function doAdd() {
if (self.state === EMPTY) {
if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
self.reset();
self.onclose();
return;
}
if (data[0] === 0x80) {
self.messageEnd = 0;
self.state = BINARYLENGTH;
data = data.slice(1);
} else {
if (data[0] !== 0x00) {
self.error('payload must start with 0x00 byte', true);
return;
}
data = data.slice(1);
self.state = BODY;
}
}
if (self.state === BINARYLENGTH) {
var i = 0;
while ((i < data.length) && (data[i] & 0x80)) {
self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
++i;
}
if (i < data.length) {
self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
self.state = BINARYBODY;
++i;
}
if (i > 0)
data = data.slice(i);
}
if (self.state === BINARYBODY) {
var dataleft = self.messageEnd - self.spanLength;
if (data.length >= dataleft) {
// consume the whole buffer to finish the frame
self.buffers.push(data);
self.spanLength += dataleft;
self.messageEnd = dataleft;
return self.parse();
}
// frame's not done even if we consume it all
self.buffers.push(data);
self.spanLength += data.length;
return;
}
self.buffers.push(data);
if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
self.spanLength += self.messageEnd;
return self.parse();
}
else self.spanLength += data.length;
}
while(data) data = doAdd();
};
/**
* Releases all resources used by the receiver.
*
* @api public
*/
Receiver.prototype.cleanup = function() {
this.dead = true;
this.state = EMPTY;
this.buffers = [];
};
/**
* Process buffered data.
*
* @api public
*/
Receiver.prototype.parse = function() {
var output = new Buffer(this.spanLength);
var outputIndex = 0;
for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
var buffer = this.buffers[bi];
buffer.copy(output, outputIndex);
outputIndex += buffer.length;
}
var lastBuffer = this.buffers[this.buffers.length - 1];
if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
if (this.state !== BODY) --this.messageEnd;
var tail = null;
if (this.messageEnd < lastBuffer.length - 1) {
tail = lastBuffer.slice(this.messageEnd + 1);
}
this.reset();
this.ontext(output.toString('utf8'));
return tail;
};
/**
* Handles an error
*
* @api private
*/
Receiver.prototype.error = function (reason, terminate) {
this.reset();
this.onerror(reason, terminate);
return this;
};
/**
* Reset parser state
*
* @api private
*/
Receiver.prototype.reset = function (reason) {
if (this.dead) return;
this.state = EMPTY;
this.buffers = [];
this.messageEnd = -1;
this.spanLength = 0;
};
/**
* Internal api
*/
function bufferIndex(buffer, byte) {
for (var i = 0, l = buffer.length; i < l; ++i) {
if (buffer[i] === byte) return i;
}
return -1;
}

702
node_modules/ws/lib/Receiver.js generated vendored Normal file
View File

@ -0,0 +1,702 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util')
, Validation = require('./Validation').Validation
, ErrorCodes = require('./ErrorCodes')
, BufferPool = require('./BufferPool')
, bufferUtil = require('./BufferUtil').BufferUtil
, PerMessageDeflate = require('./PerMessageDeflate');
/**
* HyBi Receiver implementation
*/
function Receiver (extensions) {
if (this instanceof Receiver === false) {
throw new TypeError("Classes can't be function-called");
}
// memory pool for fragmented messages
var fragmentedPoolPrevUsed = -1;
this.fragmentedBufferPool = new BufferPool(1024, function(db, length) {
return db.used + length;
}, function(db) {
return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ?
Math.ceil((fragmentedPoolPrevUsed + db.used) / 2) :
db.used;
});
// memory pool for unfragmented messages
var unfragmentedPoolPrevUsed = -1;
this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) {
return db.used + length;
}, function(db) {
return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ?
Math.ceil((unfragmentedPoolPrevUsed + db.used) / 2) :
db.used;
});
this.extensions = extensions || {};
this.state = {
activeFragmentedOperation: null,
lastFragment: false,
masked: false,
opcode: 0,
fragmentedOperation: false
};
this.overflow = [];
this.headerBuffer = new Buffer(10);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
this.currentMessage = [];
this.messageHandlers = [];
this.expectHeader(2, this.processPacket);
this.dead = false;
this.processing = false;
this.onerror = function() {};
this.ontext = function() {};
this.onbinary = function() {};
this.onclose = function() {};
this.onping = function() {};
this.onpong = function() {};
}
module.exports = Receiver;
/**
* Add new data to the parser.
*
* @api public
*/
Receiver.prototype.add = function(data) {
var dataLength = data.length;
if (dataLength == 0) return;
if (this.expectBuffer == null) {
this.overflow.push(data);
return;
}
var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset);
fastCopy(toRead, data, this.expectBuffer, this.expectOffset);
this.expectOffset += toRead;
if (toRead < dataLength) {
this.overflow.push(data.slice(toRead));
}
while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) {
var bufferForHandler = this.expectBuffer;
this.expectBuffer = null;
this.expectOffset = 0;
this.expectHandler.call(this, bufferForHandler);
}
};
/**
* Releases all resources used by the receiver.
*
* @api public
*/
Receiver.prototype.cleanup = function() {
this.dead = true;
this.overflow = null;
this.headerBuffer = null;
this.expectBuffer = null;
this.expectHandler = null;
this.unfragmentedBufferPool = null;
this.fragmentedBufferPool = null;
this.state = null;
this.currentMessage = null;
this.onerror = null;
this.ontext = null;
this.onbinary = null;
this.onclose = null;
this.onping = null;
this.onpong = null;
};
/**
* Waits for a certain amount of header bytes to be available, then fires a callback.
*
* @api private
*/
Receiver.prototype.expectHeader = function(length, handler) {
if (length == 0) {
handler(null);
return;
}
this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length);
this.expectHandler = handler;
var toRead = length;
while (toRead > 0 && this.overflow.length > 0) {
var fromOverflow = this.overflow.pop();
if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
var read = Math.min(fromOverflow.length, toRead);
fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
this.expectOffset += read;
toRead -= read;
}
};
/**
* Waits for a certain amount of data bytes to be available, then fires a callback.
*
* @api private
*/
Receiver.prototype.expectData = function(length, handler) {
if (length == 0) {
handler(null);
return;
}
this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation);
this.expectHandler = handler;
var toRead = length;
while (toRead > 0 && this.overflow.length > 0) {
var fromOverflow = this.overflow.pop();
if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
var read = Math.min(fromOverflow.length, toRead);
fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
this.expectOffset += read;
toRead -= read;
}
};
/**
* Allocates memory from the buffer pool.
*
* @api private
*/
Receiver.prototype.allocateFromPool = function(length, isFragmented) {
return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length);
};
/**
* Start processing a new packet.
*
* @api private
*/
Receiver.prototype.processPacket = function (data) {
if (this.extensions[PerMessageDeflate.extensionName]) {
if ((data[0] & 0x30) != 0) {
this.error('reserved fields (2, 3) must be empty', 1002);
return;
}
} else {
if ((data[0] & 0x70) != 0) {
this.error('reserved fields must be empty', 1002);
return;
}
}
this.state.lastFragment = (data[0] & 0x80) == 0x80;
this.state.masked = (data[1] & 0x80) == 0x80;
var compressed = (data[0] & 0x40) == 0x40;
var opcode = data[0] & 0xf;
if (opcode === 0) {
if (compressed) {
this.error('continuation frame cannot have the Per-message Compressed bits', 1002);
return;
}
// continuation frame
this.state.fragmentedOperation = true;
this.state.opcode = this.state.activeFragmentedOperation;
if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
this.error('continuation frame cannot follow current opcode', 1002);
return;
}
}
else {
if (opcode < 3 && this.state.activeFragmentedOperation != null) {
this.error('data frames after the initial data frame must have opcode 0', 1002);
return;
}
if (opcode >= 8 && compressed) {
this.error('control frames cannot have the Per-message Compressed bits', 1002);
return;
}
this.state.compressed = compressed;
this.state.opcode = opcode;
if (this.state.lastFragment === false) {
this.state.fragmentedOperation = true;
this.state.activeFragmentedOperation = opcode;
}
else this.state.fragmentedOperation = false;
}
var handler = opcodes[this.state.opcode];
if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002);
else {
handler.start.call(this, data);
}
};
/**
* Endprocessing a packet.
*
* @api private
*/
Receiver.prototype.endPacket = function() {
if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true);
else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
if (this.state.lastFragment && this.state.opcode === this.state.activeFragmentedOperation) {
// end current fragmented operation
this.state.activeFragmentedOperation = null;
}
this.state.lastFragment = false;
this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0;
this.state.masked = false;
this.expectHeader(2, this.processPacket);
};
/**
* Reset the parser state.
*
* @api private
*/
Receiver.prototype.reset = function() {
if (this.dead) return;
this.state = {
activeFragmentedOperation: null,
lastFragment: false,
masked: false,
opcode: 0,
fragmentedOperation: false
};
this.fragmentedBufferPool.reset(true);
this.unfragmentedBufferPool.reset(true);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
this.overflow = [];
this.currentMessage = [];
this.messageHandlers = [];
};
/**
* Unmask received data.
*
* @api private
*/
Receiver.prototype.unmask = function (mask, buf, binary) {
if (mask != null && buf != null) bufferUtil.unmask(buf, mask);
if (binary) return buf;
return buf != null ? buf.toString('utf8') : '';
};
/**
* Concatenates a list of buffers.
*
* @api private
*/
Receiver.prototype.concatBuffers = function(buffers) {
var length = 0;
for (var i = 0, l = buffers.length; i < l; ++i) length += buffers[i].length;
var mergedBuffer = new Buffer(length);
bufferUtil.merge(mergedBuffer, buffers);
return mergedBuffer;
};
/**
* Handles an error
*
* @api private
*/
Receiver.prototype.error = function (reason, protocolErrorCode) {
this.reset();
this.onerror(reason, protocolErrorCode);
return this;
};
/**
* Execute message handler buffers
*
* @api private
*/
Receiver.prototype.flush = function() {
if (this.processing || this.dead) return;
var handler = this.messageHandlers.shift();
if (!handler) return;
this.processing = true;
var self = this;
handler(function() {
self.processing = false;
self.flush();
});
};
/**
* Apply extensions to message
*
* @api private
*/
Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, callback) {
var self = this;
if (compressed) {
this.extensions[PerMessageDeflate.extensionName].decompress(messageBuffer, fin, function(err, buffer) {
if (self.dead) return;
if (err) {
callback(new Error('invalid compressed data'));
return;
}
callback(null, buffer);
});
} else {
callback(null, messageBuffer);
}
};
/**
* Buffer utilities
*/
function readUInt16BE(start) {
return (this[start]<<8) +
this[start+1];
}
function readUInt32BE(start) {
return (this[start]<<24) +
(this[start+1]<<16) +
(this[start+2]<<8) +
this[start+3];
}
function fastCopy(length, srcBuffer, dstBuffer, dstOffset) {
switch (length) {
default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break;
case 16: dstBuffer[dstOffset+15] = srcBuffer[15];
case 15: dstBuffer[dstOffset+14] = srcBuffer[14];
case 14: dstBuffer[dstOffset+13] = srcBuffer[13];
case 13: dstBuffer[dstOffset+12] = srcBuffer[12];
case 12: dstBuffer[dstOffset+11] = srcBuffer[11];
case 11: dstBuffer[dstOffset+10] = srcBuffer[10];
case 10: dstBuffer[dstOffset+9] = srcBuffer[9];
case 9: dstBuffer[dstOffset+8] = srcBuffer[8];
case 8: dstBuffer[dstOffset+7] = srcBuffer[7];
case 7: dstBuffer[dstOffset+6] = srcBuffer[6];
case 6: dstBuffer[dstOffset+5] = srcBuffer[5];
case 5: dstBuffer[dstOffset+4] = srcBuffer[4];
case 4: dstBuffer[dstOffset+3] = srcBuffer[3];
case 3: dstBuffer[dstOffset+2] = srcBuffer[2];
case 2: dstBuffer[dstOffset+1] = srcBuffer[1];
case 1: dstBuffer[dstOffset] = srcBuffer[0];
}
}
function clone(obj) {
var cloned = {};
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
cloned[k] = obj[k];
}
}
return cloned;
}
/**
* Opcode handlers
*/
var opcodes = {
// text
'1': {
start: function(data) {
var self = this;
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['1'].getData.call(self, firstLength);
}
else if (firstLength == 126) {
self.expectHeader(2, function(data) {
opcodes['1'].getData.call(self, readUInt16BE.call(data, 0));
});
}
else if (firstLength == 127) {
self.expectHeader(8, function(data) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
return;
}
opcodes['1'].getData.call(self, readUInt32BE.call(data, 4));
});
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['1'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['1'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
var packet = this.unmask(mask, data, true) || new Buffer(0);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
if (err) return self.error(err.message, 1007);
if (buffer != null) self.currentMessage.push(buffer);
if (state.lastFragment) {
var messageBuffer = self.concatBuffers(self.currentMessage);
self.currentMessage = [];
if (!Validation.isValidUTF8(messageBuffer)) {
self.error('invalid utf8 sequence', 1007);
return;
}
self.ontext(messageBuffer.toString('utf8'), {masked: state.masked, buffer: messageBuffer});
}
callback();
});
});
this.flush();
this.endPacket();
}
},
// binary
'2': {
start: function(data) {
var self = this;
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['2'].getData.call(self, firstLength);
}
else if (firstLength == 126) {
self.expectHeader(2, function(data) {
opcodes['2'].getData.call(self, readUInt16BE.call(data, 0));
});
}
else if (firstLength == 127) {
self.expectHeader(8, function(data) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
return;
}
opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true));
});
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['2'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['2'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
var packet = this.unmask(mask, data, true) || new Buffer(0);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
if (err) return self.error(err.message, 1007);
if (buffer != null) self.currentMessage.push(buffer);
if (state.lastFragment) {
var messageBuffer = self.concatBuffers(self.currentMessage);
self.currentMessage = [];
self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer});
}
callback();
});
});
this.flush();
this.endPacket();
}
},
// close
'8': {
start: function(data) {
var self = this;
if (self.state.lastFragment == false) {
self.error('fragmented close is not supported', 1002);
return;
}
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['8'].getData.call(self, firstLength);
}
else {
self.error('control frames cannot have more than 125 bytes of data', 1002);
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['8'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['8'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
data = self.unmask(mask, data, true);
var state = clone(this.state);
this.messageHandlers.push(function() {
if (data && data.length == 1) {
self.error('close packets with data must be at least two bytes long', 1002);
return;
}
var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000;
if (!ErrorCodes.isValidErrorCode(code)) {
self.error('invalid error code', 1002);
return;
}
var message = '';
if (data && data.length > 2) {
var messageBuffer = data.slice(2);
if (!Validation.isValidUTF8(messageBuffer)) {
self.error('invalid utf8 sequence', 1007);
return;
}
message = messageBuffer.toString('utf8');
}
self.onclose(code, message, {masked: state.masked});
self.reset();
});
this.flush();
},
},
// ping
'9': {
start: function(data) {
var self = this;
if (self.state.lastFragment == false) {
self.error('fragmented ping is not supported', 1002);
return;
}
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['9'].getData.call(self, firstLength);
}
else {
self.error('control frames cannot have more than 125 bytes of data', 1002);
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['9'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['9'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
data = this.unmask(mask, data, true);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.onping(data, {masked: state.masked, binary: true});
callback();
});
this.flush();
this.endPacket();
}
},
// pong
'10': {
start: function(data) {
var self = this;
if (self.state.lastFragment == false) {
self.error('fragmented pong is not supported', 1002);
return;
}
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['10'].getData.call(self, firstLength);
}
else {
self.error('control frames cannot have more than 125 bytes of data', 1002);
}
},
getData: function(length) {
var self = this;
if (this.state.masked) {
this.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['10'].finish.call(self, mask, data);
});
});
}
else {
this.expectData(length, function(data) {
opcodes['10'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
data = self.unmask(mask, data, true);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.onpong(data, {masked: state.masked, binary: true});
callback();
});
this.flush();
this.endPacket();
}
}
}

124
node_modules/ws/lib/Sender.hixie.js generated vendored Normal file
View File

@ -0,0 +1,124 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var events = require('events')
, util = require('util')
, EventEmitter = events.EventEmitter;
/**
* Hixie Sender implementation
*/
function Sender(socket) {
if (this instanceof Sender === false) {
throw new TypeError("Classes can't be function-called");
}
events.EventEmitter.call(this);
this.socket = socket;
this.continuationFrame = false;
this.isClosed = false;
}
module.exports = Sender;
/**
* Inherits from EventEmitter.
*/
util.inherits(Sender, events.EventEmitter);
/**
* Frames and writes data.
*
* @api public
*/
Sender.prototype.send = function(data, options, cb) {
if (this.isClosed) return;
var isString = typeof data == 'string'
, length = isString ? Buffer.byteLength(data) : data.length
, lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes
, writeStartMarker = this.continuationFrame == false
, writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin)
, buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0))
, offset = writeStartMarker ? 1 : 0;
if (writeStartMarker) {
if (options && options.binary) {
buffer.write('\x80', 'binary');
// assume length less than 2**14 bytes
if (lengthbytes > 1)
buffer.write(String.fromCharCode(128+length/128), offset++, 'binary');
buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary');
} else
buffer.write('\x00', 'binary');
}
if (isString) buffer.write(data, offset, 'utf8');
else data.copy(buffer, offset, 0);
if (writeEndMarker) {
if (options && options.binary) {
// sending binary, not writing end marker
} else
buffer.write('\xff', offset + length, 'binary');
this.continuationFrame = false;
}
else this.continuationFrame = true;
try {
this.socket.write(buffer, 'binary', cb);
} catch (e) {
this.error(e.toString());
}
};
/**
* Sends a close instruction to the remote party.
*
* @api public
*/
Sender.prototype.close = function(code, data, mask, cb) {
if (this.isClosed) return;
this.isClosed = true;
try {
if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary'));
this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb);
} catch (e) {
this.error(e.toString());
}
};
/**
* Sends a ping message to the remote party. Not available for hixie.
*
* @api public
*/
Sender.prototype.ping = function(data, options) {};
/**
* Sends a pong message to the remote party. Not available for hixie.
*
* @api public
*/
Sender.prototype.pong = function(data, options) {};
/**
* Handles an error
*
* @api private
*/
Sender.prototype.error = function (reason) {
this.emit('error', reason);
return this;
};

324
node_modules/ws/lib/Sender.js generated vendored Normal file
View File

@ -0,0 +1,324 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var events = require('events')
, util = require('util')
, EventEmitter = events.EventEmitter
, ErrorCodes = require('./ErrorCodes')
, bufferUtil = require('./BufferUtil').BufferUtil
, PerMessageDeflate = require('./PerMessageDeflate');
/**
* HyBi Sender implementation
*/
function Sender(socket, extensions) {
if (this instanceof Sender === false) {
throw new TypeError("Classes can't be function-called");
}
events.EventEmitter.call(this);
this._socket = socket;
this.extensions = extensions || {};
this.firstFragment = true;
this.compress = false;
this.messageHandlers = [];
this.processing = false;
}
/**
* Inherits from EventEmitter.
*/
util.inherits(Sender, events.EventEmitter);
/**
* Sends a close instruction to the remote party.
*
* @api public
*/
Sender.prototype.close = function(code, data, mask, cb) {
if (typeof code !== 'undefined') {
if (typeof code !== 'number' ||
!ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number');
}
code = code || 1000;
var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0));
writeUInt16BE.call(dataBuffer, code, 0);
if (dataBuffer.length > 2) dataBuffer.write(data, 2);
var self = this;
this.messageHandlers.push(function(callback) {
self.frameAndSend(0x8, dataBuffer, true, mask);
callback();
if (typeof cb == 'function') cb();
});
this.flush();
};
/**
* Sends a ping message to the remote party.
*
* @api public
*/
Sender.prototype.ping = function(data, options) {
var mask = options && options.mask;
var self = this;
this.messageHandlers.push(function(callback) {
self.frameAndSend(0x9, data || '', true, mask);
callback();
});
this.flush();
};
/**
* Sends a pong message to the remote party.
*
* @api public
*/
Sender.prototype.pong = function(data, options) {
var mask = options && options.mask;
var self = this;
this.messageHandlers.push(function(callback) {
self.frameAndSend(0xa, data || '', true, mask);
callback();
});
this.flush();
};
/**
* Sends text or binary data to the remote party.
*
* @api public
*/
Sender.prototype.send = function(data, options, cb) {
var finalFragment = options && options.fin === false ? false : true;
var mask = options && options.mask;
var compress = options && options.compress;
var opcode = options && options.binary ? 2 : 1;
if (this.firstFragment === false) {
opcode = 0;
compress = false;
} else {
this.firstFragment = false;
this.compress = compress;
}
if (finalFragment) this.firstFragment = true
var compressFragment = this.compress;
var self = this;
this.messageHandlers.push(function(callback) {
self.applyExtensions(data, finalFragment, compressFragment, function(err, data) {
if (err) {
if (typeof cb == 'function') cb(err);
else self.emit('error', err);
return;
}
self.frameAndSend(opcode, data, finalFragment, mask, compress, cb);
callback();
});
});
this.flush();
};
/**
* Frames and sends a piece of data according to the HyBi WebSocket protocol.
*
* @api private
*/
Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, compressed, cb) {
var canModifyData = false;
if (!data) {
try {
this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
return;
}
if (!Buffer.isBuffer(data)) {
canModifyData = true;
if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) {
data = getArrayBuffer(data);
} else {
//
// If people want to send a number, this would allocate the number in
// bytes as memory size instead of storing the number as buffer value. So
// we need to transform it to string in order to prevent possible
// vulnerabilities / memory attacks.
//
if (typeof data === 'number') data = data.toString();
data = new Buffer(data);
}
}
var dataLength = data.length
, dataOffset = maskData ? 6 : 2
, secondByte = dataLength;
if (dataLength >= 65536) {
dataOffset += 8;
secondByte = 127;
}
else if (dataLength > 125) {
dataOffset += 2;
secondByte = 126;
}
var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData);
var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset;
var outputBuffer = new Buffer(totalLength);
outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode;
if (compressed) outputBuffer[0] |= 0x40;
switch (secondByte) {
case 126:
writeUInt16BE.call(outputBuffer, dataLength, 2);
break;
case 127:
writeUInt32BE.call(outputBuffer, 0, 2);
writeUInt32BE.call(outputBuffer, dataLength, 6);
}
if (maskData) {
outputBuffer[1] = secondByte | 0x80;
var mask = this._randomMask || (this._randomMask = getRandomMask());
outputBuffer[dataOffset - 4] = mask[0];
outputBuffer[dataOffset - 3] = mask[1];
outputBuffer[dataOffset - 2] = mask[2];
outputBuffer[dataOffset - 1] = mask[3];
if (mergeBuffers) {
bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength);
try {
this._socket.write(outputBuffer, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
else {
bufferUtil.mask(data, mask, data, 0, dataLength);
try {
this._socket.write(outputBuffer, 'binary');
this._socket.write(data, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
}
else {
outputBuffer[1] = secondByte;
if (mergeBuffers) {
data.copy(outputBuffer, dataOffset);
try {
this._socket.write(outputBuffer, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
else {
try {
this._socket.write(outputBuffer, 'binary');
this._socket.write(data, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
}
};
/**
* Execute message handler buffers
*
* @api private
*/
Sender.prototype.flush = function() {
if (this.processing) return;
var handler = this.messageHandlers.shift();
if (!handler) return;
this.processing = true;
var self = this;
handler(function() {
self.processing = false;
self.flush();
});
};
/**
* Apply extensions to message
*
* @api private
*/
Sender.prototype.applyExtensions = function(data, fin, compress, callback) {
if (compress && data) {
if ((data.buffer || data) instanceof ArrayBuffer) {
data = getArrayBuffer(data);
}
this.extensions[PerMessageDeflate.extensionName].compress(data, fin, callback);
} else {
callback(null, data);
}
};
module.exports = Sender;
function writeUInt16BE(value, offset) {
this[offset] = (value & 0xff00)>>8;
this[offset+1] = value & 0xff;
}
function writeUInt32BE(value, offset) {
this[offset] = (value & 0xff000000)>>24;
this[offset+1] = (value & 0xff0000)>>16;
this[offset+2] = (value & 0xff00)>>8;
this[offset+3] = value & 0xff;
}
function getArrayBuffer(data) {
// data is either an ArrayBuffer or ArrayBufferView.
var array = new Uint8Array(data.buffer || data)
, l = data.byteLength || data.length
, o = data.byteOffset || 0
, buffer = new Buffer(l);
for (var i = 0; i < l; ++i) {
buffer[i] = array[o+i];
}
return buffer;
}
function getRandomMask() {
return new Buffer([
~~(Math.random() * 255),
~~(Math.random() * 255),
~~(Math.random() * 255),
~~(Math.random() * 255)
]);
}

12
node_modules/ws/lib/Validation.fallback.js generated vendored Normal file
View File

@ -0,0 +1,12 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
module.exports.Validation = {
isValidUTF8: function(buffer) {
return true;
}
};

13
node_modules/ws/lib/Validation.js generated vendored Normal file
View File

@ -0,0 +1,13 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
try {
module.exports = require('utf-8-validate');
} catch (e) {
module.exports = require('./Validation.fallback');
}

965
node_modules/ws/lib/WebSocket.js generated vendored Normal file
View File

@ -0,0 +1,965 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var url = require('url')
, util = require('util')
, http = require('http')
, https = require('https')
, crypto = require('crypto')
, stream = require('stream')
, Ultron = require('ultron')
, Options = require('options')
, Sender = require('./Sender')
, Receiver = require('./Receiver')
, SenderHixie = require('./Sender.hixie')
, ReceiverHixie = require('./Receiver.hixie')
, Extensions = require('./Extensions')
, PerMessageDeflate = require('./PerMessageDeflate')
, EventEmitter = require('events').EventEmitter;
/**
* Constants
*/
// Default protocol version
var protocolVersion = 13;
// Close timeout
var closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly
/**
* WebSocket implementation
*
* @constructor
* @param {String} address Connection address.
* @param {String|Array} protocols WebSocket protocols.
* @param {Object} options Additional connection options.
* @api public
*/
function WebSocket(address, protocols, options) {
if (this instanceof WebSocket === false) {
return new WebSocket(address, protocols, options);
}
EventEmitter.call(this);
if (protocols && !Array.isArray(protocols) && 'object' === typeof protocols) {
// accept the "options" Object as the 2nd argument
options = protocols;
protocols = null;
}
if ('string' === typeof protocols) {
protocols = [ protocols ];
}
if (!Array.isArray(protocols)) {
protocols = [];
}
this._socket = null;
this._ultron = null;
this._closeReceived = false;
this.bytesReceived = 0;
this.readyState = null;
this.supports = {};
this.extensions = {};
if (Array.isArray(address)) {
initAsServerClient.apply(this, address.concat(options));
} else {
initAsClient.apply(this, [address, protocols, options]);
}
}
/**
* Inherits from EventEmitter.
*/
util.inherits(WebSocket, EventEmitter);
/**
* Ready States
*/
["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function each(state, index) {
WebSocket.prototype[state] = WebSocket[state] = index;
});
/**
* Gracefully closes the connection, after sending a description message to the server
*
* @param {Object} data to be sent to the server
* @api public
*/
WebSocket.prototype.close = function close(code, data) {
if (this.readyState === WebSocket.CLOSED) return;
if (this.readyState === WebSocket.CONNECTING) {
this.readyState = WebSocket.CLOSED;
return;
}
if (this.readyState === WebSocket.CLOSING) {
if (this._closeReceived && this._isServer) {
this.terminate();
}
return;
}
var self = this;
try {
this.readyState = WebSocket.CLOSING;
this._closeCode = code;
this._closeMessage = data;
var mask = !this._isServer;
this._sender.close(code, data, mask, function(err) {
if (err) self.emit('error', err);
if (self._closeReceived && self._isServer) {
self.terminate();
} else {
// ensure that the connection is cleaned up even when no response of closing handshake.
clearTimeout(self._closeTimer);
self._closeTimer = setTimeout(cleanupWebsocketResources.bind(self, true), closeTimeout);
}
});
} catch (e) {
this.emit('error', e);
}
};
/**
* Pause the client stream
*
* @api public
*/
WebSocket.prototype.pause = function pauser() {
if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
return this._socket.pause();
};
/**
* Sends a ping
*
* @param {Object} data to be sent to the server
* @param {Object} Members - mask: boolean, binary: boolean
* @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
* @api public
*/
WebSocket.prototype.ping = function ping(data, options, dontFailWhenClosed) {
if (this.readyState !== WebSocket.OPEN) {
if (dontFailWhenClosed === true) return;
throw new Error('not opened');
}
options = options || {};
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
this._sender.ping(data, options);
};
/**
* Sends a pong
*
* @param {Object} data to be sent to the server
* @param {Object} Members - mask: boolean, binary: boolean
* @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
* @api public
*/
WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) {
if (this.readyState !== WebSocket.OPEN) {
if (dontFailWhenClosed === true) return;
throw new Error('not opened');
}
options = options || {};
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
this._sender.pong(data, options);
};
/**
* Resume the client stream
*
* @api public
*/
WebSocket.prototype.resume = function resume() {
if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
return this._socket.resume();
};
/**
* Sends a piece of data
*
* @param {Object} data to be sent to the server
* @param {Object} Members - mask: boolean, binary: boolean, compress: boolean
* @param {function} Optional callback which is executed after the send completes
* @api public
*/
WebSocket.prototype.send = function send(data, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
if (this.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else throw new Error('not opened');
return;
}
if (!data) data = '';
if (this._queue) {
var self = this;
this._queue.push(function() { self.send(data, options, cb); });
return;
}
options = options || {};
options.fin = true;
if (typeof options.binary === 'undefined') {
options.binary = (data instanceof ArrayBuffer || data instanceof Buffer ||
data instanceof Uint8Array ||
data instanceof Uint16Array ||
data instanceof Uint32Array ||
data instanceof Int8Array ||
data instanceof Int16Array ||
data instanceof Int32Array ||
data instanceof Float32Array ||
data instanceof Float64Array);
}
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
if (typeof options.compress === 'undefined') options.compress = true;
if (!this.extensions[PerMessageDeflate.extensionName]) {
options.compress = false;
}
var readable = typeof stream.Readable === 'function'
? stream.Readable
: stream.Stream;
if (data instanceof readable) {
startQueue(this);
var self = this;
sendStream(this, data, options, function send(error) {
process.nextTick(function tock() {
executeQueueSends(self);
});
if (typeof cb === 'function') cb(error);
});
} else {
this._sender.send(data, options, cb);
}
};
/**
* Streams data through calls to a user supplied function
*
* @param {Object} Members - mask: boolean, binary: boolean, compress: boolean
* @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'.
* @api public
*/
WebSocket.prototype.stream = function stream(options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
var self = this;
if (typeof cb !== 'function') throw new Error('callback must be provided');
if (this.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else throw new Error('not opened');
return;
}
if (this._queue) {
this._queue.push(function () { self.stream(options, cb); });
return;
}
options = options || {};
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
if (typeof options.compress === 'undefined') options.compress = true;
if (!this.extensions[PerMessageDeflate.extensionName]) {
options.compress = false;
}
startQueue(this);
function send(data, final) {
try {
if (self.readyState !== WebSocket.OPEN) throw new Error('not opened');
options.fin = final === true;
self._sender.send(data, options);
if (!final) process.nextTick(cb.bind(null, null, send));
else executeQueueSends(self);
} catch (e) {
if (typeof cb === 'function') cb(e);
else {
delete self._queue;
self.emit('error', e);
}
}
}
process.nextTick(cb.bind(null, null, send));
};
/**
* Immediately shuts down the connection
*
* @api public
*/
WebSocket.prototype.terminate = function terminate() {
if (this.readyState === WebSocket.CLOSED) return;
if (this._socket) {
this.readyState = WebSocket.CLOSING;
// End the connection
try { this._socket.end(); }
catch (e) {
// Socket error during end() call, so just destroy it right now
cleanupWebsocketResources.call(this, true);
return;
}
// Add a timeout to ensure that the connection is completely
// cleaned up within 30 seconds, even if the clean close procedure
// fails for whatever reason
// First cleanup any pre-existing timeout from an earlier "terminate" call,
// if one exists. Otherwise terminate calls in quick succession will leak timeouts
// and hold the program open for `closeTimout` time.
if (this._closeTimer) { clearTimeout(this._closeTimer); }
this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout);
} else if (this.readyState === WebSocket.CONNECTING) {
cleanupWebsocketResources.call(this, true);
}
};
/**
* Expose bufferedAmount
*
* @api public
*/
Object.defineProperty(WebSocket.prototype, 'bufferedAmount', {
get: function get() {
var amount = 0;
if (this._socket) {
amount = this._socket.bufferSize || 0;
}
return amount;
}
});
/**
* Emulates the W3C Browser based WebSocket interface using function members.
*
* @see http://dev.w3.org/html5/websockets/#the-websocket-interface
* @api public
*/
['open', 'error', 'close', 'message'].forEach(function(method) {
Object.defineProperty(WebSocket.prototype, 'on' + method, {
/**
* Returns the current listener
*
* @returns {Mixed} the set function or undefined
* @api public
*/
get: function get() {
var listener = this.listeners(method)[0];
return listener ? (listener._listener ? listener._listener : listener) : undefined;
},
/**
* Start listening for events
*
* @param {Function} listener the listener
* @returns {Mixed} the set function or undefined
* @api public
*/
set: function set(listener) {
this.removeAllListeners(method);
this.addEventListener(method, listener);
}
});
});
/**
* Emulates the W3C Browser based WebSocket interface using addEventListener.
*
* @see https://developer.mozilla.org/en/DOM/element.addEventListener
* @see http://dev.w3.org/html5/websockets/#the-websocket-interface
* @api public
*/
WebSocket.prototype.addEventListener = function(method, listener) {
var target = this;
function onMessage (data, flags) {
listener.call(target, new MessageEvent(data, !!flags.binary, target));
}
function onClose (code, message) {
listener.call(target, new CloseEvent(code, message, target));
}
function onError (event) {
event.type = 'error';
event.target = target;
listener.call(target, event);
}
function onOpen () {
listener.call(target, new OpenEvent(target));
}
if (typeof listener === 'function') {
if (method === 'message') {
// store a reference so we can return the original function from the
// addEventListener hook
onMessage._listener = listener;
this.on(method, onMessage);
} else if (method === 'close') {
// store a reference so we can return the original function from the
// addEventListener hook
onClose._listener = listener;
this.on(method, onClose);
} else if (method === 'error') {
// store a reference so we can return the original function from the
// addEventListener hook
onError._listener = listener;
this.on(method, onError);
} else if (method === 'open') {
// store a reference so we can return the original function from the
// addEventListener hook
onOpen._listener = listener;
this.on(method, onOpen);
} else {
this.on(method, listener);
}
}
};
module.exports = WebSocket;
module.exports.buildHostHeader = buildHostHeader
/**
* W3C MessageEvent
*
* @see http://www.w3.org/TR/html5/comms.html
* @constructor
* @api private
*/
function MessageEvent(dataArg, isBinary, target) {
this.type = 'message';
this.data = dataArg;
this.target = target;
this.binary = isBinary; // non-standard.
}
/**
* W3C CloseEvent
*
* @see http://www.w3.org/TR/html5/comms.html
* @constructor
* @api private
*/
function CloseEvent(code, reason, target) {
this.type = 'close';
this.wasClean = (typeof code === 'undefined' || code === 1000);
this.code = code;
this.reason = reason;
this.target = target;
}
/**
* W3C OpenEvent
*
* @see http://www.w3.org/TR/html5/comms.html
* @constructor
* @api private
*/
function OpenEvent(target) {
this.type = 'open';
this.target = target;
}
// Append port number to Host header, only if specified in the url
// and non-default
function buildHostHeader(isSecure, hostname, port) {
var headerHost = hostname;
if (hostname) {
if ((isSecure && (port != 443)) || (!isSecure && (port != 80))){
headerHost = headerHost + ':' + port;
}
}
return headerHost;
}
/**
* Entirely private apis,
* which may or may not be bound to a sepcific WebSocket instance.
*/
function initAsServerClient(req, socket, upgradeHead, options) {
options = new Options({
protocolVersion: protocolVersion,
protocol: null,
extensions: {}
}).merge(options);
// expose state properties
this.protocol = options.value.protocol;
this.protocolVersion = options.value.protocolVersion;
this.extensions = options.value.extensions;
this.supports.binary = (this.protocolVersion !== 'hixie-76');
this.upgradeReq = req;
this.readyState = WebSocket.CONNECTING;
this._isServer = true;
// establish connection
if (options.value.protocolVersion === 'hixie-76') {
establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead);
} else {
establishConnection.call(this, Receiver, Sender, socket, upgradeHead);
}
}
function initAsClient(address, protocols, options) {
options = new Options({
origin: null,
protocolVersion: protocolVersion,
host: null,
headers: null,
protocol: protocols.join(','),
agent: null,
// ssl-related options
pfx: null,
key: null,
passphrase: null,
cert: null,
ca: null,
ciphers: null,
rejectUnauthorized: null,
perMessageDeflate: true,
localAddress: null
}).merge(options);
if (options.value.protocolVersion !== 8 && options.value.protocolVersion !== 13) {
throw new Error('unsupported protocol version');
}
// verify URL and establish http class
var serverUrl = url.parse(address);
var isUnixSocket = serverUrl.protocol === 'ws+unix:';
if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url');
var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
var httpObj = isSecure ? https : http;
var port = serverUrl.port || (isSecure ? 443 : 80);
var auth = serverUrl.auth;
// prepare extensions
var extensionsOffer = {};
var perMessageDeflate;
if (options.value.perMessageDeflate) {
perMessageDeflate = new PerMessageDeflate(typeof options.value.perMessageDeflate !== true ? options.value.perMessageDeflate : {}, false);
extensionsOffer[PerMessageDeflate.extensionName] = perMessageDeflate.offer();
}
// expose state properties
this._isServer = false;
this.url = address;
this.protocolVersion = options.value.protocolVersion;
this.supports.binary = (this.protocolVersion !== 'hixie-76');
// begin handshake
var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64');
var shasum = crypto.createHash('sha1');
shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
var expectedServerKey = shasum.digest('base64');
var agent = options.value.agent;
var headerHost = buildHostHeader(isSecure, serverUrl.hostname, port)
var requestOptions = {
port: port,
host: serverUrl.hostname,
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Host': headerHost,
'Sec-WebSocket-Version': options.value.protocolVersion,
'Sec-WebSocket-Key': key
}
};
// If we have basic auth.
if (auth) {
requestOptions.headers.Authorization = 'Basic ' + new Buffer(auth).toString('base64');
}
if (options.value.protocol) {
requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
}
if (options.value.host) {
requestOptions.headers.Host = options.value.host;
}
if (options.value.headers) {
for (var header in options.value.headers) {
if (options.value.headers.hasOwnProperty(header)) {
requestOptions.headers[header] = options.value.headers[header];
}
}
}
if (Object.keys(extensionsOffer).length) {
requestOptions.headers['Sec-WebSocket-Extensions'] = Extensions.format(extensionsOffer);
}
if (options.isDefinedAndNonNull('pfx')
|| options.isDefinedAndNonNull('key')
|| options.isDefinedAndNonNull('passphrase')
|| options.isDefinedAndNonNull('cert')
|| options.isDefinedAndNonNull('ca')
|| options.isDefinedAndNonNull('ciphers')
|| options.isDefinedAndNonNull('rejectUnauthorized')) {
if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx;
if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key;
if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase;
if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert;
if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca;
if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers;
if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized;
if (!agent) {
// global agent ignores client side certificates
agent = new httpObj.Agent(requestOptions);
}
}
requestOptions.path = serverUrl.path || '/';
if (agent) {
requestOptions.agent = agent;
}
if (isUnixSocket) {
requestOptions.socketPath = serverUrl.pathname;
}
if (options.value.localAddress) {
requestOptions.localAddress = options.value.localAddress;
}
if (options.value.origin) {
if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin;
else requestOptions.headers.Origin = options.value.origin;
}
var self = this;
var req = httpObj.request(requestOptions);
req.on('error', function onerror(error) {
self.emit('error', error);
cleanupWebsocketResources.call(self, error);
});
req.once('response', function response(res) {
var error;
if (!self.emit('unexpected-response', req, res)) {
error = new Error('unexpected server response (' + res.statusCode + ')');
req.abort();
self.emit('error', error);
}
cleanupWebsocketResources.call(self, error);
});
req.once('upgrade', function upgrade(res, socket, upgradeHead) {
if (self.readyState === WebSocket.CLOSED) {
// client closed before server accepted connection
self.emit('close');
self.removeAllListeners();
socket.end();
return;
}
var serverKey = res.headers['sec-websocket-accept'];
if (typeof serverKey === 'undefined' || serverKey !== expectedServerKey) {
self.emit('error', 'invalid server key');
self.removeAllListeners();
socket.end();
return;
}
var serverProt = res.headers['sec-websocket-protocol'];
var protList = (options.value.protocol || "").split(/, */);
var protError = null;
if (!options.value.protocol && serverProt) {
protError = 'server sent a subprotocol even though none requested';
} else if (options.value.protocol && !serverProt) {
protError = 'server sent no subprotocol even though requested';
} else if (serverProt && protList.indexOf(serverProt) === -1) {
protError = 'server responded with an invalid protocol';
}
if (protError) {
self.emit('error', protError);
self.removeAllListeners();
socket.end();
return;
} else if (serverProt) {
self.protocol = serverProt;
}
var serverExtensions = Extensions.parse(res.headers['sec-websocket-extensions']);
if (perMessageDeflate && serverExtensions[PerMessageDeflate.extensionName]) {
try {
perMessageDeflate.accept(serverExtensions[PerMessageDeflate.extensionName]);
} catch (err) {
self.emit('error', 'invalid extension parameter');
self.removeAllListeners();
socket.end();
return;
}
self.extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
}
establishConnection.call(self, Receiver, Sender, socket, upgradeHead);
// perform cleanup on http resources
req.removeAllListeners();
req = null;
agent = null;
});
req.end();
this.readyState = WebSocket.CONNECTING;
}
function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) {
var ultron = this._ultron = new Ultron(socket)
, called = false
, self = this;
socket.setTimeout(0);
socket.setNoDelay(true);
this._receiver = new ReceiverClass(this.extensions);
this._socket = socket;
// socket cleanup handlers
ultron.on('end', cleanupWebsocketResources.bind(this));
ultron.on('close', cleanupWebsocketResources.bind(this));
ultron.on('error', cleanupWebsocketResources.bind(this));
// ensure that the upgradeHead is added to the receiver
function firstHandler(data) {
if (called || self.readyState === WebSocket.CLOSED) return;
called = true;
socket.removeListener('data', firstHandler);
ultron.on('data', realHandler);
if (upgradeHead && upgradeHead.length > 0) {
realHandler(upgradeHead);
upgradeHead = null;
}
if (data) realHandler(data);
}
// subsequent packets are pushed straight to the receiver
function realHandler(data) {
self.bytesReceived += data.length;
self._receiver.add(data);
}
ultron.on('data', firstHandler);
// if data was passed along with the http upgrade,
// this will schedule a push of that on to the receiver.
// this has to be done on next tick, since the caller
// hasn't had a chance to set event handlers on this client
// object yet.
process.nextTick(firstHandler);
// receiver event handlers
self._receiver.ontext = function ontext(data, flags) {
flags = flags || {};
self.emit('message', data, flags);
};
self._receiver.onbinary = function onbinary(data, flags) {
flags = flags || {};
flags.binary = true;
self.emit('message', data, flags);
};
self._receiver.onping = function onping(data, flags) {
flags = flags || {};
self.pong(data, {
mask: !self._isServer,
binary: flags.binary === true
}, true);
self.emit('ping', data, flags);
};
self._receiver.onpong = function onpong(data, flags) {
self.emit('pong', data, flags || {});
};
self._receiver.onclose = function onclose(code, data, flags) {
flags = flags || {};
self._closeReceived = true;
self.close(code, data);
};
self._receiver.onerror = function onerror(reason, errorCode) {
// close the connection when the receiver reports a HyBi error code
self.close(typeof errorCode !== 'undefined' ? errorCode : 1002, '');
self.emit('error', reason, errorCode);
};
// finalize the client
this._sender = new SenderClass(socket, this.extensions);
this._sender.on('error', function onerror(error) {
self.close(1002, '');
self.emit('error', error);
});
this.readyState = WebSocket.OPEN;
this.emit('open');
}
function startQueue(instance) {
instance._queue = instance._queue || [];
}
function executeQueueSends(instance) {
var queue = instance._queue;
if (typeof queue === 'undefined') return;
delete instance._queue;
for (var i = 0, l = queue.length; i < l; ++i) {
queue[i]();
}
}
function sendStream(instance, stream, options, cb) {
stream.on('data', function incoming(data) {
if (instance.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else {
delete instance._queue;
instance.emit('error', new Error('not opened'));
}
return;
}
options.fin = false;
instance._sender.send(data, options);
});
stream.on('end', function end() {
if (instance.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else {
delete instance._queue;
instance.emit('error', new Error('not opened'));
}
return;
}
options.fin = true;
instance._sender.send(null, options);
if (typeof cb === 'function') cb(null);
});
}
function cleanupWebsocketResources(error) {
if (this.readyState === WebSocket.CLOSED) return;
var emitClose = this.readyState !== WebSocket.CONNECTING;
this.readyState = WebSocket.CLOSED;
clearTimeout(this._closeTimer);
this._closeTimer = null;
if (emitClose) {
// If the connection was closed abnormally (with an error), or if
// the close control frame was not received then the close code
// must default to 1006.
if (error || !this._closeReceived) {
this._closeCode = 1006;
}
this.emit('close', this._closeCode || 1000, this._closeMessage || '');
}
if (this._socket) {
if (this._ultron) this._ultron.destroy();
this._socket.on('error', function onerror() {
try { this.destroy(); }
catch (e) {}
});
try {
if (!error) this._socket.end();
else this._socket.destroy();
} catch (e) { /* Ignore termination errors */ }
this._socket = null;
this._ultron = null;
}
if (this._sender) {
this._sender.removeAllListeners();
this._sender = null;
}
if (this._receiver) {
this._receiver.cleanup();
this._receiver = null;
}
if (this.extensions[PerMessageDeflate.extensionName]) {
this.extensions[PerMessageDeflate.extensionName].cleanup();
}
this.extensions = null;
this.removeAllListeners();
this.on('error', function onerror() {}); // catch all errors after this
delete this._queue;
}

513
node_modules/ws/lib/WebSocketServer.js generated vendored Normal file
View File

@ -0,0 +1,513 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util')
, events = require('events')
, http = require('http')
, crypto = require('crypto')
, Options = require('options')
, WebSocket = require('./WebSocket')
, Extensions = require('./Extensions')
, PerMessageDeflate = require('./PerMessageDeflate')
, tls = require('tls')
, url = require('url');
/**
* WebSocket Server implementation
*/
function WebSocketServer(options, callback) {
if (this instanceof WebSocketServer === false) {
return new WebSocketServer(options, callback);
}
events.EventEmitter.call(this);
options = new Options({
host: '0.0.0.0',
port: null,
server: null,
verifyClient: null,
handleProtocols: null,
path: null,
noServer: false,
disableHixie: false,
clientTracking: true,
perMessageDeflate: true
}).merge(options);
if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) {
throw new TypeError('`port` or a `server` must be provided');
}
var self = this;
if (options.isDefinedAndNonNull('port')) {
this._server = http.createServer(function (req, res) {
var body = http.STATUS_CODES[426];
res.writeHead(426, {
'Content-Length': body.length,
'Content-Type': 'text/plain'
});
res.end(body);
});
this._server.allowHalfOpen = false;
this._server.listen(options.value.port, options.value.host, callback);
this._closeServer = function() { if (self._server) self._server.close(); };
}
else if (options.value.server) {
this._server = options.value.server;
if (options.value.path) {
// take note of the path, to avoid collisions when multiple websocket servers are
// listening on the same http server
if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) {
throw new Error('two instances of WebSocketServer cannot listen on the same http server path');
}
if (typeof this._server._webSocketPaths !== 'object') {
this._server._webSocketPaths = {};
}
this._server._webSocketPaths[options.value.path] = 1;
}
}
if (this._server) this._server.once('listening', function() { self.emit('listening'); });
if (typeof this._server != 'undefined') {
this._server.on('error', function(error) {
self.emit('error', error)
});
this._server.on('upgrade', function(req, socket, upgradeHead) {
//copy upgradeHead to avoid retention of large slab buffers used in node core
var head = new Buffer(upgradeHead.length);
upgradeHead.copy(head);
self.handleUpgrade(req, socket, head, function(client) {
self.emit('connection'+req.url, client);
self.emit('connection', client);
});
});
}
this.options = options.value;
this.path = options.value.path;
this.clients = [];
}
/**
* Inherits from EventEmitter.
*/
util.inherits(WebSocketServer, events.EventEmitter);
/**
* Immediately shuts down the connection.
*
* @api public
*/
WebSocketServer.prototype.close = function(callback) {
// terminate all associated clients
var error = null;
try {
for (var i = 0, l = this.clients.length; i < l; ++i) {
this.clients[i].terminate();
}
}
catch (e) {
error = e;
}
// remove path descriptor, if any
if (this.path && this._server._webSocketPaths) {
delete this._server._webSocketPaths[this.path];
if (Object.keys(this._server._webSocketPaths).length == 0) {
delete this._server._webSocketPaths;
}
}
// close the http server if it was internally created
try {
if (typeof this._closeServer !== 'undefined') {
this._closeServer();
}
}
finally {
delete this._server;
}
if(callback)
callback(error);
else if(error)
throw error;
}
/**
* Handle a HTTP Upgrade request.
*
* @api public
*/
WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) {
// check for wrong path
if (this.options.path) {
var u = url.parse(req.url);
if (u && u.pathname !== this.options.path) return;
}
if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') {
abortConnection(socket, 400, 'Bad Request');
return;
}
if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments);
else handleHybiUpgrade.apply(this, arguments);
}
module.exports = WebSocketServer;
/**
* Entirely private apis,
* which may or may not be bound to a sepcific WebSocket instance.
*/
function handleHybiUpgrade(req, socket, upgradeHead, cb) {
// handle premature socket errors
var errorHandler = function() {
try { socket.destroy(); } catch (e) {}
}
socket.on('error', errorHandler);
// verify key presence
if (!req.headers['sec-websocket-key']) {
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify version
var version = parseInt(req.headers['sec-websocket-version']);
if ([8, 13].indexOf(version) === -1) {
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify protocol
var protocols = req.headers['sec-websocket-protocol'];
// verify client
var origin = version < 13 ?
req.headers['sec-websocket-origin'] :
req.headers['origin'];
// handle extensions offer
var extensionsOffer = Extensions.parse(req.headers['sec-websocket-extensions']);
// handler to call when the connection sequence completes
var self = this;
var completeHybiUpgrade2 = function(protocol) {
// calc key
var key = req.headers['sec-websocket-key'];
var shasum = crypto.createHash('sha1');
shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
key = shasum.digest('base64');
var headers = [
'HTTP/1.1 101 Switching Protocols'
, 'Upgrade: websocket'
, 'Connection: Upgrade'
, 'Sec-WebSocket-Accept: ' + key
];
if (typeof protocol != 'undefined') {
headers.push('Sec-WebSocket-Protocol: ' + protocol);
}
var extensions = {};
try {
extensions = acceptExtensions.call(self, extensionsOffer);
} catch (err) {
abortConnection(socket, 400, 'Bad Request');
return;
}
if (Object.keys(extensions).length) {
var serverExtensions = {};
Object.keys(extensions).forEach(function(token) {
serverExtensions[token] = [extensions[token].params]
});
headers.push('Sec-WebSocket-Extensions: ' + Extensions.format(serverExtensions));
}
// allows external modification/inspection of handshake headers
self.emit('headers', headers);
socket.setTimeout(0);
socket.setNoDelay(true);
try {
socket.write(headers.concat('', '').join('\r\n'));
}
catch (e) {
// if the upgrade write fails, shut the connection down hard
try { socket.destroy(); } catch (e) {}
return;
}
var client = new WebSocket([req, socket, upgradeHead], {
protocolVersion: version,
protocol: protocol,
extensions: extensions
});
if (self.options.clientTracking) {
self.clients.push(client);
client.on('close', function() {
var index = self.clients.indexOf(client);
if (index != -1) {
self.clients.splice(index, 1);
}
});
}
// signal upgrade complete
socket.removeListener('error', errorHandler);
cb(client);
}
// optionally call external protocol selection handler before
// calling completeHybiUpgrade2
var completeHybiUpgrade1 = function() {
// choose from the sub-protocols
if (typeof self.options.handleProtocols == 'function') {
var protList = (protocols || "").split(/, */);
var callbackCalled = false;
var res = self.options.handleProtocols(protList, function(result, protocol) {
callbackCalled = true;
if (!result) abortConnection(socket, 401, 'Unauthorized');
else completeHybiUpgrade2(protocol);
});
if (!callbackCalled) {
// the handleProtocols handler never called our callback
abortConnection(socket, 501, 'Could not process protocols');
}
return;
} else {
if (typeof protocols !== 'undefined') {
completeHybiUpgrade2(protocols.split(/, */)[0]);
}
else {
completeHybiUpgrade2();
}
}
}
// optionally call external client verification handler
if (typeof this.options.verifyClient == 'function') {
var info = {
origin: origin,
secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined',
req: req
};
if (this.options.verifyClient.length == 2) {
this.options.verifyClient(info, function(result, code, name) {
if (typeof code === 'undefined') code = 401;
if (typeof name === 'undefined') name = http.STATUS_CODES[code];
if (!result) abortConnection(socket, code, name);
else completeHybiUpgrade1();
});
return;
}
else if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');
return;
}
}
completeHybiUpgrade1();
}
function handleHixieUpgrade(req, socket, upgradeHead, cb) {
// handle premature socket errors
var errorHandler = function() {
try { socket.destroy(); } catch (e) {}
}
socket.on('error', errorHandler);
// bail if options prevent hixie
if (this.options.disableHixie) {
abortConnection(socket, 401, 'Hixie support disabled');
return;
}
// verify key presence
if (!req.headers['sec-websocket-key2']) {
abortConnection(socket, 400, 'Bad Request');
return;
}
var origin = req.headers['origin']
, self = this;
// setup handshake completion to run after client has been verified
var onClientVerified = function() {
var wshost;
if (!req.headers['x-forwarded-host'])
wshost = req.headers.host;
else
wshost = req.headers['x-forwarded-host'];
var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url
, protocol = req.headers['sec-websocket-protocol'];
// handshake completion code to run once nonce has been successfully retrieved
var completeHandshake = function(nonce, rest) {
// calculate key
var k1 = req.headers['sec-websocket-key1']
, k2 = req.headers['sec-websocket-key2']
, md5 = crypto.createHash('md5');
[k1, k2].forEach(function (k) {
var n = parseInt(k.replace(/[^\d]/g, ''))
, spaces = k.replace(/[^ ]/g, '').length;
if (spaces === 0 || n % spaces !== 0){
abortConnection(socket, 400, 'Bad Request');
return;
}
n /= spaces;
md5.update(String.fromCharCode(
n >> 24 & 0xFF,
n >> 16 & 0xFF,
n >> 8 & 0xFF,
n & 0xFF));
});
md5.update(nonce.toString('binary'));
var headers = [
'HTTP/1.1 101 Switching Protocols'
, 'Upgrade: WebSocket'
, 'Connection: Upgrade'
, 'Sec-WebSocket-Location: ' + location
];
if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol);
if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin);
socket.setTimeout(0);
socket.setNoDelay(true);
try {
// merge header and hash buffer
var headerBuffer = new Buffer(headers.concat('', '').join('\r\n'));
var hashBuffer = new Buffer(md5.digest('binary'), 'binary');
var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length);
headerBuffer.copy(handshakeBuffer, 0);
hashBuffer.copy(handshakeBuffer, headerBuffer.length);
// do a single write, which - upon success - causes a new client websocket to be setup
socket.write(handshakeBuffer, 'binary', function(err) {
if (err) return; // do not create client if an error happens
var client = new WebSocket([req, socket, rest], {
protocolVersion: 'hixie-76',
protocol: protocol
});
if (self.options.clientTracking) {
self.clients.push(client);
client.on('close', function() {
var index = self.clients.indexOf(client);
if (index != -1) {
self.clients.splice(index, 1);
}
});
}
// signal upgrade complete
socket.removeListener('error', errorHandler);
cb(client);
});
}
catch (e) {
try { socket.destroy(); } catch (e) {}
return;
}
}
// retrieve nonce
var nonceLength = 8;
if (upgradeHead && upgradeHead.length >= nonceLength) {
var nonce = upgradeHead.slice(0, nonceLength);
var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null;
completeHandshake.call(self, nonce, rest);
}
else {
// nonce not present in upgradeHead, so we must wait for enough data
// data to arrive before continuing
var nonce = new Buffer(nonceLength);
upgradeHead.copy(nonce, 0);
var received = upgradeHead.length;
var rest = null;
var handler = function (data) {
var toRead = Math.min(data.length, nonceLength - received);
if (toRead === 0) return;
data.copy(nonce, received, 0, toRead);
received += toRead;
if (received == nonceLength) {
socket.removeListener('data', handler);
if (toRead < data.length) rest = data.slice(toRead);
completeHandshake.call(self, nonce, rest);
}
}
socket.on('data', handler);
}
}
// verify client
if (typeof this.options.verifyClient == 'function') {
var info = {
origin: origin,
secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined',
req: req
};
if (this.options.verifyClient.length == 2) {
var self = this;
this.options.verifyClient(info, function(result, code, name) {
if (typeof code === 'undefined') code = 401;
if (typeof name === 'undefined') name = http.STATUS_CODES[code];
if (!result) abortConnection(socket, code, name);
else onClientVerified.apply(self);
});
return;
}
else if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');
return;
}
}
// no client verification required
onClientVerified();
}
function acceptExtensions(offer) {
var extensions = {};
var options = this.options.perMessageDeflate;
if (options && offer[PerMessageDeflate.extensionName]) {
var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true);
perMessageDeflate.accept(offer[PerMessageDeflate.extensionName]);
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
}
return extensions;
}
function abortConnection(socket, code, name) {
try {
var response = [
'HTTP/1.1 ' + code + ' ' + name,
'Content-type: text/html'
];
socket.write(response.concat('', '').join('\r\n'));
}
catch (e) { /* ignore errors - we've aborted this connection */ }
finally {
// ensure that an early aborted connection is shut down completely
try { socket.destroy(); } catch (e) {}
}
}

76
node_modules/ws/package.json generated vendored Normal file
View File

@ -0,0 +1,76 @@
{
"author": {
"name": "Einar Otto Stangvik",
"email": "einaros@gmail.com",
"url": "http://2x.io"
},
"name": "ws",
"description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455",
"version": "1.0.1",
"license": "MIT",
"keywords": [
"Hixie",
"HyBi",
"Push",
"RFC-6455",
"WebSocket",
"WebSockets",
"real-time"
],
"repository": {
"type": "git",
"url": "git://github.com/websockets/ws.git"
},
"scripts": {
"test": "make test"
},
"dependencies": {
"options": ">=0.0.5",
"ultron": "1.0.x"
},
"devDependencies": {
"ansi": "0.3.x",
"benchmark": "0.3.x",
"bufferutil": "1.2.x",
"expect.js": "0.3.x",
"mocha": "2.3.x",
"should": "8.0.x",
"tinycolor": "0.0.x",
"utf-8-validate": "1.2.x"
},
"gypfile": true,
"gitHead": "40a9d686288b5d0be13f2bf2f3f5da07afc8cda2",
"bugs": {
"url": "https://github.com/websockets/ws/issues"
},
"homepage": "https://github.com/websockets/ws#readme",
"_id": "ws@1.0.1",
"_shasum": "7d0b2a2e58cddd819039c29c9de65045e1b310e9",
"_from": "ws@latest",
"_npmVersion": "3.5.1",
"_nodeVersion": "4.2.3",
"_npmUser": {
"name": "3rdeden",
"email": "npm@3rd-Eden.com"
},
"maintainers": [
{
"name": "einaros",
"email": "einaros@gmail.com"
},
{
"name": "v1",
"email": "info@3rd-Eden.com"
},
{
"name": "3rdeden",
"email": "npm@3rd-Eden.com"
}
],
"dist": {
"shasum": "7d0b2a2e58cddd819039c29c9de65045e1b310e9",
"tarball": "http://registry.npmjs.org/ws/-/ws-1.0.1.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/ws/-/ws-1.0.1.tgz"
}