Added logging, changed some directory structure

This commit is contained in:
2018-01-13 21:33:40 -05:00
parent f079a5f067
commit 8e72ffb917
73656 changed files with 35284 additions and 53718 deletions

View File

@@ -0,0 +1,6 @@
.gitignore
lib/.placeholder
VERSION-GEN
src
node_modules
*~

View File

@@ -0,0 +1,6 @@
Parts of the code are derived from various open source projects.
For code derived from Socket.IO by Guillermo Rauch see
https://github.com/LearnBoost/socket.io/tree/0.6.17#readme.
All other code is released on MIT license, see LICENSE.

View File

@@ -0,0 +1,257 @@
0.3.18
======
* Change to using `res.statusCode` instead of manual parsing of `res._header` #213
* Update sockjs-protocol filename in README #203
0.3.17
======
* Fix usage of undefined `session` in `heartbeat_timeout` #179
0.3.16
======
* Fix CORS response for null origin #177
* Add websocket ping-pong and close if no response #129, #162, #169
* Update sockjs-client version in examples #182
* Add koa example #180
* Disable raw websocket endpoint when websocket = false #183
* Upgrade to faye-websocket 0.10.0 and use proper close code
* When connection is aborted, don't delay the teardown
* Forward additional headers #188
* Add `no-transform` to Cache-Control headers #189
* Update documentation about heartbeats #192
0.3.15
======
* Remove usage of naked '@' function params to be compatible with coffeescript 1.9.0 #175
0.3.14
======
* Re-publish to npm because of build issue in 0.3.13
0.3.13
======
* Upgrade faye-websocket to 0.9.3 to fix #171
0.3.12
======
* Allow Faye socket constructor options to be passed with
faye_server_options option to createServer
* Fix websocket bad json tests
* Upgrade Faye to allow 0.9.*
0.3.11
======
* #133 - only delay disconnect on non-websocket transports
* Upgrade Faye to 0.8.0
0.3.10
======
* #168 - Add CORS headers for eventsource
* #158 - schedule heartbeat timer even if send_buffer is not empty
* #96 - remove rbytes dependency
* #83 - update documentation for prefix
* #163 - add protection to JSON for SWF exploit
* #104 - delete unused parameters in code
* #106 - update CDN urls
* #79 - Don't remove stream listeners until after end so 'close' event is heard
* Get rid of need for _sockjs_onload global variable
* Use Faye for websocket request validation
* Upgrade Faye to 0.7.3
* Upgrade node-uuid to 1.4.1
0.3.9
=====
* #130 - Set Vary: Origin on CORS requests
* Upgrade Faye to 0.7.2 from 0.7.0
0.3.8
=====
* #118 - Allow servers to specify a base URL in /info
* #131 - Don't look up session id undefined
* #124 - Small grammar updates for ReadMe
* Upgrade Faye to 0.7.0 from 0.4.0
0.3.7
=====
* Expose "protocol" on raw websocket connection instance, correctly
0.3.6
=====
* When the server closes a connection, make sure the send buffer still
gets flushed.
* Expose "protocol" on raw websocket connection instance
* #105, #109, #113 - expose 'host', 'user-agent', and 'accept-language'
headers
* Serve SockJS over https CDN by default
* Upgrade Faye to 0.4.4 from 0.4.0
0.3.5
=====
* #103 - connection.protocol might have been empty on some rare
occasions.
* #99 - faye-websocket was leaking sockets in "closed" state
when dealing with rfc websockets
0.3.4
=====
* #73 - apparently 'package' is a reserved keyword (use 'pkg' instead)
* #93 - Coffescript can leak a variable when the same name is used
in catch statement. Let's always use 'x' as the variable in catch.
* #76 - decorateConnection could throw an error if remote connection
was closed before setup was complete
* #90 - Fix "TypeError: 'addListener'" exception (via @pl).
* remove 'optionalDependencies' section from package.json,
'rbytes' was always optional.
* #91 - Fix rare null exception.
0.3.3
=====
* sockjs/sockjs-protocol#56, #88 Fix for iOS 6 caching POSTs
0.3.1
=====
* #58 - websocket transport emitted an array instead of a string
during onmessage event.
* Running under node.js 0.7 caused infinite recursion (Stephan Kochen)
* #59 - restrict characters allowed in callback parameter
* Updated readme - rbytes package is optional
* Updated readme WRT deployments on heroku
* Add minimalistic license block to every source file.
0.3.0
=====
* Sending JSESSIONID cookie is now *disabled* by default.
* sockjs/sockjs-protocol#46 - introduce new service
required for protocol tests "/cookie_needed_echo"
* Initial work towards better integration with
"connect" (Stephan Kochen). See discusion:
https://github.com/senchalabs/connect/pull/506
* More documentation about the Cookie and Origin headers.
* #51 - expose "readyState" on connection instance
* #53 - expose "protocol" on connection instance
* #52 - Some protocols may not emit 'close' event with IE.
* sockjs/sockjs-client#49 - Support 'null' origin - aka: allow SockJS
client to be served from file:// paths.
0.2.1
=====
* Bumped "faye-websocket" dependency to 0.4. Updated
code to take advantage of introduced changes.
* Pinned "node-static" and bumped "node-uuid" dependencies.
* Removed "Origin" header list of headers exposed to the user.
This header is not really meaningful in sockjs context.
* Header "Access-Control-Allow-Methods" was misspelled.
0.2.0
=====
* #36, #3 - Replace a custom WebSocket server implementation
with faye-websocket-node.
* Multiple changes to support SockJS-protocol 0.2.
* The session is now closed on network errors immediately
(instead of waiting 5 seconds)
* Raw websocket interface available - to make it easier
to write command line SockJS clients.
* Support '/info' url.
* The test server got moved from SockJS-client to SockJS-node.
* Dropped deprecated Server API (use createServer method instead).
* Option `websocket` is now used instead of `disabled_transports`.
0.1.2
=====
* #27 - Allow all unicode characters to be send over SockJS.
* #14 - Make it possible to customize JSESSIONID cookie logic.
0.1.1
=====
* #32 Expose various request headers on connection.
* #30 Expose request path on connection.
0.1.0
=====
* The API changed, there is now an idiomatic API, modelled on node.js
Stream API. The old API is deprecated and there is a dummy wrapper
that emulates it. Please do upgrade to the new idiomatic API.
* #22 Initial support for hybi13 (stephank)
* New options accepted by the `Server` constructor: `log`,
`heartbeat_delay` and `disconnect_delay`.
* SockJS is now not able to send rich data structures - all data
passed to `write` is converted to a string.
* #23 `Connection.remoteAddress` property introduced (Stéphan Kochen)
* Loads of small changes in order to adhere to protocol spec.
0.0.5
=====
* #20: `npm submodule sockjs` didn't work due to outdated github
path.
0.0.4
=====
* Support for htmlfile transport, used by IE in a deployment
dependent on cookies.
* Added /chunking_test API, used to detect support for HTTP chunking
on client side.
* Unified code logic for all the chunking transports - the same code
is reused for polling versions.
* All the chunking transports are closed by the server after 128K was
send, in order to force client to GC and reconnect.
* Don't distribute source coffeescript with npm.
* Minor fixes in websocket code.
* Dropped jQuery dependency.
* Unicode encoding could been garbled during XHR upload.
* Other minor fixes.
0.0.3
======
* EventSource transport didn't emit 'close' event.
0.0.2
=====
* By default set JSESSIONID cookie, useful for load balancing.
0.0.1
=====
* Initial release.

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (C) 2011 VMware, Inc.
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,55 @@
.PHONY: all serve clean
COFFEE:=./node_modules/.bin/coffee
#### General
all: build
build: src/*coffee
@$(COFFEE) -v > /dev/null
$(COFFEE) -o lib/ -c src/*.coffee
clean:
rm -f lib/*.js
#### Testing
test_server: build
node tests/test_server/server.js
serve:
@if [ -e .pidfile.pid ]; then \
kill `cat .pidfile.pid`; \
rm .pidfile.pid; \
fi
@while [ 1 ]; do \
make build; \
echo " [*] Running http server"; \
make test_server & \
SRVPID=$$!; \
echo $$SRVPID > .pidfile.pid; \
echo " [*] Server pid: $$SRVPID"; \
inotifywait -r -q -e modify .; \
kill `cat .pidfile.pid`; \
rm -f .pidfile.pid; \
sleep 0.1; \
done
#### Release process
# 1) commit everything
# 2) amend version in package.json
# 3) run 'make tag' and run suggested 'git push' variants
# 4) run 'npm publish'
RVER:=$(shell grep "version" package.json|tr '\t"' ' \t'|cut -f 4)
VER:=$(shell ./VERSION-GEN)
.PHONY: tag
tag: all
git commit $(TAG_OPTS) package.json Changelog -m "Release $(RVER)"
git tag v$(RVER) -m "Release $(RVER)"
@echo ' [*] Now run'
@echo 'git push; git push --tag'

View File

@@ -0,0 +1,443 @@
[![NPM version](https://badge.fury.io/js/sockjs.svg)](http://badge.fury.io/js/sockjs)
SockJS family:
* [SockJS-client](https://github.com/sockjs/sockjs-client) JavaScript client library
* [SockJS-node](https://github.com/sockjs/sockjs-node) Node.js server
* [SockJS-erlang](https://github.com/sockjs/sockjs-erlang) Erlang server
* [SockJS-tornado](https://github.com/MrJoes/sockjs-tornado) Python/Tornado server
* [vert.x](https://github.com/eclipse/vert.x) Java/vert.x server
Work in progress:
* [SockJS-ruby](https://github.com/nyarly/sockjs-ruby)
* [SockJS-netty](https://github.com/cgbystrom/sockjs-netty)
* [SockJS-gevent](https://github.com/sdiehl/sockjs-gevent) ([and a fork](https://github.com/njoyce/sockjs-gevent))
* [pyramid-SockJS](https://github.com/fafhrd91/pyramid_sockjs)
* [wildcloud-websockets](https://github.com/wildcloud/wildcloud-websockets)
* [SockJS-cyclone](https://github.com/flaviogrossi/sockjs-cyclone)
* [SockJS-twisted](https://github.com/Fugiman/sockjs-twisted/)
* [wai-SockJS](https://github.com/Palmik/wai-sockjs)
* [SockJS-perl](https://github.com/vti/sockjs-perl)
* [SockJS-go](https://github.com/igm/sockjs-go/)
What is SockJS?
===============
SockJS is a JavaScript library (for browsers) that provides a WebSocket-like
object. SockJS gives you a coherent, cross-browser, Javascript API
which creates a low latency, full duplex, cross-domain communication
channel between the browser and the web server, with WebSockets or without.
This necessitates the use of a server, which this is one version of, for Node.js.
SockJS-node server
==================
SockJS-node is a Node.js server side counterpart of
[SockJS-client browser library](https://github.com/sockjs/sockjs-client)
written in CoffeeScript.
To install `sockjs-node` run:
npm install sockjs
A simplified echo SockJS server could look more or less like:
```javascript
var http = require('http');
var sockjs = require('sockjs');
var echo = sockjs.createServer({ sockjs_url: 'http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js' });
echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
conn.on('close', function() {});
});
var server = http.createServer();
echo.installHandlers(server, {prefix:'/echo'});
server.listen(9999, '0.0.0.0');
```
(Take look at
[examples](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
directory for a complete version.)
Subscribe to
[SockJS mailing list](https://groups.google.com/forum/#!forum/sockjs) for
discussions and support.
SockJS-node API
---------------
The API design is based on the common Node API's like
[Streams API](http://nodejs.org/docs/v0.5.8/api/streams.html) or
[Http.Server API](http://nodejs.org/docs/v0.5.8/api/http.html#http.Server).
### Server class
SockJS module is generating a `Server` class, similar to
[Node.js http.createServer](http://nodejs.org/docs/v0.5.8/api/http.html#http.createServer)
module.
```javascript
var sockjs_server = sockjs.createServer(options);
```
Where `options` is a hash which can contain:
<dl>
<dt>sockjs_url (string, required)</dt>
<dd>Transports which don't support cross-domain communication natively
('eventsource' to name one) use an iframe trick. A simple page is
served from the SockJS server (using its foreign domain) and is
placed in an invisible iframe. Code run from this iframe doesn't
need to worry about cross-domain issues, as it's being run from
domain local to the SockJS server. This iframe also does need to
load SockJS javascript client library, and this option lets you specify
its url (if you're unsure, point it to
<a href="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js">
the latest minified SockJS client release</a>, this is the default).
You must explicitly specify this url on the server side for security
reasons - we don't want the possibility of running any foreign
javascript within the SockJS domain (aka cross site scripting attack).
Also, sockjs javascript library is probably already cached by the
browser - it makes sense to reuse the sockjs url you're using in
normally.</dd>
<dt>prefix (string regex)</dt>
<dd>A url prefix for the server. All http requests which paths begins
with selected prefix will be handled by SockJS. All other requests
will be passed through, to previously registered handlers.</dd>
<dt>response_limit (integer)</dt>
<dd>Most streaming transports save responses on the client side and
don't free memory used by delivered messages. Such transports need
to be garbage-collected once in a while. `response_limit` sets
a minimum number of bytes that can be send over a single http streaming
request before it will be closed. After that client needs to open
new request. Setting this value to one effectively disables
streaming and will make streaming transports to behave like polling
transports. The default value is 128K.</dd>
<dt>websocket (boolean)</dt>
<dd>Some load balancers don't support websockets. This option can be used
to disable websockets support by the server. By default websockets are
enabled.</dd>
<dt>jsessionid (boolean or function)</dt>
<dd>Some hosting providers enable sticky sessions only to requests that
have JSESSIONID cookie set. This setting controls if the server should
set this cookie to a dummy value. By default setting JSESSIONID cookie
is disabled. More sophisticated behaviour can be achieved by supplying
a function.</dd>
<dt>log (function(severity, message))</dt>
<dd>It's quite useful, especially for debugging, to see some messages
printed by a SockJS-node library. This is done using this `log`
function, which is by default set to `console.log`. If this
behaviour annoys you for some reason, override `log` setting with a
custom handler. The following `severities` are used: `debug`
(miscellaneous logs), `info` (requests logs), `error` (serious
errors, consider filing an issue).</dd>
<dt>heartbeat_delay (milliseconds)</dt>
<dd>In order to keep proxies and load balancers from closing long
running http requests we need to pretend that the connection is
active and send a heartbeat packet once in a while. This setting
controls how often this is done. By default a heartbeat packet is
sent every 25 seconds. </dd>
<dt>disconnect_delay (milliseconds)</dt>
<dd>The server sends a `close` event when a client receiving
connection have not been seen for a while. This delay is configured
by this setting. By default the `close` event will be emitted when a
receiving connection wasn't seen for 5 seconds. </dd>
</dl>
### Server instance
Once you have create `Server` instance you can hook it to the
[http.Server instance](http://nodejs.org/docs/v0.5.8/api/http.html#http.createServer).
```javascript
var http_server = http.createServer();
sockjs_server.installHandlers(http_server, options);
http_server.listen(...);
```
Where `options` can overshadow options given when creating `Server`
instance.
`Server` instance is an
[EventEmitter](http://nodejs.org/docs/v0.4.10/api/events.html#events.EventEmitter),
and emits following event:
<dl>
<dt>Event: connection (connection)</dt>
<dd>A new connection has been successfully opened.</dd>
</dl>
All http requests that don't go under the path selected by `prefix`
will remain unanswered and will be passed to previously registered
handlers. You must install your custom http handlers before calling
`installHandlers`.
### Connection instance
A `Connection` instance supports
[Node Stream API](http://nodejs.org/docs/v0.5.8/api/streams.html) and
has following methods and properties:
<dl>
<dt>Property: readable (boolean)</dt>
<dd>Is the stream readable?</dd>
<dt>Property: writable (boolean)</dt>
<dd>Is the stream writable?</dd>
<dt>Property: remoteAddress (string)</dt>
<dd>Last known IP address of the client.</dd>
<dt>Property: remotePort (number)</dt>
<dd>Last known port number of the client.</dd>
<dt>Property: address (object)</dt>
<dd>Hash with 'address' and 'port' fields.</dd>
<dt>Property: headers (object)</dt>
<dd>Hash containing various headers copied from last receiving request
on that connection. Exposed headers include: `origin`, `referer`
and `x-forwarded-for` (and friends). We explicitly do not grant
access to `cookie` header, as using it may easily lead to security
issues (for details read the section "Authorisation").</dd>
<dt>Property: url (string)</dt>
<dd><a href="http://nodejs.org/docs/v0.4.10/api/http.html#request.url">Url</a>
property copied from last request.</dd>
<dt>Property: pathname (string)</dt>
<dd>`pathname` from parsed url, for convenience.</dd>
<dt>Property: prefix (string)</dt>
<dd>Prefix of the url on which the request was handled.</dd>
<dt>Property: protocol (string)</dt>
<dd>Protocol used by the connection. Keep in mind that some protocols
are indistinguishable - for example "xhr-polling" and "xdr-polling".</dd>
<dt>Property: readyState (integer)</dt>
<dd>Current state of the connection:
0-connecting, 1-open, 2-closing, 3-closed.</dd>
<dt>write(message)</dt>
<dd>Sends a message over opened connection. A message must be a
non-empty string. It's illegal to send a message after the connection was
closed (either after 'close' or 'end' method or 'close' event).</dd>
<dt>close([code], [reason])</dt>
<dd>Asks the remote client to disconnect. 'code' and 'reason'
parameters are optional and can be used to share the reason of
disconnection.</dd>
<dt>end()</dt>
<dd>Asks the remote client to disconnect with default 'code' and
'reason' values.</dd>
</dl>
A `Connection` instance emits the following events:
<dl>
<dt>Event: data (message)</dt>
<dd>A message arrived on the connection. Message is a unicode
string.</dd>
<dt>Event: close ()</dt>
<dd>Connection was closed. This event is triggered exactly once for
every connection.</dd>
</dl>
For example:
```javascript
sockjs_server.on('connection', function(conn) {
console.log('connection' + conn);
conn.on('close', function() {
console.log('close ' + conn);
});
conn.on('data', function(message) {
console.log('message ' + conn,
message);
});
});
```
### Footnote
A fully working echo server does need a bit more boilerplate (to
handle requests unanswered by SockJS), see the
[`echo` example](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
for a complete code.
### Examples
If you want to see samples of running code, take a look at:
* [./examples/echo](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
directory, which contains a full example of a echo server.
* [./examples/test_server](https://github.com/sockjs/sockjs-node/tree/master/examples/test_server) a standard SockJS test server.
Connecting to SockJS-node without the client
--------------------------------------------
Although the main point of SockJS it to enable browser-to-server
connectivity, it is possible to connect to SockJS from an external
application. Any SockJS server complying with 0.3 protocol does
support a raw WebSocket url. The raw WebSocket url for the test server
looks like:
* ws://localhost:8081/echo/websocket
You can connect any WebSocket RFC 6455 compliant WebSocket client to
this url. This can be a command line client, external application,
third party code or even a browser (though I don't know why you would
want to do so).
Note: This endpoint will *not send any heartbeat packets*.
Deployment and load balancing
-----------------------------
There are two issues that need to be considered when planning a
non-trivial SockJS-node deployment: WebSocket-compatible load balancer
and sticky sessions (aka session affinity).
### WebSocket compatible load balancer
Often WebSockets don't play nicely with proxies and load balancers.
Deploying a SockJS server behind Nginx or Apache could be painful.
Fortunately recent versions of an excellent load balancer
[HAProxy](http://haproxy.1wt.eu/) are able to proxy WebSocket
connections. We propose to put HAProxy as a front line load balancer
and use it to split SockJS traffic from normal HTTP data. Take a look
at the sample
[SockJS HAProxy configuration](https://github.com/sockjs/sockjs-node/blob/master/examples/haproxy.cfg).
The config also shows how to use HAproxy balancing to split traffic
between multiple Node.js servers. You can also do balancing using dns
names.
### Sticky sessions
If you plan deploying more than one SockJS server, you must make sure
that all HTTP requests for a single session will hit the same server.
SockJS has two mechanisms that can be useful to achieve that:
* Urls are prefixed with server and session id numbers, like:
`/resource/<server_number>/<session_id>/transport`. This is
useful for load balancers that support prefix-based affinity
(HAProxy does).
* `JSESSIONID` cookie is being set by SockJS-node. Many load
balancers turn on sticky sessions if that cookie is set. This
technique is derived from Java applications, where sticky sessions
are often necessary. HAProxy does support this method, as well as
some hosting providers, for example CloudFoundry. In order to
enable this method on the client side, please supply a
`cookie:true` option to SockJS constructor.
Development and testing
-----------------------
If you want to work on SockJS-node source code, you need to clone the
git repo and follow these steps. First you need to install
dependencies:
cd sockjs-node
npm install
npm install --dev
ln -s .. node_modules/sockjs
You're ready to compile CoffeeScript:
make build
If compilation succeeds you may want to test if your changes pass all
the tests. Currently, there are two separate test suites. For both of
them you need to start a SockJS-node test server (by default listening
on port 8081):
make test_server
### SockJS-protocol Python tests
To run it run something like:
cd sockjs-protocol
make test_deps
./venv/bin/python sockjs-protocol.py
For details see
[SockJS-protocol README](https://github.com/sockjs/sockjs-protocol#readme).
### SockJS-client QUnit tests
You need to start a second web server (by default listening on 8080)
that is serving various static html and javascript files:
cd sockjs-client
make test
At that point you should have two web servers running: sockjs-node on
8081 and sockjs-client on 8080. When you open the browser on
[http://localhost:8080/](http://localhost:8080/) you should be able
run the QUnit tests against your sockjs-node server.
For details see
[SockJS-client README](https://github.com/sockjs/sockjs-client#readme).
Additionally, if you're doing more serious development consider using
`make serve`, which will automatically the server when you modify the
source code.
Various issues and design considerations
----------------------------------------
### Authorisation
SockJS-node does not expose cookies to the application. This is done
deliberately as using cookie-based authorisation with SockJS simply
doesn't make sense and will lead to security issues.
Cookies are a contract between a browser and an http server, and are
identified by a domain name. If a browser has a cookie set for
particular domain, it will pass it as a part of all http requests to
the host. But to get various transports working, SockJS uses a middleman
- an iframe hosted from target SockJS domain. That means the server
will receive requests from the iframe, and not from the real
domain. The domain of an iframe is the same as the SockJS domain. The
problem is that any website can embed the iframe and communicate with
it - and request establishing SockJS connection. Using cookies for
authorisation in this scenario will result in granting full access to
SockJS communication with your website from any website. This is a
classic CSRF attack.
Basically - cookies are not suited for SockJS model. If you want to
authorise a session - provide a unique token on a page, send it as a
first thing over SockJS connection and validate it on the server
side. In essence, this is how cookies work.
### Deploying SockJS on Heroku
Long polling is known to cause problems on Heroku, but
[workaround for SockJS is available](https://github.com/sockjs/sockjs-node/issues/57#issuecomment-5242187).

View File

@@ -0,0 +1,15 @@
SockJS-node Echo example
========================
To run this example, first install dependencies:
npm install
And run a server:
node server.js
That will spawn an http server at http://127.0.0.1:9999/ which will
serve both html (served from the current directory) and also SockJS
server (under the [/echo](http://127.0.0.1:9999/echo) path).

View File

@@ -0,0 +1,71 @@
<!doctype html>
<html><head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
<style>
.box {
width: 300px;
float: left;
margin: 0 20px 0 20px;
}
.box div, .box input {
border: 1px solid;
-moz-border-radius: 4px;
border-radius: 4px;
width: 100%;
padding: 0px;
margin: 5px;
}
.box div {
border-color: grey;
height: 300px;
overflow: auto;
}
.box input {
height: 30px;
}
h1 {
margin-left: 30px;
}
body {
background-color: #F0F0F0;
font-family: "Arial";
}
</style>
</head><body lang="en">
<h1>SockJS Echo example</h1>
<div id="first" class="box">
<div></div>
<form><input autocomplete="off" value="Type here..."></input></form>
</div>
<script>
var sockjs_url = '/echo';
var sockjs = new SockJS(sockjs_url);
$('#first input').focus();
var div = $('#first div');
var inp = $('#first input');
var form = $('#first form');
var print = function(m, p) {
p = (p === undefined) ? '' : JSON.stringify(p);
div.append($("<code>").text(m + ' ' + p));
div.append($("<br>"));
div.scrollTop(div.scrollTop()+10000);
};
sockjs.onopen = function() {print('[*] open', sockjs.protocol);};
sockjs.onmessage = function(e) {print('[.] message', e.data);};
sockjs.onclose = function() {print('[*] close');};
form.submit(function() {
print('[ ] sending', inp.val());
sockjs.send(inp.val());
inp.val('');
return false;
});
</script>
</body></html>

View File

@@ -0,0 +1,8 @@
{
"name": "sockjs-echo",
"version": "0.0.0-unreleasable",
"dependencies": {
"node-static": "0.5.9",
"sockjs": "*"
}
}

View File

@@ -0,0 +1,30 @@
var http = require('http');
var sockjs = require('sockjs');
var node_static = require('node-static');
// 1. Echo sockjs server
var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var sockjs_echo = sockjs.createServer(sockjs_opts);
sockjs_echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
});
// 2. Static files server
var static_directory = new node_static.Server(__dirname);
// 3. Usual http stuff
var server = http.createServer();
server.addListener('request', function(req, res) {
static_directory.serve(req, res);
});
server.addListener('upgrade', function(req,res){
res.end();
});
sockjs_echo.installHandlers(server, {prefix:'/echo'});
console.log(' [*] Listening on 0.0.0.0:9999' );
server.listen(9999, '0.0.0.0');

View File

@@ -0,0 +1,71 @@
<!doctype html>
<html><head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
<style>
.box {
width: 300px;
float: left;
margin: 0 20px 0 20px;
}
.box div, .box input {
border: 1px solid;
-moz-border-radius: 4px;
border-radius: 4px;
width: 100%;
padding: 0px;
margin: 5px;
}
.box div {
border-color: grey;
height: 300px;
overflow: auto;
}
.box input {
height: 30px;
}
h1 {
margin-left: 30px;
}
body {
background-color: #F0F0F0;
font-family: "Arial";
}
</style>
</head><body lang="en">
<h1>SockJS Express example</h1>
<div id="first" class="box">
<div></div>
<form><input autocomplete="off" value="Type here..."></input></form>
</div>
<script>
var sockjs_url = '/echo';
var sockjs = new SockJS(sockjs_url);
$('#first input').focus();
var div = $('#first div');
var inp = $('#first input');
var form = $('#first form');
var print = function(m, p) {
p = (p === undefined) ? '' : JSON.stringify(p);
div.append($("<code>").text(m + ' ' + p));
div.append($("<br>"));
div.scrollTop(div.scrollTop()+10000);
};
sockjs.onopen = function() {print('[*] open', sockjs.protocol);};
sockjs.onmessage = function(e) {print('[.] message', e.data);};
sockjs.onclose = function() {print('[*] close');};
form.submit(function() {
print('[ ] sending', inp.val());
sockjs.send(inp.val());
inp.val('');
return false;
});
</script>
</body></html>

View File

@@ -0,0 +1,8 @@
{
"name": "sockjs-express",
"version": "0.0.0-unreleasable",
"dependencies": {
"express": "~3*",
"sockjs": "*"
}
}

View File

@@ -0,0 +1,26 @@
var express = require('express');
var sockjs = require('sockjs');
var http = require('http');
// 1. Echo sockjs server
var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var sockjs_echo = sockjs.createServer(sockjs_opts);
sockjs_echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
});
// 2. Express server
var app = express(); /* express.createServer will not work here */
var server = http.createServer(app);
sockjs_echo.installHandlers(server, {prefix:'/echo'});
console.log(' [*] Listening on 0.0.0.0:9999' );
server.listen(9999, '0.0.0.0');
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});

