Started on node.js+socket.io+mongoDB on the backend for more responsivnes

This commit is contained in:
KasperRT
2015-04-09 00:18:13 +02:00
parent 076f8e821f
commit a8a705bd77
1889 changed files with 322175 additions and 68 deletions

4
server/node_modules/socket.io/.npmignore generated vendored Executable file
View File

@@ -0,0 +1,4 @@
support
test
examples
.gitignore

16
server/node_modules/socket.io/.travis.yml generated vendored Executable file
View File

@@ -0,0 +1,16 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.11"
git:
depth: 1
matrix:
fast_finish: true
allow_failures:
- node_js: "0.11"
notifications:
irc: "irc.freenode.org#socket.io"

491
server/node_modules/socket.io/History.md generated vendored Executable file
View File

@@ -0,0 +1,491 @@
1.3.5 / 2015-03-03
==================
* package: bump `socket.io-parser`
1.3.4 / 2015-02-14
==================
* package: bump `socket.io-client`
1.3.3 / 2015-02-03
==================
* socket: warn node_redis-style about missing `error`
* package: bump parser to better handle bad binary packets
1.3.2 / 2015-01-19
==================
* no change on this release
1.3.1 / 2015-01-19
==================
* no change on this release
* package: bump `engine.io`
1.3.0 / 2015-01-19
==================
* package: bump `engine.io`
* add test for reconnection after server restarts [rase-]
* update license with up-to-date year range [fay-jai]
* fix leaving unknown rooms [defunctzombie]
* allow null origins when allowed origins is a function [drewblaisdell]
* fix tests on node 0.11
* package: fix `npm test` to run on windows
* package: bump `debug` v2.1.0 [coderaiser]
* added tests for volatile [rase-]
1.2.1 / 2014-11-21
==================
* fix protocol violations and improve error handling (GH-1880)
* package: bump `engine.io` for websocket leak fix [3rd-Eden]
* style tweaks
1.2.0 / 2014-10-27
==================
* package: bump `engine.io`
* downloads badge
* add test to check that empty rooms are autopruned
* added Server#origins(v:Function) description for dynamic CORS
* added test coverage for Server#origins(function) for dynamic CORS
* added optional Server#origins(function) for dynamic CORS
* fix usage example for Server#close
* package: fix main file for example application 'chat'
* package: bump `socket.io-parser`
* update README http ctor to createServer()
* bump adapter with a lot of fixes for room bookkeeping
1.1.0 / 2014-09-04
==================
* examples: minor fix of escaping
* testing for equivalence of namespaces starting with / or without
* update index.js
* added relevant tests
* take "" and "/" as equivalent namespaces on server
* use svg instead of png to get better image quality in readme
* make CI build faster
* fix splice arguments and `socket.rooms` value update in `socket.leaveAll`.
* client cannot connect to non-existing namespaces
* bump engine.io version to get the cached IP address
* fixed handshake object address property and made the test case more strict.
* package: bump `engine.io`
* fixed the failing test where server crashes on disconnect involving connectBuffer
* npmignore: ignore `.gitignore` (fixes #1607)
* test: added failing case for `socket.disconnect` and nsps
* fix repo in package.json
* improve Close documentation
* use ephemeral ports
* fix: We should use the standard http protocol to handler the etag header.
* override default browser font-family for inputs
* update has-binary-data to 1.0.3
* add close specs
* add ability to stop the http server even if not created inside socket.io
* make sure server gets close
* Add test case for checking that reconnect_failed is fired only once upon failure
* package: bump `socket.io-parser` for `component-emitter` dep fix
1.0.6 / 2014-06-19
==================
* package: bump `socket.io-client`
1.0.5 / 2014-06-16
==================
* package: bump `engine.io` to fix jsonp `\n` bug and CORS warnings
* index: fix typo [yanatan16]
* add `removeListener` to blacklisted events
* examples: clearer instructions to install chat example
* index: fix namespace `connectBuffer` issue
1.0.4 / 2014-06-02
==================
* package: bump socket.io-client
1.0.3 / 2014-05-31
==================
* package: bump `socket.io-client`
* package: bump `socket.io-parser` for binary ACK fix
* package: bump `engine.io` for binary UTF8 fix
* example: fix XSS in chat example
1.0.2 / 2014-05-28
==================
* package: bump `socket.io-parser` for windows fix
1.0.1 / 2014-05-28
==================
* bump due to bad npm tag
1.0.0 / 2014-05-28
==================
* stable release
1.0.0-pre5 / 2014-05-22
=======================
* package: bump `socket.io-client` for parser fixes
* package: bump `engine.io`
1.0.0-pre4 / 2014-05-19
=======================
* package: bump client
1.0.0-pre3 / 2014-05-17
=======================
* package: bump parser
* package: bump engine.io
1.0.0-pre2 / 2014-04-27
=======================
* package: bump `engine.io`
* added backwards compatible of engine.io maxHttpBufferSize
* added test that server and client using same protocol
* added support for setting allowed origins
* added information about logging
* the set function in server can be used to set some attributes for BC
* fix error in callback call 'done' instead of 'next' in docs
* package: bump `socket.io-parser`
* package: bump `expect.js`
* added some new tests, including binary with acks
1.0.0-pre / 2014-03-14
======================
* implemented `engine.io`
* implemented `socket.io-adapter`
* implemented `socket.io-protocol`
* implemented `debug` and improved instrumentation
* added binary support
* added new `require('io')(srv)` signature
* simplified `socket.io-client` serving
0.9.14 / 2013-03-29
===================
* manager: fix memory leak with SSL [jpallen]
0.9.13 / 2012-12-13
===================
* package: fixed `base64id` requirement
0.9.12 / 2012-12-13
===================
* manager: fix for latest node which is returning a clone with `listeners` [viirya]
0.9.11 / 2012-11-02
===================
* package: move redis to optionalDependenices [3rd-Eden]
* bumped client
0.9.10 / 2012-08-10
===================
* Don't lowercase log messages
* Always set the HTTP response in case an error should be returned to the client
* Create or destroy the flash policy server on configuration change
* Honour configuration to disable flash policy server
* Add express 3.0 instructions on Readme.md
* Bump client
0.9.9 / 2012-08-01
==================
* Fixed sync disconnect xhrs handling
* Put license text in its own file (#965)
* Add warning to .listen() to ease the migration to Express 3.x
* Restored compatibility with node 0.4.x
0.9.8 / 2012-07-24
==================
* Bumped client.
0.9.7 / 2012-07-24
==================
* Prevent crash when socket leaves a room twice.
* Corrects unsafe usage of for..in
* Fix for node 0.8 with `gzip compression` [vadimi]
* Update redis to support Node 0.8.x
* Made ID generation securely random
* Fix Redis Store race condition in manager onOpen unsubscribe callback
* Fix for EventEmitters always reusing the same Array instance for listeners
0.9.6 / 2012-04-17
==================
* Fixed XSS in jsonp-polling.
0.9.5 / 2012-04-05
==================
* Added test for polling and socket close.
* Ensure close upon request close.
* Fix disconnection reason being lost for polling transports.
* Ensure that polling transports work with Connection: close.
* Log disconnection reason.
0.9.4 / 2012-04-01
==================
* Disconnecting from namespace improvement (#795) [DanielBaulig]
* Bumped client with polling reconnection loop (#438)
0.9.3 / 2012-03-28
==================
* Fix "Syntax error" on FF Web Console with XHR Polling [mikito]
0.9.2 / 2012-03-13
==================
* More sensible close `timeout default` (fixes disconnect issue)
0.9.1-1 / 2012-03-02
====================
* Bumped client with NPM dependency fix.
0.9.1 / 2012-03-02
==================
* Changed heartbeat timeout and interval defaults (60 and 25 seconds)
* Make tests work both on 0.4 and 0.6
* Updated client (improvements + bug fixes).
0.9.0 / 2012-02-26
==================
* Make it possible to use a regexp to match the socket.io resource URL.
We need this because we have to prefix the socket.io URL with a variable ID.
* Supplemental fix to gavinuhma/authfix, it looks like the same Access-Control-Origin logic is needed in the http and xhr-polling transports
* Updated express dep for windows compatibility.
* Combine two substr calls into one in decodePayload to improve performance
* Minor documentation fix
* Minor. Conform to style of other files.
* Switching setting to 'match origin protocol'
* Revert "Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect()."
* Revert "Handle leaked dispatch:[id] subscription."
* Merge pull request #667 from dshaw/patch/redis-disconnect
* Handle leaked dispatch:[id] subscription.
* Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect().
* Prevent memory leaking on uncompleted requests & add max post size limitation
* Fix for testcase
* Set Access-Control-Allow-Credentials true, regardless of cookie
* Remove assertvarnish from package as it breaks on 0.6
* Correct irc channel
* Added proper return after reserved field error
* Fixes manager.js failure to close connection after transport error has happened
* Added implicit port 80 for origin checks. fixes #638
* Fixed bug #432 in 0.8.7
* Set Access-Control-Allow-Origin header to origin to enable withCredentials
* Adding configuration variable matchOriginProtocol
* Fixes location mismatch error in Safari.
* Use tty to detect if we should add colors or not by default.
* Updated the package location.
0.8.7 / 2011-11-05
==================
* Fixed memory leaks in closed clients.
* Fixed memory leaks in namespaces.
* Fixed websocket handling for malformed requests from proxies. [einaros]
* Node 0.6 compatibility. [einaros] [3rd-Eden]
* Adapted tests and examples.
0.8.6 / 2011-10-27
==================
* Added JSON decoding on jsonp-polling transport.
* Fixed README example.
* Major speed optimizations [3rd-Eden] [einaros] [visionmedia]
* Added decode/encode benchmarks [visionmedia]
* Added support for black-listing client sent events.
* Fixed logging options, closes #540 [3rd-Eden]
* Added vary header for gzip [3rd-Eden]
* Properly cleaned up async websocket / flashsocket tests, after patching node-websocket-client
* Patched to properly shut down when a finishClose call is made during connection establishment
* Added support for socket.io version on url and far-future Expires [3rd-Eden] [getify]
* Began IE10 compatibility [einaros] [tbranyen]
* Misc WebSocket fixes [einaros]
* Added UTF8 to respone headers for htmlfile [3rd-Eden]
0.8.5 / 2011-10-07
==================
* Added websocket draft HyBi-16 support. [einaros]
* Fixed websocket continuation bugs. [einaros]
* Fixed flashsocket transport name.
* Fixed websocket tests.
* Ensured `parser#decodePayload` doesn't choke.
* Added http referrer verification to manager verifyOrigin.
* Added access control for cross domain xhr handshakes [3rd-Eden]
* Added support for automatic generation of socket.io files [3rd-Eden]
* Added websocket binary support [einaros]
* Added gzip support for socket.io.js [3rd-Eden]
* Expose socket.transport [3rd-Eden]
* Updated client.
0.8.4 / 2011-09-06
==================
* Client build
0.8.3 / 2011-09-03
==================
* Fixed `\n` parsing for non-JSON packets (fixes #479).
* Fixed parsing of certain unicode characters (fixes #451).
* Fixed transport message packet logging.
* Fixed emission of `error` event resulting in an uncaught exception if unhandled (fixes #476).
* Fixed; allow for falsy values as the configuration value of `log level` (fixes #491).
* Fixed repository URI in `package.json`. Fixes #504.
* Added text/plain content-type to handshake responses [einaros]
* Improved single byte writes [einaros]
* Updated socket.io-flashsocket default port from 843 to 10843 [3rd-Eden]
* Updated client.
0.8.2 / 2011-08-29
==================
* Updated client.
0.8.1 / 2011-08-29
==================
* Fixed utf8 bug in send framing in websocket [einaros]
* Fixed typo in docs [Znarkus]
* Fixed bug in send framing for over 64kB of data in websocket [einaros]
* Corrected ping handling in websocket transport [einaros]
0.8.0 / 2011-08-28
==================
* Updated to work with two-level websocket versioning. [einaros]
* Added hybi07 support. [einaros]
* Added hybi10 support. [einaros]
* Added http referrer verification to manager.js verifyOrigin. [einaors]
0.7.11 / 2011-08-27
===================
* Updated socket.io-client.
0.7.10 / 2011-08-27
===================
* Updated socket.io-client.
0.7.9 / 2011-08-12
==================
* Updated socket.io-client.
* Make sure we only do garbage collection when the server we receive is actually run.
0.7.8 / 2011-08-08
==================
* Changed; make sure sio#listen passes options to both HTTP server and socket.io manager.
* Added docs for sio#listen.
* Added options parameter support for Manager constructor.
* Added memory leaks tests and test-leaks Makefile task.
* Removed auto npm-linking from make test.
* Make sure that you can disable heartbeats. [3rd-Eden]
* Fixed rooms memory leak [3rd-Eden]
* Send response once we got all POST data, not immediately [Pita]
* Fixed onLeave behavior with missing clientsk [3rd-Eden]
* Prevent duplicate references in rooms.
* Added alias for `to` to `in` and `in` to `to`.
* Fixed roomClients definition.
* Removed dependency on redis for installation without npm [3rd-Eden]
* Expose path and querystring in handshakeData [3rd-Eden]
0.7.7 / 2011-07-12
==================
* Fixed double dispatch handling with emit to closed clients.
* Added test for emitting to closed clients to prevent regression.
* Fixed race condition in redis test.
* Changed Transport#end instrumentation.
* Leveraged $emit instead of emit internally.
* Made tests faster.
* Fixed double disconnect events.
* Fixed disconnect logic
* Simplified remote events handling in Socket.
* Increased testcase timeout.
* Fixed unknown room emitting (GH-291). [3rd-Eden]
* Fixed `address` in handshakeData. [3rd-Eden]
* Removed transports definition in chat example.
* Fixed room cleanup
* Fixed; make sure the client is cleaned up after booting.
* Make sure to mark the client as non-open if the connection is closed.
* Removed unneeded `buffer` declarations.
* Fixed; make sure to clear socket handlers and subscriptions upon transport close.
0.7.6 / 2011-06-30
==================
* Fixed general dispatching when a client has closed.
0.7.5 / 2011-06-30
==================
* Fixed dispatching to clients that are disconnected.
0.7.4 / 2011-06-30
==================
* Fixed; only clear handlers if they were set. [level09]
0.7.3 / 2011-06-30
==================
* Exposed handshake data to clients.
* Refactored dispatcher interface.
* Changed; Moved id generation method into the manager.
* Added sub-namespace authorization. [3rd-Eden]
* Changed; normalized SocketNamespace local eventing [dvv]
* Changed; Use packet.reason or default to 'packet' [3rd-Eden]
* Changed console.error to console.log.
* Fixed; bind both servers at the same time do that the test never times out.
* Added 304 support.
* Removed `Transport#name` for abstract interface.
* Changed; lazily require http and https module only when needed. [3rd-Eden]
0.7.2 / 2011-06-22
==================
* Make sure to write a packet (of type `noop`) when closing a poll.
This solves a problem with cross-domain requests being flagged as aborted and
reconnection being triggered.
* Added `noop` message type.
0.7.1 / 2011-06-21
==================
* Fixed cross-domain XHR.
* Added CORS test to xhr-polling suite.
0.7.0 / 2010-06-21
==================
* http://socket.io/announcement.html

22
server/node_modules/socket.io/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2014-2015 Automattic <dev@cloudup.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

15
server/node_modules/socket.io/Makefile generated vendored Executable file
View File

@@ -0,0 +1,15 @@
REPORTER = dot
test:
@./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--slow 200ms \
--bail
test-cov:
@./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- \
--reporter $(REPORTER) \
test/
.PHONY: test

374
server/node_modules/socket.io/Readme.md generated vendored Executable file
View File

@@ -0,0 +1,374 @@
# socket.io
[![Build Status](https://secure.travis-ci.org/Automattic/socket.io.svg)](http://travis-ci.org/Automattic/socket.io)
![NPM version](https://badge.fury.io/js/socket.io.svg)
![Downloads](http://img.shields.io/npm/dm/socket.io.svg?style=flat)
## How to use
The following example attaches socket.io to a plain Node.JS
HTTP server listening on port `3000`.
```js
var server = require('http').createServer();
var io = require('socket.io')(server);
io.on('connection', function(socket){
socket.on('event', function(data){});
socket.on('disconnect', function(){});
});
server.listen(3000);
```
### Standalone
```js
var io = require('socket.io')();
io.on('connection', function(socket){});
io.listen(3000);
```
### In conjunction with Express
Starting with **3.0**, express applications have become request handler
functions that you pass to `http` or `http` `Server` instances. You need
to pass the `Server` to `socket.io`, and not the express application
function.
```js
var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
io.on('connection', function(){ /* … */ });
server.listen(3000);
```
### In conjunction with Koa
Like Express.JS, Koa works by exposing an application as a request
handler function, but only by calling the `callback` method.
```js
var app = require('koa')();
var server = require('http').createServer(app.callback());
var io = require('socket.io')(server);
io.on('connection', function(){ /* … */ });
server.listen(3000);
```
## API
### Server
Exposed by `require('socket.io')`.
### Server()
Creates a new `Server`. Works with and without `new`:
```js
var io = require('socket.io')();
// or
var Server = require('socket.io');
var io = new Server();
```
### Server(opts:Object)
Optionally, the first or second argument (see below) of the `Server`
constructor can be an options object.
The following options are supported:
- `serveClient` sets the value for Server#serveClient()
- `path` sets the value for Server#path()
The same options passed to socket.io are always passed to
the `engine.io` `Server` that gets created. See engine.io
[options](https://github.com/learnboost/engine.io#methods-1)
as reference.
### Server(srv:http#Server, opts:Object)
Creates a new `Server` and attaches it to the given `srv`. Optionally
`opts` can be passed.
### Server(port:Number, opts:Object)
Binds socket.io to a new `http.Server` that listens on `port`.
### Server#serveClient(v:Boolean):Server
If `v` is `true` the attached server (see `Server#attach`) will serve
the client files. Defaults to `true`.
This method has no effect after `attach` is called.
```js
// pass a server and the `serveClient` option
var io = require('socket.io')(http, { serveClient: false });
// or pass no server and then you can call the method
var io = require('socket.io')();
io.serveClient(false);
io.attach(http);
```
If no arguments are supplied this method returns the current value.
### Server#path(v:String):Server
Sets the path `v` under which `engine.io` and the static files will be
served. Defaults to `/socket.io`.
If no arguments are supplied this method returns the current value.
### Server#adapter(v:Adapter):Server
Sets the adapter `v`. Defaults to an instance of the `Adapter` that
ships with socket.io which is memory based. See
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
If no arguments are supplied this method returns the current value.
### Server#origins(v:String):Server
Sets the allowed origins `v`. Defaults to any origins being allowed.
If no arguments are supplied this method returns the current value.
### Server#origins(v:Function):Server
Sets the allowed origins as dynamic function. Function takes two arguments `origin:String` and `callback(error, success)`, where `success` is a boolean value indicating whether origin is allowed or not.
__Potential drawbacks__:
* in some situations, when it is not possible to determine `origin` it may have value of `*`
* As this function will be executed for every request, it is advised to make this function work as fast as possible
* If `socket.io` is used together with `Express`, the CORS headers will be affected only for `socket.io` requests. For Express can use [cors](https://github.com/troygoode/node-cors/)
### Server#sockets:Namespace
The default (`/`) namespace.
### Server#attach(srv:http#Server, opts:Object):Server
Attaches the `Server` to an engine.io instance on `srv` with the
supplied `opts` (optionally).
### Server#attach(port:Number, opts:Object):Server
Attaches the `Server` to an engine.io instance that is bound to `port`
with the given `opts` (optionally).
### Server#listen
Synonym of `Server#attach`.
### Server#bind(srv:engine#Server):Server
Advanced use only. Binds the server to a specific engine.io `Server`
(or compatible API) instance.
### Server#onconnection(socket:engine#Socket):Server
Advanced use only. Creates a new `socket.io` client from the incoming
engine.io (or compatible API) `socket`.
### Server#of(nsp:String):Namespace
Initializes and retrieves the given `Namespace` by its pathname
identifier `nsp`.
If the namespace was already initialized it returns it right away.
### Server#emit
Emits an event to all connected clients. The following two are
equivalent:
```js
var io = require('socket.io')();
io.sockets.emit('an event sent to all connected clients');
io.emit('an event sent to all connected clients');
```
For other available methods, see `Namespace` below.
### Server#close
Closes socket server
```js
var Server = require('socket.io');
var PORT = 3030;
var server = require('http').Server();
var io = Server(PORT);
io.close(); // Close current server
server.listen(PORT); // PORT is free to use
io = Server(server);
```
### Server#use
See `Namespace#use` below.
### Namespace
Represents a pool of sockets connected under a given scope identified
by a pathname (eg: `/chat`).
By default the client always connects to `/`.
#### Events
- `connection` / `connect`. Fired upon a connection.
Parameters:
- `Socket` the incoming socket.
### Namespace#name:String
The namespace identifier property.
### Namespace#connected:Object<Socket>
Hash of `Socket` objects that are connected to this namespace indexed
by `id`.
### Namespace#use(fn:Function):Namespace
Registers a middleware, which is a function that gets executed for
every incoming `Socket` and receives as parameter the socket and a
function to optionally defer execution to the next registered
middleware.
```js
var io = require('socket.io')();
io.use(function(socket, next){
if (socket.request.headers.cookie) return next();
next(new Error('Authentication error'));
});
```
Errors passed to middleware callbacks are sent as special `error`
packets to clients.
### Socket
A `Socket` is the fundamental class for interacting with browser
clients. A `Socket` belongs to a certain `Namespace` (by default `/`)
and uses an underlying `Client` to communicate.
### Socket#rooms:Array
A list of strings identifying the rooms this socket is in.
### Socket#client:Client
A reference to the underlying `Client` object.
### Socket#conn:Socket
A reference to the underlying `Client` transport connection (engine.io
`Socket` object).
### Socket#request:Request
A getter proxy that returns the reference to the `request` that
originated the underlying engine.io `Client`. Useful for accessing
request headers such as `Cookie` or `User-Agent`.
### Socket#id:String
A unique identifier for the socket session, that comes from the
underlying `Client`.
### Socket#emit(name:String[, …]):Socket
Emits an event to the socket identified by the string `name`. Any
other parameters can be included.
All datastructures are supported, including `Buffer`. JavaScript
functions can't be serialized/deserialized.
```js
var io = require('socket.io')();
io.on('connection', function(socket){
socket.emit('an event', { some: 'data' });
});
```
### Socket#join(name:String[, fn:Function]):Socket
Adds the socket to the `room`, and fires optionally a callback `fn`
with `err` signature (if any).
The socket is automatically a member of a room identified with its
session id (see `Socket#id`).
The mechanics of joining rooms are handled by the `Adapter`
that has been configured (see `Server#adapter` above), defaulting to
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
### Socket#leave(name:String[, fn:Function]):Socket
Removes the socket from `room`, and fires optionally a callback `fn`
with `err` signature (if any).
**Rooms are left automatically upon disconnection**.
The mechanics of leaving rooms are handled by the `Adapter`
that has been configured (see `Server#adapter` above), defaulting to
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
### Socket#to(room:String):Socket
### Socket#in(room:String):Socket
Sets a modifier for a subsequent event emission that the event will
only be _broadcasted_ to sockets that have joined the given `room`.
To emit to multiple rooms, you can call `to` several times.
```js
var io = require('socket.io')();
io.on('connection', function(socket){
socket.to('others').emit('an event', { some: 'data' });
});
```
### Client
The `Client` class represents an incoming transport (engine.io)
connection. A `Client` can be associated with many multiplexed `Socket`
that belong to different `Namespace`s.
### Client#conn
A reference to the underlying `engine.io` `Socket` connection.
### Client#request
A getter proxy that returns the reference to the `request` that
originated the engine.io connection. Useful for accessing
request headers such as `Cookie` or `User-Agent`.
## Debug / logging
Socket.IO is powered by [debug](http://github.com/visionmedia/debug).
In order to see all the debug output, run your app with the environment variable
`DEBUG` including the desired scope.
To see the output from all of Socket.IO's debugging scopes you can use:
```
DEBUG=socket.io* node myapp
```
## License
MIT

2
server/node_modules/socket.io/index.js generated vendored Executable file
View File

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

247
server/node_modules/socket.io/lib/client.js generated vendored Executable file
View File

@@ -0,0 +1,247 @@
/**
* Module dependencies.
*/
var parser = require('socket.io-parser');
var debug = require('debug')('socket.io:client');
/**
* Module exports.
*/
module.exports = Client;
/**
* Client constructor.
*
* @param {Server} server instance
* @param {Socket} connection
* @api private
*/
function Client(server, conn){
this.server = server;
this.conn = conn;
this.encoder = new parser.Encoder();
this.decoder = new parser.Decoder();
this.id = conn.id;
this.request = conn.request;
this.setup();
this.sockets = [];
this.nsps = {};
this.connectBuffer = [];
}
/**
* Sets up event listeners.
*
* @api private
*/
Client.prototype.setup = function(){
this.onclose = this.onclose.bind(this);
this.ondata = this.ondata.bind(this);
this.onerror = this.onerror.bind(this);
this.ondecoded = this.ondecoded.bind(this);
this.decoder.on('decoded', this.ondecoded);
this.conn.on('data', this.ondata);
this.conn.on('error', this.onerror);
this.conn.on('close', this.onclose);
};
/**
* Connects a client to a namespace.
*
* @param {String} namespace name
* @api private
*/
Client.prototype.connect = function(name){
debug('connecting to namespace %s', name);
if (!this.server.nsps[name]) {
this.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'});
return;
}
var nsp = this.server.of(name);
if ('/' != name && !this.nsps['/']) {
this.connectBuffer.push(name);
return;
}
var self = this;
var socket = nsp.add(this, function(){
self.sockets.push(socket);
self.nsps[nsp.name] = socket;
if ('/' == nsp.name && self.connectBuffer.length > 0) {
self.connectBuffer.forEach(self.connect, self);
self.connectBuffer = [];
}
});
};
/**
* Disconnects from all namespaces and closes transport.
*
* @api private
*/
Client.prototype.disconnect = function(){
var socket;
// we don't use a for loop because the length of
// `sockets` changes upon each iteration
while (socket = this.sockets.shift()) {
socket.disconnect();
}
this.close();
};
/**
* Removes a socket. Called by each `Socket`.
*
* @api private
*/
Client.prototype.remove = function(socket){
var i = this.sockets.indexOf(socket);
if (~i) {
var nsp = this.sockets[i].nsp.name;
this.sockets.splice(i, 1);
delete this.nsps[nsp];
} else {
debug('ignoring remove for %s', socket.id);
}
};
/**
* Closes the underlying connection.
*
* @api private
*/
Client.prototype.close = function(){
if ('open' == this.conn.readyState) {
debug('forcing transport close');
this.conn.close();
this.onclose('forced server close');
}
};
/**
* Writes a packet to the transport.
*
* @param {Object} packet object
* @param {Boolean} whether packet is already encoded
* @param {Boolean} whether packet is volatile
* @api private
*/
Client.prototype.packet = function(packet, preEncoded, volatile){
var self = this;
// this writes to the actual connection
function writeToEngine(encodedPackets) {
if (volatile && !self.conn.transport.writable) return;
for (var i = 0; i < encodedPackets.length; i++) {
self.conn.write(encodedPackets[i]);
}
}
if ('open' == this.conn.readyState) {
debug('writing packet %j', packet);
if(!preEncoded) { // not broadcasting, need to encode
this.encoder.encode(packet, function (encodedPackets) { // encode, then write results to engine
writeToEngine(encodedPackets);
});
} else { // a broadcast pre-encodes a packet
writeToEngine(packet);
}
} else {
debug('ignoring packet write %j', packet);
}
};
/**
* Called with incoming transport data.
*
* @api private
*/
Client.prototype.ondata = function(data){
// try/catch is needed for protocol violations (GH-1880)
try {
this.decoder.add(data);
} catch(e) {
this.onerror(e);
}
};
/**
* Called when parser fully decodes a packet.
*
* @api private
*/
Client.prototype.ondecoded = function(packet) {
if (parser.CONNECT == packet.type) {
this.connect(packet.nsp);
} else {
var socket = this.nsps[packet.nsp];
if (socket) {
socket.onpacket(packet);
} else {
debug('no socket for namespace %s', packet.nsp);
}
}
};
/**
* Handles an error.
*
* @param {Objcet} error object
* @api private
*/
Client.prototype.onerror = function(err){
this.sockets.forEach(function(socket){
socket.onerror(err);
});
this.onclose('client error');
};
/**
* Called upon transport close.
*
* @param {String} reason
* @api private
*/
Client.prototype.onclose = function(reason){
debug('client close with reason %s', reason);
// ignore a potential subsequent `close` event
this.destroy();
// `nsps` and `sockets` are cleaned up seamlessly
var socket;
while (socket = this.sockets.shift()) {
socket.onclose(reason);
}
this.decoder.destroy(); // clean up decoder
};
/**
* Cleans up event listeners.
*
* @api private
*/
Client.prototype.destroy = function(){
this.conn.removeListener('data', this.ondata);
this.conn.removeListener('error', this.onerror);
this.conn.removeListener('close', this.onclose);
this.decoder.removeListener('decoded', this.ondecoded);
};

379
server/node_modules/socket.io/lib/index.js generated vendored Executable file
View File

@@ -0,0 +1,379 @@
/**
* Module dependencies.
*/
var http = require('http');
var read = require('fs').readFileSync;
var parse = require('url').parse;
var engine = require('engine.io');
var client = require('socket.io-client');
var clientVersion = require('socket.io-client/package').version;
var Client = require('./client');
var Namespace = require('./namespace');
var Adapter = require('socket.io-adapter');
var debug = require('debug')('socket.io:server');
var url = require('url');
/**
* Module exports.
*/
module.exports = Server;
/**
* Socket.IO client source.
*/
var clientSource = read(require.resolve('socket.io-client/socket.io.js'), 'utf-8');
/**
* Server constructor.
*
* @param {http.Server|Number|Object} http server, port or options
* @param {Object} options
* @api public
*/
function Server(srv, opts){
if (!(this instanceof Server)) return new Server(srv, opts);
if ('object' == typeof srv && !srv.listen) {
opts = srv;
srv = null;
}
opts = opts || {};
this.nsps = {};
this.path(opts.path || '/socket.io');
this.serveClient(false !== opts.serveClient);
this.adapter(opts.adapter || Adapter);
this.origins(opts.origins || '*:*');
this.sockets = this.of('/');
if (srv) this.attach(srv, opts);
}
/**
* Server request verification function, that checks for allowed origins
*
* @param {http.IncomingMessage} request
* @param {Function} callback to be called with the result: `fn(err, success)`
*/
Server.prototype.checkRequest = function(req, fn) {
var origin = req.headers.origin || req.headers.referer;
// file:// URLs produce a null Origin which can't be authorized via echo-back
if ('null' == origin || null == origin) origin = '*';
if (!!origin && typeof(this._origins) == 'function') return this._origins(origin, fn);
if (this._origins.indexOf('*:*') !== -1) return fn(null, true);
if (origin) {
try {
var parts = url.parse(origin);
parts.port = parts.port || 80;
var ok =
~this._origins.indexOf(parts.hostname + ':' + parts.port) ||
~this._origins.indexOf(parts.hostname + ':*') ||
~this._origins.indexOf('*:' + parts.port);
return fn(null, !!ok);
} catch (ex) {
}
}
fn(null, false);
};
/**
* Sets/gets whether client code is being served.
*
* @param {Boolean} whether to serve client code
* @return {Server|Boolean} self when setting or value when getting
* @api public
*/
Server.prototype.serveClient = function(v){
if (!arguments.length) return this._serveClient;
this._serveClient = v;
return this;
};
/**
* Old settings for backwards compatibility
*/
var oldSettings = {
"transports": "transports",
"heartbeat timeout": "pingTimeout",
"heartbeat interval": "pingInterval",
"destroy buffer size": "maxHttpBufferSize"
};
/**
* Backwards compatiblity.
*
* @api public
*/
Server.prototype.set = function(key, val){
if ('authorization' == key && val) {
this.use(function(socket, next) {
val(socket.request, function(err, authorized) {
if (err) return next(new Error(err));
if (!authorized) return next(new Error('Not authorized'));
next();
});
});
} else if ('origins' == key && val) {
this.origins(val);
} else if ('resource' == key) {
this.path(val);
} else if (oldSettings[key] && this.eio[oldSettings[key]]) {
this.eio[oldSettings[key]] = val;
} else {
console.error('Option %s is not valid. Please refer to the README.', key);
}
return this;
};
/**
* Sets the client serving path.
*
* @param {String} pathname
* @return {Server|String} self when setting or value when getting
* @api public
*/
Server.prototype.path = function(v){
if (!arguments.length) return this._path;
this._path = v.replace(/\/$/, '');
return this;
};
/**
* Sets the adapter for rooms.
*
* @param {Adapter} pathname
* @return {Server|Adapter} self when setting or value when getting
* @api public
*/
Server.prototype.adapter = function(v){
if (!arguments.length) return this._adapter;
this._adapter = v;
for (var i in this.nsps) {
if (this.nsps.hasOwnProperty(i)) {
this.nsps[i].initAdapter();
}
}
return this;
};
/**
* Sets the allowed origins for requests.
*
* @param {String} origins
* @return {Server|Adapter} self when setting or value when getting
* @api public
*/
Server.prototype.origins = function(v){
if (!arguments.length) return this._origins;
this._origins = v;
return this;
};
/**
* Attaches socket.io to a server or port.
*
* @param {http.Server|Number} server or port
* @param {Object} options passed to engine.io
* @return {Server} self
* @api public
*/
Server.prototype.listen =
Server.prototype.attach = function(srv, opts){
if ('function' == typeof srv) {
var msg = 'You are trying to attach socket.io to an express ' +
'request handler function. Please pass a http.Server instance.';
throw new Error(msg);
}
// handle a port as a string
if (Number(srv) == srv) {
srv = Number(srv);
}
if ('number' == typeof srv) {
debug('creating http server and binding to %d', srv);
var port = srv;
srv = http.Server(function(req, res){
res.writeHead(404);
res.end();
});
srv.listen(port);
}
// set engine.io path to `/socket.io`
opts = opts || {};
opts.path = opts.path || this.path();
// set origins verification
opts.allowRequest = this.checkRequest.bind(this);
// initialize engine
debug('creating engine.io instance with opts %j', opts);
this.eio = engine.attach(srv, opts);
// attach static file serving
if (this._serveClient) this.attachServe(srv);
// Export http server
this.httpServer = srv;
// bind to engine events
this.bind(this.eio);
return this;
};
/**
* Attaches the static file serving.
*
* @param {Function|http.Server} http server
* @api private
*/
Server.prototype.attachServe = function(srv){
debug('attaching client serving req handler');
var url = this._path + '/socket.io.js';
var evs = srv.listeners('request').slice(0);
var self = this;
srv.removeAllListeners('request');
srv.on('request', function(req, res) {
if (0 == req.url.indexOf(url)) {
self.serve(req, res);
} else {
for (var i = 0; i < evs.length; i++) {
evs[i].call(srv, req, res);
}
}
});
};
/**
* Handles a request serving `/socket.io.js`
*
* @param {http.Request} req
* @param {http.Response} res
* @api private
*/
Server.prototype.serve = function(req, res){
var etag = req.headers['if-none-match'];
if (etag) {
if (clientVersion == etag) {
debug('serve client 304');
res.writeHead(304);
res.end();
return;
}
}
debug('serve client source');
res.setHeader('Content-Type', 'application/javascript');
res.setHeader('ETag', clientVersion);
res.writeHead(200);
res.end(clientSource);
};
/**
* Binds socket.io to an engine.io instance.
*
* @param {engine.Server} engine.io (or compatible) server
* @return {Server} self
* @api public
*/
Server.prototype.bind = function(engine){
this.engine = engine;
this.engine.on('connection', this.onconnection.bind(this));
return this;
};
/**
* Called with each incoming transport connection.
*
* @param {engine.Socket} socket
* @return {Server} self
* @api public
*/
Server.prototype.onconnection = function(conn){
debug('incoming connection with id %s', conn.id);
var client = new Client(this, conn);
client.connect('/');
return this;
};
/**
* Looks up a namespace.
*
* @param {String} nsp name
* @param {Function} optional, nsp `connection` ev handler
* @api public
*/
Server.prototype.of = function(name, fn){
if (String(name)[0] !== '/') name = '/' + name;
if (!this.nsps[name]) {
debug('initializing namespace %s', name);
var nsp = new Namespace(this, name);
this.nsps[name] = nsp;
}
if (fn) this.nsps[name].on('connect', fn);
return this.nsps[name];
};
/**
* Closes server connection
*
* @api public
*/
Server.prototype.close = function(){
this.nsps['/'].sockets.forEach(function(socket){
socket.onclose();
});
this.engine.close();
if(this.httpServer){
this.httpServer.close();
}
};
/**
* Expose main namespace (/).
*/
['on', 'to', 'in', 'use', 'emit', 'send', 'write'].forEach(function(fn){
Server.prototype[fn] = function(){
var nsp = this.sockets[fn];
return nsp.apply(this.sockets, arguments);
};
});
Namespace.flags.forEach(function(flag){
Server.prototype.__defineGetter__(flag, function(name){
this.flags.push(name);
return this;
});
});
/**
* BC with `io.listen`
*/
Server.listen = Server;

242
server/node_modules/socket.io/lib/namespace.js generated vendored Executable file
View File

@@ -0,0 +1,242 @@
/**
* Module dependencies.
*/
var Socket = require('./socket');
var Emitter = require('events').EventEmitter;
var parser = require('socket.io-parser');
var debug = require('debug')('socket.io:namespace');
var hasBin = require('has-binary-data');
/**
* Module exports.
*/
module.exports = exports = Namespace;
/**
* Blacklisted events.
*/
exports.events = [
'connect', // for symmetry with client
'connection',
'newListener'
];
/**
* Flags.
*/
exports.flags = ['json'];
/**
* `EventEmitter#emit` reference.
*/
var emit = Emitter.prototype.emit;
/**
* Namespace constructor.
*
* @param {Server} server instance
* @param {Socket} name
* @api private
*/
function Namespace(server, name){
this.name = name;
this.server = server;
this.sockets = [];
this.connected = {};
this.fns = [];
this.ids = 0;
this.acks = {};
this.initAdapter();
}
/**
* Inherits from `EventEmitter`.
*/
Namespace.prototype.__proto__ = Emitter.prototype;
/**
* Apply flags from `Socket`.
*/
exports.flags.forEach(function(flag){
Namespace.prototype.__defineGetter__(flag, function(){
this.flags = this.flags || {};
this.flags[flag] = true;
return this;
});
});
/**
* Initializes the `Adapter` for this nsp.
* Run upon changing adapter by `Server#adapter`
* in addition to the constructor.
*
* @api private
*/
Namespace.prototype.initAdapter = function(){
this.adapter = new (this.server.adapter())(this);
};
/**
* Sets up namespace middleware.
*
* @return {Namespace} self
* @api public
*/
Namespace.prototype.use = function(fn){
this.fns.push(fn);
return this;
};
/**
* Executes the middleware for an incoming client.
*
* @param {Socket} socket that will get added
* @param {Function} last fn call in the middleware
* @api private
*/
Namespace.prototype.run = function(socket, fn){
var fns = this.fns.slice(0);
if (!fns.length) return fn(null);
function run(i){
fns[i](socket, function(err){
// upon error, short-circuit
if (err) return fn(err);
// if no middleware left, summon callback
if (!fns[i + 1]) return fn(null);
// go on to next
run(i + 1);
});
}
run(0);
};
/**
* Targets a room when emitting.
*
* @param {String} name
* @return {Namespace} self
* @api public
*/
Namespace.prototype.to =
Namespace.prototype['in'] = function(name){
this.rooms = this.rooms || [];
if (!~this.rooms.indexOf(name)) this.rooms.push(name);
return this;
};
/**
* Adds a new client.
*
* @return {Socket}
* @api private
*/
Namespace.prototype.add = function(client, fn){
debug('adding socket to nsp %s', this.name);
var socket = new Socket(this, client);
var self = this;
this.run(socket, function(err){
process.nextTick(function(){
if ('open' == client.conn.readyState) {
if (err) return socket.error(err.data || err.message);
// track socket
self.sockets.push(socket);
// it's paramount that the internal `onconnect` logic
// fires before user-set events to prevent state order
// violations (such as a disconnection before the connection
// logic is complete)
socket.onconnect();
if (fn) fn();
// fire user-set events
self.emit('connect', socket);
self.emit('connection', socket);
} else {
debug('next called after client was closed - ignoring socket');
}
});
});
return socket;
};
/**
* Removes a client. Called by each `Socket`.
*
* @api private
*/
Namespace.prototype.remove = function(socket){
var i = this.sockets.indexOf(socket);
if (~i) {
this.sockets.splice(i, 1);
} else {
debug('ignoring remove for %s', socket.id);
}
};
/**
* Emits to all clients.
*
* @return {Namespace} self
* @api public
*/
Namespace.prototype.emit = function(ev){
if (~exports.events.indexOf(ev)) {
emit.apply(this, arguments);
} else {
// set up packet object
var args = Array.prototype.slice.call(arguments);
var parserType = parser.EVENT; // default
if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary
var packet = { type: parserType, data: args };
if ('function' == typeof args[args.length - 1]) {
throw new Error('Callbacks are not supported when broadcasting');
}
this.adapter.broadcast(packet, {
rooms: this.rooms,
flags: this.flags
});
delete this.rooms;
delete this.flags;
}
return this;
};
/**
* Sends a `message` event to all clients.
*
* @return {Namespace} self
* @api public
*/
Namespace.prototype.send =
Namespace.prototype.write = function(){
var args = Array.prototype.slice.call(arguments);
args.unshift('message');
this.emit.apply(this, args);
return this;
};

449
server/node_modules/socket.io/lib/socket.js generated vendored Executable file
View File

@@ -0,0 +1,449 @@
/**
* Module dependencies.
*/
var Emitter = require('events').EventEmitter;
var parser = require('socket.io-parser');
var url = require('url');
var debug = require('debug')('socket.io:socket');
var hasBin = require('has-binary-data');
/**
* Module exports.
*/
module.exports = exports = Socket;
/**
* Blacklisted events.
*
* @api public
*/
exports.events = [
'error',
'connect',
'disconnect',
'newListener',
'removeListener'
];
/**
* Flags.
*
* @api private
*/
var flags = [
'json',
'volatile',
'broadcast'
];
/**
* `EventEmitter#emit` reference.
*/
var emit = Emitter.prototype.emit;
/**
* Interface to a `Client` for a given `Namespace`.
*
* @param {Namespace} nsp
* @param {Client} client
* @api public
*/
function Socket(nsp, client){
this.nsp = nsp;
this.server = nsp.server;
this.adapter = this.nsp.adapter;
this.id = client.id;
this.request = client.request;
this.client = client;
this.conn = client.conn;
this.rooms = [];
this.acks = {};
this.connected = true;
this.disconnected = false;
this.handshake = this.buildHandshake();
}
/**
* Inherits from `EventEmitter`.
*/
Socket.prototype.__proto__ = Emitter.prototype;
/**
* Apply flags from `Socket`.
*/
flags.forEach(function(flag){
Socket.prototype.__defineGetter__(flag, function(){
this.flags = this.flags || {};
this.flags[flag] = true;
return this;
});
});
/**
* `request` engine.io shorcut.
*
* @api public
*/
Socket.prototype.__defineGetter__('request', function(){
return this.conn.request;
});
/**
* Builds the `handshake` BC object
*
* @api private
*/
Socket.prototype.buildHandshake = function(){
return {
headers: this.request.headers,
time: (new Date) + '',
address: this.conn.remoteAddress,
xdomain: !!this.request.headers.origin,
secure: !!this.request.connection.encrypted,
issued: +(new Date),
url: this.request.url,
query: url.parse(this.request.url, true).query || {}
};
};
/**
* Emits to this client.
*
* @return {Socket} self
* @api public
*/
Socket.prototype.emit = function(ev){
if (~exports.events.indexOf(ev)) {
emit.apply(this, arguments);
} else {
var args = Array.prototype.slice.call(arguments);
var packet = {};
packet.type = hasBin(args) ? parser.BINARY_EVENT : parser.EVENT;
packet.data = args;
// access last argument to see if it's an ACK callback
if ('function' == typeof args[args.length - 1]) {
if (this._rooms || (this.flags && this.flags.broadcast)) {
throw new Error('Callbacks are not supported when broadcasting');
}
debug('emitting packet with ack id %d', this.nsp.ids);
this.acks[this.nsp.ids] = args.pop();
packet.id = this.nsp.ids++;
}
if (this._rooms || (this.flags && this.flags.broadcast)) {
this.adapter.broadcast(packet, {
except: [this.id],
rooms: this._rooms,
flags: this.flags
});
} else {
// dispatch packet
this.packet(packet);
}
// reset flags
delete this._rooms;
delete this.flags;
}
return this;
};
/**
* Targets a room when broadcasting.
*
* @param {String} name
* @return {Socket} self
* @api public
*/
Socket.prototype.to =
Socket.prototype.in = function(name){
this._rooms = this._rooms || [];
if (!~this._rooms.indexOf(name)) this._rooms.push(name);
return this;
};
/**
* Sends a `message` event.
*
* @return {Socket} self
* @api public
*/
Socket.prototype.send =
Socket.prototype.write = function(){
var args = Array.prototype.slice.call(arguments);
args.unshift('message');
this.emit.apply(this, args);
return this;
};
/**
* Writes a packet.
*
* @param {Object} packet object
* @api private
*/
Socket.prototype.packet = function(packet, preEncoded){
packet.nsp = this.nsp.name;
var volatile = this.flags && this.flags.volatile;
this.client.packet(packet, preEncoded, volatile);
};
/**
* Joins a room.
*
* @param {String} room
* @param {Function} optional, callback
* @return {Socket} self
* @api private
*/
Socket.prototype.join = function(room, fn){
debug('joining room %s', room);
var self = this;
if (~this.rooms.indexOf(room)) return this;
this.adapter.add(this.id, room, function(err){
if (err) return fn && fn(err);
debug('joined room %s', room);
self.rooms.push(room);
fn && fn(null);
});
return this;
};
/**
* Leaves a room.
*
* @param {String} room
* @param {Function} optional, callback
* @return {Socket} self
* @api private
*/
Socket.prototype.leave = function(room, fn){
debug('leave room %s', room);
var self = this;
this.adapter.del(this.id, room, function(err){
if (err) return fn && fn(err);
debug('left room %s', room);
var idx = self.rooms.indexOf(room);
if (idx >= 0) {
self.rooms.splice(idx, 1);
}
fn && fn(null);
});
return this;
};
/**
* Leave all rooms.
*
* @api private
*/
Socket.prototype.leaveAll = function(){
this.adapter.delAll(this.id);
this.rooms = [];
};
/**
* Called by `Namespace` upon succesful
* middleware execution (ie: authorization).
*
* @api private
*/
Socket.prototype.onconnect = function(){
debug('socket connected - writing packet');
this.join(this.id);
this.packet({ type: parser.CONNECT });
this.nsp.connected[this.id] = this;
};
/**
* Called with each packet. Called by `Client`.
*
* @param {Object} packet
* @api private
*/
Socket.prototype.onpacket = function(packet){
debug('got packet %j', packet);
switch (packet.type) {
case parser.EVENT:
this.onevent(packet);
break;
case parser.BINARY_EVENT:
this.onevent(packet);
break;
case parser.ACK:
this.onack(packet);
break;
case parser.BINARY_ACK:
this.onack(packet);
break;
case parser.DISCONNECT:
this.ondisconnect();
break;
case parser.ERROR:
this.emit('error', packet.data);
}
};
/**
* Called upon event packet.
*
* @param {Object} packet object
* @api private
*/
Socket.prototype.onevent = function(packet){
var args = packet.data || [];
debug('emitting event %j', args);
if (null != packet.id) {
debug('attaching ack callback to event');
args.push(this.ack(packet.id));
}
emit.apply(this, args);
};
/**
* Produces an ack callback to emit with an event.
*
* @param {Number} packet id
* @api private
*/
Socket.prototype.ack = function(id){
var self = this;
var sent = false;
return function(){
// prevent double callbacks
if (sent) return;
var args = Array.prototype.slice.call(arguments);
debug('sending ack %j', args);
var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;
self.packet({
id: id,
type: type,
data: args
});
};
};
/**
* Called upon ack packet.
*
* @api private
*/
Socket.prototype.onack = function(packet){
var ack = this.acks[packet.id];
if ('function' == typeof ack) {
debug('calling ack %s with %j', packet.id, packet.data);
ack.apply(this, packet.data);
delete this.acks[packet.id];
} else {
debug('bad ack %s', packet.id);
}
};
/**
* Called upon client disconnect packet.
*
* @api private
*/
Socket.prototype.ondisconnect = function(){
debug('got disconnect packet');
this.onclose('client namespace disconnect');
};
/**
* Handles a client error.
*
* @api private
*/
Socket.prototype.onerror = function(err){
if (this.listeners('error').length) {
this.emit('error', err);
} else {
console.error('Missing error handler on `socket`.');
console.error(err.stack);
}
};
/**
* Called upon closing. Called by `Client`.
*
* @param {String} reason
* @param {Error} optional error object
* @api private
*/
Socket.prototype.onclose = function(reason){
if (!this.connected) return this;
debug('closing socket - reason %s', reason);
this.leaveAll();
this.nsp.remove(this);
this.client.remove(this);
this.connected = false;
this.disconnected = true;
delete this.nsp.connected[this.id];
this.emit('disconnect', reason);
};
/**
* Produces an `error` packet.
*
* @param {Object} error object
* @api private
*/
Socket.prototype.error = function(err){
this.packet({ type: parser.ERROR, data: err });
};
/**
* Disconnects this client.
*
* @param {Boolean} if `true`, closes the underlying connection
* @return {Socket} self
* @api public
*/
Socket.prototype.disconnect = function(close){
if (!this.connected) return this;
if (close) {
this.client.disconnect();
} else {
this.packet({ type: parser.DISCONNECT });
this.onclose('server namespace disconnect');
}
return this;
};

3
server/node_modules/socket.io/node_modules/debug/.jshintrc generated vendored Executable file
View File

@@ -0,0 +1,3 @@
{
"laxbreak": true
}

View File

@@ -0,0 +1,6 @@
support
test
examples
example
*.sock
dist

157
server/node_modules/socket.io/node_modules/debug/History.md generated vendored Executable file
View File

@@ -0,0 +1,157 @@
2.1.0 / 2014-10-15
==================
* node: implement `DEBUG_FD` env variable support
* package: update "browserify" to v6.1.0
* package: add "license" field to package.json (#135, @panuhorsmalahti)
2.0.0 / 2014-09-01
==================
* package: update "browserify" to v5.11.0
* node: use stderr rather than stdout for logging (#29, @stephenmathieson)
1.0.4 / 2014-07-15
==================
* dist: recompile
* example: remove `console.info()` log usage
* example: add "Content-Type" UTF-8 header to browser example
* browser: place %c marker after the space character
* browser: reset the "content" color via `color: inherit`
* browser: add colors support for Firefox >= v31
* debug: prefer an instance `log()` function over the global one (#119)
* Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
1.0.3 / 2014-07-09
==================
* Add support for multiple wildcards in namespaces (#122, @seegno)
* browser: fix lint
1.0.2 / 2014-06-10
==================
* browser: update color palette (#113, @gscottolson)
* common: make console logging function configurable (#108, @timoxley)
* node: fix %o colors on old node <= 0.8.x
* Makefile: find node path using shell/which (#109, @timoxley)
1.0.1 / 2014-06-06
==================
* browser: use `removeItem()` to clear localStorage
* browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
* package: add "contributors" section
* node: fix comment typo
* README: list authors
1.0.0 / 2014-06-04
==================
* make ms diff be global, not be scope
* debug: ignore empty strings in enable()
* node: make DEBUG_COLORS able to disable coloring
* *: export the `colors` array
* npmignore: don't publish the `dist` dir
* Makefile: refactor to use browserify
* package: add "browserify" as a dev dependency
* Readme: add Web Inspector Colors section
* node: reset terminal color for the debug content
* node: map "%o" to `util.inspect()`
* browser: map "%j" to `JSON.stringify()`
* debug: add custom "formatters"
* debug: use "ms" module for humanizing the diff
* Readme: add "bash" syntax highlighting
* browser: add Firebug color support
* browser: add colors for WebKit browsers
* node: apply log to `console`
* rewrite: abstract common logic for Node & browsers
* add .jshintrc file
0.8.1 / 2014-04-14
==================
* package: re-add the "component" section
0.8.0 / 2014-03-30
==================
* add `enable()` method for nodejs. Closes #27
* change from stderr to stdout
* remove unnecessary index.js file
0.7.4 / 2013-11-13
==================
* remove "browserify" key from package.json (fixes something in browserify)
0.7.3 / 2013-10-30
==================
* fix: catch localStorage security error when cookies are blocked (Chrome)
* add debug(err) support. Closes #46
* add .browser prop to package.json. Closes #42
0.7.2 / 2013-02-06
==================
* fix package.json
* fix: Mobile Safari (private mode) is broken with debug
* fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
0.7.1 / 2013-02-05
==================
* add repository URL to package.json
* add DEBUG_COLORED to force colored output
* add browserify support
* fix component. Closes #24
0.7.0 / 2012-05-04
==================
* Added .component to package.json
* Added debug.component.js build
0.6.0 / 2012-03-16
==================
* Added support for "-" prefix in DEBUG [Vinay Pulim]
* Added `.enabled` flag to the node version [TooTallNate]
0.5.0 / 2012-02-02
==================
* Added: humanize diffs. Closes #8
* Added `debug.disable()` to the CS variant
* Removed padding. Closes #10
* Fixed: persist client-side variant again. Closes #9
0.4.0 / 2012-02-01
==================
* Added browser variant support for older browsers [TooTallNate]
* Added `debug.enable('project:*')` to browser variant [TooTallNate]
* Added padding to diff (moved it to the right)
0.3.0 / 2012-01-26
==================
* Added millisecond diff when isatty, otherwise UTC string
0.2.0 / 2012-01-22
==================
* Added wildcard support
0.1.0 / 2011-12-02
==================
* Added: remove colors unless stderr isatty [TooTallNate]
0.0.1 / 2010-01-03
==================
* Initial release

33
server/node_modules/socket.io/node_modules/debug/Makefile generated vendored Executable file
View File

@@ -0,0 +1,33 @@
# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
# BIN directory
BIN := $(THIS_DIR)/node_modules/.bin
# applications
NODE ?= $(shell which node)
NPM ?= $(NODE) $(shell which npm)
BROWSERIFY ?= $(NODE) $(BIN)/browserify
all: dist/debug.js
install: node_modules
clean:
@rm -rf node_modules dist
dist:
@mkdir -p $@
dist/debug.js: node_modules browser.js debug.js dist
@$(BROWSERIFY) \
--standalone debug \
. > $@
node_modules: package.json
@NODE_ENV= $(NPM) install
@touch node_modules
.PHONY: all install clean

156
server/node_modules/socket.io/node_modules/debug/Readme.md generated vendored Executable file
View File

@@ -0,0 +1,156 @@
# debug
tiny node.js debugging utility modelled after node core's debugging technique.
## Installation
```bash
$ npm install debug
```
## Usage
With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility.
Example _app.js_:
```js
var debug = require('debug')('http')
, http = require('http')
, name = 'My App';
// fake app
debug('booting %s', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
res.end('hello\n');
}).listen(3000, function(){
debug('listening');
});
// fake worker of some kind
require('./worker');
```
Example _worker.js_:
```js
var debug = require('debug')('worker');
setInterval(function(){
debug('doing some work');
}, 1000);
```
The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
## Millisecond diff
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)
## Conventions
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".
## Wildcards
The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:".
## Browser support
Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`.
```js
a = debug('worker:a');
b = debug('worker:b');
setInterval(function(){
a('doing some work');
}, 1000);
setInterval(function(){
b('doing some work');
}, 1200);
```
#### Web Inspector Colors
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).
Colored output looks something like:
![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
### stderr vs stdout
You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally:
Example _stderr.js_:
```js
var debug = require('../');
var log = debug('app:log');
// by default console.log is used
log('goes to stdout!');
var error = debug('app:error');
// set this namespace to log via console.error
error.log = console.error.bind(console); // don't forget to bind to console!
error('goes to stderr');
log('still goes to stdout!');
// set all output to go via console.warn
// overrides all per-namespace log settings
debug.log = console.warn.bind(console);
log('now goes to stderr via console.warn');
error('still goes to stderr, but via console.warn now');
```
## Authors
- TJ Holowaychuk
- Nathan Rajlich
## License
(The MIT License)
Copyright (c) 2014 TJ Holowaychuk &lt;tj@vision-media.ca&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.

147
server/node_modules/socket.io/node_modules/debug/browser.js generated vendored Executable file
View File

@@ -0,0 +1,147 @@
/**
* This is the web browser implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [
'lightseagreen',
'forestgreen',
'goldenrod',
'dodgerblue',
'darkorchid',
'crimson'
];
/**
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
* and the Firebug extension (any Firefox version) are known
* to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
function useColors() {
// is webkit? http://stackoverflow.com/a/16459606/376773
return ('WebkitAppearance' in document.documentElement.style) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(window.console && (console.firebug || (console.exception && console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
return JSON.stringify(v);
};
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs() {
var args = arguments;
var useColors = this.useColors;
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? ' %c' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
if (!useColors) return args;
var c = 'color: ' + this.color;
args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
// the final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
var index = 0;
var lastC = 0;
args[0].replace(/%[a-z%]/g, function(match) {
if ('%%' === match) return;
index++;
if ('%c' === match) {
// we only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
return args;
}
/**
* Invokes `console.log()` when available.
* No-op when `console.log` is not a "function".
*
* @api public
*/
function log() {
// This hackery is required for IE8,
// where the `console.log` function doesn't have 'apply'
return 'object' == typeof console
&& 'function' == typeof console.log
&& Function.prototype.apply.call(console.log, console, arguments);
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (null == namespaces) {
localStorage.removeItem('debug');
} else {
localStorage.debug = namespaces;
}
} catch(e) {}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
var r;
try {
r = localStorage.debug;
} catch(e) {}
return r;
}
/**
* Enable namespaces listed in `localStorage.debug` initially.
*/
exports.enable(load());

View File

@@ -0,0 +1,19 @@
{
"name": "debug",
"repo": "visionmedia/debug",
"description": "small debugging utility",
"version": "2.1.0",
"keywords": [
"debug",
"log",
"debugger"
],
"main": "browser.js",
"scripts": [
"browser.js",
"debug.js"
],
"dependencies": {
"guille/ms.js": "0.6.1"
}
}

197
server/node_modules/socket.io/node_modules/debug/debug.js generated vendored Executable file
View File

@@ -0,0 +1,197 @@
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = debug;
exports.coerce = coerce;
exports.disable = disable;
exports.enable = enable;
exports.enabled = enabled;
exports.humanize = require('ms');
/**
* The currently active debug mode names, and names to skip.
*/
exports.names = [];
exports.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lowercased letter, i.e. "n".
*/
exports.formatters = {};
/**
* Previously assigned color.
*/
var prevColor = 0;
/**
* Previous log timestamp.
*/
var prevTime;
/**
* Select a color.
*
* @return {Number}
* @api private
*/
function selectColor() {
return exports.colors[prevColor++ % exports.colors.length];
}
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function debug(namespace) {
// define the `disabled` version
function disabled() {
}
disabled.enabled = false;
// define the `enabled` version
function enabled() {
var self = enabled;
// set `diff` timestamp
var curr = +new Date();
var ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
// add the `color` if not set
if (null == self.useColors) self.useColors = exports.useColors();
if (null == self.color && self.useColors) self.color = selectColor();
var args = Array.prototype.slice.call(arguments);
args[0] = exports.coerce(args[0]);
if ('string' !== typeof args[0]) {
// anything else let's inspect with %o
args = ['%o'].concat(args);
}
// apply any `formatters` transformations
var index = 0;
args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
// if we encounter an escaped % then don't increase the array index
if (match === '%%') return match;
index++;
var formatter = exports.formatters[format];
if ('function' === typeof formatter) {
var val = args[index];
match = formatter.call(self, val);
// now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
if ('function' === typeof exports.formatArgs) {
args = exports.formatArgs.apply(self, args);
}
var logFn = enabled.log || exports.log || console.log.bind(console);
logFn.apply(self, args);
}
enabled.enabled = true;
var fn = exports.enabled(namespace) ? enabled : disabled;
fn.namespace = namespace;
return fn;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
exports.save(namespaces);
var split = (namespaces || '').split(/[\s,]+/);
var len = split.length;
for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else {
exports.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @api public
*/
function disable() {
exports.enable('');
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
var i, len;
for (i = 0, len = exports.skips.length; i < len; i++) {
if (exports.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = exports.names.length; i < len; i++) {
if (exports.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) return val.stack || val.message;
return val;
}

209
server/node_modules/socket.io/node_modules/debug/node.js generated vendored Executable file
View File

@@ -0,0 +1,209 @@
/**
* Module dependencies.
*/
var tty = require('tty');
var util = require('util');
/**
* This is the Node.js implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [6, 2, 3, 4, 5, 1];
/**
* The file descriptor to write the `debug()` calls to.
* Set the `DEBUG_FD` env variable to override with another value. i.e.:
*
* $ DEBUG_FD=3 node script.js 3>debug.log
*/
var fd = parseInt(process.env.DEBUG_FD, 10) || 2;
var stream = 1 === fd ? process.stdout :
2 === fd ? process.stderr :
createWritableStdioStream(fd);
/**
* Is stdout a TTY? Colored output is enabled when `true`.
*/
function useColors() {
var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase();
if (0 === debugColors.length) {
return tty.isatty(fd);
} else {
return '0' !== debugColors
&& 'no' !== debugColors
&& 'false' !== debugColors
&& 'disabled' !== debugColors;
}
}
/**
* Map %o to `util.inspect()`, since Node doesn't do that out of the box.
*/
var inspect = (4 === util.inspect.length ?
// node <= 0.8.x
function (v, colors) {
return util.inspect(v, void 0, void 0, colors);
} :
// node > 0.8.x
function (v, colors) {
return util.inspect(v, { colors: colors });
}
);
exports.formatters.o = function(v) {
return inspect(v, this.useColors)
.replace(/\s*\n\s*/g, ' ');
};
/**
* Adds ANSI color escape codes if enabled.
*
* @api public
*/
function formatArgs() {
var args = arguments;
var useColors = this.useColors;
var name = this.namespace;
if (useColors) {
var c = this.color;
args[0] = ' \u001b[9' + c + 'm' + name + ' '
+ '\u001b[0m'
+ args[0] + '\u001b[3' + c + 'm'
+ ' +' + exports.humanize(this.diff) + '\u001b[0m';
} else {
args[0] = new Date().toUTCString()
+ ' ' + name + ' ' + args[0];
}
return args;
}
/**
* Invokes `console.error()` with the specified arguments.
*/
function log() {
return stream.write(util.format.apply(this, arguments) + '\n');
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
if (null == namespaces) {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete process.env.DEBUG;
} else {
process.env.DEBUG = namespaces;
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
return process.env.DEBUG;
}
/**
* Copied from `node/src/node.js`.
*
* XXX: It's lame that node doesn't expose this API out-of-the-box. It also
* relies on the undocumented `tty_wrap.guessHandleType()` which is also lame.
*/
function createWritableStdioStream (fd) {
var stream;
var tty_wrap = process.binding('tty_wrap');
// Note stream._type is used for test-module-load-list.js
switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
stream = new tty.WriteStream(fd);
stream._type = 'tty';
// Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
case 'FILE':
var fs = require('fs');
stream = new fs.SyncWriteStream(fd, { autoClose: false });
stream._type = 'fs';
break;
case 'PIPE':
case 'TCP':
var net = require('net');
stream = new net.Socket({
fd: fd,
readable: false,
writable: true
});
// FIXME Should probably have an option in net.Socket to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stream.readable = false;
stream.read = null;
stream._type = 'pipe';
// FIXME Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');
}
// For supporting legacy API we put the FD here.
stream.fd = fd;
stream._isStdio = true;
return stream;
}
/**
* Enable namespaces listed in `process.env.DEBUG` initially.
*/
exports.enable(load());

View File

@@ -0,0 +1,5 @@
node_modules
test
History.md
Makefile
component.json

View File

@@ -0,0 +1,33 @@
# ms.js: miliseconds conversion utility
```js
ms('1d') // 86400000
ms('10h') // 36000000
ms('2h') // 7200000
ms('1m') // 60000
ms('5s') // 5000
ms('100') // 100
```
```js
ms(60000) // "1m"
ms(2 * 60000) // "2m"
ms(ms('10 hours')) // "10h"
```
```js
ms(60000, { long: true }) // "1 minute"
ms(2 * 60000, { long: true }) // "2 minutes"
ms(ms('10 hours', { long: true })) // "10 hours"
```
- Node/Browser compatible. Published as `ms` in NPM.
- If a number is supplied to `ms`, a string with a unit is returned.
- If a string that contains the number is supplied, it returns it as
a number (e.g: it returns `100` for `'100'`).
- If you pass a string with a number and a valid unit, the number of
equivalent ms is returned.
## License
MIT

View File

@@ -0,0 +1,111 @@
/**
* Helpers.
*/
var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var y = d * 365.25;
/**
* Parse or format the given `val`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} options
* @return {String|Number}
* @api public
*/
module.exports = function(val, options){
options = options || {};
if ('string' == typeof val) return parse(val);
return options.long
? long(val)
: short(val);
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
if (!match) return;
var n = parseFloat(match[1]);
var type = (match[2] || 'ms').toLowerCase();
switch (type) {
case 'years':
case 'year':
case 'y':
return n * y;
case 'days':
case 'day':
case 'd':
return n * d;
case 'hours':
case 'hour':
case 'h':
return n * h;
case 'minutes':
case 'minute':
case 'm':
return n * m;
case 'seconds':
case 'second':
case 's':
return n * s;
case 'ms':
return n;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function short(ms) {
if (ms >= d) return Math.round(ms / d) + 'd';
if (ms >= h) return Math.round(ms / h) + 'h';
if (ms >= m) return Math.round(ms / m) + 'm';
if (ms >= s) return Math.round(ms / s) + 's';
return ms + 'ms';
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function long(ms) {
return plural(ms, d, 'day')
|| plural(ms, h, 'hour')
|| plural(ms, m, 'minute')
|| plural(ms, s, 'second')
|| ms + ' ms';
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) return;
if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
return Math.ceil(ms / n) + ' ' + name + 's';
}

View File

@@ -0,0 +1,45 @@
{
"name": "ms",
"version": "0.6.2",
"description": "Tiny ms conversion utility",
"repository": {
"type": "git",
"url": "git://github.com/guille/ms.js.git"
},
"main": "./index",
"devDependencies": {
"mocha": "*",
"expect.js": "*",
"serve": "*"
},
"component": {
"scripts": {
"ms/index.js": "index.js"
}
},
"bugs": {
"url": "https://github.com/guille/ms.js/issues"
},
"_id": "ms@0.6.2",
"dist": {
"shasum": "d89c2124c6fdc1353d65a8b77bf1aac4b193708c",
"tarball": "http://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
},
"_from": "ms@0.6.2",
"_npmVersion": "1.2.30",
"_npmUser": {
"name": "rauchg",
"email": "rauchg@gmail.com"
},
"maintainers": [
{
"name": "rauchg",
"email": "rauchg@gmail.com"
}
],
"directories": {},
"_shasum": "d89c2124c6fdc1353d65a8b77bf1aac4b193708c",
"_resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz",
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/guille/ms.js"
}

View File

@@ -0,0 +1,72 @@
{
"name": "debug",
"version": "2.1.0",
"repository": {
"type": "git",
"url": "git://github.com/visionmedia/debug.git"
},
"description": "small debugging utility",
"keywords": [
"debug",
"log",
"debugger"
],
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
"contributors": [
{
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
"url": "http://n8.io"
}
],
"license": "MIT",
"dependencies": {
"ms": "0.6.2"
},
"devDependencies": {
"browserify": "6.1.0",
"mocha": "*"
},
"main": "./node.js",
"browser": "./browser.js",
"component": {
"scripts": {
"debug/index.js": "browser.js",
"debug/debug.js": "debug.js"
}
},
"gitHead": "953162b4fa8849268d147f4bac91c737baee55bb",
"bugs": {
"url": "https://github.com/visionmedia/debug/issues"
},
"homepage": "https://github.com/visionmedia/debug",
"_id": "debug@2.1.0",
"scripts": {},
"_shasum": "33ab915659d8c2cc8a41443d94d6ebd37697ed21",
"_from": "debug@2.1.0",
"_npmVersion": "2.1.3",
"_nodeVersion": "0.10.32",
"_npmUser": {
"name": "tootallnate",
"email": "nathan@tootallnate.net"
},
"maintainers": [
{
"name": "tjholowaychuk",
"email": "tj@vision-media.ca"
},
{
"name": "tootallnate",
"email": "nathan@tootallnate.net"
}
],
"dist": {
"shasum": "33ab915659d8c2cc8a41443d94d6ebd37697ed21",
"tarball": "http://registry.npmjs.org/debug/-/debug-2.1.0.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/debug/-/debug-2.1.0.tgz"
}

View File

@@ -0,0 +1,6 @@
examples
node_modules
test
npm-debug.log
coverage.html
.gitignore

View File

@@ -0,0 +1,7 @@
language: node_js
node_js:
- "0.10"
- "0.8"
notifications:
irc: "irc.freenode.org#socket.io"

View File

@@ -0,0 +1,408 @@
1.5.1 / 2015-01-19
==================
* no change on this release
* package: bump `engine.io-client`
1.5.0 / 2015-01-18
==================
* package: bump `engine.io-parser`
* polling: correctly abort the ongoing data request when closing [lpinca]
* add cert-related client tests [rase-]
1.4.3 / 2014-11-21
==================
* package: bump `ws` to fix fd leaks
* socket: flush the write buffer before closing the socket [nkzawa]
* polling: close the pending poll request when closing transport [nkzawa]
1.4.2 / 2014-10-08
==================
* add iframe onload handling to jsonp tests [rase-]
1.4.1 / 2014-10-03
==================
* socket: allow upgrades if the socket is still in closing state
* README: fix typo
1.4.0 / 2014-09-03
==================
* readme: fix formatting for goals numbering
* server: ref fix by @nicokaiser
* server: fix ws memory leak (fixes #268)
* cache remote address in handshake since it might be lost later.
* correct git ref
* update client to commit with bumped parser
* package: bump parser
* npmignore: ignore `.gitignore`
* package: bump `debug`
* package: bump `engine.io-parser` for memleak fix
1.3.1 / 2014-06-19
==================
* package: bump `engine.io-client`
1.3.0 / 2014-06-13
==================
* update example to use v1.2.2
* fixed newline parsing in jsonp
* make require('engine.io')() return a new Server instance [defunctzombie]
* add Server.attach method [defunctzombie]
* fix GH-211, set CORS headers when sending error message [mokesmokes]
1.2.2 / 2014-05-30
==================
* package: bump `engine.io-parser` for binary utf8 fix
1.2.1 / 2014-05-22
==================
* package: bump engine.io-client
1.2.0 / 2014-05-18
==================
* removed flashsocket, moving to userland
1.1.1 / 2014-05-14
==================
* test: reduce packet size
* package: bump parser
1.1.0 / 2014-04-27
==================
* socket: removed unneeded `clearTimeout` (fixes #250)
* made the request verification process async
* package: bump `engine.io-parser`
* use _query instead of query, fixes compat with restify
* added a maximum buffer size to received data from polling
* fixing looping array via for in to normal loop
1.0.5 / 2014-03-18
==================
* package: bump `engine.io-parser` and `engine.io-client`
1.0.4 / 2014-03-14
==================
* package: bump `engine.io-client`
1.0.3 / 2014-03-12
==================
* package: bump `engine.io-client`
1.0.2 / 2014-03-12
==================
* bump engine.io-client
1.0.1 / 2014-03-06
==================
* package: bump `engine.io-parser`
* transports: fix jshint warnings and style
1.0.0 / 2014-03-06
==================
* polling-xhr: added `OPTIONS` support, fixes CORS
* close() properly when triggered in connection handler
* fix DDOS vector by setting up too many intervals
* binary support
0.9.0 / 2014-02-09
==================
* Prevent errors with connections behind proxies without WS support
like Squid [nicklagrow, samaanghani, davidhcummings]
* Socket#request a simple property [mokesmokes]
* Changed `Socket`'s `upgrade` event to happen after upgrade [mokesmokes]
* Document `Socket#id` [mokesmokes]
0.8.2 / 2014-01-18
==================
* package: bump `engine.io-client`
0.8.1 / 2014-01-17
==================
* package: bump `engine.io-client`
* package: pin dev deps
* examples: fix port output
* fix latency example
0.8.0 / 2014-01-05
==================
* package: bump `engine.io-client` to `0.8.0`
* test: fix syntax, remove globals
0.7.14 / 2014-01-01
===================
* package: bump `engine.io-client` to `0.7.14`
0.7.13 / 2013-12-20
===================
* package: bump `engine.io-client`
* transports: added support for XSS filters on IE [guille, 3rd-eden]
0.7.12 / 2013-11-11
===================
* package: bump `engine.io-client`
0.7.11 / 2013-11-06
===================
* package: bump engine.io-client
* fix GH-198
0.7.10 / 2013-10-28
===================
* package: bump `engine.io-client`
* package: update "ws" to v0.4.31
0.7.9 / 2013-08-30
==================
* package: bump `engine.io-client`
0.7.8 / 2013-08-30
==================
* package: bump `engine.io-client`
* package: bump ws
0.7.7 / 2013-08-30
==================
* package: bump `engine.io-client`
0.7.6 / 2013-08-30
==================
* package: bump engine.io-client
0.7.5 / 2013-08-30
==================
* package: bump engine.io-client
0.7.4 / 2013-08-25
==================
* package: bump `engine.io-client`
0.7.3 / 2013-08-23
==================
* package: bump engine.io-client (noop)
* package: fix regresison in upgrade cause by ws update
0.7.2 / 2013-08-23
==================
* package: bump `engine.io-client` for `WebSocket` browser fix
0.7.1 / 2013-08-23
==================
* package: bump engine.io-client for ws fix
0.7.0 / 2013-08-23
==================
* package: bump engine.io-client
* updated example
* inline merge
* added support node version 0.10 to .travis.yml
* fixed respond to flash policy request test. Closes #184
* fixed upgrade with timeout test. Closes #185
* engine.io: don't use __proto__, closes #170
0.6.3 / 2013-06-21
==================
* package: bumped `engine.io-client` to `0.6.3`
0.6.2 / 2013-06-15
==================
* fix upgrade stalling edge case introduced with #174 fix
* remove unneeded client code related to iOS
* added test for `engine.io-client` `0.6.1`
0.6.1 / 2013-06-06
==================
* package: bumped `engine.io-client` to `0.6.1`
0.6.0 / 2013-05-31
==================
* socket: clear timer after sending one noop packet (fixes #174)
* clear all timers on socket close
* sending error on transport creation upon a bad request
* added test for client-side buffer cleanup
* changed flushComplete to flush
* ended support for node 0.6
0.5.0 / 2013-03-16
==================
* polling: implemented new parser
* test writeBuffer isn't cleared onError, removed 'closing' check in .flush()
* fixed bug89 and added tests: writeBuffer not flushed until nextTick
0.4.3 / 2013-02-08
==================
* package: bumped `engine.io-client` to `0.4.3`
0.4.2 / 2013-02-08
==================
* Only end upgrade socket connections if unhandled
* Fix websocket dependency
* Close socket if upgrade is received and socket.readyState != open
0.4.1 / 2013-01-18
==================
* package: bumped versions
* Fixed bugs in previous send callback fix and updated test cases
* Added a test case which makes the code before the send callback fix fail
* socket: emit `data` event (synonym with `message`)
* socket: added `Socket#write`
* engine.io: cleanup
* engine.io: deprecated `resource`
* `npm docs engine.io` works now
0.3.10 / 2012-12-03
===================
* package: bumped `engine.io-client` with `close` fixes
* add packetCreate event [jxck]
* add packet event to socket [jxck]
* transport: remove `Connection` headers and let node handle it
* server: send validation failure reason to clients
* engine: invoking as a function causes attach
* socket: reset `writeBuffer` before send
0.3.9 / 2012-10-23
==================
* package: bumped `engine.io-client`
0.3.8 / 2012-10-23
==================
* package: bumped engine.io-client
* examples: added first example
0.3.7 / 2012-10-21
==================
* package: bumped `engine.io-client`
0.3.6 / 2012-10-21
==================
[skipped]
0.3.5 / 2012-10-14
==================
* package: reverted last commit - we use the parser from the client
0.3.4 / 2012-10-14
==================
* package: `engine.io-client` moved to `devDependencies`
* socket: added missing jsdoc
0.3.3 / 2012-10-10
==================
* socket: fixed check interval clearing [joewalnes]
* transports: improved instrumentation
0.3.2 / 2012-10-08
==================
* socket: improve check interval for upgrade
0.3.1 / 2012-10-08
==================
* socket: faster upgrades (we perform a check immediately)
* server: don't assume sid is numeric
0.3.0 / 2012-10-04
==================
* socket: `writeBuffer` now gets sliced, and is recoverable after `close` [afshinm]
* server: expect ping from client and send interval with handshake [cadorn]
* polling-jsonp: prevent client breakage with utf8 whitespace
* socket: fix `flush` and `drain` events
* socket: add `send` callback [afshinm]
* transport: avoid unhandled error events for stale transports
* README: documentation improvements [EugenDueck]
0.2.2 / 2012-08-26
==================
* server: remove buffering for flash policy requests
* transport: avoid unhandled error events for stale transports (fixes #69)
* readme: documented `toString` behavior on `send` [EugenDueck]
0.2.1 / 2012-08-13
==================
* polling-xhr: skip Keep-Alive when it's implied [EugenDueck]
* polling-jsonp: skip Keep-Alive when it's implied [EugenDueck]
* README: added plugins list with engine.io-conflation
* socket: added flush/drain events (fixes #56)
* server: avoid passing websocket to non-websocket transports (fixes #24)
0.2.0 / 2012-08-06
==================
* Bumped client
* test: added closing connection test
* server: implemented stronger id generator with collision detection
0.1.2 / 2012-08-02
==================
* Fixed a jsonp bug in Nokia mobile phones and potentially other UAs.
0.1.1 / 2012-08-01
==================
* Fixed errors when a socket is closed while upgrade probe is happening.
* Improved WS error handling
* Replaced websocket.io with ws, now that it supports older drafts
* README fixes
0.1.0 / 2012-07-03
==================
* Initial release.

View File

@@ -0,0 +1,23 @@
TESTS = test/*.js
BENCHMARKS = $(shell find bench -type f ! -name 'runner.js')
REPORTER = dot
test:
@./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--slow 500ms \
--bail \
--globals ___eio,document \
$(TESTS)
test-cov: lib-cov
EIO_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
lib-cov:
jscoverage --no-highlight lib lib-cov
bench:
@node $(PROFILEFLAGS) bench/runner.js $(BENCHMARKS)
.PHONY: test test-cov bench

View File

@@ -0,0 +1,517 @@
# Engine.IO: the realtime engine
[![Build Status](https://secure.travis-ci.org/Automattic/engine.io.png)](http://travis-ci.org/Automattic/engine.io)
[![NPM version](https://badge.fury.io/js/engine.io.png)](http://badge.fury.io/js/engine.io)
`Engine.IO` is the implementation of transport-based
cross-browser/cross-device bi-directional communication layer for
[Socket.IO](http://github.com/learnboost/socket.io).
## How to use
### Server
#### (A) Listening on a port
```js
var engine = require('engine.io');
var server = engine.listen(80);
server.on('connection', function(socket){
socket.send('utf 8 string');
socket.send(new Buffer([0, 1, 2, 3, 4, 5])); // binary data
});
```
#### (B) Intercepting requests for a http.Server
```js
var engine = require('engine.io');
var http = require('http').createServer().listen(3000);
var server = engine.attach(http);
server.on('connection', function (socket) {
socket.on('message', function(data){ });
socket.on('close', function(){ });
});
```
#### (C) Passing in requests
```js
var engine = require('engine.io');
var server = new engine.Server();
server.on('connection', function(socket){
socket.send('hi');
});
// …
httpServer.on('upgrade', function(req, socket, head){
server.handleUpgrade(req, socket, head);
});
httpServer.on('request', function(req, res){
server.handleRequest(req, res);
});
```
### Client
```html
<script src="/path/to/engine.io.js"></script>
<script>
var socket = new eio.Socket('ws://localhost/');
socket.on('open', function(){
socket.on('message', function(data){});
socket.on('close', function(){});
});
</script>
```
For more information on the client refer to the
[engine-client](http://github.com/learnboost/engine.io-client) repository.
## What features does it have?
- **Maximum reliability**. Connections are established even in the presence of:
- proxies and load balancers.
- personal firewall and antivirus software.
- for more information refer to **Goals** and **Architecture** sections
- **Minimal client size** aided by:
- lazy loading of flash transports.
- lack of redundant transports.
- **Scalable**
- load balancer friendly
- **Future proof**
- **100% Node.JS core style**
- No API sugar (left for higher level projects)
- Written in readable vanilla JavaScript
## API
### Server
<hr><br>
#### Top-level
These are exposed by `require('engine.io')`:
##### Events
- `flush`
- Called when a socket buffer is being flushed.
- **Arguments**
- `Socket`: socket being flushed
- `Array`: write buffer
- `drain`
- Called when a socket buffer is drained
- **Arguments**
- `Socket`: socket being flushed
##### Properties
- `protocol` _(Number)_: protocol revision number
- `Server`: Server class constructor
- `Socket`: Socket class constructor
- `Transport` _(Function)_: transport constructor
- `transports` _(Object)_: map of available transports
##### Methods
- `()`
- Returns a new `Server` instance. If the first argument is an `http.Server` then the
new `Server` instance will be attached to it. Otherwise, the arguments are passed
directly to the `Server` constructor.
- **Parameters**
- `http.Server`: optional, server to attach to.
- `Object`: optional, options object (see `Server#constructor` api docs below)
The following are identical ways to instantiate a server and then attach it.
```js
var httpServer; // previously created with `http.createServer();` from node.js api.
// create a server first, and then attach
var eioServer = require('engine.io').Server();
eioServer.attach(httpServer);
// or call the module as a function to get `Server`
var eioServer = require('engine.io')();
eioServer.attach(httpServer);
// immediately attach
var eioServer = require('engine.io')(httpServer);
```
- `listen`
- Creates an `http.Server` which listens on the given port and attaches WS
to it. It returns `501 Not Implemented` for regular http requests.
- **Parameters**
- `Number`: port to listen on.
- `Object`: optional, options object
- `Function`: callback for `listen`.
- **Options**
- All options from `Server.attach` method, documented below.
- **Additionally** See Server `constructor` below for options you can pass for creating the new Server
- **Returns** `Server`
- `attach`
- Captures `upgrade` requests for a `http.Server`. In other words, makes
a regular http.Server WebSocket-compatible.
- **Parameters**
- `http.Server`: server to attach to.
- `Object`: optional, options object
- **Options**
- All options from `Server.attach` method, documented below.
- **Additionally** See Server `constructor` below for options you can pass for creating the new Server
- **Returns** `Server` a new Server instance.
<hr><br>
#### Server
The main server/manager. _Inherits from EventEmitter_.
##### Events
- `connection`
- Fired when a new connection is established.
- **Arguments**
- `Socket`: a Socket object
##### Properties
**Important**: if you plan to use Engine.IO in a scalable way, please
keep in mind the properties below will only reflect the clients connected
to a single process.
- `clients` _(Object)_: hash of connected clients by id.
- `clientsCount` _(Number)_: number of connected clients.
##### Methods
- **constructor**
- Initializes the server
- **Parameters**
- `Object`: optional, options object
- **Options**
- `pingTimeout` (`Number`): how many ms without a pong packet to
consider the connection closed (`60000`)
- `pingInterval` (`Number`): how many ms before sending a new ping
packet (`25000`)
- `maxHttpBufferSize` (`Number`): how many bytes or characters a message
can be when polling, before closing the session (to avoid DoS). Default
value is `10E7`.
- `allowRequest` (`Function`): A function that receives a given handshake
or upgrade request as its first parameter, and can decide whether to
continue or not. The second argument is a function that needs to be
called with the decided information: `fn(err, success)`, where
`success` is a boolean value where false means that the request is
rejected, and err is an error code.
- `transports` (`<Array> String`): transports to allow connections
to (`['polling', 'websocket']`)
- `allowUpgrades` (`Boolean`): whether to allow transport upgrades
(`true`)
- `cookie` (`String|Boolean`): name of the HTTP cookie that
contains the client sid to send as part of handshake response
headers. Set to `false` to not send one. (`io`)
- `close`
- Closes all clients
- **Returns** `Server` for chaining
- `handleRequest`
- Called internally when a `Engine` request is intercepted.
- **Parameters**
- `http.ServerRequest`: a node request object
- `http.ServerResponse`: a node response object
- **Returns** `Server` for chaining
- `handleUpgrade`
- Called internally when a `Engine` ws upgrade is intercepted.
- **Parameters** (same as `upgrade` event)
- `http.ServerRequest`: a node request object
- `net.Stream`: TCP socket for the request
- `Buffer`: legacy tail bytes
- **Returns** `Server` for chaining
- `attach`
- Attach this Server instance to an `http.Server`
- Captures `upgrade` requests for a `http.Server`. In other words, makes
a regular http.Server WebSocket-compatible.
- **Parameters**
- `http.Server`: server to attach to.
- `Object`: optional, options object
- **Options**
- `path` (`String`): name of the path to capture (`/engine.io`).
- `destroyUpgrade` (`Boolean`): destroy unhandled upgrade requests (`true`)
- `destroyUpgradeTimeout` (`Number`): milliseconds after which unhandled requests are ended (`1000`)
<hr><br>
#### Socket
A representation of a client. _Inherits from EventEmitter_.
##### Events
- `close`
- Fired when the client is disconnected.
- **Arguments**
- `String`: reason for closing
- `Object`: description object (optional)
- `message`
- Fired when the client sends a message.
- **Arguments**
- `String` or `Buffer`: Unicode string or Buffer with binary contents
- `error`
- Fired when an error occurs.
- **Arguments**
- `Error`: error object
- `flush`
- Called when the write buffer is being flushed.
- **Arguments**
- `Array`: write buffer
- `drain`
- Called when the write buffer is drained
- `packet`
- Called when a socket received a packet (`message`, `ping`)
- **Arguments**
- `type`: packet type
- `data`: packet data (if type is message)
- `packetCreate`
- Called before a socket sends a packet (`message`, `pong`)
- **Arguments**
- `type`: packet type
- `data`: packet data (if type is message)
##### Properties
- `id` _(String)_: unique identifier
- `server` _(Server)_: engine parent reference
- `request` _(http.ServerRequest)_: request that originated the Socket
- `upgraded` _(Boolean)_: whether the transport has been upgraded
- `readyState` _(String)_: opening|open|closing|closed
- `transport` _(Transport)_: transport reference
##### Methods
- `send`:
- Sends a message, performing `message = toString(arguments[0])` unless
sending binary data, which is sent as is.
- **Parameters**
- `String` | `Buffer` | `ArrayBuffer` | `ArrayBufferView`: a string or any object implementing `toString()`, with outgoing data, or a Buffer or ArrayBuffer with binary data. Also any ArrayBufferView can be sent as is.
- `Function`: optional, a callback executed when the message gets flushed out by the transport
- **Returns** `Socket` for chaining
- `close`
- Disconnects the client
- **Returns** `Socket` for chaining
### Client
<hr><br>
Exposed in the `eio` global namespace (in the browser), or by
`require('engine.io-client')` (in Node.JS).
For the client API refer to the
[engine-client](http://github.com/learnboost/engine.io-client) repository.
## Debug / logging
Engine.IO is powered by [debug](http://github.com/visionmedia/debug).
In order to see all the debug output, run your app with the environment variable
`DEBUG` including the desired scope.
To see the output from all of Engine.IO's debugging scopes you can use:
```
DEBUG=engine* node myapp
```
## Transports
- `polling`: XHR / JSONP polling transport.
- `websocket`: WebSocket transport.
## Plugins
- [engine.io-conflation](https://github.com/EugenDueck/engine.io-conflation): Makes **conflation and aggregation** of messages straightforward.
## Support
The support channels for `engine.io` are the same as `socket.io`:
- irc.freenode.net **#socket.io**
- [Google Groups](http://groups.google.com/group/socket_io)
- [Website](http://socket.io)
## Development
To contribute patches, run tests or benchmarks, make sure to clone the
repository:
```
git clone git://github.com/LearnBoost/engine.io.git
```
Then:
```
cd engine.io
npm install
```
## Tests
Tests run with `make test`. It runs the server tests that are aided by
the usage of `engine.io-client`.
Make sure `npm install` is run first.
## Goals
The main goal of `Engine` is ensuring the most reliable realtime communication.
Unlike the previous Socket.IO core, it always establishes a long-polling
connection first, then tries to upgrade to better transports that are "tested" on
the side.
During the lifetime of the Socket.IO projects, we've found countless drawbacks
to relying on `HTML5 WebSocket` or `Flash Socket` as the first connection
mechanisms.
Both are clearly the _right way_ of establishing a bidirectional communication,
with HTML5 WebSocket being the way of the future. However, to answer most business
needs, alternative traditional HTTP 1.1 mechanisms are just as good as delivering
the same solution.
WebSocket based connections have two fundamental benefits:
1. **Better server performance**
- _A: Load balancers_<br>
Load balancing a long polling connection poses a serious architectural nightmare
since requests can come from any number of open sockets by the user agent, but
they all need to be routed to the process and computer that owns the `Engine`
connection. This negatively impacts RAM and CPU usage.
- _B: Network traffic_<br>
WebSocket is designed around the premise that each message frame has to be
surrounded by the least amount of data. In HTTP 1.1 transports, each message
frame is surrounded by HTTP headers and chunked encoding frames. If you try to
send the message _"Hello world"_ with xhr-polling, the message ultimately
becomes larger than if you were to send it with WebSocket.
- _C: Lightweight parser_<br>
As an effect of **B**, the server has to do a lot more work to parse the network
data and figure out the message when traditional HTTP requests are used
(as in long polling). This means that another advantage of WebSocket is
less server CPU usage.
2. **Better user experience**
Due to the reasons stated in point **1**, the most important effect of being able
to establish a WebSocket connection is raw data transfer speed, which translates
in _some_ cases in better user experience.
Applications with heavy realtime interaction (such as games) will benefit greatly,
whereas applications like realtime chat (Gmail/Facebook), newsfeeds (Facebook) or
timelines (Twitter) will have negligible user experience improvements.
Having said this, attempting to establish a WebSocket connection directly so far has
proven problematic:
1. **Proxies**<br>
Many corporate proxies block WebSocket traffic.
2. **Personal firewall and antivirus software**<br>
As a result of our research, we've found that at least 3 personal security
applications block WebSocket traffic.
3. **Cloud application platforms**<br>
Platforms like Heroku or No.de have had trouble keeping up with the fast-paced
nature of the evolution of the WebSocket protocol. Applications therefore end up
inevitably using long polling, but the seamless installation experience of
Socket.IO we strive for (_"require() it and it just works"_) disappears.
Some of these problems have solutions. In the case of proxies and personal programs,
however, the solutions many times involve upgrading software. Experience has shown
that relying on client software upgrades to deliver a business solution is
fruitless: the very existence of this project has to do with a fragmented panorama
of user agent distribution, with clients connecting with latest versions of the most
modern user agents (Chrome, Firefox and Safari), but others with versions as low as
IE 5.5.
From the user perspective, an unsuccessful WebSocket connection can translate in
up to at least 10 seconds of waiting for the realtime application to begin
exchanging data. This **perceptively** hurts user experience.
To summarize, **Engine** focuses on reliability and user experience first, marginal
potential UX improvements and increased server performance second. `Engine` is the
result of all the lessons learned with WebSocket in the wild.
## Architecture
The main premise of `Engine`, and the core of its existence, is the ability to
swap transports on the fly. A connection starts as xhr-polling, but it can
switch to WebSocket.
The central problem this poses is: how do we switch transports without losing
messages?
`Engine` only switches from polling to another transport in between polling
cycles. Since the server closes the connection after a certain timeout when
there's no activity, and the polling transport implementation buffers messages
in between connections, this ensures no message loss and optimal performance.
Another benefit of this design is that we workaround almost all the limitations
of **Flash Socket**, such as slow connection times, increased file size (we can
safely lazy load it without hurting user experience), etc.
## FAQ
### Can I use engine without Socket.IO ?
Absolutely. Although the recommended framework for building realtime applications
is Socket.IO, since it provides fundamental features for real-world applications
such as multiplexing, reconnection support, etc.
`Engine` is to Socket.IO what Connect is to Express. An essential piece for building
realtime frameworks, but something you _probably_ won't be using for building
actual applications.
### Does the server serve the client?
No. The main reason is that `Engine` is meant to be bundled with frameworks.
Socket.IO includes `Engine`, therefore serving two clients is not necessary. If
you use Socket.IO, including
```html
<script src="/socket.io/socket.io.js">
```
has you covered.
### Can I implement `Engine` in other languages?
Absolutely. The [engine.io-protocol](https://github.com/LearnBoost/engine.io-protocol)
repository contains the most up to date description of the specification
at all times, and the parser implementation in JavaScript.
## License
(The MIT License)
Copyright (c) 2014 Guillermo Rauch &lt;guillermo@learnboost.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.

View File

@@ -0,0 +1,4 @@
module.exports = process.env.EIO_COV
? require('./lib-cov/engine.io')
: require('./lib/engine.io');

View File

@@ -0,0 +1,126 @@
/**
* Module dependencies.
*/
var http = require('http');
/**
* Invoking the library as a function delegates to attach if the first argument
* is an `http.Server`.
*
* If there are no arguments or the first argument is an options object, then
* a new Server instance is returned.
*
* @param {http.Server} server (if specified, will be attached to by the new Server instance)
* @param {Object} options
* @return {Server} engine server
* @api public
*/
exports = module.exports = function() {
// backwards compatible use as `.attach`
// if first argument is an http server
if (arguments.length && arguments[0] instanceof http.Server) {
return attach.apply(this, arguments);
}
// if first argument is not an http server, then just make a regular eio server
return exports.Server.apply(null, arguments);
};
/**
* Protocol revision number.
*
* @api public
*/
exports.protocol = 1;
/**
* Expose Server constructor.
*
* @api public
*/
exports.Server = require('./server');
/**
* Expose Server constructor.
*
* @api public
*/
exports.Socket = require('./socket');
/**
* Expose Transport constructor.
*
* @api public
*/
exports.Transport = require('./transport');
/**
* Expose mutable list of available transports.
*
* @api public
*/
exports.transports = require('./transports');
/**
* Exports parser.
*
* @api public
*/
exports.parser = require('engine.io-parser');
/**
* Creates an http.Server exclusively used for WS upgrades.
*
* @param {Number} port
* @param {Function} callback
* @param {Object} options
* @return {Server} websocket.io server
* @api public
*/
exports.listen = listen;
function listen(port, options, fn) {
if ('function' == typeof options) {
fn = options;
options = {};
}
var server = http.createServer(function (req, res) {
res.writeHead(501);
res.end('Not Implemented');
});
server.listen(port, fn);
// create engine server
var engine = exports.attach(server, options);
engine.httpServer = server;
return engine;
};
/**
* Captures upgrade requests for a http.Server.
*
* @param {http.Server} server
* @param {Object} options
* @return {Server} engine server
* @api public
*/
exports.attach = attach;
function attach(server, options) {
var engine = new exports.Server(options);
engine.attach(server, options);
return engine;
};

View File

@@ -0,0 +1,391 @@
/**
* Module dependencies.
*/
var qs = require('querystring')
, parse = require('url').parse
, readFileSync = require('fs').readFileSync
, crypto = require('crypto')
, base64id = require('base64id')
, transports = require('./transports')
, EventEmitter = require('events').EventEmitter
, Socket = require('./socket')
, WebSocketServer = require('ws').Server
, debug = require('debug')('engine');
/**
* Module exports.
*/
module.exports = Server;
/**
* Server constructor.
*
* @param {Object} options
* @api public
*/
function Server(opts){
if (!(this instanceof Server)) {
return new Server(opts);
}
this.clients = {};
this.clientsCount = 0;
opts = opts || {};
this.pingTimeout = opts.pingTimeout || 60000;
this.pingInterval = opts.pingInterval || 25000;
this.upgradeTimeout = opts.upgradeTimeout || 10000;
this.maxHttpBufferSize = opts.maxHttpBufferSize || 10E7;
this.transports = opts.transports || Object.keys(transports);
this.allowUpgrades = false !== opts.allowUpgrades;
this.allowRequest = opts.allowRequest;
this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false;
// initialize websocket server
if (~this.transports.indexOf('websocket')) {
this.ws = new WebSocketServer({ noServer: true, clientTracking: false });
}
}
/**
* Protocol errors mappings.
*/
Server.errors = {
UNKNOWN_TRANSPORT: 0,
UNKNOWN_SID: 1,
BAD_HANDSHAKE_METHOD: 2,
BAD_REQUEST: 3
};
Server.errorMessages = {
0: 'Transport unknown',
1: 'Session ID unknown',
2: 'Bad handshake method',
3: 'Bad request'
};
/**
* Inherits from EventEmitter.
*/
Server.prototype.__proto__ = EventEmitter.prototype;
/**
* Hash of open clients.
*
* @api public
*/
Server.prototype.clients;
/**
* Returns a list of available transports for upgrade given a certain transport.
*
* @return {Array}
* @api public
*/
Server.prototype.upgrades = function(transport){
if (!this.allowUpgrades) return [];
return transports[transport].upgradesTo || [];
};
/**
* Verifies a request.
*
* @param {http.ServerRequest}
* @return {Boolean} whether the request is valid
* @api private
*/
Server.prototype.verify = function(req, upgrade, fn){
// transport check
var transport = req._query.transport;
if (!~this.transports.indexOf(transport)) {
debug('unknown transport "%s"', transport);
return fn(Server.errors.UNKNOWN_TRANSPORT, false);
}
// sid check
var sid = req._query.sid;
if (sid) {
if (!this.clients.hasOwnProperty(sid))
return fn(Server.errors.UNKNOWN_SID, false);
if (!upgrade && this.clients[sid].transport.name !== transport) {
debug('bad request: unexpected transport without upgrade');
return fn(Server.errors.BAD_REQUEST, false);
}
} else {
// handshake is GET only
if ('GET' != req.method) return fn(Server.errors.BAD_HANDSHAKE_METHOD, false);
if (!this.allowRequest) return fn(null, true);
return this.allowRequest(req, fn);
}
fn(null, true);
};
/**
* Prepares a request by processing the query string.
*
* @api private
*/
Server.prototype.prepare = function(req){
// try to leverage pre-existing `req._query` (e.g: from connect)
if (!req._query) {
req._query = ~req.url.indexOf('?') ? qs.parse(parse(req.url).query) : {};
}
};
/**
* Closes all clients.
*
* @api public
*/
Server.prototype.close = function(){
debug('closing all open clients');
for (var i in this.clients) {
this.clients[i].close();
}
return this;
};
/**
* Handles an Engine.IO HTTP request.
*
* @param {http.ServerRequest} request
* @param {http.ServerResponse|http.OutgoingMessage} response
* @api public
*/
Server.prototype.handleRequest = function(req, res){
debug('handling "%s" http request "%s"', req.method, req.url);
this.prepare(req);
req.res = res;
var self = this;
this.verify(req, false, function(err, success) {
if (!success) {
sendErrorMessage(req, res, err);
return;
}
if (req._query.sid) {
debug('setting new request for existing client');
self.clients[req._query.sid].transport.onRequest(req);
} else {
self.handshake(req._query.transport, req);
}
});
};
/**
* Sends an Engine.IO Error Message
*
* @param {http.ServerResponse} response
* @param {code} error code
* @api private
*/
function sendErrorMessage(req, res, code) {
var headers = { 'Content-Type': 'application/json' };
if (req.headers.origin) {
headers['Access-Control-Allow-Credentials'] = 'true';
headers['Access-Control-Allow-Origin'] = req.headers.origin;
} else {
headers['Access-Control-Allow-Origin'] = '*';
}
res.writeHead(400, headers);
res.end(JSON.stringify({
code: code,
message: Server.errorMessages[code]
}));
}
/**
* Handshakes a new client.
*
* @param {String} transport name
* @param {Object} request object
* @api private
*/
Server.prototype.handshake = function(transport, req){
var id = base64id.generateId();
debug('handshaking client "%s"', id);
var transportName = transport;
try {
var transport = new transports[transport](req);
if ('polling' == transportName) {
transport.maxHttpBufferSize = this.maxHttpBufferSize;
}
if (req._query && req._query.b64) {
transport.supportsBinary = false;
} else {
transport.supportsBinary = true;
}
}
catch (e) {
sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST);
return;
}
var socket = new Socket(id, this, transport, req);
var self = this;
if (false !== this.cookie) {
transport.on('headers', function(headers){
headers['Set-Cookie'] = self.cookie + '=' + id;
});
}
transport.onRequest(req);
this.clients[id] = socket;
this.clientsCount++;
socket.once('close', function(){
delete self.clients[id];
self.clientsCount--;
});
this.emit('connection', socket);
};
/**
* Handles an Engine.IO HTTP Upgrade.
*
* @api public
*/
Server.prototype.handleUpgrade = function(req, socket, upgradeHead){
this.prepare(req);
var self = this;
this.verify(req, true, function(err, success) {
if (!success) {
socket.end();
return;
}
var head = new Buffer(upgradeHead.length);
upgradeHead.copy(head);
upgradeHead = null;
// delegate to ws
self.ws.handleUpgrade(req, socket, head, function(conn){
self.onWebSocket(req, conn);
});
});
};
/**
* Called upon a ws.io connection.
*
* @param {ws.Socket} websocket
* @api private
*/
Server.prototype.onWebSocket = function(req, socket){
if (!transports[req._query.transport].prototype.handlesUpgrades) {
debug('transport doesnt handle upgraded requests');
socket.close();
return;
}
// get client id
var id = req._query.sid;
// keep a reference to the ws.Socket
req.websocket = socket;
if (id) {
if (!this.clients[id]) {
debug('upgrade attempt for closed client');
socket.close();
} else if (this.clients[id].upgraded) {
debug('transport had already been upgraded');
socket.close();
} else {
debug('upgrading existing transport');
var transport = new transports[req._query.transport](req);
if (req._query && req._query.b64) {
transport.supportsBinary = false;
} else {
transport.supportsBinary = true;
}
this.clients[id].maybeUpgrade(transport);
}
} else {
this.handshake(req._query.transport, req);
}
};
/**
* Captures upgrade requests for a http.Server.
*
* @param {http.Server} server
* @param {Object} options
* @api public
*/
Server.prototype.attach = function(server, options){
var self = this;
var options = options || {};
var path = (options.path || '/engine.io').replace(/\/$/, '');
var destroyUpgrade = (options.destroyUpgrade !== undefined) ? options.destroyUpgrade : true;
var destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000;
// normalize path
path += '/';
function check (req) {
return path == req.url.substr(0, path.length);
}
// cache and clean up listeners
var listeners = server.listeners('request').slice(0);
server.removeAllListeners('request');
server.on('close', self.close.bind(self));
// add request handler
server.on('request', function(req, res){
if (check(req)) {
debug('intercepting request for path "%s"', path);
self.handleRequest(req, res);
} else {
for (var i = 0, l = listeners.length; i < l; i++) {
listeners[i].call(server, req, res);
}
}
});
if(~self.transports.indexOf('websocket')) {
server.on('upgrade', function (req, socket, head) {
if (check(req)) {
self.handleUpgrade(req, socket, head);
} else if (false !== options.destroyUpgrade) {
// default node behavior is to disconnect when no handlers
// but by adding a handler, we prevent that
// and if no eio thing handles the upgrade
// then the socket needs to die!
setTimeout(function() {
if (socket.writable && socket.bytesWritten <= 0) {
return socket.end();
}
}, options.destroyUpgradeTimeout);
}
});
}
};

View File

@@ -0,0 +1,393 @@
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('engine:socket');
/**
* Module exports.
*/
module.exports = Socket;
/**
* Client class (abstract).
*
* @api private
*/
function Socket (id, server, transport, req) {
this.id = id;
this.server = server;
this.upgraded = false;
this.readyState = 'opening';
this.writeBuffer = [];
this.packetsFn = [];
this.sentCallbackFn = [];
this.request = req;
// Cache IP since it might not be in the req later
this.remoteAddress = req.connection.remoteAddress;
this.checkIntervalTimer = null;
this.upgradeTimeoutTimer = null;
this.pingTimeoutTimer = null;
this.setTransport(transport);
this.onOpen();
}
/**
* Inherits from EventEmitter.
*/
Socket.prototype.__proto__ = EventEmitter.prototype;
/**
* Called upon transport considered open.
*
* @api private
*/
Socket.prototype.onOpen = function () {
this.readyState = 'open';
// sends an `open` packet
this.transport.sid = this.id;
this.sendPacket('open', JSON.stringify({
sid: this.id
, upgrades: this.getAvailableUpgrades()
, pingInterval: this.server.pingInterval
, pingTimeout: this.server.pingTimeout
}));
this.emit('open');
this.setPingTimeout();
};
/**
* Called upon transport packet.
*
* @param {Object} packet
* @api private
*/
Socket.prototype.onPacket = function (packet) {
if ('open' == this.readyState) {
// export packet event
debug('packet');
this.emit('packet', packet);
// Reset ping timeout on any packet, incoming data is a good sign of
// other side's liveness
this.setPingTimeout();
switch (packet.type) {
case 'ping':
debug('got ping');
this.sendPacket('pong');
this.emit('heartbeat');
break;
case 'error':
this.onClose('parse error');
break;
case 'message':
this.emit('data', packet.data);
this.emit('message', packet.data);
break;
}
} else {
debug('packet received with closed socket');
}
};
/**
* Called upon transport error.
*
* @param {Error} error object
* @api private
*/
Socket.prototype.onError = function (err) {
debug('transport error');
this.onClose('transport error', err);
};
/**
* Sets and resets ping timeout timer based on client pings.
*
* @api private
*/
Socket.prototype.setPingTimeout = function () {
var self = this;
clearTimeout(self.pingTimeoutTimer);
self.pingTimeoutTimer = setTimeout(function () {
self.onClose('ping timeout');
}, self.server.pingInterval + self.server.pingTimeout);
};
/**
* Attaches handlers for the given transport.
*
* @param {Transport} transport
* @api private
*/
Socket.prototype.setTransport = function (transport) {
this.transport = transport;
this.transport.once('error', this.onError.bind(this));
this.transport.on('packet', this.onPacket.bind(this));
this.transport.on('drain', this.flush.bind(this));
this.transport.once('close', this.onClose.bind(this, 'transport close'));
//this function will manage packet events (also message callbacks)
this.setupSendCallback();
};
/**
* Upgrades socket to the given transport
*
* @param {Transport} transport
* @api private
*/
Socket.prototype.maybeUpgrade = function (transport) {
debug('might upgrade socket transport from "%s" to "%s"'
, this.transport.name, transport.name);
var self = this;
// set transport upgrade timer
self.upgradeTimeoutTimer = setTimeout(function () {
debug('client did not complete upgrade - closing transport');
clearInterval(self.checkIntervalTimer);
self.checkIntervalTimer = null;
if ('open' == transport.readyState) {
transport.close();
}
}, this.server.upgradeTimeout);
function onPacket(packet){
if ('ping' == packet.type && 'probe' == packet.data) {
transport.send([{ type: 'pong', data: 'probe' }]);
clearInterval(self.checkIntervalTimer);
self.checkIntervalTimer = setInterval(check, 100);
} else if ('upgrade' == packet.type && self.readyState != 'closed') {
debug('got upgrade packet - upgrading');
self.upgraded = true;
self.clearTransport();
self.setTransport(transport);
self.emit('upgrade', transport);
self.setPingTimeout();
self.flush();
clearInterval(self.checkIntervalTimer);
self.checkIntervalTimer = null;
clearTimeout(self.upgradeTimeoutTimer);
transport.removeListener('packet', onPacket);
if (self.readyState == 'closing') {
transport.close(function () {
self.onClose('forced close');
});
}
} else {
transport.close();
}
}
// we force a polling cycle to ensure a fast upgrade
function check(){
if ('polling' == self.transport.name && self.transport.writable) {
debug('writing a noop packet to polling for fast upgrade');
self.transport.send([{ type: 'noop' }]);
}
}
transport.on('packet', onPacket);
};
/**
* Clears listeners and timers associated with current transport.
*
* @api private
*/
Socket.prototype.clearTransport = function () {
// silence further transport errors and prevent uncaught exceptions
this.transport.on('error', function(){
debug('error triggered by discarded transport');
});
clearTimeout(this.pingTimeoutTimer);
};
/**
* Called upon transport considered closed.
* Possible reasons: `ping timeout`, `client error`, `parse error`,
* `transport error`, `server close`, `transport close`
*/
Socket.prototype.onClose = function (reason, description) {
if ('closed' != this.readyState) {
clearTimeout(this.pingTimeoutTimer);
clearInterval(this.checkIntervalTimer);
this.checkIntervalTimer = null;
clearTimeout(this.upgradeTimeoutTimer);
var self = this;
// clean writeBuffer in next tick, so developers can still
// grab the writeBuffer on 'close' event
process.nextTick(function() {
self.writeBuffer = [];
});
this.packetsFn = [];
this.sentCallbackFn = [];
this.clearTransport();
this.readyState = 'closed';
this.emit('close', reason, description);
}
};
/**
* Setup and manage send callback
*
* @api private
*/
Socket.prototype.setupSendCallback = function () {
var self = this;
//the message was sent successfully, execute the callback
this.transport.on('drain', function() {
if (self.sentCallbackFn.length > 0) {
var seqFn = self.sentCallbackFn.splice(0,1)[0];
if ('function' == typeof seqFn) {
debug('executing send callback');
seqFn(self.transport);
} else if (Array.isArray(seqFn)) {
debug('executing batch send callback');
for (var l = seqFn.length, i = 0; i < l; i++) {
if ('function' == typeof seqFn[i]) {
seqFn[i](self.transport);
}
}
}
}
});
};
/**
* Sends a message packet.
*
* @param {String} message
* @param {Function} callback
* @return {Socket} for chaining
* @api public
*/
Socket.prototype.send =
Socket.prototype.write = function(data, callback){
this.sendPacket('message', data, callback);
return this;
};
/**
* Sends a packet.
*
* @param {String} packet type
* @param {String} optional, data
* @api private
*/
Socket.prototype.sendPacket = function (type, data, callback) {
if ('closing' != this.readyState) {
debug('sending packet "%s" (%s)', type, data);
var packet = { type: type };
if (data) packet.data = data;
// exports packetCreate event
this.emit('packetCreate', packet);
this.writeBuffer.push(packet);
//add send callback to object
this.packetsFn.push(callback);
this.flush();
}
};
/**
* Attempts to flush the packets buffer.
*
* @api private
*/
Socket.prototype.flush = function () {
if ('closed' != this.readyState && this.transport.writable
&& this.writeBuffer.length) {
debug('flushing buffer to transport');
this.emit('flush', this.writeBuffer);
this.server.emit('flush', this, this.writeBuffer);
var wbuf = this.writeBuffer;
this.writeBuffer = [];
if (!this.transport.supportsFraming) {
this.sentCallbackFn.push(this.packetsFn);
} else {
this.sentCallbackFn.push.apply(this.sentCallbackFn, this.packetsFn);
}
this.packetsFn = [];
this.transport.send(wbuf);
this.emit('drain');
this.server.emit('drain', this);
}
};
/**
* Get available upgrades for this socket.
*
* @api private
*/
Socket.prototype.getAvailableUpgrades = function () {
var availableUpgrades = [];
var allUpgrades = this.server.upgrades(this.transport.name);
for (var i = 0, l = allUpgrades.length; i < l; ++i) {
var upg = allUpgrades[i];
if (this.server.transports.indexOf(upg) != -1) {
availableUpgrades.push(upg);
}
}
return availableUpgrades;
};
/**
* Closes the socket and underlying transport.
*
* @return {Socket} for chaining
* @api public
*/
Socket.prototype.close = function () {
if ('open' != this.readyState) return;
this.readyState = 'closing';
if (this.writeBuffer.length) {
this.once('drain', this.closeTransport.bind(this));
return;
}
this.closeTransport();
};
/**
* Closes the underlying transport.
*
* @api private
*/
Socket.prototype.closeTransport = function () {
this.transport.close(this.onClose.bind(this, 'forced close'));
};

View File

@@ -0,0 +1,114 @@
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, parser = require('engine.io-parser')
, debug = require('debug')('engine:transport');
/**
* Expose the constructor.
*/
module.exports = Transport;
/**
* Noop function.
*
* @api private
*/
function noop () {};
/**
* Transport constructor.
*
* @param {http.ServerRequest} request
* @api public
*/
function Transport (req) {
this.readyState = 'opening';
};
/**
* Inherits from EventEmitter.
*/
Transport.prototype.__proto__ = EventEmitter.prototype;
/**
* Called with an incoming HTTP request.
*
* @param {http.ServerRequest} request
* @api private
*/
Transport.prototype.onRequest = function (req) {
debug('setting request');
this.req = req;
};
/**
* Closes the transport.
*
* @api private
*/
Transport.prototype.close = function (fn) {
this.readyState = 'closing';
this.doClose(fn || noop);
};
/**
* Called with a transport error.
*
* @param {String} message error
* @param {Object} error description
* @api private
*/
Transport.prototype.onError = function (msg, desc) {
if (this.listeners('error').length) {
var err = new Error(msg);
err.type = 'TransportError';
err.description = desc;
this.emit('error', err);
} else {
debug('ignored transport error %s (%s)', msg, desc);
}
};
/**
* Called with parsed out a packets from the data stream.
*
* @param {Object} packet
* @api private
*/
Transport.prototype.onPacket = function (packet) {
this.emit('packet', packet);
};
/**
* Called with the encoded packet data.
*
* @param {String} data
* @api private
*/
Transport.prototype.onData = function (data) {
this.onPacket(parser.decodePacket(data));
};
/**
* Called upon transport close.
*
* @api private
*/
Transport.prototype.onClose = function () {
this.readyState = 'closed';
this.emit('close');
};

View File

@@ -0,0 +1,36 @@
/**
* Module dependencies.
*/
var XHR = require('./polling-xhr');
var JSONP = require('./polling-jsonp');
/**
* Export transports.
*/
module.exports = exports = {
polling: polling,
websocket: require('./websocket')
};
/**
* Export upgrades map.
*/
exports.polling.upgradesTo = ['websocket'];
/**
* Polling polimorphic constructor.
*
* @api private
*/
function polling (req) {
if ('string' == typeof req._query.j) {
return new JSONP(req);
} else {
return new XHR(req);
}
}

View File

@@ -0,0 +1,108 @@
/**
* Module dependencies.
*/
var Polling = require('./polling');
var qs = require('querystring');
var rDoubleSlashes = /\\\\n/g;
var rSlashes = /(\\)?\\n/g;
/**
* Module exports.
*/
module.exports = JSONP;
/**
* JSON-P polling transport.
*
* @api public
*/
function JSONP (req) {
Polling.call(this, req);
this.head = '___eio[' + (req._query.j || '').replace(/[^0-9]/g, '') + '](';
this.foot = ');';
};
/**
* Inherits from Polling.
*/
JSONP.prototype.__proto__ = Polling.prototype;
/**
* Handles incoming data.
* Due to a bug in \n handling by browsers, we expect a escaped string.
*
* @api private
*/
JSONP.prototype.onData = function (data) {
// we leverage the qs module so that we get built-in DoS protection
// and the fast alternative to decodeURIComponent
data = qs.parse(data).d;
if ('string' == typeof data) {
//client will send already escaped newlines as \\\\n and newlines as \\n
// \\n must be replaced with \n and \\\\n with \\n
data = data.replace(rSlashes, function(match, slashes) {
return slashes ? match : '\n';
});
Polling.prototype.onData.call(this, data.replace(rDoubleSlashes, '\\n'));
}
};
/**
* Performs the write.
*
* @api private
*/
JSONP.prototype.doWrite = function (data) {
// we must output valid javascript, not valid json
// see: http://timelessrepo.com/json-isnt-a-javascript-subset
var js = JSON.stringify(data)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
// prepare response
data = this.head + js + this.foot;
// explicit UTF-8 is required for pages not served under utf
var headers = {
'Content-Type': 'text/javascript; charset=UTF-8',
'Content-Length': Buffer.byteLength(data)
};
// prevent XSS warnings on IE
// https://github.com/LearnBoost/socket.io/pull/1333
var ua = this.req.headers['user-agent'];
if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) {
headers['X-XSS-Protection'] = '0';
}
this.res.writeHead(200, this.headers(this.req, headers));
this.res.end(data);
};
/**
* Returns headers for a response.
*
* @param {http.ServerRequest} request
* @param {Object} extra headers
* @api private
*/
JSONP.prototype.headers = function (req, headers) {
headers = headers || {};
// disable XSS protection for IE
if (/MSIE 8\.0/.test(req.headers['user-agent'])) {
headers['X-XSS-Protection'] = '0';
}
this.emit('headers', headers);
return headers;
};

View File

@@ -0,0 +1,101 @@
/**
* Module dependencies.
*/
var Polling = require('./polling');
var Transport = require('../transport');
var debug = require('debug')('engine:polling-xhr');
/**
* Module exports.
*/
module.exports = XHR;
/**
* Ajax polling transport.
*
* @api public
*/
function XHR(req){
Polling.call(this, req);
}
/**
* Inherits from Polling.
*/
XHR.prototype.__proto__ = Polling.prototype;
/**
* Overrides `onRequest` to handle `OPTIONS`..
*
* @param {http.ServerRequest}
* @api private
*/
XHR.prototype.onRequest = function (req) {
if ('OPTIONS' == req.method) {
var res = req.res;
var headers = this.headers(req);
headers['Access-Control-Allow-Headers'] = 'Content-Type';
res.writeHead(200, headers);
res.end();
} else {
Polling.prototype.onRequest.call(this, req);
}
};
/**
* Frames data prior to write.
*
* @api private
*/
XHR.prototype.doWrite = function(data){
// explicit UTF-8 is required for pages not served under utf
var isString = typeof data == 'string';
var contentType = isString
? 'text/plain; charset=UTF-8'
: 'application/octet-stream';
var contentLength = '' + (isString ? Buffer.byteLength(data) : data.length);
var headers = {
'Content-Type': contentType,
'Content-Length': contentLength
};
// prevent XSS warnings on IE
// https://github.com/LearnBoost/socket.io/pull/1333
var ua = this.req.headers['user-agent'];
if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) {
headers['X-XSS-Protection'] = '0';
}
this.res.writeHead(200, this.headers(this.req, headers));
this.res.end(data);
};
/**
* Returns headers for a response.
*
* @param {http.ServerRequest} request
* @param {Object} extra headers
* @api private
*/
XHR.prototype.headers = function(req, headers){
headers = headers || {};
if (req.headers.origin) {
headers['Access-Control-Allow-Credentials'] = 'true';
headers['Access-Control-Allow-Origin'] = req.headers.origin;
} else {
headers['Access-Control-Allow-Origin'] = '*';
}
this.emit('headers', headers);
return headers;
};

View File

@@ -0,0 +1,279 @@
/**
* Module requirements.
*/
var Transport = require('../transport')
, parser = require('engine.io-parser')
, debug = require('debug')('engine:polling');
/**
* Exports the constructor.
*/
module.exports = Polling;
/**
* HTTP polling constructor.
*
* @api public.
*/
function Polling (req) {
Transport.call(this, req);
}
/**
* Inherits from Transport.
*
* @api public.
*/
Polling.prototype.__proto__ = Transport.prototype;
/**
* Transport name
*
* @api public
*/
Polling.prototype.name = 'polling';
/**
* Overrides onRequest.
*
* @param {http.ServerRequest}
* @api private
*/
Polling.prototype.onRequest = function (req) {
var res = req.res;
if ('GET' == req.method) {
this.onPollRequest(req, res);
} else if ('POST' == req.method) {
this.onDataRequest(req, res);
} else {
res.writeHead(500);
res.end();
}
};
/**
* The client sends a request awaiting for us to send data.
*
* @api private
*/
Polling.prototype.onPollRequest = function (req, res) {
if (this.req) {
debug('request overlap');
// assert: this.res, '.req and .res should be (un)set together'
this.onError('overlap from client');
res.writeHead(500);
return;
}
debug('setting request');
this.req = req;
this.res = res;
var self = this;
function onClose () {
self.onError('poll connection closed prematurely');
}
function cleanup () {
req.removeListener('close', onClose);
self.req = self.res = null;
}
req.cleanup = cleanup;
req.on('close', onClose);
this.writable = true;
this.emit('drain');
// if we're still writable but had a pending close, trigger an empty send
if (this.writable && this.shouldClose) {
debug('triggering empty send to append close packet');
this.send([{ type: 'noop' }]);
}
};
/**
* The client sends a request with data.
*
* @api private
*/
Polling.prototype.onDataRequest = function (req, res) {
if (this.dataReq) {
// assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
this.onError('data request overlap from client');
res.writeHead(500);
return;
}
var isBinary = 'application/octet-stream' == req.headers['content-type'];
this.dataReq = req;
this.dataRes = res;
var chunks = isBinary ? new Buffer(0) : '';
var self = this;
function cleanup () {
chunks = isBinary ? new Buffer(0) : '';
req.removeListener('data', onData);
req.removeListener('end', onEnd);
req.removeListener('close', onClose);
self.dataReq = self.dataRes = null;
}
function onClose () {
cleanup();
self.onError('data request connection closed prematurely');
}
function onData (data) {
var contentLength;
if (typeof data == 'string') {
chunks += data;
contentLength = Buffer.byteLength(chunks);
} else {
chunks = Buffer.concat([chunks, data]);
contentLength = chunks.length;
}
if (contentLength > self.maxHttpBufferSize) {
chunks = '';
req.connection.destroy();
}
}
function onEnd () {
self.onData(chunks);
var headers = {
// text/html is required instead of text/plain to avoid an
// unwanted download dialog on certain user-agents (GH-43)
'Content-Type': 'text/html',
'Content-Length': 2
};
// prevent XSS warnings on IE
// https://github.com/LearnBoost/socket.io/pull/1333
var ua = req.headers['user-agent'];
if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) {
headers['X-XSS-Protection'] = '0';
}
res.writeHead(200, self.headers(req, headers));
res.end('ok');
cleanup();
}
req.on('close', onClose);
req.on('data', onData);
req.on('end', onEnd);
if (!isBinary) req.setEncoding('utf8');
};
/**
* Processes the incoming data payload.
*
* @param {String} encoded payload
* @api private
*/
Polling.prototype.onData = function (data) {
debug('received "%s"', data);
var self = this;
var callback = function(packet) {
if ('close' == packet.type) {
debug('got xhr close packet');
self.onClose();
return false;
}
self.onPacket(packet);
};
parser.decodePayload(data, callback);
};
/**
* Overrides onClose.
*
* @api private
*/
Polling.prototype.onClose = function () {
if (this.writable) {
// close pending poll request
this.send([{ type: 'noop' }]);
}
Transport.prototype.onClose.call(this);
};
/**
* Writes a packet payload.
*
* @param {Object} packet
* @api private
*/
Polling.prototype.send = function (packets) {
if (this.shouldClose) {
debug('appending close packet to payload');
packets.push({ type: 'close' });
this.shouldClose();
this.shouldClose = null;
}
var self = this;
parser.encodePayload(packets, this.supportsBinary, function(data) {
self.write(data);
});
};
/**
* Writes data as response to poll request.
*
* @param {String} data
* @api private
*/
Polling.prototype.write = function (data) {
debug('writing "%s"', data);
this.doWrite(data);
this.req.cleanup();
this.writable = false;
};
/**
* Closes the transport.
*
* @api private
*/
Polling.prototype.doClose = function (fn) {
debug('closing');
if (this.dataReq) {
debug('aborting ongoing data request');
this.dataReq.destroy();
}
if (this.writable) {
debug('transport writable - closing right away');
this.send([{ type: 'close' }]);
fn();
} else {
debug('transport not writable - buffering orderly close');
this.shouldClose = fn;
}
};

View File

@@ -0,0 +1,110 @@
/**
* Module dependencies.
*/
var Transport = require('../transport')
, parser = require('engine.io-parser')
, debug = require('debug')('engine:ws')
/**
* Export the constructor.
*/
module.exports = WebSocket;
/**
* WebSocket transport
*
* @param {http.ServerRequest}
* @api public
*/
function WebSocket (req) {
Transport.call(this, req);
var self = this;
this.socket = req.websocket;
this.socket.on('message', this.onData.bind(this));
this.socket.once('close', this.onClose.bind(this));
this.socket.on('error', this.onError.bind(this));
this.socket.on('headers', function (headers) {
self.emit('headers', headers);
});
this.writable = true;
};
/**
* Inherits from Transport.
*/
WebSocket.prototype.__proto__ = Transport.prototype;
/**
* Transport name
*
* @api public
*/
WebSocket.prototype.name = 'websocket';
/**
* Advertise upgrade support.
*
* @api public
*/
WebSocket.prototype.handlesUpgrades = true;
/**
* Advertise framing support.
*
* @api public
*/
WebSocket.prototype.supportsFraming = true;
/**
* Processes the incoming data.
*
* @param {String} encoded packet
* @api private
*/
WebSocket.prototype.onData = function (data) {
debug('received "%s"', data);
Transport.prototype.onData.call(this, data);
};
/**
* Writes a packet payload.
*
* @param {Array} packets
* @api private
*/
WebSocket.prototype.send = function (packets) {
var self = this;
for (var i = 0, l = packets.length; i < l; i++) {
parser.encodePacket(packets[i], this.supportsBinary, function(data) {
debug('writing "%s"', data);
self.writable = false;
self.socket.send(data, function (err){
if (err) return self.onError('write error', err.stack);
self.writable = true;
self.emit('drain');
});
});
}
};
/**
* Closes the transport.
*
* @api private
*/
WebSocket.prototype.doClose = function (fn) {
debug('closing');
this.socket.close();
fn && fn();
};

View File

@@ -0,0 +1,3 @@
support
test
examples

View File

@@ -0,0 +1,18 @@
base64id
========
Node.js module that generates a base64 id.
Uses crypto.randomBytes when available, falls back to unsafe methods for node.js <= 0.4.
To increase performance, random bytes are buffered to minimize the number of synchronous calls to crypto.randomBytes.
## Installation
$ npm install mongoose
## Usage
var base64id = require('base64id');
var id = base64id.generateId();

View File

@@ -0,0 +1,103 @@
/*!
* base64id v0.1.0
*/
/**
* Module dependencies
*/
var crypto = require('crypto');
/**
* Constructor
*/
var Base64Id = function() { };
/**
* Get random bytes
*
* Uses a buffer if available, falls back to crypto.randomBytes
*/
Base64Id.prototype.getRandomBytes = function(bytes) {
var BUFFER_SIZE = 4096
var self = this;
bytes = bytes || 12;
if (bytes > BUFFER_SIZE) {
return crypto.randomBytes(bytes);
}
var bytesInBuffer = parseInt(BUFFER_SIZE/bytes);
var threshold = parseInt(bytesInBuffer*0.85);
if (!threshold) {
return crypto.randomBytes(bytes);
}
if (this.bytesBufferIndex == null) {
this.bytesBufferIndex = -1;
}
if (this.bytesBufferIndex == bytesInBuffer) {
this.bytesBuffer = null;
this.bytesBufferIndex = -1;
}
// No buffered bytes available or index above threshold
if (this.bytesBufferIndex == -1 || this.bytesBufferIndex > threshold) {
if (!this.isGeneratingBytes) {
this.isGeneratingBytes = true;
crypto.randomBytes(BUFFER_SIZE, function(err, bytes) {
self.bytesBuffer = bytes;
self.bytesBufferIndex = 0;
self.isGeneratingBytes = false;
});
}
// Fall back to sync call when no buffered bytes are available
if (this.bytesBufferIndex == -1) {
return crypto.randomBytes(bytes);
}
}
var result = this.bytesBuffer.slice(bytes*this.bytesBufferIndex, bytes*(this.bytesBufferIndex+1));
this.bytesBufferIndex++;
return result;
}
/**
* Generates a base64 id
*
* (Original version from socket.io <http://socket.io>)
*/
Base64Id.prototype.generateId = function () {
var rand = new Buffer(15); // multiple of 3 for base64
if (!rand.writeInt32BE) {
return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString()
+ Math.abs(Math.random() * Math.random() * Date.now() | 0).toString();
}
this.sequenceNumber = (this.sequenceNumber + 1) | 0;
rand.writeInt32BE(this.sequenceNumber, 11);
if (crypto.randomBytes) {
this.getRandomBytes(12).copy(rand);
} else {
// not secure for node 0.4
[0, 4, 8].forEach(function(i) {
rand.writeInt32BE(Math.random() * Math.pow(2, 32) | 0, i);
});
}
return rand.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
};
/**
* Export
*/
exports = module.exports = new Base64Id();

View File

@@ -0,0 +1,43 @@
{
"name": "base64id",
"version": "0.1.0",
"description": "Generates a base64 id",
"author": {
"name": "Kristian Faeldt",
"email": "faeldt_kristian@cyberagent.co.jp"
},
"repository": {
"type": "git",
"url": "git://github.com/faeldt/base64id.git"
},
"main": "./lib/base64id.js",
"engines": {
"node": ">= 0.4.0"
},
"_npmUser": {
"name": "faeldt_kristian",
"email": "faeldt_kristian@cyberagent.co.jp"
},
"_id": "base64id@0.1.0",
"dependencies": {},
"devDependencies": {},
"optionalDependencies": {},
"_engineSupported": true,
"_npmVersion": "1.1.16",
"_nodeVersion": "v0.6.15",
"_defaultsLoaded": true,
"dist": {
"shasum": "02ce0fdeee0cef4f40080e1e73e834f0b1bfce3f",
"tarball": "http://registry.npmjs.org/base64id/-/base64id-0.1.0.tgz"
},
"maintainers": [
{
"name": "faeldt_kristian",
"email": "faeldt_kristian@cyberagent.co.jp"
}
],
"directories": {},
"_shasum": "02ce0fdeee0cef4f40080e1e73e834f0b1bfce3f",
"_from": "base64id@0.1.0",
"_resolved": "https://registry.npmjs.org/base64id/-/base64id-0.1.0.tgz"
}

View File

@@ -0,0 +1,3 @@
{
"laxbreak": true
}

View File

@@ -0,0 +1,6 @@
support
test
examples
example
*.sock
dist

View File

@@ -0,0 +1,132 @@
1.0.3 / 2014-07-09
==================
* Add support for multiple wildcards in namespaces (#122, @seegno)
* browser: fix lint
1.0.2 / 2014-06-10
==================
* browser: update color palette (#113, @gscottolson)
* common: make console logging function configurable (#108, @timoxley)
* node: fix %o colors on old node <= 0.8.x
* Makefile: find node path using shell/which (#109, @timoxley)
1.0.1 / 2014-06-06
==================
* browser: use `removeItem()` to clear localStorage
* browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
* package: add "contributors" section
* node: fix comment typo
* README: list authors
1.0.0 / 2014-06-04
==================
* make ms diff be global, not be scope
* debug: ignore empty strings in enable()
* node: make DEBUG_COLORS able to disable coloring
* *: export the `colors` array
* npmignore: don't publish the `dist` dir
* Makefile: refactor to use browserify
* package: add "browserify" as a dev dependency
* Readme: add Web Inspector Colors section
* node: reset terminal color for the debug content
* node: map "%o" to `util.inspect()`
* browser: map "%j" to `JSON.stringify()`
* debug: add custom "formatters"
* debug: use "ms" module for humanizing the diff
* Readme: add "bash" syntax highlighting
* browser: add Firebug color support
* browser: add colors for WebKit browsers
* node: apply log to `console`
* rewrite: abstract common logic for Node & browsers
* add .jshintrc file
0.8.1 / 2014-04-14
==================
* package: re-add the "component" section
0.8.0 / 2014-03-30
==================
* add `enable()` method for nodejs. Closes #27
* change from stderr to stdout
* remove unnecessary index.js file
0.7.4 / 2013-11-13
==================
* remove "browserify" key from package.json (fixes something in browserify)
0.7.3 / 2013-10-30
==================
* fix: catch localStorage security error when cookies are blocked (Chrome)
* add debug(err) support. Closes #46
* add .browser prop to package.json. Closes #42
0.7.2 / 2013-02-06
==================
* fix package.json
* fix: Mobile Safari (private mode) is broken with debug
* fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
0.7.1 / 2013-02-05
==================
* add repository URL to package.json
* add DEBUG_COLORED to force colored output
* add browserify support
* fix component. Closes #24
0.7.0 / 2012-05-04
==================
* Added .component to package.json
* Added debug.component.js build
0.6.0 / 2012-03-16
==================
* Added support for "-" prefix in DEBUG [Vinay Pulim]
* Added `.enabled` flag to the node version [TooTallNate]
0.5.0 / 2012-02-02
==================
* Added: humanize diffs. Closes #8
* Added `debug.disable()` to the CS variant
* Removed padding. Closes #10
* Fixed: persist client-side variant again. Closes #9
0.4.0 / 2012-02-01
==================
* Added browser variant support for older browsers [TooTallNate]
* Added `debug.enable('project:*')` to browser variant [TooTallNate]
* Added padding to diff (moved it to the right)
0.3.0 / 2012-01-26
==================
* Added millisecond diff when isatty, otherwise UTC string
0.2.0 / 2012-01-22
==================
* Added wildcard support
0.1.0 / 2011-12-02
==================
* Added: remove colors unless stderr isatty [TooTallNate]
0.0.1 / 2010-01-03
==================
* Initial release

View File

@@ -0,0 +1,33 @@
# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
# BIN directory
BIN := $(THIS_DIR)/node_modules/.bin
# applications
NODE ?= $(shell which node)
NPM ?= $(NODE) $(shell which npm)
BROWSERIFY ?= $(NODE) $(BIN)/browserify
all: dist/debug.js
install: node_modules
clean:
@rm -rf node_modules dist
dist:
@mkdir -p $@
dist/debug.js: node_modules browser.js debug.js dist
@$(BROWSERIFY) \
--standalone debug \
. > $@
node_modules: package.json
@NODE_ENV= $(NPM) install
@touch node_modules
.PHONY: all install clean

View File

@@ -0,0 +1,153 @@
# debug
tiny node.js debugging utility modelled after node core's debugging technique.
## Installation
```bash
$ npm install debug
```
## Usage
With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility.
Example _app.js_:
```js
var debug = require('debug')('http')
, http = require('http')
, name = 'My App';
// fake app
debug('booting %s', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
res.end('hello\n');
}).listen(3000, function(){
debug('listening');
});
// fake worker of some kind
require('./worker');
```
Example _worker.js_:
```js
var debug = require('debug')('worker');
setInterval(function(){
debug('doing some work');
}, 1000);
```
The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
## Millisecond diff
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)
## Conventions
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".
## Wildcards
The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:".
## Browser support
Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`.
```js
a = debug('worker:a');
b = debug('worker:b');
setInterval(function(){
a('doing some work');
}, 1000);
setInterval(function(){
b('doing some work');
}, 1200);
```
#### Web Inspector Colors
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, and the Firebug plugin for Firefox.
Colored output looks something like:
![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
### stderr vs stdout
You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally:
Example _stderr.js_:
```js
var debug = require('../');
var log = debug('app:log');
// by default console.log is used
log('goes to stdout!');
var error = debug('app:error');
// set this namespace to log via console.error
error.log = console.error.bind(console); // don't forget to bind to console!
error('goes to stderr');
log('still goes to stdout!');
// set all output to go via console.warn
// overrides all per-namespace log settings
debug.log = console.warn.bind(console);
log('now goes to stderr via console.warn');
error('still goes to stderr, but via console.warn now');
```
## Authors
- TJ Holowaychuk
- Nathan Rajlich
## License
(The MIT License)
Copyright (c) 2014 TJ Holowaychuk &lt;tj@vision-media.ca&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.

View File

@@ -0,0 +1,144 @@
/**
* This is the web browser implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [
'lightseagreen',
'forestgreen',
'goldenrod',
'dodgerblue',
'darkorchid',
'crimson'
];
/**
* Currently only WebKit-based Web Inspectors and the Firebug
* extension (*not* the built-in Firefox web inpector) are
* known to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
function useColors() {
// is webkit? http://stackoverflow.com/a/16459606/376773
return ('WebkitAppearance' in document.documentElement.style) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(window.console && (console.firebug || (console.exception && console.table)));
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
return JSON.stringify(v);
};
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs() {
var args = arguments;
var useColors = this.useColors;
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? '%c ' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
if (!useColors) return args;
var c = 'color: ' + this.color;
args = [args[0], c, ''].concat(Array.prototype.slice.call(args, 1));
// the final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
var index = 0;
var lastC = 0;
args[0].replace(/%[a-z%]/g, function(match) {
if ('%%' === match) return;
index++;
if ('%c' === match) {
// we only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
return args;
}
/**
* Invokes `console.log()` when available.
* No-op when `console.log` is not a "function".
*
* @api public
*/
function log() {
// This hackery is required for IE8,
// where the `console.log` function doesn't have 'apply'
return 'object' == typeof console
&& 'function' == typeof console.log
&& Function.prototype.apply.call(console.log, console, arguments);
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (null == namespaces) {
localStorage.removeItem('debug');
} else {
localStorage.debug = namespaces;
}
} catch(e) {}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
var r;
try {
r = localStorage.debug;
} catch(e) {}
return r;
}
/**
* Enable namespaces listed in `localStorage.debug` initially.
*/
exports.enable(load());

View File

@@ -0,0 +1,19 @@
{
"name": "debug",
"repo": "visionmedia/debug",
"description": "small debugging utility",
"version": "1.0.3",
"keywords": [
"debug",
"log",
"debugger"
],
"main": "browser.js",
"scripts": [
"browser.js",
"debug.js"
],
"dependencies": {
"guille/ms.js": "0.6.1"
}
}

View File

@@ -0,0 +1,197 @@
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = debug;
exports.coerce = coerce;
exports.disable = disable;
exports.enable = enable;
exports.enabled = enabled;
exports.humanize = require('ms');
/**
* The currently active debug mode names, and names to skip.
*/
exports.names = [];
exports.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lowercased letter, i.e. "n".
*/
exports.formatters = {};
/**
* Previously assigned color.
*/
var prevColor = 0;
/**
* Previous log timestamp.
*/
var prevTime;
/**
* Select a color.
*
* @return {Number}
* @api private
*/
function selectColor() {
return exports.colors[prevColor++ % exports.colors.length];
}
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function debug(namespace) {
// define the `disabled` version
function disabled() {
}
disabled.enabled = false;
// define the `enabled` version
function enabled() {
var self = enabled;
// set `diff` timestamp
var curr = +new Date();
var ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
// add the `color` if not set
if (null == self.useColors) self.useColors = exports.useColors();
if (null == self.color && self.useColors) self.color = selectColor();
var args = Array.prototype.slice.call(arguments);
args[0] = exports.coerce(args[0]);
if ('string' !== typeof args[0]) {
// anything else let's inspect with %o
args = ['%o'].concat(args);
}
// apply any `formatters` transformations
var index = 0;
args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
// if we encounter an escaped % then don't increase the array index
if (match === '%%') return match;
index++;
var formatter = exports.formatters[format];
if ('function' === typeof formatter) {
var val = args[index];
match = formatter.call(self, val);
// now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
if ('function' === typeof exports.formatArgs) {
args = exports.formatArgs.apply(self, args);
}
var logFn = exports.log || enabled.log || console.log.bind(console);
logFn.apply(self, args);
}
enabled.enabled = true;
var fn = exports.enabled(namespace) ? enabled : disabled;
fn.namespace = namespace;
return fn;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
exports.save(namespaces);
var split = (namespaces || '').split(/[\s,]+/);
var len = split.length;
for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else {
exports.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @api public
*/
function disable() {
exports.enable('');
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
var i, len;
for (i = 0, len = exports.skips.length; i < len; i++) {
if (exports.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = exports.names.length; i < len; i++) {
if (exports.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) return val.stack || val.message;
return val;
}

View File

@@ -0,0 +1,129 @@
/**
* Module dependencies.
*/
var tty = require('tty');
var util = require('util');
/**
* This is the Node.js implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [6, 2, 3, 4, 5, 1];
/**
* Is stdout a TTY? Colored output is enabled when `true`.
*/
function useColors() {
var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase();
if (0 === debugColors.length) {
return tty.isatty(1);
} else {
return '0' !== debugColors
&& 'no' !== debugColors
&& 'false' !== debugColors
&& 'disabled' !== debugColors;
}
}
/**
* Map %o to `util.inspect()`, since Node doesn't do that out of the box.
*/
var inspect = (4 === util.inspect.length ?
// node <= 0.8.x
function (v, colors) {
return util.inspect(v, void 0, void 0, colors);
} :
// node > 0.8.x
function (v, colors) {
return util.inspect(v, { colors: colors });
}
);
exports.formatters.o = function(v) {
return inspect(v, this.useColors)
.replace(/\s*\n\s*/g, ' ');
};
/**
* Adds ANSI color escape codes if enabled.
*
* @api public
*/
function formatArgs() {
var args = arguments;
var useColors = this.useColors;
var name = this.namespace;
if (useColors) {
var c = this.color;
args[0] = ' \u001b[9' + c + 'm' + name + ' '
+ '\u001b[0m'
+ args[0] + '\u001b[3' + c + 'm'
+ ' +' + exports.humanize(this.diff) + '\u001b[0m';
} else {
args[0] = new Date().toUTCString()
+ ' ' + name + ' ' + args[0];
}
return args;
}
/**
* Invokes `console.log()` with the specified arguments.
*/
function log() {
return console.log.apply(console, arguments);
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
if (null == namespaces) {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete process.env.DEBUG;
} else {
process.env.DEBUG = namespaces;
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
return process.env.DEBUG;
}
/**
* Enable namespaces listed in `process.env.DEBUG` initially.
*/
exports.enable(load());

View File

@@ -0,0 +1,5 @@
node_modules
test
History.md
Makefile
component.json

View File

@@ -0,0 +1,33 @@
# ms.js: miliseconds conversion utility
```js
ms('1d') // 86400000
ms('10h') // 36000000
ms('2h') // 7200000
ms('1m') // 60000
ms('5s') // 5000
ms('100') // 100
```
```js
ms(60000) // "1m"
ms(2 * 60000) // "2m"
ms(ms('10 hours')) // "10h"
```
```js
ms(60000, { long: true }) // "1 minute"
ms(2 * 60000, { long: true }) // "2 minutes"
ms(ms('10 hours', { long: true })) // "10 hours"
```
- Node/Browser compatible. Published as `ms` in NPM.
- If a number is supplied to `ms`, a string with a unit is returned.
- If a string that contains the number is supplied, it returns it as
a number (e.g: it returns `100` for `'100'`).
- If you pass a string with a number and a valid unit, the number of
equivalent ms is returned.
## License
MIT

View File

@@ -0,0 +1,111 @@
/**
* Helpers.
*/
var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var y = d * 365.25;
/**
* Parse or format the given `val`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} options
* @return {String|Number}
* @api public
*/
module.exports = function(val, options){
options = options || {};
if ('string' == typeof val) return parse(val);
return options.long
? long(val)
: short(val);
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
if (!match) return;
var n = parseFloat(match[1]);
var type = (match[2] || 'ms').toLowerCase();
switch (type) {
case 'years':
case 'year':
case 'y':
return n * y;
case 'days':
case 'day':
case 'd':
return n * d;
case 'hours':
case 'hour':
case 'h':
return n * h;
case 'minutes':
case 'minute':
case 'm':
return n * m;
case 'seconds':
case 'second':
case 's':
return n * s;
case 'ms':
return n;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function short(ms) {
if (ms >= d) return Math.round(ms / d) + 'd';
if (ms >= h) return Math.round(ms / h) + 'h';
if (ms >= m) return Math.round(ms / m) + 'm';
if (ms >= s) return Math.round(ms / s) + 's';
return ms + 'ms';
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function long(ms) {
return plural(ms, d, 'day')
|| plural(ms, h, 'hour')
|| plural(ms, m, 'minute')
|| plural(ms, s, 'second')
|| ms + ' ms';
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) return;
if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
return Math.ceil(ms / n) + ' ' + name + 's';
}

View File

@@ -0,0 +1,46 @@
{
"name": "ms",
"version": "0.6.2",
"description": "Tiny ms conversion utility",
"repository": {
"type": "git",
"url": "git://github.com/guille/ms.js.git"
},
"main": "./index",
"devDependencies": {
"mocha": "*",
"expect.js": "*",
"serve": "*"
},
"component": {
"scripts": {
"ms/index.js": "index.js"
}
},
"bugs": {
"url": "https://github.com/guille/ms.js/issues"
},
"_id": "ms@0.6.2",
"dist": {
"shasum": "d89c2124c6fdc1353d65a8b77bf1aac4b193708c",
"tarball": "http://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
},
"_from": "ms@0.6.2",
"_npmVersion": "1.2.30",
"_npmUser": {
"name": "rauchg",
"email": "rauchg@gmail.com"
},
"maintainers": [
{
"name": "rauchg",
"email": "rauchg@gmail.com"
}
],
"directories": {},
"_shasum": "d89c2124c6fdc1353d65a8b77bf1aac4b193708c",
"_resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz",
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/guille/ms.js",
"scripts": {}
}

View File

@@ -0,0 +1,70 @@
{
"name": "debug",
"version": "1.0.3",
"repository": {
"type": "git",
"url": "git://github.com/visionmedia/debug.git"
},
"description": "small debugging utility",
"keywords": [
"debug",
"log",
"debugger"
],
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
"contributors": [
{
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
"url": "http://n8.io"
}
],
"dependencies": {
"ms": "0.6.2"
},
"devDependencies": {
"browserify": "4.1.6",
"mocha": "*"
},
"main": "./node.js",
"browser": "./browser.js",
"component": {
"scripts": {
"debug/index.js": "browser.js",
"debug/debug.js": "debug.js"
}
},
"gitHead": "93c759961d53ad7f06a1892a8dd0bf4be4a7b9df",
"bugs": {
"url": "https://github.com/visionmedia/debug/issues"
},
"homepage": "https://github.com/visionmedia/debug",
"_id": "debug@1.0.3",
"scripts": {},
"_shasum": "fc8c6b2d6002804b4081c0208e0f6460ba1fa3e4",
"_from": "debug@1.0.3",
"_npmVersion": "1.4.14",
"_npmUser": {
"name": "tootallnate",
"email": "nathan@tootallnate.net"
},
"maintainers": [
{
"name": "tjholowaychuk",
"email": "tj@vision-media.ca"
},
{
"name": "tootallnate",
"email": "nathan@tootallnate.net"
}
],
"dist": {
"shasum": "fc8c6b2d6002804b4081c0208e0f6460ba1fa3e4",
"tarball": "http://registry.npmjs.org/debug/-/debug-1.0.3.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/debug/-/debug-1.0.3.tgz"
}

View File

@@ -0,0 +1,17 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
npm-debug.log
node_modules/*
test

View File

@@ -0,0 +1,45 @@
language: node_js
node_js:
- 0.10
notifications:
irc: irc.freenode.org##socket.io
env:
global:
- secure: ZS6AzV1n1K1exomhZoK0SorBHEy4/7/qdk1p5/dm0tQdStSVwRDJyQk5wUIgJFsJaVlN8O/MH8LBkiLlalohR/DdE2ZtkNJqfMnetE/ZqPX7r8mzwDasnHJNJnKWJlBVqOpy7hciiPV2yZGIJoe2OQfwWxFEcLJ6NGOXCEkyLAg=
- secure: hcqk+nIqzrwJSQs+5T1sKN4YiCghQdP849RjH64bb7Ayslh+o0DmihTE3Wl+cCWFcRBvwBJdmDV2gJpsVcODTc2VdOrJnv8ezfQ8zvAyDZFYxno47PtbjQUi0By2wBPp6zlfigcnXxQ2z6997EDRvsI4VgQVqKsGLot4cMU9oz0=
matrix:
include:
- node_js: '0.10'
env: BROWSER_NAME=chrome BROWSER_VERSION=latest
- node_js: '0.10'
env: BROWSER_NAME=safari BROWSER_VERSION=latest
- node_js: '0.10'
env: BROWSER_NAME=ie BROWSER_VERSION=6
- node_js: '0.10'
env: BROWSER_NAME=ie BROWSER_VERSION=7
- node_js: '0.10'
env: BROWSER_NAME=ie BROWSER_VERSION=8
- node_js: '0.10'
env: BROWSER_NAME=ie BROWSER_VERSION=9
- node_js: '0.10'
env: BROWSER_NAME=ie BROWSER_VERSION=10 BROWSER_PLATFORM="Windows 2012"
- node_js: '0.10'
env: BROWSER_NAME=ie BROWSER_VERSION=latest BROWSER_PLATFORM="Windows 2012"
- node_js: '0.10'
env: BROWSER_NAME=iphone BROWSER_VERSION=4.3
- node_js: '0.10'
env: BROWSER_NAME=iphone BROWSER_VERSION=5.1
- node_js: '0.10'
env: BROWSER_NAME=iphone BROWSER_VERSION=6.1
- node_js: '0.10'
env: BROWSER_NAME=iphone BROWSER_VERSION=7.1
- node_js: '0.10'
env: BROWSER_NAME=android BROWSER_VERSION=4.0
- node_js: '0.10'
env: BROWSER_NAME=android BROWSER_VERSION=4.1
- node_js: '0.10'
env: BROWSER_NAME=android BROWSER_VERSION=4.2
- node_js: '0.10'
env: BROWSER_NAME=android BROWSER_VERSION=4.3
- node_js: '0.10'
env: BROWSER_NAME=android BROWSER_VERSION=4.4

View File

@@ -0,0 +1,18 @@
ui: mocha-bdd
browsers:
- name: chrome
version: 29..latest
- name: firefox
version: latest
- name: safari
version: latest
- name: ie
version: 10
platform: Windows 2012
- name: ie
version: 9
version: [6..9, latest]
- name: iphone
version: oldest..latest
- name: android
version: latest

View File

@@ -0,0 +1,100 @@
1.2.1 / 2015-01-17
==================
* pass has-binary result to encodePacket [rase-]
* Fix parse error [rase-]
1.2.0 / 2015-01-11
==================
* fix return type for decodePacket
* README fixes
* use travis matrix for better test runs
* encode into binary only if needed
* add test cases for base64 object encoding.
* add encodeBase64Object to encoder for browser
* avoid sending Blobs on PhantomJS (as on Android)
* test that utf8 encoding is not on by default but can be switched on manually
1.1.0 / 2014-07-16
==================
* make utf8 encoding/decoding optional
1.0.8 / 2014-07-16
==================
* adjust protocol revision
* handle invalid utf8 errors gracefully
* fix memory leak on browser
1.0.7 / 2014-06-24
==================
* fix decodePayloadAsBinary memory leak [christophwitzko]
* README improvements
1.0.6 / 2014-05-30
==================
* utf8 fixes when using binary encoding [nkzawa]
1.0.5 / 2014-05-06
==================
* fix range error
1.0.4 / 2014-04-13
==================
* fix `encodePayloadAsBinary` method encodes packets to base64
1.0.3 / 2014-04-10
==================
* Fix length calculation when encoding as binary [binlain]
1.0.2 / 2014-03-16
==================
* fix binary for android due to a bug in Blob XHR2 implementation [Rase-]
1.0.1 / 2014-03-06
==================
* implement `blob` module to simplify code
* bump `arraybuffer.slice`
* style fixes
1.0.0 / 2014-02-18
==================
* parser: added binary encoding [Rase-]
* parser: switched to an async interface [Rase-]
0.3.0 / 2013-03-16
==================
* parser: if callback returns `false` ignore rest of payload
* test: fixed all broken tests
0.2.1 / 2013-03-16
==================
* added protocol version to index.js [albertyfwu]
0.2.0 / 2013-02-26
==================
* Changed `decodePayload` to use a callback instead of returning an array [sweetieSong, albertyfwu]
0.1.1 / 2013-01-26
==================
* package.json fixes
0.1.0 / 2013-01-19
==================
* Initial release

View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2014 Automattic <dev@cloudup.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,26 @@
REPORTER = dot
test:
@if [ "x$(BROWSER_NAME)" = "x" ]; then make test-node; else make test-zuul; fi
test-node:
@./node_modules/.bin/mocha \
--reporter $(REPORTER) \
test/index.js
test-zuul:
@if [ "x$(BROWSER_PLATFORM)" = "x" ]; then \
./node_modules/zuul/bin/zuul \
--browser-name $(BROWSER_NAME) \
--browser-version $(BROWSER_VERSION) \
test/index.js; \
else \
./node_modules/zuul/bin/zuul \
--browser-name $(BROWSER_NAME) \
--browser-version $(BROWSER_VERSION) \
--browser-platform "$(BROWSER_PLATFORM)" \
test/index.js; \
fi
.PHONY: test

View File

@@ -0,0 +1,202 @@
# engine.io-parser
[![Build Status](https://secure.travis-ci.org/Automattic/engine.io-parser.svg)](http://travis-ci.org/Automattic/engine.io-parser)
[![NPM version](https://badge.fury.io/js/engine.io-parser.svg)](http://badge.fury.io/js/engine.io-parser)
This is the JavaScript parser for the engine.io protocol encoding,
shared by both
[engine.io-client](https://github.com/Automattic/engine.io-client) and
[engine.io](https://github.com/Automattic/engine.io).
## How to use
### Standalone
The parser can encode/decode packets, payloads, and payloads as binary
with the following methods: `encodePacket`, `decodePacket`, `encodePayload`,
`decodePayload`, `encodePayloadAsBinary`, `decodePayloadAsBinary`.
The browser-side parser also includes `encodePayloadAsArrayBuffer` and `encodePayloadAsBlob`.
Example:
```js
var parser = require('engine.io-parser');
var data = new Buffer(5);
for (var i = 0; i < data.length; i++) { data[i] = i; }
parser.encodePacket({ type: 'message', data: data }, function(encoded) {
var decodedData = parser.decodePacket(encoded); // { type: 'message', data: data }
});
```
### With browserify
Engine.IO Parser is a commonjs module, which means you can include it by using
`require` on the browser and package using [browserify](http://browserify.org/):
1. install the parser package
```shell
npm install engine.io-parser
```
1. write your app code
```js
var parser = require('engine.io-parser');
var testBuffer = new Int8Array(10);
for (var i = 0; i < testBuffer.length; i++) testBuffer[i] = i;
var packets = [{ type: 'message', data: testBuffer.buffer }, { type: 'message', data: 'hello' }];
parser.encodePayload(packets, function(encoded) {
parser.decodePayload(encoded,
function(packet, index, total) {
var isLast = index + 1 == total;
if (!isLast) {
var buffer = new Int8Array(packet.data); // testBuffer
} else {
var message = packet.data; // 'hello'
}
});
});
```
1. build your app bundle
```bash
$ browserify app.js > bundle.js
```
1. include on your page
```html
<script src="/path/to/bundle.js"></script>
```
## Features
- Runs on browser and node.js seamlessly
- Runs inside HTML5 WebWorker
- Can encode and decode packets
- Encodes from/to ArrayBuffer or Blob when in browser, and Buffer or ArrayBuffer in Node
## API
Note: `cb(type)` means the type is a callback function that contains a parameter of type `type` when called.
### Node
- `encodePacket`
- Encodes a packet.
- **Parameters**
- `Object`: the packet to encode, has `type` and `data`.
- `data`: can be a `String`, `Number`, `Buffer`, `ArrayBuffer`
- `Boolean`: optional, binary support
- `Function`: callback, returns the encoded packet (`cb(String)`)
- `decodePacket`
- Decodes a packet. Data also available as an ArrayBuffer if requested.
- Returns data as `String` or (`Blob` on browser, `ArrayBuffer` on Node)
- **Parameters**
- `String` | `ArrayBuffer`: the packet to decode, has `type` and `data`
- `String`: optional, the binary type
- `encodeBase64Packet`
- Encodes a packet with binary data in a base64 string (`String`)
- **Parameters**
- `Object`: the packet to encode, has `type` and `data`
- `Function`: callback, returns the base64 encoded message (`cb(String)`)
- `decodeBase64Packet`
- Decodes a packet encoded in a base64 string.
- **Parameters**
- `String`: the base64 encoded message
- `String`: optional, the binary type
- `encodePayload`
- Encodes multiple messages (payload).
- If any contents are binary, they will be encoded as base64 strings. Base64
encoded strings are marked with a b before the length specifier
- **Parameters**
- `Array`: an array of packets
- `Boolean`: optional, binary support
- `Function`: callback, returns the encoded payload (`cb(String)`)
- `decodePayload`
- Decodes data when a payload is maybe expected. Possible binary contents are
decoded from their base64 representation.
- **Parameters**
- `String`: the payload
- `String`: optional, the binary type
- `Function`: callback, returns (cb(`Object`: packet, `Number`:packet index, `Number`:packet total))
- `encodePayloadAsBinary`
- Encodes multiple messages (payload) as binary.
- **Parameters**
- `Array`: an array of packets
- `Function`: callback, returns the encoded payload (`cb(Buffer)`)
- `decodePayloadAsBinary`
- Decodes data when a payload is maybe expected. Strings are decoded by
interpreting each byte as a key code for entries marked to start with 0. See
description of encodePayloadAsBinary.
- **Parameters**
- `Buffer`: the buffer
- `String`: optional, the binary type
- `Function`: callback, returns the decoded packet (`cb(Object)`)
### Browser
- `encodePayloadAsArrayBuffer`
- Encodes multiple messages (payload) as binary.
- **Parameters**
- `Array`: an array of packets
- `Function`: callback, returns the encoded payload (`cb(ArrayBuffer)`)
- `encodePayloadAsBlob`
- Encodes multiple messages (payload) as blob.
- **Parameters**
- `Array`: an array of packets
- `Function`: callback, returns the encoded payload (`cb(Blob)`)
## Tests
Standalone tests can be run with `make test` which will run both node.js and browser tests.
Browser tests are run using [zuul](https://github.com/defunctzombie/zuul).
(You must have zuul setup with a saucelabs account.)
You can run the tests locally using the following command:
```
./node_modules/.bin/zuul --local 8080 -- test/index.js
```
## Support
The support channels for `engine.io-parser` are the same as `socket.io`:
- irc.freenode.net **#socket.io**
- [Google Groups](http://groups.google.com/group/socket_io)
- [Website](http://socket.io)
## Development
To contribute patches, run tests or benchmarks, make sure to clone the
repository:
```bash
git clone git://github.com/LearnBoost/engine.io-parser.git
```
Then:
```bash
cd engine.io-parser
npm install
```
See the `Tests` section above for how to run tests before submitting any patches.
## License
MIT

View File

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

View File

@@ -0,0 +1,594 @@
/**
* Module dependencies.
*/
var keys = require('./keys');
var hasBinary = require('has-binary');
var sliceBuffer = require('arraybuffer.slice');
var base64encoder = require('base64-arraybuffer');
var after = require('after');
var utf8 = require('utf8');
/**
* Check if we are running an android browser. That requires us to use
* ArrayBuffer with polling transports...
*
* http://ghinda.net/jpeg-blob-ajax-android/
*/
var isAndroid = navigator.userAgent.match(/Android/i);
/**
* Check if we are running in PhantomJS.
* Uploading a Blob with PhantomJS does not work correctly, as reported here:
* https://github.com/ariya/phantomjs/issues/11395
* @type boolean
*/
var isPhantomJS = /PhantomJS/i.test(navigator.userAgent);
/**
* When true, avoids using Blobs to encode payloads.
* @type boolean
*/
var dontSendBlobs = isAndroid || isPhantomJS;
/**
* Current protocol version.
*/
exports.protocol = 3;
/**
* Packet types.
*/
var packets = exports.packets = {
open: 0 // non-ws
, close: 1 // non-ws
, ping: 2
, pong: 3
, message: 4
, upgrade: 5
, noop: 6
};
var packetslist = keys(packets);
/**
* Premade error packet.
*/
var err = { type: 'error', data: 'parser error' };
/**
* Create a blob api even for blob builder when vendor prefixes exist
*/
var Blob = require('blob');
/**
* Encodes a packet.
*
* <packet type id> [ <data> ]
*
* Example:
*
* 5hello world
* 3
* 4
*
* Binary is encoded in an identical principle
*
* @api private
*/
exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
if ('function' == typeof supportsBinary) {
callback = supportsBinary;
supportsBinary = false;
}
if ('function' == typeof utf8encode) {
callback = utf8encode;
utf8encode = null;
}
var data = (packet.data === undefined)
? undefined
: packet.data.buffer || packet.data;
if (global.ArrayBuffer && data instanceof ArrayBuffer) {
return encodeArrayBuffer(packet, supportsBinary, callback);
} else if (Blob && data instanceof global.Blob) {
return encodeBlob(packet, supportsBinary, callback);
}
// might be an object with { base64: true, data: dataAsBase64String }
if (data && data.base64) {
return encodeBase64Object(packet, callback);
}
// Sending data as a utf-8 string
var encoded = packets[packet.type];
// data fragment is optional
if (undefined !== packet.data) {
encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
}
return callback('' + encoded);
};
function encodeBase64Object(packet, callback) {
// packet data is an object { base64: true, data: dataAsBase64String }
var message = 'b' + exports.packets[packet.type] + packet.data.data;
return callback(message);
}
/**
* Encode packet helpers for binary types
*/
function encodeArrayBuffer(packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var data = packet.data;
var contentArray = new Uint8Array(data);
var resultBuffer = new Uint8Array(1 + data.byteLength);
resultBuffer[0] = packets[packet.type];
for (var i = 0; i < contentArray.length; i++) {
resultBuffer[i+1] = contentArray[i];
}
return callback(resultBuffer.buffer);
}
function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var fr = new FileReader();
fr.onload = function() {
packet.data = fr.result;
exports.encodePacket(packet, supportsBinary, true, callback);
};
return fr.readAsArrayBuffer(packet.data);
}
function encodeBlob(packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
if (dontSendBlobs) {
return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
}
var length = new Uint8Array(1);
length[0] = packets[packet.type];
var blob = new Blob([length.buffer, packet.data]);
return callback(blob);
}
/**
* Encodes a packet with binary data in a base64 string
*
* @param {Object} packet, has `type` and `data`
* @return {String} base64 encoded message
*/
exports.encodeBase64Packet = function(packet, callback) {
var message = 'b' + exports.packets[packet.type];
if (Blob && packet.data instanceof Blob) {
var fr = new FileReader();
fr.onload = function() {
var b64 = fr.result.split(',')[1];
callback(message + b64);
};
return fr.readAsDataURL(packet.data);
}
var b64data;
try {
b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
} catch (e) {
// iPhone Safari doesn't let you apply with typed arrays
var typed = new Uint8Array(packet.data);
var basic = new Array(typed.length);
for (var i = 0; i < typed.length; i++) {
basic[i] = typed[i];
}
b64data = String.fromCharCode.apply(null, basic);
}
message += global.btoa(b64data);
return callback(message);
};
/**
* Decodes a packet. Changes format to Blob if requested.
*
* @return {Object} with `type` and `data` (if any)
* @api private
*/
exports.decodePacket = function (data, binaryType, utf8decode) {
// String data
if (typeof data == 'string' || data === undefined) {
if (data.charAt(0) == 'b') {
return exports.decodeBase64Packet(data.substr(1), binaryType);
}
if (utf8decode) {
try {
data = utf8.decode(data);
} catch (e) {
return err;
}
}
var type = data.charAt(0);
if (Number(type) != type || !packetslist[type]) {
return err;
}
if (data.length > 1) {
return { type: packetslist[type], data: data.substring(1) };
} else {
return { type: packetslist[type] };
}
}
var asArray = new Uint8Array(data);
var type = asArray[0];
var rest = sliceBuffer(data, 1);
if (Blob && binaryType === 'blob') {
rest = new Blob([rest]);
}
return { type: packetslist[type], data: rest };
};
/**
* Decodes a packet encoded in a base64 string
*
* @param {String} base64 encoded message
* @return {Object} with `type` and `data` (if any)
*/
exports.decodeBase64Packet = function(msg, binaryType) {
var type = packetslist[msg.charAt(0)];
if (!global.ArrayBuffer) {
return { type: type, data: { base64: true, data: msg.substr(1) } };
}
var data = base64encoder.decode(msg.substr(1));
if (binaryType === 'blob' && Blob) {
data = new Blob([data]);
}
return { type: type, data: data };
};
/**
* Encodes multiple messages (payload).
*
* <length>:data
*
* Example:
*
* 11:hello world2:hi
*
* If any contents are binary, they will be encoded as base64 strings. Base64
* encoded strings are marked with a b before the length specifier
*
* @param {Array} packets
* @api private
*/
exports.encodePayload = function (packets, supportsBinary, callback) {
if (typeof supportsBinary == 'function') {
callback = supportsBinary;
supportsBinary = null;
}
var isBinary = hasBinary(packets);
if (supportsBinary && isBinary) {
if (Blob && !dontSendBlobs) {
return exports.encodePayloadAsBlob(packets, callback);
}
return exports.encodePayloadAsArrayBuffer(packets, callback);
}
if (!packets.length) {
return callback('0:');
}
function setLengthHeader(message) {
return message.length + ':' + message;
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
doneCallback(null, setLengthHeader(message));
});
}
map(packets, encodeOne, function(err, results) {
return callback(results.join(''));
});
};
/**
* Async array map using after
*/
function map(ary, each, done) {
var result = new Array(ary.length);
var next = after(ary.length, done);
var eachWithIndex = function(i, el, cb) {
each(el, function(error, msg) {
result[i] = msg;
cb(error, result);
});
};
for (var i = 0; i < ary.length; i++) {
eachWithIndex(i, ary[i], next);
}
}
/*
* Decodes data when a payload is maybe expected. Possible binary contents are
* decoded from their base64 representation
*
* @param {String} data, callback method
* @api public
*/
exports.decodePayload = function (data, binaryType, callback) {
if (typeof data != 'string') {
return exports.decodePayloadAsBinary(data, binaryType, callback);
}
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var packet;
if (data == '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
var length = ''
, n, msg;
for (var i = 0, l = data.length; i < l; i++) {
var chr = data.charAt(i);
if (':' != chr) {
length += chr;
} else {
if ('' == length || (length != (n = Number(length)))) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
msg = data.substr(i + 1, n);
if (length != msg.length) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
if (msg.length) {
packet = exports.decodePacket(msg, binaryType, true);
if (err.type == packet.type && err.data == packet.data) {
// parser error in individual packet - ignoring payload
return callback(err, 0, 1);
}
var ret = callback(packet, i + n, l);
if (false === ret) return;
}
// advance cursor
i += n;
length = '';
}
}
if (length != '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
};
/**
* Encodes multiple messages (payload) as binary.
*
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
* 255><data>
*
* Example:
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
*
* @param {Array} packets
* @return {ArrayBuffer} encoded payload
* @api private
*/
exports.encodePayloadAsArrayBuffer = function(packets, callback) {
if (!packets.length) {
return callback(new ArrayBuffer(0));
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, true, true, function(data) {
return doneCallback(null, data);
});
}
map(packets, encodeOne, function(err, encodedPackets) {
var totalLength = encodedPackets.reduce(function(acc, p) {
var len;
if (typeof p === 'string'){
len = p.length;
} else {
len = p.byteLength;
}
return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
}, 0);
var resultArray = new Uint8Array(totalLength);
var bufferIndex = 0;
encodedPackets.forEach(function(p) {
var isString = typeof p === 'string';
var ab = p;
if (isString) {
var view = new Uint8Array(p.length);
for (var i = 0; i < p.length; i++) {
view[i] = p.charCodeAt(i);
}
ab = view.buffer;
}
if (isString) { // not true binary
resultArray[bufferIndex++] = 0;
} else { // true binary
resultArray[bufferIndex++] = 1;
}
var lenStr = ab.byteLength.toString();
for (var i = 0; i < lenStr.length; i++) {
resultArray[bufferIndex++] = parseInt(lenStr[i]);
}
resultArray[bufferIndex++] = 255;
var view = new Uint8Array(ab);
for (var i = 0; i < view.length; i++) {
resultArray[bufferIndex++] = view[i];
}
});
return callback(resultArray.buffer);
});
};
/**
* Encode as Blob
*/
exports.encodePayloadAsBlob = function(packets, callback) {
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, true, true, function(encoded) {
var binaryIdentifier = new Uint8Array(1);
binaryIdentifier[0] = 1;
if (typeof encoded === 'string') {
var view = new Uint8Array(encoded.length);
for (var i = 0; i < encoded.length; i++) {
view[i] = encoded.charCodeAt(i);
}
encoded = view.buffer;
binaryIdentifier[0] = 0;
}
var len = (encoded instanceof ArrayBuffer)
? encoded.byteLength
: encoded.size;
var lenStr = len.toString();
var lengthAry = new Uint8Array(lenStr.length + 1);
for (var i = 0; i < lenStr.length; i++) {
lengthAry[i] = parseInt(lenStr[i]);
}
lengthAry[lenStr.length] = 255;
if (Blob) {
var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
doneCallback(null, blob);
}
});
}
map(packets, encodeOne, function(err, results) {
return callback(new Blob(results));
});
};
/*
* Decodes data when a payload is maybe expected. Strings are decoded by
* interpreting each byte as a key code for entries marked to start with 0. See
* description of encodePayloadAsBinary
*
* @param {ArrayBuffer} data, callback method
* @api public
*/
exports.decodePayloadAsBinary = function (data, binaryType, callback) {
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var bufferTail = data;
var buffers = [];
var numberTooLong = false;
while (bufferTail.byteLength > 0) {
var tailArray = new Uint8Array(bufferTail);
var isString = tailArray[0] === 0;
var msgLength = '';
for (var i = 1; ; i++) {
if (tailArray[i] == 255) break;
if (msgLength.length > 310) {
numberTooLong = true;
break;
}
msgLength += tailArray[i];
}
if(numberTooLong) return callback(err, 0, 1);
bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
msgLength = parseInt(msgLength);
var msg = sliceBuffer(bufferTail, 0, msgLength);
if (isString) {
try {
msg = String.fromCharCode.apply(null, new Uint8Array(msg));
} catch (e) {
// iPhone Safari doesn't let you apply to typed arrays
var typed = new Uint8Array(msg);
msg = '';
for (var i = 0; i < typed.length; i++) {
msg += String.fromCharCode(typed[i]);
}
}
}
buffers.push(msg);
bufferTail = sliceBuffer(bufferTail, msgLength);
}
var total = buffers.length;
buffers.forEach(function(buffer, i) {
callback(exports.decodePacket(buffer, binaryType, true), i, total);
});
};

View File

@@ -0,0 +1,467 @@
/**
* Module dependencies.
*/
var utf8 = require('utf8');
var after = require('after');
var keys = require('./keys');
/**
* Current protocol version.
*/
exports.protocol = 3;
/**
* Packet types.
*/
var packets = exports.packets = {
open: 0 // non-ws
, close: 1 // non-ws
, ping: 2
, pong: 3
, message: 4
, upgrade: 5
, noop: 6
};
var packetslist = keys(packets);
/**
* Premade error packet.
*/
var err = { type: 'error', data: 'parser error' };
/**
* Encodes a packet.
*
* <packet type id> [ <data> ]
*
* Example:
*
* 5hello world
* 3
* 4
*
* Binary is encoded in an identical principle
*
* @api private
*/
exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
if ('function' == typeof supportsBinary) {
callback = supportsBinary;
supportsBinary = null;
}
if ('function' == typeof utf8encode ) {
callback = utf8encode;
utf8encode = null;
}
var data = (packet.data === undefined)
? undefined
: packet.data.buffer || packet.data;
if (Buffer.isBuffer(data)) {
return encodeBuffer(packet, supportsBinary, callback);
} else if (data instanceof ArrayBuffer) {
return encodeArrayBuffer(packet, supportsBinary, callback);
}
// Sending data as a utf-8 string
var encoded = packets[packet.type];
// data fragment is optional
if (undefined !== packet.data) {
encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
}
return callback('' + encoded);
};
/**
* Encode Buffer data
*/
function encodeBuffer(packet, supportsBinary, callback) {
var data = packet.data;
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var typeBuffer = new Buffer(1);
typeBuffer[0] = packets[packet.type];
return callback(Buffer.concat([typeBuffer, data]));
}
function encodeArrayBuffer(packet, supportsBinary, callback) {
var data = (packet.data === undefined)
? undefined
: packet.data.buffer || packet.data;
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var contentArray = new Uint8Array(data);
var resultBuffer = new Buffer(1 + data.byteLength);
resultBuffer[0] = packets[packet.type];
for (var i = 0; i < contentArray.length; i++){
resultBuffer[i+1] = contentArray[i];
}
return callback(resultBuffer);
}
/**
* Encodes a packet with binary data in a base64 string
*
* @param {Object} packet, has `type` and `data`
* @return {String} base64 encoded message
*/
exports.encodeBase64Packet = function(packet, callback){
var data = packet.data.buffer || packet.data;
if (data instanceof ArrayBuffer) {
var buf = new Buffer(data.byteLength);
for (var i = 0; i < buf.length; i++) {
buf[i] = data[i];
}
packet.data = buf;
}
var message = 'b' + packets[packet.type];
message += packet.data.toString('base64');
return callback(message);
};
/**
* Decodes a packet. Data also available as an ArrayBuffer if requested.
*
* @return {Object} with `type` and `data` (if any)
* @api private
*/
exports.decodePacket = function (data, binaryType, utf8decode) {
// String data
if (typeof data == 'string' || data === undefined) {
if (data.charAt(0) == 'b') {
return exports.decodeBase64Packet(data.substr(1), binaryType);
}
var type = data.charAt(0);
if (utf8decode) {
try {
data = utf8.decode(data);
} catch (e) {
return err;
}
}
if (Number(type) != type || !packetslist[type]) {
return err;
}
if (data.length > 1) {
return { type: packetslist[type], data: data.substring(1) };
} else {
return { type: packetslist[type] };
}
}
// Binary data
if (binaryType === 'arraybuffer') {
var type = data[0];
var intArray = new Uint8Array(data.length - 1);
for (var i = 1; i < data.length; i++) {
intArray[i - 1] = data[i];
}
return { type: packetslist[type], data: intArray.buffer };
}
var type = data[0];
return { type: packetslist[type], data: data.slice(1) };
};
/**
* Decodes a packet encoded in a base64 string.
*
* @param {String} base64 encoded message
* @return {Object} with `type` and `data` (if any)
*/
exports.decodeBase64Packet = function(msg, binaryType) {
var type = packetslist[msg.charAt(0)];
var data = new Buffer(msg.substr(1), 'base64');
if (binaryType === 'arraybuffer') {
var abv = new Uint8Array(data.length);
for (var i = 0; i < abv.length; i++){
abv[i] = data[i];
}
data = abv.buffer;
}
return { type: type, data: data };
};
/**
* Encodes multiple messages (payload).
*
* <length>:data
*
* Example:
*
* 11:hello world2:hi
*
* If any contents are binary, they will be encoded as base64 strings. Base64
* encoded strings are marked with a b before the length specifier
*
* @param {Array} packets
* @api private
*/
exports.encodePayload = function (packets, supportsBinary, callback) {
if (typeof supportsBinary == 'function') {
callback = supportsBinary;
supportsBinary = null;
}
if (supportsBinary) {
return exports.encodePayloadAsBinary(packets, callback);
}
if (!packets.length) {
return callback('0:');
}
function setLengthHeader(message) {
return message.length + ':' + message;
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, supportsBinary, true, function(message) {
doneCallback(null, setLengthHeader(message));
});
}
map(packets, encodeOne, function(err, results) {
return callback(results.join(''));
});
};
/**
* Async array map using after
*/
function map(ary, each, done) {
var result = new Array(ary.length);
var next = after(ary.length, done);
var eachWithIndex = function(i, el, cb) {
each(el, function(error, msg) {
result[i] = msg;
cb(error, result);
});
};
for (var i = 0; i < ary.length; i++) {
eachWithIndex(i, ary[i], next);
}
}
/*
* Decodes data when a payload is maybe expected. Possible binary contents are
* decoded from their base64 representation
*
* @param {String} data, callback method
* @api public
*/
exports.decodePayload = function (data, binaryType, callback) {
if ('string' != typeof data) {
return exports.decodePayloadAsBinary(data, binaryType, callback);
}
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var packet;
if (data == '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
var length = ''
, n, msg;
for (var i = 0, l = data.length; i < l; i++) {
var chr = data.charAt(i);
if (':' != chr) {
length += chr;
} else {
if ('' == length || (length != (n = Number(length)))) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
msg = data.substr(i + 1, n);
if (length != msg.length) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
if (msg.length) {
packet = exports.decodePacket(msg, binaryType, true);
if (err.type == packet.type && err.data == packet.data) {
// parser error in individual packet - ignoring payload
return callback(err, 0, 1);
}
var ret = callback(packet, i + n, l);
if (false === ret) return;
}
// advance cursor
i += n;
length = '';
}
}
if (length != '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
};
/**
*
* Converts a buffer to a utf8.js encoded string
*
* @api private
*/
function bufferToString(buffer) {
var str = '';
for (var i = 0; i < buffer.length; i++) {
str += String.fromCharCode(buffer[i]);
}
return str;
}
/**
*
* Converts a utf8.js encoded string to a buffer
*
* @api private
*/
function stringToBuffer(string) {
var buf = new Buffer(string.length);
for (var i = 0; i < string.length; i++) {
buf.writeUInt8(string.charCodeAt(i), i);
}
return buf;
}
/**
* Encodes multiple messages (payload) as binary.
*
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
* 255><data>
*
* Example:
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
*
* @param {Array} packets
* @return {Buffer} encoded payload
* @api private
*/
exports.encodePayloadAsBinary = function (packets, callback) {
if (!packets.length) {
return callback(new Buffer(0));
}
function encodeOne(p, doneCallback) {
exports.encodePacket(p, true, true, function(packet) {
if (typeof packet === 'string') {
var encodingLength = '' + packet.length;
var sizeBuffer = new Buffer(encodingLength.length + 2);
sizeBuffer[0] = 0; // is a string (not true binary = 0)
for (var i = 0; i < encodingLength.length; i++) {
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
}
sizeBuffer[sizeBuffer.length - 1] = 255;
return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
}
var encodingLength = '' + packet.length;
var sizeBuffer = new Buffer(encodingLength.length + 2);
sizeBuffer[0] = 1; // is binary (true binary = 1)
for (var i = 0; i < encodingLength.length; i++) {
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
}
sizeBuffer[sizeBuffer.length - 1] = 255;
doneCallback(null, Buffer.concat([sizeBuffer, packet]));
});
}
map(packets, encodeOne, function(err, results) {
return callback(Buffer.concat(results));
});
};
/*
* Decodes data when a payload is maybe expected. Strings are decoded by
* interpreting each byte as a key code for entries marked to start with 0. See
* description of encodePayloadAsBinary
* @param {Buffer} data, callback method
* @api public
*/
exports.decodePayloadAsBinary = function (data, binaryType, callback) {
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var bufferTail = data;
var buffers = [];
while (bufferTail.length > 0) {
var strLen = '';
var isString = bufferTail[0] === 0;
var numberTooLong = false;
for (var i = 1; ; i++) {
if (bufferTail[i] == 255) break;
// 310 = char length of Number.MAX_VALUE
if (strLen.length > 310) {
numberTooLong = true;
break;
}
strLen += '' + bufferTail[i];
}
if(numberTooLong) return callback(err, 0, 1);
bufferTail = bufferTail.slice(strLen.length + 1);
var msgLength = parseInt(strLen, 10);
var msg = bufferTail.slice(1, msgLength + 1);
if (isString) msg = bufferToString(msg);
buffers.push(msg);
bufferTail = bufferTail.slice(msgLength + 1);
}
var total = buffers.length;
buffers.forEach(function(buffer, i) {
callback(exports.decodePacket(buffer, binaryType, true), i, total);
});
};

View File

@@ -0,0 +1,19 @@
/**
* Gets the keys for an object.
*
* @return {Array} keys
* @api private
*/
module.exports = Object.keys || function keys (obj){
var arr = [];
var has = Object.prototype.hasOwnProperty;
for (var i in obj) {
if (has.call(obj, i)) {
arr.push(i);
}
}
return arr;
};

View File

@@ -0,0 +1,2 @@
node_modules
.monitor

View File

@@ -0,0 +1,5 @@
language: node_js
node_js:
- 0.6
- 0.8
- 0.9

View File

@@ -0,0 +1,19 @@
Copyright (c) 2011 Raynos.
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.

View File

@@ -0,0 +1,75 @@
# After [![Build Status][1]][2]
Invoke callback after n calls
## Status: production ready
## Example
var after = require("after")
, next = after(3, logItWorks)
next()
next()
next() // it works
function logItWorks() {
console.log("it works!")
}
## Example with error handling
var after = require("after")
, next = after(3, logError)
next()
next(new Error("oops")) // logs oops
next() // does nothing
function logError(err) {
console.log(err)
}
## After < 0.6.0
Older versions of after had iterators and flows in them.
These have been replaced with seperate modules
- [iterators][8]
- [composite][9]
## Installation
`npm install after`
## Tests
`npm test`
## Blog post
- [Flow control in node.js][3]
## Examples :
- [Determining the end of asynchronous operations][4]
- [In javascript what are best practices for executing multiple asynchronous functions][5]
- [JavaScript performance long running tasks][6]
- [Synchronous database queries with node.js][7]
## Contributors
- Raynos
## MIT Licenced
[1]: https://secure.travis-ci.org/Raynos/after.png
[2]: http://travis-ci.org/Raynos/after
[3]: http://raynos.org/blog/2/Flow-control-in-node.js
[4]: http://stackoverflow.com/questions/6852059/determining-the-end-of-asynchronous-operations-javascript/6852307#6852307
[5]: http://stackoverflow.com/questions/6869872/in-javascript-what-are-best-practices-for-executing-multiple-asynchronous-functi/6870031#6870031
[6]: http://stackoverflow.com/questions/6864397/javascript-performance-long-running-tasks/6889419#6889419
[7]: http://stackoverflow.com/questions/6597493/synchronous-database-queries-with-node-js/6620091#6620091
[8]: http://github.com/Raynos/iterators
[9]: http://github.com/Raynos/composite

View File

@@ -0,0 +1,28 @@
module.exports = after
function after(count, callback, err_cb) {
var bail = false
err_cb = err_cb || noop
proxy.count = count
return (count === 0) ? callback() : proxy
function proxy(err, result) {
if (proxy.count <= 0) {
throw new Error('after called too many times')
}
--proxy.count
// after first error, rest are passed to err_cb
if (err) {
bail = true
callback(err)
// future error callbacks will go to error handler
callback = err_cb
} else if (proxy.count === 0 && !bail) {
callback(null, result)
}
}
}
function noop() {}

View File

@@ -0,0 +1,60 @@
{
"name": "after",
"description": "after - tiny flow control",
"version": "0.8.1",
"author": {
"name": "Raynos",
"email": "raynos2@gmail.com"
},
"contributors": [
{
"name": "Raynos",
"email": "raynos2@gmail.com",
"url": "http://raynos.org"
}
],
"scripts": {
"test": "mocha --ui tdd --reporter spec test/*.js"
},
"devDependencies": {
"mocha": "~1.8.1"
},
"keywords": [
"flowcontrol",
"after",
"flow",
"control",
"arch"
],
"repository": {
"type": "git",
"url": "git://github.com/Raynos/after.git"
},
"bugs": {
"url": "https://github.com/Raynos/after/issues"
},
"_id": "after@0.8.1",
"dist": {
"shasum": "ab5d4fb883f596816d3515f8f791c0af486dd627",
"tarball": "http://registry.npmjs.org/after/-/after-0.8.1.tgz"
},
"_from": "after@0.8.1",
"_npmVersion": "1.2.25",
"_npmUser": {
"name": "raynos",
"email": "raynos2@gmail.com"
},
"maintainers": [
{
"name": "raynos",
"email": "raynos2@gmail.com"
},
{
"name": "shtylman",
"email": "shtylman@gmail.com"
}
],
"directories": {},
"_shasum": "ab5d4fb883f596816d3515f8f791c0af486dd627",
"_resolved": "https://registry.npmjs.org/after/-/after-0.8.1.tgz"
}

View File

@@ -0,0 +1,120 @@
/*global suite, test*/
var assert = require("assert")
, after = require("../")
test("exists", function () {
assert(typeof after === "function", "after is not a function")
})
test("after when called with 0 invokes", function (done) {
after(0, done)
});
test("after 1", function (done) {
var next = after(1, done)
next()
})
test("after 5", function (done) {
var next = after(5, done)
, i = 5
while (i--) {
next()
}
})
test("manipulate count", function (done) {
var next = after(1, done)
, i = 5
next.count = i
while (i--) {
next()
}
})
test("after terminates on error", function (done) {
var next = after(2, function(err) {
assert.equal(err.message, 'test');
done();
})
next(new Error('test'))
next(new Error('test2'))
})
test('gee', function(done) {
done = after(2, done)
function cb(err) {
assert.equal(err.message, 1);
done()
}
var next = after(3, cb, function(err) {
assert.equal(err.message, 2)
done()
});
next()
next(new Error(1))
next(new Error(2))
})
test('eee', function(done) {
done = after(3, done)
function cb(err) {
assert.equal(err.message, 1);
done()
}
var next = after(3, cb, function(err) {
assert.equal(err.message, 2)
done()
});
next(new Error(1))
next(new Error(2))
next(new Error(2))
})
test('gge', function(done) {
function cb(err) {
assert.equal(err.message, 1);
done()
}
var next = after(3, cb, function(err) {
// should not happen
assert.ok(false);
});
next()
next()
next(new Error(1))
})
test('egg', function(done) {
function cb(err) {
assert.equal(err.message, 1);
done()
}
var next = after(3, cb, function(err) {
// should not happen
assert.ok(false);
});
next(new Error(1))
next()
next()
})
test('throws on too many calls', function(done) {
var next = after(1, done);
next()
assert.throws(next, /after called too many times/);
});

View File

@@ -0,0 +1,17 @@
lib-cov
lcov.info
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
build
.grunt
node_modules

View File

@@ -0,0 +1,8 @@
REPORTER = dot
test:
@./node_modules/.bin/mocha \
--reporter $(REPORTER)
.PHONY: test

View File

@@ -0,0 +1,17 @@
# How to
```javascript
var sliceBuffer = require('arraybuffer.slice');
var ab = (new Int8Array(5)).buffer;
var sliced = sliceBuffer(ab, 1, 3);
sliced = sliceBuffer(ab, 1);
```
# Licence (MIT)
Copyright (C) 2013 Rase-
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.

View File

@@ -0,0 +1,29 @@
/**
* An abstraction for slicing an arraybuffer even when
* ArrayBuffer.prototype.slice is not supported
*
* @api public
*/
module.exports = function(arraybuffer, start, end) {
var bytes = arraybuffer.byteLength;
start = start || 0;
end = end || bytes;
if (arraybuffer.slice) { return arraybuffer.slice(start, end); }
if (start < 0) { start += bytes; }
if (end < 0) { end += bytes; }
if (end > bytes) { end = bytes; }
if (start >= bytes || start >= end || bytes === 0) {
return new ArrayBuffer(0);
}
var abv = new Uint8Array(arraybuffer);
var result = new Uint8Array(end - start);
for (var i = start, ii = 0; i < end; i++, ii++) {
result[ii] = abv[i];
}
return result.buffer;
};

View File

@@ -0,0 +1,38 @@
{
"name": "arraybuffer.slice",
"description": "Exports a function for slicing ArrayBuffers (no polyfilling)",
"version": "0.0.6",
"homepage": "https://github.com/rase-/arraybuffer.slice",
"dependencies": {},
"devDependencies": {
"mocha": "1.17.1",
"expect.js": "0.2.0"
},
"repository": {
"type": "git",
"url": "git@github.com:rase-/arraybuffer.slice.git"
},
"bugs": {
"url": "https://github.com/rase-/arraybuffer.slice/issues"
},
"_id": "arraybuffer.slice@0.0.6",
"dist": {
"shasum": "f33b2159f0532a3f3107a272c0ccfbd1ad2979ca",
"tarball": "http://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz"
},
"_from": "arraybuffer.slice@0.0.6",
"_npmVersion": "1.3.5",
"_npmUser": {
"name": "rase-",
"email": "tonykovanen@hotmail.com"
},
"maintainers": [
{
"name": "rase-",
"email": "tonykovanen@hotmail.com"
}
],
"directories": {},
"_shasum": "f33b2159f0532a3f3107a272c0ccfbd1ad2979ca",
"_resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz"
}

View File

@@ -0,0 +1,227 @@
/*
* Test dependencies
*/
var sliceBuffer = require('../index.js');
var expect = require('expect.js');
/**
* Tests
*/
describe('sliceBuffer', function() {
describe('using standard slice', function() {
it('should slice correctly with only start provided', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, 3);
var sabv = new Uint8Array(sliced);
for (var i = 3, ii = 0; i < abv.length; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with start and end provided', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, 3, 8);
var sabv = new Uint8Array(sliced);
for (var i = 3, ii = 0; i < 8; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with negative start', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, -3);
var sabv = new Uint8Array(sliced);
for (var i = abv.length - 3, ii = 0; i < abv.length; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with negative end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, 0, -3);
var sabv = new Uint8Array(sliced);
for (var i = 0, ii = 0; i < abv.length - 3; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with negative start and end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, -6, -3);
var sabv = new Uint8Array(sliced);
for (var i = abv.length - 6, ii = 0; i < abv.length - 3; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with equal start and end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, 1, 1);
expect(sliced.byteLength).to.equal(0);
});
it('should slice correctly when end larger than buffer', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, 0, 100);
expect(new Uint8Array(sliced)).to.eql(abv);
});
it('shoud slice correctly when start larger than end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var sliced = sliceBuffer(abv.buffer, 6, 5);
expect(sliced.byteLength).to.equal(0);
});
});
describe('using fallback', function() {
it('should slice correctly with only start provided', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, 3);
var sabv = new Uint8Array(sliced);
for (var i = 3, ii = 0; i < abv.length; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with start and end provided', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, 3, 8);
var sabv = new Uint8Array(sliced);
for (var i = 3, ii = 0; i < 8; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with negative start', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, -3);
var sabv = new Uint8Array(sliced);
for (var i = abv.length - 3, ii = 0; i < abv.length; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with negative end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, 0, -3);
var sabv = new Uint8Array(sliced);
for (var i = 0, ii = 0; i < abv.length - 3; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with negative start and end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, -6, -3);
var sabv = new Uint8Array(sliced);
for (var i = abv.length - 6, ii = 0; i < abv.length - 3; i++, ii++) {
expect(abv[i]).to.equal(sabv[ii]);
}
});
it('should slice correctly with equal start and end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, 1, 1);
expect(sliced.byteLength).to.equal(0);
});
it('should slice correctly when end larger than buffer', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, 0, 100);
var sabv = new Uint8Array(sliced);
for (var i = 0; i < abv.length; i++) {
expect(abv[i]).to.equal(sabv[i]);
}
});
it('shoud slice correctly when start larger than end', function() {
var abv = new Uint8Array(10);
for (var i = 0; i < abv.length; i++) {
abv[i] = i;
}
var ab = abv.buffer;
ab.slice = undefined;
var sliced = sliceBuffer(ab, 6, 5);
expect(sliced.byteLength).to.equal(0);
});
});
});

View File

@@ -0,0 +1,6 @@
---
language: node_js
node_js:
- '0.10'
before_script:
- npm install

View File

@@ -0,0 +1,22 @@
Copyright (c) 2012 Niklas von Hertzen
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.

View File

@@ -0,0 +1,23 @@
# base64-arraybuffer
[![Build Status](https://travis-ci.org/niklasvh/base64-arraybuffer.png)](https://travis-ci.org/niklasvh/base64-arraybuffer)
Encode/decode base64 data into ArrayBuffers
## Getting Started
Install the module with: `npm install base64-arraybuffer`
## API
The library encodes and decodes base64 to and from ArrayBuffers
- __encode(buffer)__ - Encodes `ArrayBuffer` into base64 string
- __decode(str)__ - Decodes base64 string to `ArrayBuffer`
## Release History
- 0.1.2 - Fix old format of typed arrays
- 0.1.0 - Initial version, basic decode/encode base64 to and from ArrayBuffer
## License
Copyright (c) 2012 Niklas von Hertzen
Licensed under the MIT license.

View File

@@ -0,0 +1,23 @@
# base64-arraybuffer
[![Build Status](https://travis-ci.org/niklasvh/base64-arraybuffer.png)](https://travis-ci.org/niklasvh/base64-arraybuffer)
Encode/decode base64 data into ArrayBuffers
## Getting Started
Install the module with: `npm install base64-arraybuffer`
## API
The library encodes and decodes base64 to and from ArrayBuffers
- __encode(buffer)__ - Encodes `ArrayBuffer` into base64 string
- __decode(str)__ - Decodes base64 string to `ArrayBuffer`
## Release History
- 0.1.2 - Fix old format of typed arrays
- 0.1.0 - Initial version, basic decode/encode base64 to and from ArrayBuffer
## License
Copyright (c) 2012 Niklas von Hertzen
Licensed under the MIT license.

View File

@@ -0,0 +1,39 @@
module.exports = function(grunt) {
"use strict";
// Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
test: {
files: ['test/**/*.js']
},
lint: {
files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js']
},
watch: {
files: '<config:lint.files>',
tasks: 'default'
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
boss: true,
eqnull: true,
node: true
},
globals: {
exports: true
}
}
});
// Default task.
grunt.registerTask('default', 'test');
};

View File

@@ -0,0 +1,59 @@
/*
* base64-arraybuffer
* https://github.com/niklasvh/base64-arraybuffer
*
* Copyright (c) 2012 Niklas von Hertzen
* Licensed under the MIT license.
*/
(function(chars){
"use strict";
exports.encode = function(arraybuffer) {
var bytes = new Uint8Array(arraybuffer),
i, len = bytes.length, base64 = "";
for (i = 0; i < len; i+=3) {
base64 += chars[bytes[i] >> 2];
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
base64 += chars[bytes[i + 2] & 63];
}
if ((len % 3) === 2) {
base64 = base64.substring(0, base64.length - 1) + "=";
} else if (len % 3 === 1) {
base64 = base64.substring(0, base64.length - 2) + "==";
}
return base64;
};
exports.decode = function(base64) {
var bufferLength = base64.length * 0.75,
len = base64.length, i, p = 0,
encoded1, encoded2, encoded3, encoded4;
if (base64[base64.length - 1] === "=") {
bufferLength--;
if (base64[base64.length - 2] === "=") {
bufferLength--;
}
}
var arraybuffer = new ArrayBuffer(bufferLength),
bytes = new Uint8Array(arraybuffer);
for (i = 0; i < len; i+=4) {
encoded1 = chars.indexOf(base64[i]);
encoded2 = chars.indexOf(base64[i+1]);
encoded3 = chars.indexOf(base64[i+2]);
encoded4 = chars.indexOf(base64[i+3]);
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
}
return arraybuffer;
};
})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");

View File

@@ -0,0 +1,55 @@
{
"name": "base64-arraybuffer",
"description": "Encode/decode base64 data into ArrayBuffers",
"version": "0.1.2",
"homepage": "https://github.com/niklasvh/base64-arraybuffer",
"author": {
"name": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
"url": "http://hertzen.com"
},
"repository": {
"type": "git",
"url": "https://github.com/niklasvh/base64-arraybuffer"
},
"bugs": {
"url": "https://github.com/niklasvh/base64-arraybuffer/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/niklasvh/base64-arraybuffer/blob/master/LICENSE-MIT"
}
],
"main": "lib/base64-arraybuffer",
"engines": {
"node": ">= 0.6.0"
},
"scripts": {
"test": "grunt test"
},
"devDependencies": {
"grunt": "~0.3.17"
},
"keywords": [],
"_id": "base64-arraybuffer@0.1.2",
"dist": {
"shasum": "474df4a9f2da24e05df3158c3b1db3c3cd46a154",
"tarball": "http://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.2.tgz"
},
"_from": "base64-arraybuffer@0.1.2",
"_npmVersion": "1.3.21",
"_npmUser": {
"name": "niklasvh",
"email": "niklasvh@gmail.com"
},
"maintainers": [
{
"name": "niklasvh",
"email": "niklasvh@gmail.com"
}
],
"directories": {},
"_shasum": "474df4a9f2da24e05df3158c3b1db3c3cd46a154",
"_resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.2.tgz"
}

View File

@@ -0,0 +1,35 @@
{
"name": "base64-arraybuffer",
"description": "Encode/decode base64 data into ArrayBuffers",
"version": "0.1.1",
"homepage": "https://github.com/niklasvh/base64-arraybuffer",
"author": {
"name": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
"url": "http://hertzen.com"
},
"repository": {
"type": "git",
"url": "https://github.com/niklasvh/base64-arraybuffer"
},
"bugs": {
"url": "https://github.com/niklasvh/base64-arraybuffer/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/niklasvh/base64-arraybuffer/blob/master/LICENSE-MIT"
}
],
"main": "lib/base64-arraybuffer",
"engines": {
"node": ">= 0.6.0"
},
"scripts": {
"test": "grunt test"
},
"devDependencies": {
"grunt": "~0.3.17"
},
"keywords": []
}

View File

@@ -0,0 +1,72 @@
(function(){
"use strict";
var base64_arraybuffer = require('../lib/base64-arraybuffer.js');
/*
======== A Handy Little Nodeunit Reference ========
https://github.com/caolan/nodeunit
Test methods:
test.expect(numAssertions)
test.done()
Test assertions:
test.ok(value, [message])
test.equal(actual, expected, [message])
test.notEqual(actual, expected, [message])
test.deepEqual(actual, expected, [message])
test.notDeepEqual(actual, expected, [message])
test.strictEqual(actual, expected, [message])
test.notStrictEqual(actual, expected, [message])
test.throws(block, [error], [message])
test.doesNotThrow(block, [error], [message])
test.ifError(value)
*/
function stringArrayBuffer(str) {
var buffer = new ArrayBuffer(str.length);
var bytes = new Uint8Array(buffer);
str.split('').forEach(function(str, i) {
bytes[i] = str.charCodeAt(0);
});
return buffer;
}
function testArrayBuffers(buffer1, buffer2) {
var len1 = buffer1.byteLength,
len2 = buffer2.byteLength;
if (len1 !== len2) {
console.log(buffer1, buffer2);
return false;
}
for (var i = 0; i < len1; i++) {
if (buffer1[i] !== buffer1[i]) {
console.log(i, buffer1, buffer2);
return false;
}
}
return true;
}
exports['base64tests'] = {
'encode': function(test) {
test.expect(4);
test.equal(base64_arraybuffer.encode(stringArrayBuffer("Hello world")), "SGVsbG8gd29ybGQ=", 'encode "Hello world"');
test.equal(base64_arraybuffer.encode(stringArrayBuffer("Man")), 'TWFu', 'encode "Man"');
test.equal(base64_arraybuffer.encode(stringArrayBuffer("Ma")), "TWE=", 'encode "Ma"');
test.equal(base64_arraybuffer.encode(stringArrayBuffer("Hello worlds!")), "SGVsbG8gd29ybGRzIQ==", 'encode "Hello worlds!"');
test.done();
},
'decode': function(test) {
test.expect(3);
test.ok(testArrayBuffers(base64_arraybuffer.decode("TWFu"), stringArrayBuffer("Man")), 'decode "Man"');
test.ok(testArrayBuffers(base64_arraybuffer.decode("SGVsbG8gd29ybGQ="), stringArrayBuffer("Hello world")), 'decode "Hello world"');
test.ok(testArrayBuffers(base64_arraybuffer.decode("SGVsbG8gd29ybGRzIQ=="), stringArrayBuffer("Hello worlds!")), 'decode "Hello worlds!"');
test.done();
}
};
})();

View File

@@ -0,0 +1,2 @@
node_modules
blob.js

View File

@@ -0,0 +1,8 @@
ui: mocha-bdd
browsers:
- name: chrome
version: [8, latest]
- name: ie
version: 10
- name: android
version: latest

View File

@@ -0,0 +1,14 @@
REPORTER = dot
build: blob.js
blob.js:
@./node_modules/.bin/browserify --standalone blob index.js > blob.js
test:
@./node_modules/.bin/zuul -- test/index.js
clean:
rm blob.js
.PHONY: test blob.js

View File

@@ -0,0 +1,11 @@
Blob
====
A module that exports a constructor that uses window.Blob when available, and a BlobBuilder with any vendor prefix in other cases. If neither is available, it exports undefined.
Usage:
```javascript
var Blob = require('blob');
var b = new Blob(['hi', 'constructing', 'a', 'blob']);
```

View File

@@ -0,0 +1,49 @@
/**
* Create a blob builder even when vendor prefixes exist
*/
var BlobBuilder = global.BlobBuilder
|| global.WebKitBlobBuilder
|| global.MSBlobBuilder
|| global.MozBlobBuilder;
/**
* Check if Blob constructor is supported
*/
var blobSupported = (function() {
try {
var b = new Blob(['hi']);
return b.size == 2;
} catch(e) {
return false;
}
})();
/**
* Check if BlobBuilder is supported
*/
var blobBuilderSupported = BlobBuilder
&& BlobBuilder.prototype.append
&& BlobBuilder.prototype.getBlob;
function BlobBuilderConstructor(ary, options) {
options = options || {};
var bb = new BlobBuilder();
for (var i = 0; i < ary.length; i++) {
bb.append(ary[i]);
}
return (options.type) ? bb.getBlob(options.type) : bb.getBlob();
};
module.exports = (function() {
if (blobSupported) {
return global.Blob;
} else if (blobBuilderSupported) {
return BlobBuilderConstructor;
} else {
return undefined;
}
})();

View File

@@ -0,0 +1,43 @@
{
"name": "blob",
"description": "Abstracts out Blob and uses BlobBulder in cases where it is supported with any vendor prefix.",
"version": "0.0.2",
"homepage": "https://github.com/rase-/blob",
"dependencies": {},
"devDependencies": {
"mocha": "1.17.1",
"expect.js": "0.2.0",
"zuul": "1.5.4",
"browserify": "3.30.1"
},
"repository": {
"type": "git",
"url": "git@github.com:rase-/blob.git"
},
"scripts": {
"test": "make test"
},
"bugs": {
"url": "https://github.com/rase-/blob/issues"
},
"_id": "blob@0.0.2",
"dist": {
"shasum": "b89562bd6994af95ba1e812155536333aa23cf24",
"tarball": "http://registry.npmjs.org/blob/-/blob-0.0.2.tgz"
},
"_from": "blob@0.0.2",
"_npmVersion": "1.3.5",
"_npmUser": {
"name": "rase-",
"email": "tonykovanen@hotmail.com"
},
"maintainers": [
{
"name": "rase-",
"email": "tonykovanen@hotmail.com"
}
],
"directories": {},
"_shasum": "b89562bd6994af95ba1e812155536333aa23cf24",
"_resolved": "https://registry.npmjs.org/blob/-/blob-0.0.2.tgz"
}

View File

@@ -0,0 +1,52 @@
var Blob = require('../');
var expect = require('expect.js');
if (!Blob) {
return;
}
describe('blob', function() {
it('should encode a proper sized blob when given a string argument', function() {
var b = new Blob(['hi']);
expect(b.size).to.be(2);
});
it('should encode a blob with proper size when given two strings as arguments', function() {
var b = new Blob(['hi', 'hello']);
expect(b.size).to.be(7);
});
it('should encode arraybuffers with right content', function() {
var ary = new Uint8Array(5);
for (var i = 0; i < 5; i++) ary[i] = i;
var b = new Blob([ary.buffer]);
var fr = new FileReader();
fr.onload = function() {
var newAry = new Uint8Array(this.result);
for (var i = 0; i < 5; i++) expect(newAry[i]).to.be(i);
};
});
it('should encode with blobs', function() {
var ary = new Uint8Array(5);
for (var i = 0; i < 5; i++) ary[i] = i;
var b = new Blob([new Blob([ary.buffer])]);
var fr = new FileReader();
fr.onload = function() {
var newAry = new Uint8Array(this.result);
for (var i = 0; i < 5; i++) expect(newAry[i]).to.be(i);
};
});
it('should enode mixed contents to right size', function() {
var ary = new Uint8Array(5);
for (var i = 0; i < 5; i++) ary[i] = i;
var b = new Blob([ary.buffer, 'hello']);
expect(b.size).to.be(10);
});
it('should accept mime type', function() {
var b = new Blob(['hi', 'hello'], { type: 'text/html' });
expect(b.type).to.be('text/html');
});
});

View File

@@ -0,0 +1,15 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
npm-debug.log
node_modules

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