View File

@@ -0,0 +1,71 @@
<!doctype html>
<html><head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
<style>
.box {
width: 300px;
float: left;
margin: 0 20px 0 20px;
}
.box div, .box input {
border: 1px solid;
-moz-border-radius: 4px;
border-radius: 4px;
width: 100%;
padding: 0px;
margin: 5px;
}
.box div {
border-color: grey;
height: 300px;
overflow: auto;
}
.box input {
height: 30px;
}
h1 {
margin-left: 30px;
}
body {
background-color: #F0F0F0;
font-family: "Arial";
}
</style>
</head><body lang="en">
<h1>SockJS Express example</h1>
<div id="first" class="box">
<div></div>
<form><input autocomplete="off" value="Type here..."></input></form>
</div>
<script>
var sockjs_url = '/echo';
var sockjs = new SockJS(sockjs_url);
$('#first input').focus();
var div = $('#first div');
var inp = $('#first input');
var form = $('#first form');
var print = function(m, p) {
p = (p === undefined) ? '' : JSON.stringify(p);
div.append($("<code>").text(m + ' ' + p));
div.append($("<br>"));
div.scrollTop(div.scrollTop()+10000);
};
sockjs.onopen = function() {print('[*] open', sockjs.protocol);};
sockjs.onmessage = function(e) {print('[.] message', e.data);};
sockjs.onclose = function() {print('[*] close');};
form.submit(function() {
print('[ ] sending', inp.val());
sockjs.send(inp.val());
inp.val('');
return false;
});
</script>
</body></html>

View File

@@ -0,0 +1,8 @@
{
"name": "sockjs-express",
"version": "0.0.0-unreleasable",
"dependencies": {
"express": "<3",
"sockjs": "*"
}
}

View File

@@ -0,0 +1,23 @@
var express = require('express');
var sockjs = require('sockjs');
// 1. Echo sockjs server
var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var sockjs_echo = sockjs.createServer(sockjs_opts);
sockjs_echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
});
// 2. Express server
var app = express.createServer();
sockjs_echo.installHandlers(app, {prefix:'/echo'});
console.log(' [*] Listening on 0.0.0.0:9999' );
app.listen(9999, '0.0.0.0');
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});

View File

@@ -0,0 +1,71 @@
<!doctype html>
<html><head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
<style>
.box {
width: 300px;
float: left;
margin: 0 20px 0 20px;
}
.box div, .box input {
border: 1px solid;
-moz-border-radius: 4px;
border-radius: 4px;
width: 100%;
padding: 0px;
margin: 5px;
}
.box div {
border-color: grey;
height: 300px;
overflow: auto;
}
.box input {
height: 30px;
}
h1 {
margin-left: 30px;
}
body {
background-color: #F0F0F0;
font-family: "Arial";
}
</style>
</head><body lang="en">
<h1>SockJS Echo example</h1>
<div id="first" class="box">
<div></div>
<form><input autocomplete="off" value="Type here..."></input></form>
</div>
<script>
var sockjs_url = '/echo';
var sockjs = new SockJS(sockjs_url);
$('#first input').focus();
var div = $('#first div');
var inp = $('#first input');
var form = $('#first form');
var print = function(m, p) {
p = (p === undefined) ? '' : JSON.stringify(p);
div.append($("<code>").text(m + ' ' + p));
div.append($("<br>"));
div.scrollTop(div.scrollTop()+10000);
};
sockjs.onopen = function() {print('[*] open', sockjs.protocol);};
sockjs.onmessage = function(e) {print('[.] message', e.data);};
sockjs.onclose = function() {print('[*] close');};
form.submit(function() {
print('[ ] sending', inp.val());
sockjs.send(inp.val());
inp.val('');
return false;
});
</script>
</body></html>

View File

@@ -0,0 +1,8 @@
{
"name": "sockjs-hapi",
"version": "0.0.0-unreleasable",
"dependencies": {
"hapi": "*",
"sockjs": "*"
}
}

View File

@@ -0,0 +1,33 @@
/**
* Created by Tony on 11/1/13.
*/
var http = require('http');
var sockjs = require('sockjs');
var Hapi = require('hapi');
// 1. Echo sockjs server
var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var sockjs_echo = sockjs.createServer(sockjs_opts);
sockjs_echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
});
// Create a server with a host and port
var hapi_server = Hapi.createServer('0.0.0.0', 9999);
hapi_server.route({
method: 'GET',
path: '/{path*}',
handler: {
directory: { path: './html', listing: false, index: true }
}
});
//hapi_server.listener is the http listener hapi uses
sockjs_echo.installHandlers(hapi_server.listener, {prefix:'/echo'});
console.log(' [*] Listening on 0.0.0.0:9999' );
hapi_server.start();

View File

@@ -0,0 +1,42 @@
# Requires recent Haproxy to work with websockets (for example 1.4.16).
defaults
mode http
# Set timeouts to your needs
timeout client 5s
timeout connect 5s
timeout server 5s
frontend all 0.0.0.0:8888
mode http
timeout client 120s
option forwardfor
# Fake connection:close, required in this setup.
option http-server-close
option http-pretend-keepalive
acl is_sockjs path_beg /echo /broadcast /close
acl is_stats path_beg /stats
use_backend sockjs if is_sockjs
use_backend stats if is_stats
default_backend static
backend sockjs
# Load-balance according to hash created from first two
# directories in url path. For example requests going to /1/
# should be handled by single server (assuming resource prefix is
# one-level deep, like "/echo").
balance uri depth 2
timeout server 120s
server srv_sockjs1 127.0.0.1:9999
# server srv_sockjs2 127.0.0.1:9998
backend static
balance roundrobin
server srv_static 127.0.0.1:8000
backend stats
stats uri /stats
stats enable

View File

@@ -0,0 +1,71 @@
<!doctype html>
<html><head>
<script src="//cdn.jsdelivr.net/jquery/2.1.4/jquery.min.js"></script>
<script src="//cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
<style>
.box {
width: 300px;
float: left;
margin: 0 20px 0 20px;
}
.box div, .box input {
border: 1px solid;
-moz-border-radius: 4px;
border-radius: 4px;
width: 100%;
padding: 0px;
margin: 5px;
}
.box div {
border-color: grey;
height: 300px;
overflow: auto;
}
.box input {
height: 30px;
}
h1 {
margin-left: 30px;
}
body {
background-color: #F0F0F0;
font-family: "Arial";
}
</style>
</head><body lang="en">
<h1>SockJS Express example</h1>
<div id="first" class="box">
<div></div>
<form><input autocomplete="off" value="Type here..."></input></form>
</div>
<script>
var sockjs_url = '/echo';
var sockjs = new SockJS(sockjs_url);
$('#first input').focus();
var div = $('#first div');
var inp = $('#first input');
var form = $('#first form');
var print = function(m, p) {
p = (p === undefined) ? '' : JSON.stringify(p);
div.append($("<code>").text(m + ' ' + p));
div.append($("<br>"));
div.scrollTop(div.scrollTop()+10000);
};
sockjs.onopen = function() {print('[*] open', sockjs.protocol);};
sockjs.onmessage = function(e) {print('[.] message', e.data);};
sockjs.onclose = function() {print('[*] close');};
form.submit(function() {
print('[ ] sending', inp.val());
sockjs.send(inp.val());
inp.val('');
return false;
});
</script>
</body></html>

View File

@@ -0,0 +1,8 @@
{
"name": "sockjs-koa",
"version": "0.0.0-unreleasable",
"dependencies": {
"koa": "^0.21.0",
"sockjs": "*"
}
}

View File

@@ -0,0 +1,29 @@
var koa = require('koa');
var sockjs = require('sockjs');
var http = require('http');
var fs = require('fs');
var path = require('path');
// 1. Echo sockjs server
var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var sockjs_echo = sockjs.createServer(sockjs_opts);
sockjs_echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
});
// 2. koa server
var app = koa();
app.use(function *() {
var filePath = __dirname + '/index.html';
this.type = path.extname(filePath);
this.body = fs.createReadStream(filePath);
});
var server = http.createServer(app.callback());
sockjs_echo.installHandlers(server, {prefix:'/echo'});
server.listen(9999, '0.0.0.0');
console.log(' [*] Listening on 0.0.0.0:9999' );

View File

@@ -0,0 +1,26 @@
WebSocket-multiplex SockJS example
==================================
This example is a copy of example from
[websocket-multiplex](https://github.com/sockjs/websocket-multiplex/)
project:
* https://github.com/sockjs/websocket-multiplex/
To run this example, first install dependencies:
npm install
And run a server:
node server.js
That will spawn an http server at http://127.0.0.1:9999/ which will
serve both html (served from the current directory) and also SockJS
service (under the [/multiplex](http://127.0.0.1:9999/multiplex)
path).
With that set up, WebSocket-multiplex is able to push three virtual
connections over a single SockJS connection. See the code for details.

View File

@@ -0,0 +1,96 @@
<!doctype html>
<html><head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
<script src="http://cdn.sockjs.org/websocket-multiplex-0.1.js"></script>
<style>
.box {
width: 300px;
float: left;
margin: 0 20px 0 20px;
}
.box div, .box input {
border: 1px solid;
-moz-border-radius: 4px;
border-radius: 4px;
width: 100%;
padding: 0px;
margin: 5px;
}
.box div {
border-color: grey;
height: 300px;
overflow: auto;
}
.box input {
height: 30px;
}
h1 {
margin-left: 75px;
}
body {
background-color: #F0F0F0;
font-family: "Arial";
}
</style>
</head><body lang="en">
<h1>SockJS Multiplex example</h1>
<div id="first" class="box">
<div></div>
<form><input autocomplete="off" value="Type here..."></input></form>
</div>
<div id="second" class="box">
<div></div>
<form><input autocomplete="off"></input></form>
</div>
<div id="third" class="box">
<div></div>
<form><input autocomplete="off"></input></form>
</div>
<script>
// Pipe - convenience wrapper to present data received from an
// object supporting WebSocket API in an html element. And the other
// direction: data typed into an input box shall be sent back.
var pipe = function(ws, el_name) {
var div = $(el_name + ' div');
var inp = $(el_name + ' input');
var form = $(el_name + ' form');
var print = function(m, p) {
p = (p === undefined) ? '' : JSON.stringify(p);
div.append($("<code>").text(m + ' ' + p));
div.append($("<br>"));
div.scrollTop(div.scrollTop() + 10000);
};
ws.onopen = function() {print('[*] open', ws.protocol);};
ws.onmessage = function(e) {print('[.] message', e.data);};
ws.onclose = function() {print('[*] close');};
form.submit(function() {
print('[ ] sending', inp.val());
ws.send(inp.val());
inp.val('');
return false;
});
};
var sockjs_url = '/multiplex';
var sockjs = new SockJS(sockjs_url);
var multiplexer = new WebSocketMultiplex(sockjs);
var ann = multiplexer.channel('ann');
var bob = multiplexer.channel('bob');
var carl = multiplexer.channel('carl');
pipe(ann, '#first');
pipe(bob, '#second');
pipe(carl, '#third');
$('#first input').focus();
</script>
</body></html>

View File

@@ -0,0 +1,9 @@
{
"name": "sockjs-multiplex",
"version": "0.0.0-unreleasable",
"dependencies": {
"express": "2.5.8",
"sockjs": "*",
"websocket-multiplex" : "0.1.x"
}
}

View File

@@ -0,0 +1,52 @@
var express = require('express');
var sockjs = require('sockjs');
var websocket_multiplex = require('websocket-multiplex');
// 1. Setup SockJS server
var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var service = sockjs.createServer(sockjs_opts);
// 2. Setup multiplexing
var multiplexer = new websocket_multiplex.MultiplexServer(service);
var ann = multiplexer.registerChannel('ann');
ann.on('connection', function(conn) {
conn.write('Ann says hi!');
conn.on('data', function(data) {
conn.write('Ann nods: ' + data);
});
});
var bob = multiplexer.registerChannel('bob');
bob.on('connection', function(conn) {
conn.write('Bob doesn\'t agree.');
conn.on('data', function(data) {
conn.write('Bob says no to: ' + data);
});
});
var carl = multiplexer.registerChannel('carl');
carl.on('connection', function(conn) {
conn.write('Carl says goodbye!');
// Explicitly cancel connection
conn.end();
});
// 3. Express server
var app = express.createServer();
service.installHandlers(app, {prefix:'/multiplex'});
console.log(' [*] Listening on 0.0.0.0:9999' );
app.listen(9999, '0.0.0.0');
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
app.get('/multiplex.js', function (req, res) {
res.sendfile(__dirname + '/multiplex.js');
});

View File

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

View File

@@ -0,0 +1,95 @@
// Generated by CoffeeScript 1.11.0
(function() {
var utils;
utils = require('./utils');
exports.app = {
chunking_test: function(req, res, _, next_filter) {
var write;
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
res.writeHead(200);
write = (function(_this) {
return function(payload) {
var x;
try {
return res.write(payload + '\n');
} catch (error) {
x = error;
}
};
})(this);
utils.timeout_chain([
[
0, (function(_this) {
return function() {
return write('h');
};
})(this)
], [
1, (function(_this) {
return function() {
return write(Array(2049).join(' ') + 'h');
};
})(this)
], [
5, (function(_this) {
return function() {
return write('h');
};
})(this)
], [
25, (function(_this) {
return function() {
return write('h');
};
})(this)
], [
125, (function(_this) {
return function() {
return write('h');
};
})(this)
], [
625, (function(_this) {
return function() {
return write('h');
};
})(this)
], [
3125, (function(_this) {
return function() {
write('h');
return res.end();
};
})(this)
]
]);
return true;
},
info: function(req, res, _) {
var info;
info = {
websocket: this.options.websocket,
origins: ['*:*'],
cookie_needed: !!this.options.jsessionid,
entropy: utils.random32()
};
if (typeof this.options.base_url === 'function') {
info.base_url = this.options.base_url();
} else if (this.options.base_url) {
info.base_url = this.options.base_url;
}
res.setHeader('Content-Type', 'application/json; charset=UTF-8');
res.writeHead(200);
return res.end(JSON.stringify(info));
},
info_options: function(req, res) {
res.statusCode = 204;
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
res.setHeader('Access-Control-Max-Age', res.cache_for);
return '';
}
};
}).call(this);

View File

@@ -0,0 +1,30 @@
// Generated by CoffeeScript 1.11.0
(function() {
var iframe_template, utils;
utils = require('./utils');
iframe_template = "<!DOCTYPE html>\n<html>\n<head>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n <script src=\"{{ sockjs_url }}\"></script>\n <script>\n document.domain = document.domain;\n SockJS.bootstrap_iframe();\n </script>\n</head>\n<body>\n <h2>Don't panic!</h2>\n <p>This is a SockJS hidden iframe. It's used for cross domain magic.</p>\n</body>\n</html>";
exports.app = {
iframe: function(req, res) {
var content, context, k, quoted_md5;
context = {
'{{ sockjs_url }}': this.options.sockjs_url
};
content = iframe_template;
for (k in context) {
content = content.replace(k, context[k]);
}
quoted_md5 = '"' + utils.md5_hex(content) + '"';
if ('if-none-match' in req.headers && req.headers['if-none-match'] === quoted_md5) {
res.statusCode = 304;
return '';
}
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
res.setHeader('ETag', quoted_md5);
return content;
}
};
}).call(this);

View File

@@ -0,0 +1,233 @@
// Generated by CoffeeScript 1.11.0
(function() {
var App, Listener, Server, chunking_test, events, fs, generate_dispatcher, iframe, sockjsVersion, trans_eventsource, trans_htmlfile, trans_jsonp, trans_websocket, trans_xhr, utils, webjs,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
events = require('events');
fs = require('fs');
webjs = require('./webjs');
utils = require('./utils');
trans_websocket = require('./trans-websocket');
trans_jsonp = require('./trans-jsonp');
trans_xhr = require('./trans-xhr');
iframe = require('./iframe');
trans_eventsource = require('./trans-eventsource');
trans_htmlfile = require('./trans-htmlfile');
chunking_test = require('./chunking-test');
sockjsVersion = function() {
var pkg, x;
try {
pkg = fs.readFileSync(__dirname + '/../package.json', 'utf-8');
} catch (error) {
x = error;
}
if (pkg) {
return JSON.parse(pkg).version;
} else {
return null;
}
};
App = (function(superClass) {
extend(App, superClass);
function App() {
return App.__super__.constructor.apply(this, arguments);
}
App.prototype.welcome_screen = function(req, res) {
res.setHeader('content-type', 'text/plain; charset=UTF-8');
res.writeHead(200);
res.end("Welcome to SockJS!\n");
return true;
};
App.prototype.handle_404 = function(req, res) {
res.setHeader('content-type', 'text/plain; charset=UTF-8');
res.writeHead(404);
res.end('404 Error: Page not found\n');
return true;
};
App.prototype.disabled_transport = function(req, res, data) {
return this.handle_404(req, res, data);
};
App.prototype.h_sid = function(req, res, data) {
var jsid;
req.cookies = utils.parseCookie(req.headers.cookie);
if (typeof this.options.jsessionid === 'function') {
this.options.jsessionid(req, res);
} else if (this.options.jsessionid && res.setHeader) {
jsid = req.cookies['JSESSIONID'] || 'dummy';
res.setHeader('Set-Cookie', 'JSESSIONID=' + jsid + '; path=/');
}
return data;
};
App.prototype.log = function(severity, line) {
return this.options.log(severity, line);
};
return App;
})(webjs.GenericApp);
utils.objectExtend(App.prototype, iframe.app);
utils.objectExtend(App.prototype, chunking_test.app);
utils.objectExtend(App.prototype, trans_websocket.app);
utils.objectExtend(App.prototype, trans_jsonp.app);
utils.objectExtend(App.prototype, trans_xhr.app);
utils.objectExtend(App.prototype, trans_eventsource.app);
utils.objectExtend(App.prototype, trans_htmlfile.app);
generate_dispatcher = function(options) {
var opts_filters, p, prefix_dispatcher, t, transport_dispatcher;
p = (function(_this) {
return function(s) {
return new RegExp('^' + options.prefix + s + '[/]?$');
};
})(this);
t = (function(_this) {
return function(s) {
return [p('/([^/.]+)/([^/.]+)' + s), 'server', 'session'];
};
})(this);
opts_filters = function(options_filter) {
if (options_filter == null) {
options_filter = 'xhr_options';
}
return ['h_sid', 'xhr_cors', 'cache_for', options_filter, 'expose'];
};
prefix_dispatcher = [['GET', p(''), ['welcome_screen']], ['GET', p('/iframe[0-9-.a-z_]*.html'), ['iframe', 'cache_for', 'expose']], ['OPTIONS', p('/info'), opts_filters('info_options')], ['GET', p('/info'), ['xhr_cors', 'h_no_cache', 'info', 'expose']], ['OPTIONS', p('/chunking_test'), opts_filters()], ['POST', p('/chunking_test'), ['xhr_cors', 'expect_xhr', 'chunking_test']]];
transport_dispatcher = [['GET', t('/jsonp'), ['h_sid', 'h_no_cache', 'jsonp']], ['POST', t('/jsonp_send'), ['h_sid', 'h_no_cache', 'expect_form', 'jsonp_send']], ['POST', t('/xhr'), ['h_sid', 'h_no_cache', 'xhr_cors', 'xhr_poll']], ['OPTIONS', t('/xhr'), opts_filters()], ['POST', t('/xhr_send'), ['h_sid', 'h_no_cache', 'xhr_cors', 'expect_xhr', 'xhr_send']], ['OPTIONS', t('/xhr_send'), opts_filters()], ['POST', t('/xhr_streaming'), ['h_sid', 'h_no_cache', 'xhr_cors', 'xhr_streaming']], ['OPTIONS', t('/xhr_streaming'), opts_filters()], ['GET', t('/eventsource'), ['h_sid', 'h_no_cache', 'eventsource']], ['GET', t('/htmlfile'), ['h_sid', 'h_no_cache', 'htmlfile']]];
if (options.websocket) {
prefix_dispatcher.push(['GET', p('/websocket'), ['raw_websocket']]);
transport_dispatcher.push(['GET', t('/websocket'), ['sockjs_websocket']]);
} else {
prefix_dispatcher.push(['GET', p('/websocket'), ['cache_for', 'disabled_transport']]);
transport_dispatcher.push(['GET', t('/websocket'), ['cache_for', 'disabled_transport']]);
}
return prefix_dispatcher.concat(transport_dispatcher);
};
Listener = (function() {
function Listener(options1, emit) {
this.options = options1;
this.handler = bind(this.handler, this);
this.app = new App();
this.app.options = this.options;
this.app.emit = emit;
this.app.log('debug', 'SockJS v' + sockjsVersion() + ' ' + 'bound to ' + JSON.stringify(this.options.prefix));
this.dispatcher = generate_dispatcher(this.options);
this.webjs_handler = webjs.generateHandler(this.app, this.dispatcher);
this.path_regexp = new RegExp('^' + this.options.prefix + '([/].+|[/]?)$');
}
Listener.prototype.handler = function(req, res, extra) {
if (!req.url.match(this.path_regexp)) {
return false;
}
this.webjs_handler(req, res, extra);
return true;
};
Listener.prototype.getHandler = function() {
return (function(_this) {
return function(a, b, c) {
return _this.handler(a, b, c);
};
})(this);
};
return Listener;
})();
Server = (function(superClass) {
extend(Server, superClass);
function Server(user_options) {
this.options = {
prefix: '',
response_limit: 128 * 1024,
websocket: true,
faye_server_options: null,
jsessionid: false,
heartbeat_delay: 25000,
disconnect_delay: 5000,
log: function(severity, line) {
return console.log(line);
},
sockjs_url: 'https://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js'
};
if (user_options) {
utils.objectExtend(this.options, user_options);
}
}
Server.prototype.listener = function(handler_options) {
var options;
options = utils.objectExtend({}, this.options);
if (handler_options) {
utils.objectExtend(options, handler_options);
}
return new Listener(options, (function(_this) {
return function() {
return _this.emit.apply(_this, arguments);
};
})(this));
};
Server.prototype.installHandlers = function(http_server, handler_options) {
var handler;
handler = this.listener(handler_options).getHandler();
utils.overshadowListeners(http_server, 'request', handler);
utils.overshadowListeners(http_server, 'upgrade', handler);
return true;
};
Server.prototype.middleware = function(handler_options) {
var handler;
handler = this.listener(handler_options).getHandler();
handler.upgrade = handler;
return handler;
};
return Server;
})(events.EventEmitter);
exports.createServer = function(options) {
return new Server(options);
};
exports.listen = function(http_server, options) {
var srv;
srv = exports.createServer(options);
if (http_server) {
srv.installHandlers(http_server);
}
return srv;
};
}).call(this);

View File

@@ -0,0 +1,53 @@
// Generated by CoffeeScript 1.11.0
(function() {
var EventSourceReceiver, transport, utils,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
utils = require('./utils');
transport = require('./transport');
EventSourceReceiver = (function(superClass) {
extend(EventSourceReceiver, superClass);
function EventSourceReceiver() {
return EventSourceReceiver.__super__.constructor.apply(this, arguments);
}
EventSourceReceiver.prototype.protocol = "eventsource";
EventSourceReceiver.prototype.doSendFrame = function(payload) {
var data;
data = ['data: ', utils.escape_selected(payload, '\r\n\x00'), '\r\n\r\n'];
return EventSourceReceiver.__super__.doSendFrame.call(this, data.join(''));
};
return EventSourceReceiver;
})(transport.ResponseReceiver);
exports.app = {
eventsource: function(req, res) {
var headers, origin;
if (!req.headers['origin'] || req.headers['origin'] === 'null') {
origin = '*';
} else {
origin = req.headers['origin'];
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin');
headers = req.headers['access-control-request-headers'];
if (headers) {
res.setHeader('Access-Control-Allow-Headers', headers);
}
res.writeHead(200);
res.write('\r\n');
transport.register(req, this, new EventSourceReceiver(req, res, this.options));
return true;
}
};
}).call(this);

View File

@@ -0,0 +1,58 @@
// Generated by CoffeeScript 1.11.0
(function() {
var HtmlFileReceiver, iframe_template, transport, utils,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
utils = require('./utils');
transport = require('./transport');
iframe_template = "<!doctype html>\n<html><head>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n</head><body><h2>Don't panic!</h2>\n <script>\n document.domain = document.domain;\n var c = parent.{{ callback }};\n c.start();\n function p(d) {c.message(d);};\n window.onload = function() {c.stop();};\n </script>";
iframe_template += Array(1024 - iframe_template.length + 14).join(' ');
iframe_template += '\r\n\r\n';
HtmlFileReceiver = (function(superClass) {
extend(HtmlFileReceiver, superClass);
function HtmlFileReceiver() {
return HtmlFileReceiver.__super__.constructor.apply(this, arguments);
}
HtmlFileReceiver.prototype.protocol = "htmlfile";
HtmlFileReceiver.prototype.doSendFrame = function(payload) {
return HtmlFileReceiver.__super__.doSendFrame.call(this, '<script>\np(' + JSON.stringify(payload) + ');\n</script>\r\n');
};
return HtmlFileReceiver;
})(transport.ResponseReceiver);
exports.app = {
htmlfile: function(req, res) {
var callback;
if (!('c' in req.query || 'callback' in req.query)) {
throw {
status: 500,
message: '"callback" parameter required'
};
}
callback = 'c' in req.query ? req.query['c'] : req.query['callback'];
if (/[^a-zA-Z0-9-_.]/.test(callback)) {
throw {
status: 500,
message: 'invalid "callback" parameter'
};
}
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
res.writeHead(200);
res.write(iframe_template.replace(/{{ callback }}/g, callback));
transport.register(req, this, new HtmlFileReceiver(req, res, this.options));
return true;
}
};
}).call(this);

View File

@@ -0,0 +1,107 @@
// Generated by CoffeeScript 1.11.0
(function() {
var JsonpReceiver, transport,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
transport = require('./transport');
JsonpReceiver = (function(superClass) {
extend(JsonpReceiver, superClass);
JsonpReceiver.prototype.protocol = "jsonp-polling";
JsonpReceiver.prototype.max_response_size = 1;
function JsonpReceiver(req, res, options, callback1) {
this.callback = callback1;
JsonpReceiver.__super__.constructor.call(this, req, res, options);
}
JsonpReceiver.prototype.doSendFrame = function(payload) {
return JsonpReceiver.__super__.doSendFrame.call(this, "/**/" + this.callback + "(" + JSON.stringify(payload) + ");\r\n");
};
return JsonpReceiver;
})(transport.ResponseReceiver);
exports.app = {
jsonp: function(req, res, _, next_filter) {
var callback;
if (!('c' in req.query || 'callback' in req.query)) {
throw {
status: 500,
message: '"callback" parameter required'
};
}
callback = 'c' in req.query ? req.query['c'] : req.query['callback'];
if (/[^a-zA-Z0-9-_.]/.test(callback) || callback.length > 32) {
throw {
status: 500,
message: 'invalid "callback" parameter'
};
}
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
res.writeHead(200);
transport.register(req, this, new JsonpReceiver(req, res, this.options, callback));
return true;
},
jsonp_send: function(req, res, query) {
var d, i, jsonp, len, message, x;
if (!query) {
throw {
status: 500,
message: 'Payload expected.'
};
}
if (typeof query === 'string') {
try {
d = JSON.parse(query);
} catch (error) {
x = error;
throw {
status: 500,
message: 'Broken JSON encoding.'
};
}
} else {
d = query.d;
}
if (typeof d === 'string' && d) {
try {
d = JSON.parse(d);
} catch (error) {
x = error;
throw {
status: 500,
message: 'Broken JSON encoding.'
};
}
}
if (!d || d.__proto__.constructor !== Array) {
throw {
status: 500,
message: 'Payload expected.'
};
}
jsonp = transport.Session.bySessionId(req.session);
if (jsonp === null) {
throw {
status: 404
};
}
for (i = 0, len = d.length; i < len; i++) {
message = d[i];
jsonp.didMessage(message);
}
res.setHeader('Content-Length', '2');
res.setHeader('Content-Type', 'text/plain; charset=UTF-8');
res.writeHead(200);
res.end('ok');
return true;
}
};
}).call(this);

View File

@@ -0,0 +1,248 @@
// Generated by CoffeeScript 1.11.0
(function() {
var FayeWebsocket, RawWebsocketSessionReceiver, Transport, WebSocketReceiver, transport, utils,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
FayeWebsocket = require('faye-websocket');
utils = require('./utils');
transport = require('./transport');
exports.app = {
_websocket_check: function(req, connection, head) {
if (!FayeWebsocket.isWebSocket(req)) {
throw {
status: 400,
message: 'Not a valid websocket request'
};
}
},
sockjs_websocket: function(req, connection, head) {
var ws;
this._websocket_check(req, connection, head);
ws = new FayeWebsocket(req, connection, head, null, this.options.faye_server_options);
ws.onopen = (function(_this) {
return function() {
return transport.registerNoSession(req, _this, new WebSocketReceiver(ws, connection));
};
})(this);
return true;
},
raw_websocket: function(req, connection, head) {
var ver, ws;
this._websocket_check(req, connection, head);
ver = req.headers['sec-websocket-version'] || '';
if (['8', '13'].indexOf(ver) === -1) {
throw {
status: 400,
message: 'Only supported WebSocket protocol is RFC 6455.'
};
}
ws = new FayeWebsocket(req, connection, head, null, this.options.faye_server_options);
ws.onopen = (function(_this) {
return function() {
return new RawWebsocketSessionReceiver(req, connection, _this, ws);
};
})(this);
return true;
}
};
WebSocketReceiver = (function(superClass) {
extend(WebSocketReceiver, superClass);
WebSocketReceiver.prototype.protocol = "websocket";
function WebSocketReceiver(ws1, connection1) {
var x;
this.ws = ws1;
this.connection = connection1;
try {
this.connection.setKeepAlive(true, 5000);
this.connection.setNoDelay(true);
} catch (error) {
x = error;
}
this.ws.addEventListener('message', (function(_this) {
return function(m) {
return _this.didMessage(m.data);
};
})(this));
this.heartbeat_cb = (function(_this) {
return function() {
return _this.heartbeat_timeout();
};
})(this);
WebSocketReceiver.__super__.constructor.call(this, this.connection);
}
WebSocketReceiver.prototype.setUp = function() {
WebSocketReceiver.__super__.setUp.apply(this, arguments);
return this.ws.addEventListener('close', this.thingy_end_cb);
};
WebSocketReceiver.prototype.tearDown = function() {
this.ws.removeEventListener('close', this.thingy_end_cb);
return WebSocketReceiver.__super__.tearDown.apply(this, arguments);
};
WebSocketReceiver.prototype.didMessage = function(payload) {
var i, len, message, msg, results, x;
if (this.ws && this.session && payload.length > 0) {
try {
message = JSON.parse(payload);
} catch (error) {
x = error;
return this.didClose(3000, 'Broken framing.');
}
if (payload[0] === '[') {
results = [];
for (i = 0, len = message.length; i < len; i++) {
msg = message[i];
results.push(this.session.didMessage(msg));
}
return results;
} else {
return this.session.didMessage(message);
}
}
};
WebSocketReceiver.prototype.doSendFrame = function(payload) {
var x;
if (this.ws) {
try {
this.ws.send(payload);
return true;
} catch (error) {
x = error;
}
}
return false;
};
WebSocketReceiver.prototype.didClose = function(status, reason) {
var x;
if (status == null) {
status = 1000;
}
if (reason == null) {
reason = "Normal closure";
}
WebSocketReceiver.__super__.didClose.apply(this, arguments);
try {
this.ws.close(status, reason, false);
} catch (error) {
x = error;
}
this.ws = null;
return this.connection = null;
};
WebSocketReceiver.prototype.heartbeat = function() {
var hto_ref, supportsHeartbeats;
supportsHeartbeats = this.ws.ping(null, function() {
return clearTimeout(hto_ref);
});
if (supportsHeartbeats) {
return hto_ref = setTimeout(this.heartbeat_cb, 10000);
} else {
return WebSocketReceiver.__super__.heartbeat.apply(this, arguments);
}
};
WebSocketReceiver.prototype.heartbeat_timeout = function() {
if (this.session != null) {
return this.session.close(3000, 'No response from heartbeat');
}
};
return WebSocketReceiver;
})(transport.GenericReceiver);
Transport = transport.Transport;
RawWebsocketSessionReceiver = (function(superClass) {
extend(RawWebsocketSessionReceiver, superClass);
function RawWebsocketSessionReceiver(req, conn, server, ws1) {
this.ws = ws1;
this.prefix = server.options.prefix;
this.readyState = Transport.OPEN;
this.recv = {
connection: conn,
protocol: "websocket-raw"
};
this.connection = new transport.SockJSConnection(this);
this.decorateConnection(req);
server.emit('connection', this.connection);
this._end_cb = (function(_this) {
return function() {
return _this.didClose();
};
})(this);
this.ws.addEventListener('close', this._end_cb);
this._message_cb = (function(_this) {
return function(m) {
return _this.didMessage(m);
};
})(this);
this.ws.addEventListener('message', this._message_cb);
}
RawWebsocketSessionReceiver.prototype.didMessage = function(m) {
if (this.readyState === Transport.OPEN) {
this.connection.emit('data', m.data);
}
};
RawWebsocketSessionReceiver.prototype.send = function(payload) {
if (this.readyState !== Transport.OPEN) {
return false;
}
this.ws.send(payload);
return true;
};
RawWebsocketSessionReceiver.prototype.close = function(status, reason) {
if (status == null) {
status = 1000;
}
if (reason == null) {
reason = "Normal closure";
}
if (this.readyState !== Transport.OPEN) {
return false;
}
this.readyState = Transport.CLOSING;
this.ws.close(status, reason, false);
return true;
};
RawWebsocketSessionReceiver.prototype.didClose = function() {
var x;
if (!this.ws) {
return;
}
this.ws.removeEventListener('message', this._message_cb);
this.ws.removeEventListener('close', this._end_cb);
try {
this.ws.close(1000, "Normal closure", false);
} catch (error) {
x = error;
}
this.ws = null;
this.readyState = Transport.CLOSED;
this.connection.emit('end');
this.connection.emit('close');
return this.connection = null;
};
return RawWebsocketSessionReceiver;
})(transport.Session);
}).call(this);

View File

@@ -0,0 +1,119 @@
// Generated by CoffeeScript 1.11.0
(function() {
var XhrPollingReceiver, XhrStreamingReceiver, transport, utils,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
transport = require('./transport');
utils = require('./utils');
XhrStreamingReceiver = (function(superClass) {
extend(XhrStreamingReceiver, superClass);
function XhrStreamingReceiver() {
return XhrStreamingReceiver.__super__.constructor.apply(this, arguments);
}
XhrStreamingReceiver.prototype.protocol = "xhr-streaming";
XhrStreamingReceiver.prototype.doSendFrame = function(payload) {
return XhrStreamingReceiver.__super__.doSendFrame.call(this, payload + '\n');
};
return XhrStreamingReceiver;
})(transport.ResponseReceiver);
XhrPollingReceiver = (function(superClass) {
extend(XhrPollingReceiver, superClass);
function XhrPollingReceiver() {
return XhrPollingReceiver.__super__.constructor.apply(this, arguments);
}
XhrPollingReceiver.prototype.protocol = "xhr-polling";
XhrPollingReceiver.prototype.max_response_size = 1;
return XhrPollingReceiver;
})(XhrStreamingReceiver);
exports.app = {
xhr_options: function(req, res) {
res.statusCode = 204;
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, POST');
res.setHeader('Access-Control-Max-Age', res.cache_for);
return '';
},
xhr_send: function(req, res, data) {
var d, i, jsonp, len, message, x;
if (!data) {
throw {
status: 500,
message: 'Payload expected.'
};
}
try {
d = JSON.parse(data);
} catch (error) {
x = error;
throw {
status: 500,
message: 'Broken JSON encoding.'
};
}
if (!d || d.__proto__.constructor !== Array) {
throw {
status: 500,
message: 'Payload expected.'
};
}
jsonp = transport.Session.bySessionId(req.session);
if (!jsonp) {
throw {
status: 404
};
}
for (i = 0, len = d.length; i < len; i++) {
message = d[i];
jsonp.didMessage(message);
}
res.setHeader('Content-Type', 'text/plain; charset=UTF-8');
res.writeHead(204);
res.end();
return true;
},
xhr_cors: function(req, res, content) {
var headers, origin;
if (!req.headers['origin']) {
origin = '*';
} else {
origin = req.headers['origin'];
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin');
headers = req.headers['access-control-request-headers'];
if (headers) {
res.setHeader('Access-Control-Allow-Headers', headers);
}
return content;
},
xhr_poll: function(req, res, _, next_filter) {
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
res.writeHead(200);
transport.register(req, this, new XhrPollingReceiver(req, res, this.options));
return true;
},
xhr_streaming: function(req, res, _, next_filter) {
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
res.writeHead(200);
res.write(Array(2049).join('h') + '\n');
transport.register(req, this, new XhrStreamingReceiver(req, res, this.options));
return true;
}
};
}).call(this);

View File

@@ -0,0 +1,438 @@
// Generated by CoffeeScript 1.11.0
(function() {
var GenericReceiver, MAP, ResponseReceiver, Session, SockJSConnection, Transport, closeFrame, register, stream, utils, uuid,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
stream = require('stream');
uuid = require('uuid');
utils = require('./utils');
Transport = (function() {
function Transport() {}
return Transport;
})();
Transport.CONNECTING = 0;
Transport.OPEN = 1;
Transport.CLOSING = 2;
Transport.CLOSED = 3;
closeFrame = function(status, reason) {
return 'c' + JSON.stringify([status, reason]);
};
SockJSConnection = (function(superClass) {
extend(SockJSConnection, superClass);
function SockJSConnection(_session) {
this._session = _session;
this.id = uuid.v4();
this.headers = {};
this.prefix = this._session.prefix;
}
SockJSConnection.prototype.toString = function() {
return '<SockJSConnection ' + this.id + '>';
};
SockJSConnection.prototype.write = function(string) {
return this._session.send('' + string);
};
SockJSConnection.prototype.end = function(string) {
if (string) {
this.write(string);
}
this.close();
return null;
};
SockJSConnection.prototype.close = function(code, reason) {
return this._session.close(code, reason);
};
SockJSConnection.prototype.destroy = function() {
this.end();
return this.removeAllListeners();
};
SockJSConnection.prototype.destroySoon = function() {
return this.destroy();
};
return SockJSConnection;
})(stream.Stream);
SockJSConnection.prototype.__defineGetter__('readable', function() {
return this._session.readyState === Transport.OPEN;
});
SockJSConnection.prototype.__defineGetter__('writable', function() {
return this._session.readyState === Transport.OPEN;
});
SockJSConnection.prototype.__defineGetter__('readyState', function() {
return this._session.readyState;
});
MAP = {};
Session = (function() {
function Session(session_id1, server) {
this.session_id = session_id1;
this.heartbeat_delay = server.options.heartbeat_delay;
this.disconnect_delay = server.options.disconnect_delay;
this.prefix = server.options.prefix;
this.send_buffer = [];
this.is_closing = false;
this.readyState = Transport.CONNECTING;
if (this.session_id) {
MAP[this.session_id] = this;
}
this.timeout_cb = (function(_this) {
return function() {
return _this.didTimeout();
};
})(this);
this.to_tref = setTimeout(this.timeout_cb, this.disconnect_delay);
this.connection = new SockJSConnection(this);
this.emit_open = (function(_this) {
return function() {
_this.emit_open = null;
return server.emit('connection', _this.connection);
};
})(this);
}
Session.prototype.register = function(req, recv) {
if (this.recv) {
recv.doSendFrame(closeFrame(2010, "Another connection still open"));
recv.didClose();
return;
}
if (this.to_tref) {
clearTimeout(this.to_tref);
this.to_tref = null;
}
if (this.readyState === Transport.CLOSING) {
this.flushToRecv(recv);
recv.doSendFrame(this.close_frame);
recv.didClose();
this.to_tref = setTimeout(this.timeout_cb, this.disconnect_delay);
return;
}
this.recv = recv;
this.recv.session = this;
this.decorateConnection(req);
if (this.readyState === Transport.CONNECTING) {
this.recv.doSendFrame('o');
this.readyState = Transport.OPEN;
process.nextTick(this.emit_open);
}
if (!this.recv) {
return;
}
this.tryFlush();
};
Session.prototype.decorateConnection = function(req) {
var address, headers, i, key, len, ref, remoteAddress, remotePort, socket, x;
if (!(socket = this.recv.connection)) {
socket = this.recv.response.connection;
}
try {
remoteAddress = socket.remoteAddress;
remotePort = socket.remotePort;
address = socket.address();
} catch (error) {
x = error;
}
if (remoteAddress) {
this.connection.remoteAddress = remoteAddress;
this.connection.remotePort = remotePort;
this.connection.address = address;
}
this.connection.url = req.url;
this.connection.pathname = req.pathname;
this.connection.protocol = this.recv.protocol;
headers = {};
ref = ['referer', 'x-client-ip', 'x-forwarded-for', 'x-cluster-client-ip', 'via', 'x-real-ip', 'x-forwarded-proto', 'x-ssl', 'host', 'user-agent', 'accept-language'];
for (i = 0, len = ref.length; i < len; i++) {
key = ref[i];
if (req.headers[key]) {
headers[key] = req.headers[key];
}
}
if (headers) {
return this.connection.headers = headers;
}
};
Session.prototype.unregister = function() {
var delay;
delay = this.recv.delay_disconnect;
this.recv.session = null;
this.recv = null;
if (this.to_tref) {
clearTimeout(this.to_tref);
}
if (delay) {
return this.to_tref = setTimeout(this.timeout_cb, this.disconnect_delay);
} else {
return this.timeout_cb();
}
};
Session.prototype.flushToRecv = function(recv) {
var ref, sb;
if (this.send_buffer.length > 0) {
ref = [this.send_buffer, []], sb = ref[0], this.send_buffer = ref[1];
recv.doSendBulk(sb);
return true;
}
return false;
};
Session.prototype.tryFlush = function() {
var x;
if (!this.flushToRecv(this.recv) || !this.to_tref) {
if (this.to_tref) {
clearTimeout(this.to_tref);
}
x = (function(_this) {
return function() {
if (_this.recv) {
_this.to_tref = setTimeout(x, _this.heartbeat_delay);
return _this.recv.heartbeat();
}
};
})(this);
this.to_tref = setTimeout(x, this.heartbeat_delay);
}
};
Session.prototype.didTimeout = function() {
if (this.to_tref) {
clearTimeout(this.to_tref);
this.to_tref = null;
}
if (this.readyState !== Transport.CONNECTING && this.readyState !== Transport.OPEN && this.readyState !== Transport.CLOSING) {
throw Error('INVALID_STATE_ERR');
}
if (this.recv) {
throw Error('RECV_STILL_THERE');
}
this.readyState = Transport.CLOSED;
this.connection.emit('end');
this.connection.emit('close');
this.connection = null;
if (this.session_id) {
delete MAP[this.session_id];
return this.session_id = null;
}
};
Session.prototype.didMessage = function(payload) {
if (this.readyState === Transport.OPEN) {
this.connection.emit('data', payload);
}
};
Session.prototype.send = function(payload) {
if (this.readyState !== Transport.OPEN) {
return false;
}
this.send_buffer.push('' + payload);
if (this.recv) {
this.tryFlush();
}
return true;
};
Session.prototype.close = function(status, reason) {
if (status == null) {
status = 1000;
}
if (reason == null) {
reason = "Normal closure";
}
if (this.readyState !== Transport.OPEN) {
return false;
}
this.readyState = Transport.CLOSING;
this.close_frame = closeFrame(status, reason);
if (this.recv) {
this.recv.doSendFrame(this.close_frame);
if (this.recv) {
this.recv.didClose();
}
if (this.recv) {
this.unregister();
}
}
return true;
};
return Session;
})();
Session.bySessionId = function(session_id) {
if (!session_id) {
return null;
}
return MAP[session_id] || null;
};
register = function(req, server, session_id, receiver) {
var session;
session = Session.bySessionId(session_id);
if (!session) {
session = new Session(session_id, server);
}
session.register(req, receiver);
return session;
};
exports.register = function(req, server, receiver) {
return register(req, server, req.session, receiver);
};
exports.registerNoSession = function(req, server, receiver) {
return register(req, server, void 0, receiver);
};
GenericReceiver = (function() {
function GenericReceiver(thingy) {
this.thingy = thingy;
this.setUp(this.thingy);
}
GenericReceiver.prototype.setUp = function() {
this.thingy_end_cb = (function(_this) {
return function() {
return _this.didAbort();
};
})(this);
this.thingy.addListener('close', this.thingy_end_cb);
return this.thingy.addListener('end', this.thingy_end_cb);
};
GenericReceiver.prototype.tearDown = function() {
this.thingy.removeListener('close', this.thingy_end_cb);
this.thingy.removeListener('end', this.thingy_end_cb);
return this.thingy_end_cb = null;
};
GenericReceiver.prototype.didAbort = function() {
this.delay_disconnect = false;
return this.didClose();
};
GenericReceiver.prototype.didClose = function() {
if (this.thingy) {
this.tearDown(this.thingy);
this.thingy = null;
}
if (this.session) {
return this.session.unregister();
}
};
GenericReceiver.prototype.doSendBulk = function(messages) {
var m, q_msgs;
q_msgs = (function() {
var i, len, results;
results = [];
for (i = 0, len = messages.length; i < len; i++) {
m = messages[i];
results.push(utils.quote(m));
}
return results;
})();
return this.doSendFrame('a' + '[' + q_msgs.join(',') + ']');
};
GenericReceiver.prototype.heartbeat = function() {
return this.doSendFrame('h');
};
return GenericReceiver;
})();
ResponseReceiver = (function(superClass) {
extend(ResponseReceiver, superClass);
ResponseReceiver.prototype.max_response_size = void 0;
ResponseReceiver.prototype.delay_disconnect = true;
function ResponseReceiver(request, response, options) {
var x;
this.request = request;
this.response = response;
this.options = options;
this.curr_response_size = 0;
try {
this.request.connection.setKeepAlive(true, 5000);
} catch (error) {
x = error;
}
ResponseReceiver.__super__.constructor.call(this, this.request.connection);
if (this.max_response_size === void 0) {
this.max_response_size = this.options.response_limit;
}
}
ResponseReceiver.prototype.doSendFrame = function(payload) {
var r, x;
this.curr_response_size += payload.length;
r = false;
try {
this.response.write(payload);
r = true;
} catch (error) {
x = error;
}
if (this.max_response_size && this.curr_response_size >= this.max_response_size) {
this.didClose();
}
return r;
};
ResponseReceiver.prototype.didClose = function() {
var x;
ResponseReceiver.__super__.didClose.apply(this, arguments);
try {
this.response.end();
} catch (error) {
x = error;
}
return this.response = null;
};
return ResponseReceiver;
})(GenericReceiver);
exports.GenericReceiver = GenericReceiver;
exports.Transport = Transport;
exports.Session = Session;
exports.ResponseReceiver = ResponseReceiver;
exports.SockJSConnection = SockJSConnection;
}).call(this);

View File

@@ -0,0 +1,153 @@
// Generated by CoffeeScript 1.11.0
(function() {
var array_intersection, crypto, escapable, lookup, unroll_lookup;
crypto = require('crypto');
exports.array_intersection = array_intersection = function(arr_a, arr_b) {
var a, j, len, r;
r = [];
for (j = 0, len = arr_a.length; j < len; j++) {
a = arr_a[j];
if (arr_b.indexOf(a) !== -1) {
r.push(a);
}
}
return r;
};
exports.escape_selected = function(str, chars) {
var c, i, j, l, len, map, parts, r, ref, v;
map = {};
chars = '%' + chars;
for (j = 0, len = chars.length; j < len; j++) {
c = chars[j];
map[c] = escape(c);
}
r = new RegExp('([' + chars + '])');
parts = str.split(r);
for (i = l = 0, ref = parts.length; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) {
v = parts[i];
if (v.length === 1 && v in map) {
parts[i] = map[v];
}
}
return parts.join('');
};
exports.buffer_concat = function(buf_a, buf_b) {
var dst;
dst = new Buffer(buf_a.length + buf_b.length);
buf_a.copy(dst);
buf_b.copy(dst, buf_a.length);
return dst;
};
exports.md5_hex = function(data) {
return crypto.createHash('md5').update(data).digest('hex');
};
exports.sha1_base64 = function(data) {
return crypto.createHash('sha1').update(data).digest('base64');
};
exports.timeout_chain = function(arr) {
var fun, ref, timeout, user_fun;
arr = arr.slice(0);
if (!arr.length) {
return;
}
ref = arr.shift(), timeout = ref[0], user_fun = ref[1];
fun = (function(_this) {
return function() {
user_fun();
return exports.timeout_chain(arr);
};
})(this);
return setTimeout(fun, timeout);
};
exports.objectExtend = function(dst, src) {
var k;
for (k in src) {
if (src.hasOwnProperty(k)) {
dst[k] = src[k];
}
}
return dst;
};
exports.overshadowListeners = function(ee, event, handler) {
var new_handler, old_listeners;
old_listeners = ee.listeners(event).slice(0);
ee.removeAllListeners(event);
new_handler = function() {
var j, len, listener;
if (handler.apply(this, arguments) !== true) {
for (j = 0, len = old_listeners.length; j < len; j++) {
listener = old_listeners[j];
listener.apply(this, arguments);
}
return false;
}
return true;
};
return ee.addListener(event, new_handler);
};
escapable = /[\x00-\x1f\ud800-\udfff\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufff0-\uffff]/g;
unroll_lookup = function(escapable) {
var c, i, unrolled;
unrolled = {};
c = (function() {
var j, results;
results = [];
for (i = j = 0; j < 65536; i = ++j) {
results.push(String.fromCharCode(i));
}
return results;
})();
escapable.lastIndex = 0;
c.join('').replace(escapable, function(a) {
return unrolled[a] = '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
return unrolled;
};
lookup = unroll_lookup(escapable);
exports.quote = function(string) {
var quoted;
quoted = JSON.stringify(string);
escapable.lastIndex = 0;
if (!escapable.test(quoted)) {
return quoted;
}
return quoted.replace(escapable, function(a) {
return lookup[a];
});
};
exports.parseCookie = function(cookie_header) {
var cookie, cookies, j, len, parts, ref;
cookies = {};
if (cookie_header) {
ref = cookie_header.split(';');
for (j = 0, len = ref.length; j < len; j++) {
cookie = ref[j];
parts = cookie.split('=');
cookies[parts[0].trim()] = (parts[1] || '').trim();
}
}
return cookies;
};
exports.random32 = function() {
var foo, v;
foo = crypto.randomBytes(4);
v = [foo[0], foo[1], foo[2], foo[3]];
return v[0] + (v[1] * 256) + (v[2] * 256 * 256) + (v[3] * 256 * 256 * 256);
};
}).call(this);

View File

@@ -0,0 +1,310 @@
// Generated by CoffeeScript 1.11.0
(function() {
var GenericApp, execute_request, fake_response, fs, http, querystring, url, utils;
url = require('url');
querystring = require('querystring');
fs = require('fs');
http = require('http');
utils = require('./utils');
execute_request = function(app, funs, req, res, data) {
var fun, results, x;
try {
results = [];
while (funs.length > 0) {
fun = funs.shift();
req.last_fun = fun;
results.push(data = app[fun](req, res, data, req.next_filter));
}
return results;
} catch (error1) {
x = error1;
if (typeof x === 'object' && 'status' in x) {
if (x.status === 0) {
return;
} else if ('handle_' + x.status in app) {
app['handle_' + x.status](req, res, x);
} else {
app['handle_error'](req, res, x);
}
} else {
app['handle_error'](req, res, x);
}
return app['log_request'](req, res, true);
}
};
fake_response = function(req, res) {
var headers;
headers = {
'Connection': 'close'
};
res.writeHead = function(status, user_headers) {
var k, r, x;
if (user_headers == null) {
user_headers = {};
}
r = [];
r.push('HTTP/' + req.httpVersion + ' ' + status + ' ' + http.STATUS_CODES[status]);
utils.objectExtend(headers, user_headers);
for (k in headers) {
r.push(k + ': ' + headers[k]);
}
r = r.concat(['', '']);
try {
res.write(r.join('\r\n'));
} catch (error1) {
x = error1;
}
try {
return res.end();
} catch (error1) {
x = error1;
}
};
return res.setHeader = function(k, v) {
return headers[k] = v;
};
};
exports.generateHandler = function(app, dispatcher) {
return function(req, res, head) {
var allowed_methods, found, funs, i, j, l, len, m, method, path, ref, row;
if (typeof res.writeHead === "undefined") {
fake_response(req, res);
}
utils.objectExtend(req, url.parse(req.url, true));
req.start_date = new Date();
found = false;
allowed_methods = [];
for (j = 0, len = dispatcher.length; j < len; j++) {
row = dispatcher[j];
method = row[0], path = row[1], funs = row[2];
if (path.constructor !== Array) {
path = [path];
}
m = req.pathname.match(path[0]);
if (!m) {
continue;
}
if (!req.method.match(new RegExp(method))) {
allowed_methods.push(method);
continue;
}
for (i = l = 1, ref = path.length; 1 <= ref ? l < ref : l > ref; i = 1 <= ref ? ++l : --l) {
req[path[i]] = m[i];
}
funs = funs.slice(0);
funs.push('log_request');
req.next_filter = function(data) {
return execute_request(app, funs, req, res, data);
};
req.next_filter(head);
found = true;
break;
}
if (!found) {
if (allowed_methods.length !== 0) {
app['handle_405'](req, res, allowed_methods);
} else {
app['handle_404'](req, res);
}
app['log_request'](req, res, true);
}
};
};
exports.GenericApp = GenericApp = (function() {
function GenericApp() {}
GenericApp.prototype.handle_404 = function(req, res, x) {
if (res.finished) {
return x;
}
res.writeHead(404, {});
res.end();
return true;
};
GenericApp.prototype.handle_405 = function(req, res, methods) {
res.writeHead(405, {
'Allow': methods.join(', ')
});
res.end();
return true;
};
GenericApp.prototype.handle_error = function(req, res, x) {
if (res.finished) {
return x;
}
if (typeof x === 'object' && 'status' in x) {
res.writeHead(x.status, {});
res.end(x.message || "");
} else {
try {
res.writeHead(500, {});
res.end("500 - Internal Server Error");
} catch (error1) {
x = error1;
}
this.log('error', 'Exception on "' + req.method + ' ' + req.href + '" in filter "' + req.last_fun + '":\n' + (x.stack || x));
}
return true;
};
GenericApp.prototype.log_request = function(req, res, data) {
var td;
td = (new Date()) - req.start_date;
this.log('info', req.method + ' ' + req.url + ' ' + td + 'ms ' + (res.finished ? res.statusCode : '(unfinished)'));
return data;
};
GenericApp.prototype.log = function(severity, line) {
return console.log(line);
};
GenericApp.prototype.expose_html = function(req, res, content) {
if (res.finished) {
return content;
}
if (!res.getHeader('Content-Type')) {
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
}
return this.expose(req, res, content);
};
GenericApp.prototype.expose_json = function(req, res, content) {
if (res.finished) {
return content;
}
if (!res.getHeader('Content-Type')) {
res.setHeader('Content-Type', 'application/json');
}
return this.expose(req, res, JSON.stringify(content));
};
GenericApp.prototype.expose = function(req, res, content) {
if (res.finished) {
return content;
}
if (content && !res.getHeader('Content-Type')) {
res.setHeader('Content-Type', 'text/plain');
}
if (content) {
res.setHeader('Content-Length', content.length);
}
res.writeHead(res.statusCode);
res.end(content, 'utf8');
return true;
};
GenericApp.prototype.serve_file = function(req, res, filename, next_filter) {
var a;
a = function(error, content) {
if (error) {
res.writeHead(500);
res.end("can't read file");
} else {
res.setHeader('Content-length', content.length);
res.writeHead(res.statusCode, res.headers);
res.end(content, 'utf8');
}
return next_filter(true);
};
fs.readFile(filename, a);
throw {
status: 0
};
};
GenericApp.prototype.cache_for = function(req, res, content) {
var exp;
res.cache_for = res.cache_for || 365 * 24 * 60 * 60;
res.setHeader('Cache-Control', 'public, max-age=' + res.cache_for);
exp = new Date();
exp.setTime(exp.getTime() + res.cache_for * 1000);
res.setHeader('Expires', exp.toGMTString());
return content;
};
GenericApp.prototype.h_no_cache = function(req, res, content) {
res.setHeader('Cache-Control', 'no-store, no-cache, no-transform, must-revalidate, max-age=0');
return content;
};
GenericApp.prototype.expect_form = function(req, res, _data, next_filter) {
var data;
data = new Buffer(0);
req.on('data', (function(_this) {
return function(d) {
return data = utils.buffer_concat(data, new Buffer(d, 'binary'));
};
})(this));
req.on('end', (function(_this) {
return function() {
var q;
data = data.toString('utf-8');
switch ((req.headers['content-type'] || '').split(';')[0]) {
case 'application/x-www-form-urlencoded':
q = querystring.parse(data);
break;
case 'text/plain':
case '':
q = data;
break;
default:
_this.log('error', "Unsupported content-type " + req.headers['content-type']);
q = void 0;
}
return next_filter(q);
};
})(this));
throw {
status: 0
};
};
GenericApp.prototype.expect_xhr = function(req, res, _data, next_filter) {
var data;
data = new Buffer(0);
req.on('data', (function(_this) {
return function(d) {
return data = utils.buffer_concat(data, new Buffer(d, 'binary'));
};
})(this));
req.on('end', (function(_this) {
return function() {
var q;
data = data.toString('utf-8');
switch ((req.headers['content-type'] || '').split(';')[0]) {
case 'text/plain':
case 'T':
case 'application/json':
case 'application/xml':
case '':
case 'text/xml':
q = data;
break;
default:
_this.log('error', 'Unsupported content-type ' + req.headers['content-type']);
q = void 0;
}
return next_filter(q);
};
})(this));
throw {
status: 0
};
};
return GenericApp;
})();
}).call(this);

View File

@@ -0,0 +1,115 @@
### 0.10.0 / 2015-07-08
* Add the standard `code` and `reason` parameters to the `close` method
### 0.9.4 / 2015-03-08
* Don't send input to the driver before `start()` is called
### 0.9.3 / 2015-02-19
* Make sure the TCP socket is not left open when closing the connection
### 0.9.2 / 2014-12-21
* Only emit `error` once, and don't emit it after `close`
### 0.9.1 / 2014-12-18
* Check that all options to the WebSocket constructor are recognized
### 0.9.0 / 2014-12-13
* Allow protocol extensions to be passed into websocket-extensions
### 0.8.1 / 2014-11-12
* Send the correct hostname when upgrading a connection to TLS
### 0.8.0 / 2014-11-08
* Support connections via HTTP proxies
* Close the connection cleanly if we're still waiting for a handshake response
### 0.7.3 / 2014-10-04
* Allow sockets to be closed when they are in any state other than `CLOSED`
### 0.7.2 / 2013-12-29
* Make sure the `close` event is emitted by clients on Node v0.10
### 0.7.1 / 2013-12-03
* Support the `maxLength` websocket-driver option
* Make the client emit `error` events on network errors
### 0.7.0 / 2013-09-09
* Allow the server to send custom headers with EventSource responses
### 0.6.1 / 2013-07-05
* Add `ca` option to the client for specifying certificate authorities
* Start the server driver asynchronously so that `onopen` handlers can be added
### 0.6.0 / 2013-05-12
* Add support for custom headers
### 0.5.0 / 2013-05-05
* Extract the protocol handlers into the `websocket-driver` library
* Support the Node streaming API
### 0.4.4 / 2013-02-14
* Emit the `close` event if TCP is closed before CLOSE frame is acked
### 0.4.3 / 2012-07-09
* Add `Connection: close` to EventSource response
* Handle situations where `request.socket` is undefined
### 0.4.2 / 2012-04-06
* Add WebSocket error code `1011`.
* Handle URLs with no path correctly by sending `GET /`
### 0.4.1 / 2012-02-26
* Treat anything other than a `Buffer` as a string when calling `send()`
### 0.4.0 / 2012-02-13
* Add `ping()` method to server-side `WebSocket` and `EventSource`
* Buffer `send()` calls until the draft-76 handshake is complete
* Fix HTTPS problems on Node 0.7
### 0.3.1 / 2012-01-16
* Call `setNoDelay(true)` on `net.Socket` objects to reduce latency
### 0.3.0 / 2012-01-13
* Add support for `EventSource` connections
### 0.2.0 / 2011-12-21
* Add support for `Sec-WebSocket-Protocol` negotiation
* Support `hixie-76` close frames and 75/76 ignored segments
* Improve performance of HyBi parsing/framing functions
* Decouple parsers from TCP and reduce write volume
### 0.1.2 / 2011-12-05
* Detect closed sockets on the server side when TCP connection breaks
* Make `hixie-76` sockets work through HAProxy
### 0.1.1 / 2011-11-30
* Fix `addEventListener()` interface methods
### 0.1.0 / 2011-11-27
* Initial release, based on WebSocket components from Faye

View File

@@ -0,0 +1,336 @@
# faye-websocket
* Travis CI build: [![Build
status](https://secure.travis-ci.org/faye/faye-websocket-node.svg)](http://travis-ci.org/faye/faye-websocket-node)
* Autobahn tests: [server](http://faye.jcoglan.com/autobahn/servers/),
[client](http://faye.jcoglan.com/autobahn/clients/)
This is a general-purpose WebSocket implementation extracted from the
[Faye](http://faye.jcoglan.com) project. It provides classes for easily building
WebSocket servers and clients in Node. It does not provide a server itself, but
rather makes it easy to handle WebSocket connections within an existing
[Node](http://nodejs.org/) application. It does not provide any abstraction
other than the standard [WebSocket API](http://dev.w3.org/html5/websockets/).
It also provides an abstraction for handling
[EventSource](http://dev.w3.org/html5/eventsource/) connections, which are
one-way connections that allow the server to push data to the client. They are
based on streaming HTTP responses and can be easier to access via proxies than
WebSockets.
## Installation
```
$ npm install faye-websocket
```
## Handling WebSocket connections in Node
You can handle WebSockets on the server side by listening for HTTP Upgrade
requests, and creating a new socket for the request. This socket object exposes
the usual WebSocket methods for receiving and sending messages. For example this
is how you'd implement an echo server:
```js
var WebSocket = require('faye-websocket'),
http = require('http');
var server = http.createServer();
server.on('upgrade', function(request, socket, body) {
if (WebSocket.isWebSocket(request)) {
var ws = new WebSocket(request, socket, body);
ws.on('message', function(event) {
ws.send(event.data);
});
ws.on('close', function(event) {
console.log('close', event.code, event.reason);
ws = null;
});
}
});
server.listen(8000);
```
`WebSocket` objects are also duplex streams, so you could replace the
`ws.on('message', ...)` line with:
```js
ws.pipe(ws);
```
Note that under certain circumstances (notably a draft-76 client connecting
through an HTTP proxy), the WebSocket handshake will not be complete after you
call `new WebSocket()` because the server will not have received the entire
handshake from the client yet. In this case, calls to `ws.send()` will buffer
the message in memory until the handshake is complete, at which point any
buffered messages will be sent to the client.
If you need to detect when the WebSocket handshake is complete, you can use the
`onopen` event.
If the connection's protocol version supports it, you can call `ws.ping()` to
send a ping message and wait for the client's response. This method takes a
message string, and an optional callback that fires when a matching pong message
is received. It returns `true` if and only if a ping message was sent. If the
client does not support ping/pong, this method sends no data and returns
`false`.
```js
ws.ping('Mic check, one, two', function() {
// fires when pong is received
});
```
## Using the WebSocket client
The client supports both the plain-text `ws` protocol and the encrypted `wss`
protocol, and has exactly the same interface as a socket you would use in a web
browser. On the wire it identifies itself as `hybi-13`.
```js
var WebSocket = require('faye-websocket'),
ws = new WebSocket.Client('ws://www.example.com/');
ws.on('open', function(event) {
console.log('open');
ws.send('Hello, world!');
});
ws.on('message', function(event) {
console.log('message', event.data);
});
ws.on('close', function(event) {
console.log('close', event.code, event.reason);
ws = null;
});
```
The WebSocket client also lets you inspect the status and headers of the
handshake response via its `statusCode` and `headers` properties.
To connect via a proxy, set the `proxy` option to the HTTP origin of the proxy,
including any authorization information, custom headers and TLS config you
require. Only the `origin` setting is required.
```js
var ws = new WebSocket.Client('ws://www.example.com/', [], {
proxy: {
origin: 'http://username:password@proxy.example.com',
headers: {'User-Agent': 'node'},
tls: {cert: fs.readFileSync('client.crt')}
}
});
```
The `tls` value is a Node 'TLS options' object that will be passed to
[`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
## Subprotocol negotiation
The WebSocket protocol allows peers to select and identify the application
protocol to use over the connection. On the client side, you can set which
protocols the client accepts by passing a list of protocol names when you
construct the socket:
```js
var ws = new WebSocket.Client('ws://www.example.com/', ['irc', 'amqp']);
```
On the server side, you can likewise pass in the list of protocols the server
supports after the other constructor arguments:
```js
var ws = new WebSocket(request, socket, body, ['irc', 'amqp']);
```
If the client and server agree on a protocol, both the client- and server-side
socket objects expose the selected protocol through the `ws.protocol` property.
## Protocol extensions
faye-websocket is based on the
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
framework that allows extensions to be negotiated via the
`Sec-WebSocket-Extensions` header. To add extensions to a connection, pass an
array of extensions to the `:extensions` option. For example, to add
[permessage-deflate](https://github.com/faye/permessage-deflate-node):
```js
var deflate = require('permessage-deflate');
var ws = new WebSocket(request, socket, body, [], {extensions: [deflate]});
```
## Initialization options
Both the server- and client-side classes allow an options object to be passed in
at initialization time, for example:
```js
var ws = new WebSocket(request, socket, body, protocols, options);
var ws = new WebSocket.Client(url, protocols, options);
```
`protocols` is an array of subprotocols as described above, or `null`.
`options` is an optional object containing any of these fields:
* `extensions` - an array of
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
compatible extensions, as described above
* `headers` - an object containing key-value pairs representing HTTP headers to
be sent during the handshake process
* `maxLength` - the maximum allowed size of incoming message frames, in bytes.
The default value is `2^26 - 1`, or 1 byte short of 64 MiB.
* `ping` - an integer that sets how often the WebSocket should send ping frames,
measured in seconds
The client accepts some additional options:
* `proxy` - settings for a proxy as described above
* `tls` - a Node 'TLS options' object containing TLS settings for the origin
server, this will be passed to
[`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback)
* `ca` - (legacy) a shorthand for passing `{tls: {ca: value}}`
## WebSocket API
Both server- and client-side `WebSocket` objects support the following API.
* <b>`on('open', function(event) {})`</b> fires when the socket connection is
established. Event has no attributes.
* <b>`on('message', function(event) {})`</b> fires when the socket receives a
message. Event has one attribute, <b>`data`</b>, which is either a `String`
(for text frames) or a `Buffer` (for binary frames).
* <b>`on('error', function(event) {})`</b> fires when there is a protocol error
due to bad data sent by the other peer. This event is purely informational,
you do not need to implement error recover.
* <b>`on('close', function(event) {})`</b> fires when either the client or the
server closes the connection. Event has two optional attributes, <b>`code`</b>
and <b>`reason`</b>, that expose the status code and message sent by the peer
that closed the connection.
* <b>`send(message)`</b> accepts either a `String` or a `Buffer` and sends a
text or binary message over the connection to the other peer.
* <b>`ping(message, function() {})`</b> sends a ping frame with an optional
message and fires the callback when a matching pong is received.
* <b>`close(code, reason)`</b> closes the connection, sending the given status
code and reason text, both of which are optional.
* <b>`version`</b> is a string containing the version of the `WebSocket`
protocol the connection is using.
* <b>`protocol`</b> is a string (which may be empty) identifying the subprotocol
the socket is using.
## Handling EventSource connections in Node
EventSource connections provide a very similar interface, although because they
only allow the server to send data to the client, there is no `onmessage` API.
EventSource allows the server to push text messages to the client, where each
message has an optional event-type and ID.
```js
var WebSocket = require('faye-websocket'),
EventSource = WebSocket.EventSource,
http = require('http');
var server = http.createServer();
server.on('request', function(request, response) {
if (EventSource.isEventSource(request)) {
var es = new EventSource(request, response);
console.log('open', es.url, es.lastEventId);
// Periodically send messages
var loop = setInterval(function() { es.send('Hello') }, 1000);
es.on('close', function() {
clearInterval(loop);
es = null;
});
} else {
// Normal HTTP request
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello');
}
});
server.listen(8000);
```
The `send` method takes two optional parameters, `event` and `id`. The default
event-type is `'message'` with no ID. For example, to send a `notification`
event with ID `99`:
```js
es.send('Breaking News!', {event: 'notification', id: '99'});
```
The `EventSource` object exposes the following properties:
* <b>`url`</b> is a string containing the URL the client used to create the
EventSource.
* <b>`lastEventId`</b> is a string containing the last event ID received by the
client. You can use this when the client reconnects after a dropped connection
to determine which messages need resending.
When you initialize an EventSource with ` new EventSource()`, you can pass
configuration options after the `response` parameter. Available options are:
* <b>`headers`</b> is an object containing custom headers to be set on the
EventSource response.
* <b>`retry`</b> is a number that tells the client how long (in seconds) it
should wait after a dropped connection before attempting to reconnect.
* <b>`ping`</b> is a number that tells the server how often (in seconds) to send
'ping' packets to the client to keep the connection open, to defeat timeouts
set by proxies. The client will ignore these messages.
For example, this creates a connection that allows access from any origin, pings
every 15 seconds and is retryable every 10 seconds if the connection is broken:
```js
var es = new EventSource(request, response, {
headers: {'Access-Control-Allow-Origin': '*'},
ping: 15,
retry: 10
});
```
You can send a ping message at any time by calling `es.ping()`. Unlike
WebSocket, the client does not send a response to this; it is merely to send
some data over the wire to keep the connection alive.
## License
(The MIT License)
Copyright (c) 2010-2015 James Coglan
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,39 @@
var WebSocket = require('..').Client,
deflate = require('permessage-deflate'),
pace = require('pace');
var host = 'ws://localhost:9001',
agent = encodeURIComponent('node-' + process.version),
cases = 0,
options = {extensions: [deflate]};
var socket = new WebSocket(host + '/getCaseCount'),
url, progress;
socket.onmessage = function(event) {
console.log('Total cases to run: ' + event.data);
cases = parseInt(event.data);
progress = pace(cases);
};
var runCase = function(n) {
if (n > cases) {
url = host + '/updateReports?agent=' + agent;
socket = new WebSocket(url);
socket.onclose = process.exit;
return;
}
url = host + '/runCase?case=' + n + '&agent=' + agent;
socket = new WebSocket(url, [], options);
socket.pipe(socket);
socket.on('close', function() {
progress.op();
runCase(n + 1);
});
};
socket.onclose = function() {
runCase(1);
};

View File

@@ -0,0 +1,32 @@
var WebSocket = require('..').Client,
deflate = require('permessage-deflate'),
fs = require('fs');
var url = process.argv[2],
proxy = process.argv[3],
ca = fs.readFileSync(__dirname + '/../spec/server.crt'),
tls = {ca: ca};
var ws = new WebSocket(url, [], {
proxy: {origin: proxy, headers: {'User-Agent': 'Echo'}, tls: tls},
tls: tls,
headers: {Origin: 'http://faye.jcoglan.com'},
extensions: [deflate]
});
ws.onopen = function() {
console.log('[open]', ws.headers);
ws.send('mic check');
};
ws.onclose = function(close) {
console.log('[close]', close.code, close.reason);
};
ws.onerror = function(error) {
console.log('[error]', error.message);
};
ws.onmessage = function(message) {
console.log('[message]', message.data);
};

View File

@@ -0,0 +1,20 @@
defaults
mode http
timeout client 5s
timeout connect 5s
timeout server 5s
frontend all 0.0.0.0:3000
mode http
timeout client 120s
option forwardfor
option http-server-close
option http-pretend-keepalive
default_backend sockets
backend sockets
balance uri depth 2
timeout server 120s
server socket1 127.0.0.1:7000

View File

@@ -0,0 +1,7 @@
var ProxyServer = require('../spec/proxy_server');
var port = process.argv[2],
secure = process.argv[3] === 'tls',
proxy = new ProxyServer({debug: true, tls: secure});
proxy.listen(port);

View File

@@ -0,0 +1,69 @@
var WebSocket = require('..'),
deflate = require('permessage-deflate'),
fs = require('fs'),
http = require('http'),
https = require('https');
var port = process.argv[2] || 7000,
secure = process.argv[3] === 'tls',
options = {extensions: [deflate], ping: 5};
var upgradeHandler = function(request, socket, head) {
var ws = new WebSocket(request, socket, head, ['irc', 'xmpp'], options);
console.log('[open]', ws.url, ws.version, ws.protocol, request.headers);
ws.pipe(ws);
ws.onclose = function(event) {
console.log('[close]', event.code, event.reason);
ws = null;
};
};
var requestHandler = function(request, response) {
if (!WebSocket.EventSource.isEventSource(request))
return staticHandler(request, response);
var es = new WebSocket.EventSource(request, response),
time = parseInt(es.lastEventId, 10) || 0;
console.log('[open]', es.url, es.lastEventId);
var loop = setInterval(function() {
time += 1;
es.send('Time: ' + time);
setTimeout(function() {
if (es) es.send('Update!!', {event: 'update', id: time});
}, 1000);
}, 2000);
fs.createReadStream(__dirname + '/haproxy.conf').pipe(es, {end: false});
es.onclose = function() {
clearInterval(loop);
console.log('[close]', es.url);
es = null;
};
};
var staticHandler = function(request, response) {
var path = request.url;
fs.readFile(__dirname + path, function(err, content) {
var status = err ? 404 : 200;
response.writeHead(status, {'Content-Type': 'text/html'});
response.write(content || 'Not found');
response.end();
});
};
var server = secure
? https.createServer({
key: fs.readFileSync(__dirname + '/../spec/server.key'),
cert: fs.readFileSync(__dirname + '/../spec/server.crt')
})
: http.createServer();
server.on('request', requestHandler);
server.on('upgrade', upgradeHandler);
server.listen(port);

View File

@@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>EventSource test</title>
</head>
<body>
<h1>EventSource test</h1>
<ul></ul>
<script type="text/javascript">
var logger = document.getElementsByTagName('ul')[0],
socket = new EventSource('/');
var log = function(text) {
logger.innerHTML += '<li>' + text + '</li>';
};
socket.onopen = function() {
log('OPEN');
};
socket.onmessage = function(event) {
log('MESSAGE: ' + event.data);
};
socket.addEventListener('update', function(event) {
log('UPDATE(' + event.lastEventId + '): ' + event.data);
});
socket.onerror = function(event) {
log('ERROR: ' + event.message);
};
</script>
</body>
</html>

View File

@@ -0,0 +1,43 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>WebSocket test</title>
</head>
<body>
<h1>WebSocket test</h1>
<ul></ul>
<script type="text/javascript">
var logger = document.getElementsByTagName('ul')[0],
Socket = window.MozWebSocket || window.WebSocket,
protos = ['foo', 'bar', 'xmpp'],
socket = new Socket('ws://' + location.hostname + ':' + location.port + '/', protos),
index = 0;
var log = function(text) {
logger.innerHTML += '<li>' + text + '</li>';
};
socket.addEventListener('open', function() {
log('OPEN: ' + socket.protocol);
socket.send('Hello, world');
});
socket.onerror = function(event) {
log('ERROR: ' + event.message);
};
socket.onmessage = function(event) {
log('MESSAGE: ' + event.data);
setTimeout(function() { socket.send(++index + ' ' + event.data) }, 2000);
};
socket.onclose = function(event) {
log('CLOSE: ' + event.code + ', ' + event.reason);
};
</script>
</body>
</html>

View File

@@ -0,0 +1,131 @@
var Stream = require('stream').Stream,
util = require('util'),
driver = require('websocket-driver'),
Headers = require('websocket-driver/lib/websocket/driver/headers'),
API = require('./websocket/api'),
EventTarget = require('./websocket/api/event_target'),
Event = require('./websocket/api/event');
var EventSource = function(request, response, options) {
this.writable = true;
options = options || {};
this._stream = response.socket;
this._ping = options.ping || this.DEFAULT_PING;
this._retry = options.retry || this.DEFAULT_RETRY;
var scheme = driver.isSecureRequest(request) ? 'https:' : 'http:';
this.url = scheme + '//' + request.headers.host + request.url;
this.lastEventId = request.headers['last-event-id'] || '';
this.readyState = API.CONNECTING;
var headers = new Headers(),
self = this;
if (options.headers) {
for (var key in options.headers) headers.set(key, options.headers[key]);
}
if (!this._stream || !this._stream.writable) return;
process.nextTick(function() { self._open() });
this._stream.setTimeout(0);
this._stream.setNoDelay(true);
var handshake = 'HTTP/1.1 200 OK\r\n' +
'Content-Type: text/event-stream\r\n' +
'Cache-Control: no-cache, no-store\r\n' +
'Connection: close\r\n' +
headers.toString() +
'\r\n' +
'retry: ' + Math.floor(this._retry * 1000) + '\r\n\r\n';
this._write(handshake);
this._stream.on('drain', function() { self.emit('drain') });
if (this._ping)
this._pingTimer = setInterval(function() { self.ping() }, this._ping * 1000);
['error', 'end'].forEach(function(event) {
self._stream.on(event, function() { self.close() });
});
};
util.inherits(EventSource, Stream);
EventSource.isEventSource = function(request) {
if (request.method !== 'GET') return false;
var accept = (request.headers.accept || '').split(/\s*,\s*/);
return accept.indexOf('text/event-stream') >= 0;
};
var instance = {
DEFAULT_PING: 10,
DEFAULT_RETRY: 5,
_write: function(chunk) {
if (!this.writable) return false;
try {
return this._stream.write(chunk, 'utf8');
} catch (e) {
return false;
}
},
_open: function() {
if (this.readyState !== API.CONNECTING) return;
this.readyState = API.OPEN;
var event = new Event('open');
event.initEvent('open', false, false);
this.dispatchEvent(event);
},
write: function(message) {
return this.send(message);
},
end: function(message) {
if (message !== undefined) this.write(message);
this.close();
},
send: function(message, options) {
if (this.readyState > API.OPEN) return false;
message = String(message).replace(/(\r\n|\r|\n)/g, '$1data: ');
options = options || {};
var frame = '';
if (options.event) frame += 'event: ' + options.event + '\r\n';
if (options.id) frame += 'id: ' + options.id + '\r\n';
frame += 'data: ' + message + '\r\n\r\n';
return this._write(frame);
},
ping: function() {
return this._write(':\r\n\r\n');
},
close: function() {
if (this.readyState > API.OPEN) return false;
this.readyState = API.CLOSED;
this.writable = false;
if (this._pingTimer) clearInterval(this._pingTimer);
if (this._stream) this._stream.end();
var event = new Event('close');
event.initEvent('close', false, false);
this.dispatchEvent(event);
return true;
}
};
for (var method in instance) EventSource.prototype[method] = instance[method];
for (var key in EventTarget) EventSource.prototype[key] = EventTarget[key];
module.exports = EventSource;

View File

@@ -0,0 +1,45 @@
// API references:
//
// * http://dev.w3.org/html5/websockets/
// * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-eventtarget
// * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-event
var util = require('util'),
driver = require('websocket-driver'),
API = require('./websocket/api');
var WebSocket = function(request, socket, body, protocols, options) {
options = options || {};
this._stream = socket;
this._driver = driver.http(request, {maxLength: options.maxLength, protocols: protocols});
var self = this;
if (!this._stream || !this._stream.writable) return;
if (!this._stream.readable) return this._stream.end();
var catchup = function() { self._stream.removeListener('data', catchup) };
this._stream.on('data', catchup);
API.call(this, options);
process.nextTick(function() {
self._driver.start();
self._driver.io.write(body);
});
};
util.inherits(WebSocket, API);
WebSocket.isWebSocket = function(request) {
return driver.isWebSocket(request);
};
WebSocket.validateOptions = function(options, validKeys) {
driver.validateOptions(options, validKeys);
};
WebSocket.WebSocket = WebSocket;
WebSocket.Client = require('./websocket/client');
WebSocket.EventSource = require('./eventsource');
module.exports = WebSocket;

View File

@@ -0,0 +1,186 @@
var Stream = require('stream').Stream,
util = require('util'),
driver = require('websocket-driver'),
EventTarget = require('./api/event_target'),
Event = require('./api/event');
var API = function(options) {
options = options || {};
driver.validateOptions(options, ['headers', 'extensions', 'maxLength', 'ping', 'proxy', 'tls', 'ca']);
this.readable = this.writable = true;
var headers = options.headers;
if (headers) {
for (var name in headers) this._driver.setHeader(name, headers[name]);
}
var extensions = options.extensions;
if (extensions) {
[].concat(extensions).forEach(this._driver.addExtension, this._driver);
}
this._ping = options.ping;
this._pingId = 0;
this.readyState = API.CONNECTING;
this.bufferedAmount = 0;
this.protocol = '';
this.url = this._driver.url;
this.version = this._driver.version;
var self = this;
this._driver.on('open', function(e) { self._open() });
this._driver.on('message', function(e) { self._receiveMessage(e.data) });
this._driver.on('close', function(e) { self._beginClose(e.reason, e.code) });
this._driver.on('error', function(error) {
self._emitError(error.message);
});
this.on('error', function() {});
this._driver.messages.on('drain', function() {
self.emit('drain');
});
if (this._ping)
this._pingTimer = setInterval(function() {
self._pingId += 1;
self.ping(self._pingId.toString());
}, this._ping * 1000);
this._configureStream();
if (!this._proxy) {
this._stream.pipe(this._driver.io);
this._driver.io.pipe(this._stream);
}
};
util.inherits(API, Stream);
API.CONNECTING = 0;
API.OPEN = 1;
API.CLOSING = 2;
API.CLOSED = 3;
var instance = {
write: function(data) {
return this.send(data);
},
end: function(data) {
if (data !== undefined) this.send(data);
this.close();
},
pause: function() {
return this._driver.messages.pause();
},
resume: function() {
return this._driver.messages.resume();
},
send: function(data) {
if (this.readyState > API.OPEN) return false;
if (!(data instanceof Buffer)) data = String(data);
return this._driver.messages.write(data);
},
ping: function(message, callback) {
if (this.readyState > API.OPEN) return false;
return this._driver.ping(message, callback);
},
close: function(code, reason) {
if (code === undefined) code = 1000;
if (reason === undefined) reason = '';
if (code !== 1000 && (code < 3000 || code > 4999))
throw new Error("Failed to execute 'close' on WebSocket: " +
"The code must be either 1000, or between 3000 and 4999. " +
code + " is neither.");
if (this.readyState !== API.CLOSED) this.readyState = API.CLOSING;
this._driver.close(reason, code);
},
_configureStream: function() {
var self = this;
this._stream.setTimeout(0);
this._stream.setNoDelay(true);
['close', 'end'].forEach(function(event) {
this._stream.on(event, function() { self._finalizeClose() });
}, this);
this._stream.on('error', function(error) {
self._emitError('Network error: ' + self.url + ': ' + error.message);
self._finalizeClose();
});
},
_open: function() {
if (this.readyState !== API.CONNECTING) return;
this.readyState = API.OPEN;
this.protocol = this._driver.protocol || '';
var event = new Event('open');
event.initEvent('open', false, false);
this.dispatchEvent(event);
},
_receiveMessage: function(data) {
if (this.readyState > API.OPEN) return false;
if (this.readable) this.emit('data', data);
var event = new Event('message', {data: data});
event.initEvent('message', false, false);
this.dispatchEvent(event);
},
_emitError: function(message) {
if (this.readyState >= API.CLOSING) return;
var event = new Event('error', {message: message});
event.initEvent('error', false, false);
this.dispatchEvent(event);
},
_beginClose: function(reason, code) {
if (this.readyState === API.CLOSED) return;
this.readyState = API.CLOSING;
this._closeParams = [reason, code];
if (this._stream) {
this._stream.end();
if (!this._stream.readable) this._finalizeClose();
}
},
_finalizeClose: function() {
if (this.readyState === API.CLOSED) return;
this.readyState = API.CLOSED;
if (this._pingTimer) clearInterval(this._pingTimer);
if (this._stream) this._stream.end();
if (this.readable) this.emit('end');
this.readable = this.writable = false;
var reason = this._closeParams ? this._closeParams[0] : '',
code = this._closeParams ? this._closeParams[1] : 1006;
var event = new Event('close', {code: code, reason: reason});
event.initEvent('close', false, false);
this.dispatchEvent(event);
}
};
for (var method in instance) API.prototype[method] = instance[method];
for (var key in EventTarget) API.prototype[key] = EventTarget[key];
module.exports = API;

View File

@@ -0,0 +1,20 @@
var Event = function(eventType, options) {
this.type = eventType;
for (var key in options)
this[key] = options[key];
};
Event.prototype.initEvent = function(eventType, canBubble, cancelable) {
this.type = eventType;
this.bubbles = canBubble;
this.cancelable = cancelable;
};
Event.prototype.stopPropagation = function() {};
Event.prototype.preventDefault = function() {};
Event.CAPTURING_PHASE = 1;
Event.AT_TARGET = 2;
Event.BUBBLING_PHASE = 3;
module.exports = Event;

View File

@@ -0,0 +1,28 @@
var Event = require('./event');
var EventTarget = {
onopen: null,
onmessage: null,
onerror: null,
onclose: null,
addEventListener: function(eventType, listener, useCapture) {
this.on(eventType, listener);
},
removeEventListener: function(eventType, listener, useCapture) {
this.removeListener(eventType, listener);
},
dispatchEvent: function(event) {
event.target = event.currentTarget = this;
event.eventPhase = Event.AT_TARGET;
if (this['on' + event.type])
this['on' + event.type](event);
this.emit(event.type, event);
}
};
module.exports = EventTarget;

View File

@@ -0,0 +1,83 @@
var util = require('util'),
net = require('net'),
tls = require('tls'),
url = require('url'),
driver = require('websocket-driver'),
API = require('./api'),
Event = require('./api/event');
var DEFAULT_PORTS = {'http:': 80, 'https:': 443, 'ws:':80, 'wss:': 443},
SECURE_PROTOCOLS = ['https:', 'wss:'];
var Client = function(_url, protocols, options) {
options = options || {};
this.url = _url;
this._driver = driver.client(this.url, {maxLength: options.maxLength, protocols: protocols});
['open', 'error'].forEach(function(event) {
this._driver.on(event, function() {
self.headers = self._driver.headers;
self.statusCode = self._driver.statusCode;
});
}, this);
var proxy = options.proxy || {},
endpoint = url.parse(proxy.origin || this.url),
port = endpoint.port || DEFAULT_PORTS[endpoint.protocol],
secure = SECURE_PROTOCOLS.indexOf(endpoint.protocol) >= 0,
onConnect = function() { self._onConnect() },
originTLS = options.tls || {},
socketTLS = proxy.origin ? (proxy.tls || {}) : originTLS,
self = this;
originTLS.ca = originTLS.ca || options.ca;
this._stream = secure
? tls.connect(port, endpoint.hostname, socketTLS, onConnect)
: net.connect(port, endpoint.hostname, onConnect);
if (proxy.origin) this._configureProxy(proxy, originTLS);
API.call(this, options);
};
util.inherits(Client, API);
Client.prototype._onConnect = function() {
var worker = this._proxy || this._driver;
worker.start();
};
Client.prototype._configureProxy = function(proxy, originTLS) {
var uri = url.parse(this.url),
secure = SECURE_PROTOCOLS.indexOf(uri.protocol) >= 0,
self = this,
name;
this._proxy = this._driver.proxy(proxy.origin);
if (proxy.headers) {
for (name in proxy.headers) this._proxy.setHeader(name, proxy.headers[name]);
}
this._proxy.pipe(this._stream, {end: false});
this._stream.pipe(this._proxy);
this._proxy.on('connect', function() {
if (secure) {
var options = {socket: self._stream, servername: uri.hostname};
for (name in originTLS) options[name] = originTLS[name];
self._stream = tls.connect(options);
self._configureStream();
}
self._driver.io.pipe(self._stream);
self._stream.pipe(self._driver.io);
self._driver.start();
});
this._proxy.on('error', function(error) {
self._driver.emit('error', error);
});
};
module.exports = Client;

View File

@@ -0,0 +1,66 @@
{
"_args": [
[
"faye-websocket@0.10.0",
"C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project"
]
],
"_from": "faye-websocket@0.10.0",
"_id": "faye-websocket@0.10.0",
"_inBundle": false,
"_integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
"_location": "/react-scripts/sockjs/faye-websocket",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "faye-websocket@0.10.0",
"name": "faye-websocket",
"escapedName": "faye-websocket",
"rawSpec": "0.10.0",
"saveSpec": null,
"fetchSpec": "0.10.0"
},
"_requiredBy": [
"/react-scripts/sockjs"
],
"_resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
"_spec": "0.10.0",
"_where": "C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project",
"author": {
"name": "James Coglan",
"email": "jcoglan@gmail.com",
"url": "http://jcoglan.com/"
},
"bugs": {
"url": "http://github.com/faye/faye-websocket-node/issues"
},
"dependencies": {
"websocket-driver": ">=0.5.1"
},
"description": "Standards-compliant WebSocket server and client",
"devDependencies": {
"jstest": "",
"pace": "",
"permessage-deflate": ""
},
"engines": {
"node": ">=0.4.0"
},
"homepage": "http://github.com/faye/faye-websocket-node",
"keywords": [
"websocket",
"eventsource"
],
"license": "MIT",
"main": "./lib/faye/websocket",
"name": "faye-websocket",
"repository": {
"type": "git",
"url": "git://github.com/faye/faye-websocket-node.git"
},
"scripts": {
"test": "jstest spec/runner.js"
},
"version": "0.10.0"
}

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
Copyright (c) 2010-2012 Robert Kieffer
MIT License - http://opensource.org/licenses/mit-license.php

View File

@@ -0,0 +1,205 @@
# uuid [![Build Status](https://secure.travis-ci.org/defunctzombie/node-uuid.svg?branch=master)](http://travis-ci.org/defunctzombie/node-uuid) #
[![browser support](https://ci.testling.com/defunctzombie/node-uuid.png)](https://ci.testling.com/defunctzombie/node-uuid)
Simple, fast generation of [RFC4122](http://www.ietf.org/rfc/rfc4122.txt) UUIDS.
Features:
* Generate RFC4122 version 1 or version 4 UUIDs
* Runs in node.js and all browsers.
* Cryptographically strong random # generation on supporting platforms
* 1185 bytes minified and gzip'ed (Want something smaller? Check this [crazy shit](https://gist.github.com/982883) out! )
* [Annotated source code](http://broofa.github.com/node-uuid/docs/uuid.html)
## Getting Started
Install it in your browser:
```html
<script src="uuid.js"></script>
```
Or in node.js:
```
npm install uuid
```
```javascript
var uuid = require('uuid');
// Generate a v1 (time-based) id
uuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'
// Generate a v4 (random) id
uuid.v4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'
```
## API
### uuid.v1([`options` [, `buffer` [, `offset`]]])
Generate and return a RFC4122 v1 (timestamp-based) UUID.
* `options` - (Object) Optional uuid state to apply. Properties may include:
* `node` - (Array) Node id as Array of 6 bytes (per 4.1.6). Default: Randomly generated ID. See note 1.
* `clockseq` - (Number between 0 - 0x3fff) RFC clock sequence. Default: An internally maintained clockseq is used.
* `msecs` - (Number | Date) Time in milliseconds since unix Epoch. Default: The current time is used.
* `nsecs` - (Number between 0-9999) additional time, in 100-nanosecond units. Ignored if `msecs` is unspecified. Default: internal uuid counter is used, as per 4.2.1.2.
* `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written.
* `offset` - (Number) Starting index in `buffer` at which to begin writing.
Returns `buffer`, if specified, otherwise the string form of the UUID
Notes:
1. The randomly generated node id is only guaranteed to stay constant for the lifetime of the current JS runtime. (Future versions of this module may use persistent storage mechanisms to extend this guarantee.)
Example: Generate string UUID with fully-specified options
```javascript
uuid.v1({
node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
clockseq: 0x1234,
msecs: new Date('2011-11-01').getTime(),
nsecs: 5678
}); // -> "710b962e-041c-11e1-9234-0123456789ab"
```
Example: In-place generation of two binary IDs
```javascript
// Generate two ids in an array
var arr = new Array(32); // -> []
uuid.v1(null, arr, 0); // -> [02 a2 ce 90 14 32 11 e1 85 58 0b 48 8e 4f c1 15]
uuid.v1(null, arr, 16); // -> [02 a2 ce 90 14 32 11 e1 85 58 0b 48 8e 4f c1 15 02 a3 1c b0 14 32 11 e1 85 58 0b 48 8e 4f c1 15]
// Optionally use uuid.unparse() to get stringify the ids
uuid.unparse(buffer); // -> '02a2ce90-1432-11e1-8558-0b488e4fc115'
uuid.unparse(buffer, 16) // -> '02a31cb0-1432-11e1-8558-0b488e4fc115'
```
### uuid.v4([`options` [, `buffer` [, `offset`]]])
Generate and return a RFC4122 v4 UUID.
* `options` - (Object) Optional uuid state to apply. Properties may include:
* `random` - (Number[16]) Array of 16 numbers (0-255) to use in place of randomly generated values
* `rng` - (Function) Random # generator to use. Set to one of the built-in generators - `uuid.mathRNG` (all platforms), `uuid.nodeRNG` (node.js only), `uuid.whatwgRNG` (WebKit only) - or a custom function that returns an array[16] of byte values.
* `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written.
* `offset` - (Number) Starting index in `buffer` at which to begin writing.
Returns `buffer`, if specified, otherwise the string form of the UUID
Example: Generate string UUID with fully-specified options
```javascript
uuid.v4({
random: [
0x10, 0x91, 0x56, 0xbe, 0xc4, 0xfb, 0xc1, 0xea,
0x71, 0xb4, 0xef, 0xe1, 0x67, 0x1c, 0x58, 0x36
]
});
// -> "109156be-c4fb-41ea-b1b4-efe1671c5836"
```
Example: Generate two IDs in a single buffer
```javascript
var buffer = new Array(32); // (or 'new Buffer' in node.js)
uuid.v4(null, buffer, 0);
uuid.v4(null, buffer, 16);
```
### uuid.parse(id[, buffer[, offset]])
### uuid.unparse(buffer[, offset])
Parse and unparse UUIDs
* `id` - (String) UUID(-like) string
* `buffer` - (Array | Buffer) Array or buffer where UUID bytes are to be written. Default: A new Array or Buffer is used
* `offset` - (Number) Starting index in `buffer` at which to begin writing. Default: 0
Example parsing and unparsing a UUID string
```javascript
var bytes = uuid.parse('797ff043-11eb-11e1-80d6-510998755d10'); // -> <Buffer 79 7f f0 43 11 eb 11 e1 80 d6 51 09 98 75 5d 10>
var string = uuid.unparse(bytes); // -> '797ff043-11eb-11e1-80d6-510998755d10'
```
### uuid.noConflict()
(Browsers only) Set `uuid` property back to it's previous value.
Returns the uuid object.
Example:
```javascript
var myUuid = uuid.noConflict();
myUuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'
```
## Deprecated APIs
Support for the following v1.2 APIs is available in v1.3, but is deprecated and will be removed in the next major version.
### uuid([format [, buffer [, offset]]])
uuid() has become uuid.v4(), and the `format` argument is now implicit in the `buffer` argument. (i.e. if you specify a buffer, the format is assumed to be binary).
## Testing
In node.js
```
> cd test
> node test.js
```
In Browser
```
open test/test.html
```
### Benchmarking
Requires node.js
```
cd benchmark/
npm install
node benchmark.js
```
For a more complete discussion of uuid performance, please see the `benchmark/README.md` file, and the [benchmark wiki](https://github.com/broofa/uuid/wiki/Benchmark)
For browser performance [checkout the JSPerf tests](http://jsperf.com/node-uuid-performance).
## Release notes
### 2.0.0
* Removed uuid.BufferClass
### 1.4.0
* Improved module context detection
* Removed public RNG functions
### 1.3.2
* Improve tests and handling of v1() options (Issue #24)
* Expose RNG option to allow for perf testing with different generators
### 1.3.0
* Support for version 1 ids, thanks to [@ctavan](https://github.com/ctavan)!
* Support for node.js crypto API
* De-emphasizing performance in favor of a) cryptographic quality PRNGs where available and b) more manageable code

View File

@@ -0,0 +1,53 @@
# node-uuid Benchmarks
### Results
To see the results of our benchmarks visit https://github.com/broofa/node-uuid/wiki/Benchmark
### Run them yourself
node-uuid comes with some benchmarks to measure performance of generating UUIDs. These can be run using node.js. node-uuid is being benchmarked against some other uuid modules, that are available through npm namely `uuid` and `uuid-js`.
To prepare and run the benchmark issue;
```
npm install uuid uuid-js
node benchmark/benchmark.js
```
You'll see an output like this one:
```
# v4
nodeuuid.v4(): 854700 uuids/second
nodeuuid.v4('binary'): 788643 uuids/second
nodeuuid.v4('binary', buffer): 1336898 uuids/second
uuid(): 479386 uuids/second
uuid('binary'): 582072 uuids/second
uuidjs.create(4): 312304 uuids/second
# v1
nodeuuid.v1(): 938086 uuids/second
nodeuuid.v1('binary'): 683060 uuids/second
nodeuuid.v1('binary', buffer): 1644736 uuids/second
uuidjs.create(1): 190621 uuids/second
```
* The `uuid()` entries are for Nikhil Marathe's [uuid module](https://bitbucket.org/nikhilm/uuidjs) which is a wrapper around the native libuuid library.
* The `uuidjs()` entries are for Patrick Negri's [uuid-js module](https://github.com/pnegri/uuid-js) which is a pure javascript implementation based on [UUID.js](https://github.com/LiosK/UUID.js) by LiosK.
If you want to get more reliable results you can run the benchmark multiple times and write the output into a log file:
```
for i in {0..9}; do node benchmark/benchmark.js >> benchmark/bench_0.4.12.log; done;
```
If you're interested in how performance varies between different node versions, you can issue the above command multiple times.
You can then use the shell script `bench.sh` provided in this directory to calculate the averages over all benchmark runs and draw a nice plot:
```
(cd benchmark/ && ./bench.sh)
```
This assumes you have [gnuplot](http://www.gnuplot.info/) and [ImageMagick](http://www.imagemagick.org/) installed. You'll find a nice `bench.png` graph in the `benchmark/` directory then.

View File

@@ -0,0 +1,174 @@
#!/opt/local/bin/gnuplot -persist
#
#
# G N U P L O T
# Version 4.4 patchlevel 3
# last modified March 2011
# System: Darwin 10.8.0
#
# Copyright (C) 1986-1993, 1998, 2004, 2007-2010
# Thomas Williams, Colin Kelley and many others
#
# gnuplot home: http://www.gnuplot.info
# faq, bugs, etc: type "help seeking-assistance"
# immediate help: type "help"
# plot window: hit 'h'
set terminal postscript eps noenhanced defaultplex \
leveldefault color colortext \
solid linewidth 1.2 butt noclip \
palfuncparam 2000,0.003 \
"Helvetica" 14
set output 'bench.eps'
unset clip points
set clip one
unset clip two
set bar 1.000000 front
set border 31 front linetype -1 linewidth 1.000
set xdata
set ydata
set zdata
set x2data
set y2data
set timefmt x "%d/%m/%y,%H:%M"
set timefmt y "%d/%m/%y,%H:%M"
set timefmt z "%d/%m/%y,%H:%M"
set timefmt x2 "%d/%m/%y,%H:%M"
set timefmt y2 "%d/%m/%y,%H:%M"
set timefmt cb "%d/%m/%y,%H:%M"
set boxwidth
set style fill empty border
set style rectangle back fc lt -3 fillstyle solid 1.00 border lt -1
set style circle radius graph 0.02, first 0, 0
set dummy x,y
set format x "% g"
set format y "% g"
set format x2 "% g"
set format y2 "% g"
set format z "% g"
set format cb "% g"
set angles radians
unset grid
set key title ""
set key outside left top horizontal Right noreverse enhanced autotitles columnhead nobox
set key noinvert samplen 4 spacing 1 width 0 height 0
set key maxcolumns 2 maxrows 0
unset label
unset arrow
set style increment default
unset style line
set style line 1 linetype 1 linewidth 2.000 pointtype 1 pointsize default pointinterval 0
unset style arrow
set style histogram clustered gap 2 title offset character 0, 0, 0
unset logscale
set offsets graph 0.05, 0.15, 0, 0
set pointsize 1.5
set pointintervalbox 1
set encoding default
unset polar
unset parametric
unset decimalsign
set view 60, 30, 1, 1
set samples 100, 100
set isosamples 10, 10
set surface
unset contour
set clabel '%8.3g'
set mapping cartesian
set datafile separator whitespace
unset hidden3d
set cntrparam order 4
set cntrparam linear
set cntrparam levels auto 5
set cntrparam points 5
set size ratio 0 1,1
set origin 0,0
set style data points
set style function lines
set xzeroaxis linetype -2 linewidth 1.000
set yzeroaxis linetype -2 linewidth 1.000
set zzeroaxis linetype -2 linewidth 1.000
set x2zeroaxis linetype -2 linewidth 1.000
set y2zeroaxis linetype -2 linewidth 1.000
set ticslevel 0.5
set mxtics default
set mytics default
set mztics default
set mx2tics default
set my2tics default
set mcbtics default
set xtics border in scale 1,0.5 mirror norotate offset character 0, 0, 0
set xtics norangelimit
set xtics ()
set ytics border in scale 1,0.5 mirror norotate offset character 0, 0, 0
set ytics autofreq norangelimit
set ztics border in scale 1,0.5 nomirror norotate offset character 0, 0, 0
set ztics autofreq norangelimit
set nox2tics
set noy2tics
set cbtics border in scale 1,0.5 mirror norotate offset character 0, 0, 0
set cbtics autofreq norangelimit
set title ""
set title offset character 0, 0, 0 font "" norotate
set timestamp bottom
set timestamp ""
set timestamp offset character 0, 0, 0 font "" norotate
set rrange [ * : * ] noreverse nowriteback # (currently [8.98847e+307:-8.98847e+307] )
set autoscale rfixmin
set autoscale rfixmax
set trange [ * : * ] noreverse nowriteback # (currently [-5.00000:5.00000] )
set autoscale tfixmin
set autoscale tfixmax
set urange [ * : * ] noreverse nowriteback # (currently [-10.0000:10.0000] )
set autoscale ufixmin
set autoscale ufixmax
set vrange [ * : * ] noreverse nowriteback # (currently [-10.0000:10.0000] )
set autoscale vfixmin
set autoscale vfixmax
set xlabel ""
set xlabel offset character 0, 0, 0 font "" textcolor lt -1 norotate
set x2label ""
set x2label offset character 0, 0, 0 font "" textcolor lt -1 norotate
set xrange [ * : * ] noreverse nowriteback # (currently [-0.150000:3.15000] )
set autoscale xfixmin
set autoscale xfixmax
set x2range [ * : * ] noreverse nowriteback # (currently [0.00000:3.00000] )
set autoscale x2fixmin
set autoscale x2fixmax
set ylabel ""
set ylabel offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
set y2label ""
set y2label offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
set yrange [ 0.00000 : 1.90000e+06 ] noreverse nowriteback # (currently [:] )
set autoscale yfixmin
set autoscale yfixmax
set y2range [ * : * ] noreverse nowriteback # (currently [0.00000:1.90000e+06] )
set autoscale y2fixmin
set autoscale y2fixmax
set zlabel ""
set zlabel offset character 0, 0, 0 font "" textcolor lt -1 norotate
set zrange [ * : * ] noreverse nowriteback # (currently [-10.0000:10.0000] )
set autoscale zfixmin
set autoscale zfixmax
set cblabel ""
set cblabel offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
set cbrange [ * : * ] noreverse nowriteback # (currently [8.98847e+307:-8.98847e+307] )
set autoscale cbfixmin
set autoscale cbfixmax
set zero 1e-08
set lmargin -1
set bmargin -1
set rmargin -1
set tmargin -1
set pm3d explicit at s
set pm3d scansautomatic
set pm3d interpolate 1,1 flush begin noftriangles nohidden3d corners2color mean
set palette positive nops_allcF maxcolors 0 gamma 1.5 color model RGB
set palette rgbformulae 7, 5, 15
set colorbox default
set colorbox vertical origin screen 0.9, 0.2, 0 size screen 0.05, 0.6, 0 front bdefault
set loadpath
set fontpath
set fit noerrorvariables
GNUTERM = "aqua"
plot 'bench_results.txt' using 2:xticlabel(1) w lp lw 2, '' using 3:xticlabel(1) w lp lw 2, '' using 4:xticlabel(1) w lp lw 2, '' using 5:xticlabel(1) w lp lw 2, '' using 6:xticlabel(1) w lp lw 2, '' using 7:xticlabel(1) w lp lw 2, '' using 8:xticlabel(1) w lp lw 2, '' using 9:xticlabel(1) w lp lw 2
# EOF

View File

@@ -0,0 +1,34 @@
#!/bin/bash
# for a given node version run:
# for i in {0..9}; do node benchmark.js >> bench_0.6.2.log; done;
PATTERNS=('nodeuuid.v1()' "nodeuuid.v1('binary'," 'nodeuuid.v4()' "nodeuuid.v4('binary'," "uuid()" "uuid('binary')" 'uuidjs.create(1)' 'uuidjs.create(4)' '140byte')
FILES=(node_uuid_v1_string node_uuid_v1_buf node_uuid_v4_string node_uuid_v4_buf libuuid_v4_string libuuid_v4_binary uuidjs_v1_string uuidjs_v4_string 140byte_es)
INDICES=(2 3 2 3 2 2 2 2 2)
VERSIONS=$( ls bench_*.log | sed -e 's/^bench_\([0-9\.]*\)\.log/\1/' | tr "\\n" " " )
TMPJOIN="tmp_join"
OUTPUT="bench_results.txt"
for I in ${!FILES[*]}; do
F=${FILES[$I]}
P=${PATTERNS[$I]}
INDEX=${INDICES[$I]}
echo "version $F" > $F
for V in $VERSIONS; do
(VAL=$( grep "$P" bench_$V.log | LC_ALL=en_US awk '{ sum += $'$INDEX' } END { print sum/NR }' ); echo $V $VAL) >> $F
done
if [ $I == 0 ]; then
cat $F > $TMPJOIN
else
join $TMPJOIN $F > $OUTPUT
cp $OUTPUT $TMPJOIN
fi
rm $F
done
rm $TMPJOIN
gnuplot bench.gnu
convert -density 200 -resize 800x560 -flatten bench.eps bench.png
rm bench.eps

View File

@@ -0,0 +1,34 @@
/*
Test performance of native C UUID generation
To Compile: cc -luuid benchmark-native.c -o benchmark-native
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <uuid/uuid.h>
int main() {
uuid_t myid;
char buf[36+1];
int i;
struct timeval t;
double start, finish;
gettimeofday(&t, NULL);
start = t.tv_sec + t.tv_usec/1e6;
int n = 2e5;
for (i = 0; i < n; i++) {
uuid_generate(myid);
uuid_unparse(myid, buf);
}
gettimeofday(&t, NULL);
finish = t.tv_sec + t.tv_usec/1e6;
double dur = finish - start;
printf("%d uuids/sec", (int)(n/dur));
return 0;
}

View File

@@ -0,0 +1,84 @@
try {
var nodeuuid = require('../uuid');
} catch (e) {
console.error('node-uuid require failed - skipping tests');
}
try {
var uuid = require('uuid');
} catch (e) {
console.error('uuid require failed - skipping tests');
}
try {
var uuidjs = require('uuid-js');
} catch (e) {
console.error('uuid-js require failed - skipping tests');
}
var N = 5e5;
function rate(msg, t) {
console.log(msg + ': ' +
(N / (Date.now() - t) * 1e3 | 0) +
' uuids/second');
}
console.log('# v4');
// node-uuid - string form
if (nodeuuid) {
for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4();
rate('nodeuuid.v4() - using node.js crypto RNG', t);
for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4({rng: nodeuuid.mathRNG});
rate('nodeuuid.v4() - using Math.random() RNG', t);
for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4('binary');
rate('nodeuuid.v4(\'binary\')', t);
var buffer = new nodeuuid.BufferClass(16);
for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v4('binary', buffer);
rate('nodeuuid.v4(\'binary\', buffer)', t);
}
// libuuid - string form
if (uuid) {
for (var i = 0, t = Date.now(); i < N; i++) uuid();
rate('uuid()', t);
for (var i = 0, t = Date.now(); i < N; i++) uuid('binary');
rate('uuid(\'binary\')', t);
}
// uuid-js - string form
if (uuidjs) {
for (var i = 0, t = Date.now(); i < N; i++) uuidjs.create(4);
rate('uuidjs.create(4)', t);
}
// 140byte.es
for (var i = 0, t = Date.now(); i < N; i++) 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(s,r){r=Math.random()*16|0;return (s=='x'?r:r&0x3|0x8).toString(16)});
rate('140byte.es_v4', t);
console.log('');
console.log('# v1');
// node-uuid - v1 string form
if (nodeuuid) {
for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v1();
rate('nodeuuid.v1()', t);
for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v1('binary');
rate('nodeuuid.v1(\'binary\')', t);
var buffer = new nodeuuid.BufferClass(16);
for (var i = 0, t = Date.now(); i < N; i++) nodeuuid.v1('binary', buffer);
rate('nodeuuid.v1(\'binary\', buffer)', t);
}
// uuid-js - v1 string form
if (uuidjs) {
for (var i = 0, t = Date.now(); i < N; i++) uuidjs.create(1);
rate('uuidjs.create(1)', t);
}

View File

@@ -0,0 +1,9 @@
{
"name": "benchmark-uuid",
"private": true,
"description": "Benchmarks for node-uuid",
"dependencies": {
"uuid": "1.4.1",
"uuid-js": "0.7.4"
}
}

View File

@@ -0,0 +1,62 @@
var assert = require('assert'),
nodeuuid = require('../'),
uuidjs = require('uuid-js'),
util = require('util'),
exec = require('child_process').exec,
os = require('os');
// On Mac Os X / macports there's only the ossp-uuid package that provides uuid
// On Linux there's uuid-runtime which provides uuidgen
var uuidCmd = os.type() === 'Darwin' ? 'uuid -1' : 'uuidgen -t';
function compare(ids) {
console.log(ids);
for (var i = 0; i < ids.length; i++) {
var id = ids[i].split('-');
id = [id[2], id[1], id[0]].join('');
ids[i] = id;
}
var sorted = ([].concat(ids)).sort();
if (sorted.toString() !== ids.toString()) {
console.log('Warning: sorted !== ids');
} else {
console.log('everything in order!');
}
}
// Test time order of v1 uuids
var ids = [];
while (ids.length < 10e3) ids.push(nodeuuid.v1());
var max = 10;
console.log('node-uuid:');
ids = [];
for (var i = 0; i < max; i++) ids.push(nodeuuid.v1());
compare(ids);
console.log('');
console.log('uuidjs:');
ids = [];
for (var i = 0; i < max; i++) ids.push(uuidjs.create(1).toString());
compare(ids);
console.log('');
console.log('libuuid:');
ids = [];
var count = 0;
var last = function() {
compare(ids);
}
var cb = function(err, stdout, stderr) {
ids.push(stdout.substring(0, stdout.length-1));
count++;
if (count < max) {
return next();
}
last();
};
var next = function() {
exec(uuidCmd, cb);
};
next();

View File

@@ -0,0 +1,102 @@
var assert = require('assert');
var uuid = require('../');
var log = console.log;
var generators = {
v1: uuid.v1,
v4: uuid.v4
};
var UUID_FORMAT = {
v1: /[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i,
v4: /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i
};
var N = 1e4;
// Get %'age an actual value differs from the ideal value
function divergence(actual, ideal) {
return Math.round(100*100*(actual - ideal)/ideal)/100;
}
function rate(msg, t) {
log(msg + ': ' + (N / (Date.now() - t) * 1e3 | 0) + ' uuids\/second');
}
for (var version in generators) {
var counts = {}, max = 0;
var generator = generators[version];
var format = UUID_FORMAT[version];
log('\nSanity check ' + N + ' ' + version + ' uuids');
for (var i = 0, ok = 0; i < N; i++) {
id = generator();
if (!format.test(id)) {
throw Error(id + ' is not a valid UUID string');
}
if (id != uuid.unparse(uuid.parse(id))) {
assert(fail, id + ' is not a valid id');
}
// Count digits for our randomness check
if (version == 'v4') {
var digits = id.replace(/-/g, '').split('');
for (var j = digits.length-1; j >= 0; j--) {
var c = digits[j];
max = Math.max(max, counts[c] = (counts[c] || 0) + 1);
}
}
}
// Check randomness for v4 UUIDs
if (version == 'v4') {
// Limit that we get worried about randomness. (Purely empirical choice, this!)
var limit = 2*100*Math.sqrt(1/N);
log('\nChecking v4 randomness. Distribution of Hex Digits (% deviation from ideal)');
for (var i = 0; i < 16; i++) {
var c = i.toString(16);
var bar = '', n = counts[c], p = Math.round(n/max*100|0);
// 1-3,5-8, and D-F: 1:16 odds over 30 digits
var ideal = N*30/16;
if (i == 4) {
// 4: 1:1 odds on 1 digit, plus 1:16 odds on 30 digits
ideal = N*(1 + 30/16);
} else if (i >= 8 && i <= 11) {
// 8-B: 1:4 odds on 1 digit, plus 1:16 odds on 30 digits
ideal = N*(1/4 + 30/16);
} else {
// Otherwise: 1:16 odds on 30 digits
ideal = N*30/16;
}
var d = divergence(n, ideal);
// Draw bar using UTF squares (just for grins)
var s = n/max*50 | 0;
while (s--) bar += '=';
assert(Math.abs(d) < limit, c + ' |' + bar + '| ' + counts[c] + ' (' + d + '% < ' + limit + '%)');
}
}
}
// Perf tests
for (var version in generators) {
log('\nPerformance testing ' + version + ' UUIDs');
var generator = generators[version];
var buf = new uuid.BufferClass(16);
for (var i = 0, t = Date.now(); i < N; i++) generator();
rate('uuid.' + version + '()', t);
for (var i = 0, t = Date.now(); i < N; i++) generator('binary');
rate('uuid.' + version + '(\'binary\')', t);
for (var i = 0, t = Date.now(); i < N; i++) generator('binary', buf);
rate('uuid.' + version + '(\'binary\', buffer)', t);
}

View File

@@ -0,0 +1,81 @@
{
"_args": [
[
"uuid@2.0.3",
"C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project"
]
],
"_from": "uuid@2.0.3",
"_id": "uuid@2.0.3",
"_inBundle": false,
"_integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
"_location": "/react-scripts/sockjs/uuid",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "uuid@2.0.3",
"name": "uuid",
"escapedName": "uuid",
"rawSpec": "2.0.3",
"saveSpec": null,
"fetchSpec": "2.0.3"
},
"_requiredBy": [
"/react-scripts/sockjs"
],
"_resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"_spec": "2.0.3",
"_where": "C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project",
"author": {
"name": "Robert Kieffer",
"email": "robert@broofa.com"
},
"browser": {
"./rng.js": "./rng-browser.js"
},
"bugs": {
"url": "https://github.com/defunctzombie/node-uuid/issues"
},
"contributors": [
{
"name": "Christoph Tavan",
"email": "dev@tavan.de"
},
{
"name": "Vincent Voyer",
"email": "vincent@zeroload.net"
}
],
"description": "Rigorous implementation of RFC4122 (v1 and v4) UUIDs.",
"devDependencies": {
"mocha": "1.8.0"
},
"homepage": "https://github.com/defunctzombie/node-uuid#readme",
"keywords": [
"uuid",
"guid",
"rfc4122"
],
"license": "MIT",
"main": "./uuid.js",
"name": "uuid",
"repository": {
"type": "git",
"url": "git+https://github.com/defunctzombie/node-uuid.git"
},
"scripts": {
"test": "mocha test/test.js"
},
"testling": {
"browsers": [
"ie6..latest",
"firefox/3.6..latest",
"chrome/22..latest",
"safari/5.1..latest"
],
"harness": "mocha-tdd",
"files": "test/*.js"
},
"version": "2.0.3"
}

View File

@@ -0,0 +1,32 @@
var rng;
var crypto = global.crypto || global.msCrypto; // for IE 11
if (crypto && crypto.getRandomValues) {
// WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
// Moderately fast, high quality
var _rnds8 = new Uint8Array(16);
rng = function whatwgRNG() {
crypto.getRandomValues(_rnds8);
return _rnds8;
};
}
if (!rng) {
// Math.random()-based (RNG)
//
// If all else fails, use Math.random(). It's fast, but is of unspecified
// quality.
var _rnds = new Array(16);
rng = function() {
for (var i = 0, r; i < 16; i++) {
if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
_rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
}
return _rnds;
};
}
module.exports = rng;

View File

@@ -0,0 +1,4 @@
var rb = require('crypto').randomBytes;
module.exports = function() {
return rb(16);
};

View File

@@ -0,0 +1 @@
--ui qunit

View File

@@ -0,0 +1,105 @@
var assert = require('assert');
var uuid = require('../');
// Verify ordering of v1 ids created with explicit times
var TIME = 1321644961388; // 2011-11-18 11:36:01.388-08:00
function compare(name, ids) {
test(name, function() {
// avoid .map for older browsers
for (var i=0 ; i<ids.length ; ++i) {
ids[i] = ids[i].split('-').reverse().join('-');
}
ids = ids.sort();
var sorted = ([].concat(ids)).sort();
assert(sorted.toString() == ids.toString(), name + ' have expected order');
});
}
// Verify ordering of v1 ids created using default behavior
compare('uuids with current time', [
uuid.v1(),
uuid.v1(),
uuid.v1(),
uuid.v1(),
uuid.v1()
]);
// Verify ordering of v1 ids created with explicit times
compare('uuids with time option', [
uuid.v1({msecs: TIME - 10*3600*1000}),
uuid.v1({msecs: TIME - 1}),
uuid.v1({msecs: TIME}),
uuid.v1({msecs: TIME + 1}),
uuid.v1({msecs: TIME + 28*24*3600*1000})
]);
test('msec', function() {
assert(
uuid.v1({msecs: TIME}) != uuid.v1({msecs: TIME}),
'IDs created at same msec are different'
);
});
test('exception thrown when > 10k ids created in 1ms', function() {
// Verify throw if too many ids created
var thrown = false;
try {
uuid.v1({msecs: TIME, nsecs: 10000});
} catch (e) {
thrown = true;
}
assert(thrown, 'Exception thrown when > 10K ids created in 1 ms');
});
test('clock regression by msec', function() {
// Verify clock regression bumps clockseq
var uidt = uuid.v1({msecs: TIME});
var uidtb = uuid.v1({msecs: TIME - 1});
assert(
parseInt(uidtb.split('-')[3], 16) - parseInt(uidt.split('-')[3], 16) === 1,
'Clock regression by msec increments the clockseq'
);
});
test('clock regression by nsec', function() {
// Verify clock regression bumps clockseq
var uidtn = uuid.v1({msecs: TIME, nsecs: 10});
var uidtnb = uuid.v1({msecs: TIME, nsecs: 9});
assert(
parseInt(uidtnb.split('-')[3], 16) - parseInt(uidtn.split('-')[3], 16) === 1,
'Clock regression by nsec increments the clockseq'
);
});
test('explicit options product expected id', function() {
// Verify explicit options produce expected id
var id = uuid.v1({
msecs: 1321651533573,
nsecs: 5432,
clockseq: 0x385c,
node: [ 0x61, 0xcd, 0x3c, 0xbb, 0x32, 0x10 ]
});
assert(id == 'd9428888-122b-11e1-b85c-61cd3cbb3210', 'Explicit options produce expected id');
});
test('ids spanning 1ms boundary are 100ns apart', function() {
// Verify adjacent ids across a msec boundary are 1 time unit apart
var u0 = uuid.v1({msecs: TIME, nsecs: 9999});
var u1 = uuid.v1({msecs: TIME + 1, nsecs: 0});
var before = u0.split('-')[0], after = u1.split('-')[0];
var dt = parseInt(after, 16) - parseInt(before, 16);
assert(dt === 1, 'Ids spanning 1ms boundary are 100ns apart');
});
test('parse/unparse', function() {
var id = '00112233445566778899aabbccddeeff';
assert(uuid.unparse(uuid.parse(id.substr(0,10))) ==
'00112233-4400-0000-0000-000000000000', 'Short parse');
assert(uuid.unparse(uuid.parse('(this is the uuid -> ' + id + id)) ==
'00112233-4455-6677-8899-aabbccddeeff', 'Dirty parse');
});

View File

@@ -0,0 +1,183 @@
// uuid.js
//
// Copyright (c) 2010-2012 Robert Kieffer
// MIT License - http://opensource.org/licenses/mit-license.php
// Unique ID creation requires a high quality random # generator. We feature
// detect to determine the best RNG source, normalizing to a function that
// returns 128-bits of randomness, since that's what's usually required
var _rng = require('./rng');
// Maps for number <-> hex string conversion
var _byteToHex = [];
var _hexToByte = {};
for (var i = 0; i < 256; i++) {
_byteToHex[i] = (i + 0x100).toString(16).substr(1);
_hexToByte[_byteToHex[i]] = i;
}
// **`parse()` - Parse a UUID into it's component bytes**
function parse(s, buf, offset) {
var i = (buf && offset) || 0, ii = 0;
buf = buf || [];
s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
if (ii < 16) { // Don't overflow!
buf[i + ii++] = _hexToByte[oct];
}
});
// Zero out remaining bytes if string was short
while (ii < 16) {
buf[i + ii++] = 0;
}
return buf;
}
// **`unparse()` - Convert UUID byte array (ala parse()) into a string**
function unparse(buf, offset) {
var i = offset || 0, bth = _byteToHex;
return bth[buf[i++]] + bth[buf[i++]] +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] + '-' +
bth[buf[i++]] + bth[buf[i++]] +
bth[buf[i++]] + bth[buf[i++]] +
bth[buf[i++]] + bth[buf[i++]];
}
// **`v1()` - Generate time-based UUID**
//
// Inspired by https://github.com/LiosK/UUID.js
// and http://docs.python.org/library/uuid.html
// random #'s we need to init node and clockseq
var _seedBytes = _rng();
// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
var _nodeId = [
_seedBytes[0] | 0x01,
_seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
];
// Per 4.2.2, randomize (14 bit) clockseq
var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
// Previous uuid creation time
var _lastMSecs = 0, _lastNSecs = 0;
// See https://github.com/broofa/node-uuid for API details
function v1(options, buf, offset) {
var i = buf && offset || 0;
var b = buf || [];
options = options || {};
var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;
// UUID timestamps are 100 nano-second units since the Gregorian epoch,
// (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
// time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
// (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();
// Per 4.2.1.2, use count of uuid's generated during the current clock
// cycle to simulate higher resolution clock
var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;
// Time since last uuid creation (in msecs)
var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
// Per 4.2.1.2, Bump clockseq on clock regression
if (dt < 0 && options.clockseq === undefined) {
clockseq = clockseq + 1 & 0x3fff;
}
// Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
// time interval
if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
nsecs = 0;
}
// Per 4.2.1.2 Throw error if too many uuids are requested
if (nsecs >= 10000) {
throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
}
_lastMSecs = msecs;
_lastNSecs = nsecs;
_clockseq = clockseq;
// Per 4.1.4 - Convert from unix epoch to Gregorian epoch
msecs += 12219292800000;
// `time_low`
var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
b[i++] = tl >>> 24 & 0xff;
b[i++] = tl >>> 16 & 0xff;
b[i++] = tl >>> 8 & 0xff;
b[i++] = tl & 0xff;
// `time_mid`
var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
b[i++] = tmh >>> 8 & 0xff;
b[i++] = tmh & 0xff;
// `time_high_and_version`
b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
b[i++] = tmh >>> 16 & 0xff;
// `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
b[i++] = clockseq >>> 8 | 0x80;
// `clock_seq_low`
b[i++] = clockseq & 0xff;
// `node`
var node = options.node || _nodeId;
for (var n = 0; n < 6; n++) {
b[i + n] = node[n];
}
return buf ? buf : unparse(b);
}
// **`v4()` - Generate random UUID**
// See https://github.com/broofa/node-uuid for API details
function v4(options, buf, offset) {
// Deprecated - 'format' argument, as supported in v1.2
var i = buf && offset || 0;
if (typeof(options) == 'string') {
buf = options == 'binary' ? new Array(16) : null;
options = null;
}
options = options || {};
var rnds = options.random || (options.rng || _rng)();
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40;
rnds[8] = (rnds[8] & 0x3f) | 0x80;
// Copy bytes to buffer, if provided
if (buf) {
for (var ii = 0; ii < 16; ii++) {
buf[i + ii] = rnds[ii];
}
}
return buf || unparse(rnds);
}
// Export public API
var uuid = v4;
uuid.v1 = v1;
uuid.v4 = v4;
uuid.parse = parse;
uuid.unparse = unparse;
module.exports = uuid;

View File

@@ -0,0 +1,69 @@
{
"_args": [
[
"sockjs@0.3.18",
"C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project"
]
],
"_from": "sockjs@0.3.18",
"_id": "sockjs@0.3.18",
"_inBundle": false,
"_integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=",
"_location": "/react-scripts/sockjs",
"_phantomChildren": {
"websocket-driver": "0.7.0"
},
"_requested": {
"type": "version",
"registry": true,
"raw": "sockjs@0.3.18",
"name": "sockjs",
"escapedName": "sockjs",
"rawSpec": "0.3.18",
"saveSpec": null,
"fetchSpec": "0.3.18"
},
"_requiredBy": [
"/react-scripts/webpack-dev-server"
],
"_resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz",
"_spec": "0.3.18",
"_where": "C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project",
"author": {
"name": "Marek Majkowski"
},
"bugs": {
"url": "https://github.com/sockjs/sockjs-node/issues"
},
"contributors": [
{
"name": "Bryce Kahle",
"email": "bkahle@gmail.com"
},
{
"name": "Marek Majkowski",
"email": "deadbeef@popcount.org"
}
],
"dependencies": {
"faye-websocket": "^0.10.0",
"uuid": "^2.0.2"
},
"description": "SockJS-node is a server counterpart of SockJS-client a JavaScript library that provides a WebSocket-like object in the browser. SockJS gives you a coherent, cross-browser, Javascript API which creates a low latency, full duplex, cross-domain communication channel between the browser and the web server.",
"devDependencies": {
"coffee-script": "^1.8.0"
},
"homepage": "https://github.com/sockjs/sockjs-node",
"keywords": [
"websockets",
"websocket"
],
"license": "MIT",
"main": "index",
"name": "sockjs",
"repository": {
"type": "git",
"url": "git+https://github.com/sockjs/sockjs-node.git"
},
"version": "0.3.18"
}

View File

@@ -0,0 +1,30 @@
SockJS test server
==================
In order to test sockjs server implementation the server needs to
provide a standarized sockjs endpoint that will can used by various
sockjs-related tests. For example by QUnit or
[Sockjs-protocol](https://github.com/sockjs/sockjs-protocol) tests.
This small code does exactly that - runs a simple server that supports
the following SockJS services:
* `/echo`
* `/disabled_websocket_echo`
* `/cookie_needed_echo`
* `/close`
* `/ticker`
* `/amplify`
* `/broadcast`
If you just want to quickly run it:
npm install
node server.js
If you want to run do development it's recommended to run `make
test_server` from the top `sockjs-node` directory:
cd ../..
make test_server

View File

@@ -0,0 +1,9 @@
exports.config = {
server_opts: {
sockjs_url: 'http://localhost:8080/lib/sockjs.js',
websocket: true
},
port: 8081,
host: '0.0.0.0'
};

View File

@@ -0,0 +1,19 @@
var http = require('http');
var config = require('./config').config;
var sockjs_app = require('./sockjs_app');
var server = http.createServer();
server.addListener('request', function(req, res) {
res.setHeader('content-type', 'text/plain');
res.writeHead(404);
res.end('404 - Nothing here (via sockjs-node test_server)');
});
server.addListener('upgrade', function(req, res){
res.end();
});
sockjs_app.install(config.server_opts, server);
console.log(" [*] Listening on", config.host + ':' + config.port);
server.listen(config.port, config.host);

View File

@@ -0,0 +1,85 @@
var sockjs = require('../../index');
exports.install = function(opts, server) {
var sjs_echo = sockjs.createServer(opts);
sjs_echo.on('connection', function(conn) {
console.log(' [+] echo open ' + conn);
conn.on('close', function() {
console.log(' [-] echo close ' + conn);
});
conn.on('data', function(m) {
var d = JSON.stringify(m);
console.log(' [ ] echo message ' + conn,
d.slice(0,64)+
((d.length > 64) ? '...' : ''));
conn.write(m);
});
});
var sjs_close = sockjs.createServer(opts);
sjs_close.on('connection', function(conn) {
console.log(' [+] clos open ' + conn);
conn.close(3000, "Go away!");
conn.on('close', function() {
console.log(' [-] clos close ' + conn);
});
});
var sjs_ticker = sockjs.createServer(opts);
sjs_ticker.on('connection', function(conn) {
console.log(' [+] ticker open ' + conn);
var tref;
var schedule = function() {
conn.write('tick!');
tref = setTimeout(schedule, 1000);
};
tref = setTimeout(schedule, 1000);
conn.on('close', function() {
clearTimeout(tref);
console.log(' [-] ticker close ' + conn);
});
});
var broadcast = {};
var sjs_broadcast = sockjs.createServer(opts);
sjs_broadcast.on('connection', function(conn) {
console.log(' [+] broadcast open ' + conn);
broadcast[conn.id] = conn;
conn.on('close', function() {
delete broadcast[conn.id];
console.log(' [-] broadcast close' + conn);
});
conn.on('data', function(m) {
console.log(' [-] broadcast message', m);
for(var id in broadcast) {
broadcast[id].write(m);
}
});
});
var sjs_amplify = sockjs.createServer(opts);
sjs_amplify.on('connection', function(conn) {
console.log(' [+] amp open ' + conn);
conn.on('close', function() {
console.log(' [-] amp close ' + conn);
});
conn.on('data', function(m) {
var n = Math.floor(Number(m));
n = (n > 0 && n < 19) ? n : 1;
console.log(' [ ] amp message: 2^' + n);
conn.write(Array(Math.pow(2, n)+1).join('x'));
});
});
sjs_echo.installHandlers(server, {prefix:'/echo',
response_limit: 4096}),
sjs_echo.installHandlers(server, {prefix:'/disabled_websocket_echo',
websocket: false});
sjs_echo.installHandlers(server, {prefix:'/cookie_needed_echo',
jsessionid: true});
sjs_close.installHandlers(server, {prefix:'/close'});
sjs_ticker.installHandlers(server, {prefix:'/ticker'});
sjs_amplify.installHandlers(server, {prefix:'/amplify'});
sjs_broadcast.installHandlers(server, {prefix:'/broadcast'});
};