Compare commits

..

18 Commits

Author SHA1 Message Date
James Coglan d3e81b478e Bump version to 0.2.2. 2013-07-05 15:16:37 +01:00
James Coglan c5b3df986b Migrate to jstest. 2013-07-01 02:10:29 +01:00
James Coglan 286dea4337 Write the reflexive pipe examples as one-liners. [http://nodejsreactions.tumblr.com/post/51566814133/src-pipe-dst-pipe-src] 2013-05-29 02:24:27 +01:00
James Coglan 2378f4c484 An example in the README was missing a closing paren. 2013-05-17 12:46:35 +01:00
James Coglan 5a1dcd5773 Bump version to 0.2.1. 2013-05-17 11:51:34 +01:00
James Coglan 17e2bd4084 Messages should be queued when the driver is in the 'pre-connecting' state, as the client is initially. 2013-05-17 11:48:36 +01:00
James Coglan 3dfaaf497f Expose the isSecureRequest() method. 2013-05-17 11:47:56 +01:00
James Coglan 0e4f13d0e4 Bump version to 0.2.0. 2013-05-12 15:09:18 +01:00
James Coglan 4f8e10ead6 Add a changelog. 2013-05-12 15:08:17 +01:00
James Coglan 97451f81ab Set status and headers as soon as we have them so they're available if there is an error. 2013-05-12 14:44:17 +01:00
James Coglan 390ebee4d8 Parameterize the port we run the example server on. 2013-05-12 13:19:13 +01:00
James Coglan de001a0f54 Remove a blank line. 2013-05-12 01:45:56 +01:00
James Coglan 26c4873f3e Apply offset to the consumed offset from HTTP parsing before we check whether the whole buffer has been used. 2013-05-12 01:45:04 +01:00
James Coglan b2472e89a6 Document the Server driver. 2013-05-12 01:31:14 +01:00
James Coglan 1124df2bf5 Add a Server driver for running WebSockets through a bare TCP server. 2013-05-12 01:13:50 +01:00
James Coglan ea4e289669 Add a setHeader() function to allow peers to add headers to their handshake responses. 2013-05-11 22:26:20 +01:00
James Coglan d7c65eedd9 Expose the status and headers from the response in the client driver. 2013-05-11 21:58:37 +01:00
James Coglan d594f7d708 Include more possible headers as indicators of a secure connection. 2013-05-11 21:55:52 +01:00
19 changed files with 462 additions and 140 deletions
+18
View File
@@ -0,0 +1,18 @@
### 0.2.2 / 2013-07-05
* No functional changes, just updates to package.json
### 0.2.1 / 2013-05-17
* Export the isSecureRequest() method since faye-websocket relies on it
* Queue sent messages in the client's initial state
### 0.2.0 / 2013-05-12
* Add API for setting and reading headers
* Add Driver.server() method for getting a driver for TCP servers
### 0.1.0 / 2013-05-04
* First stable release
+67 -8
View File
@@ -43,7 +43,7 @@ streams attached; one for incoming/outgoing messages and one for managing the
wire protocol over an I/O stream. The full API is described below.
### Server-side
### Server-side with HTTP
A Node webserver emits a special event for 'upgrade' requests, and this is
where you should handle WebSockets. You first check whether the request is a
@@ -62,21 +62,66 @@ server.on('upgrade', function(request, socket, body) {
var driver = websocket.http(request);
driver.io.write(body);
socket.pipe(driver.io);
driver.io.pipe(socket);
socket.pipe(driver.io).pipe(socket);
driver.messages.on('data', function(message) {
console.log('Got a message', message);
});
driver.start();
};
});
```
Note the line `driver.io.write(body)` - you must pass the `body` buffer to the
socket driver in order to make certain versions of the protocol work.
### Server-side with TCP
You can also handle WebSocket connections in a bare TCP server, if you're not
using an HTTP server and don't want to implement HTTP parsing yourself.
The driver will emit a `connect` event when a request is received, and at this
point you can detect whether it's a WebSocket and handle it as such. Here's an
example using the Node `net` module:
```js
var net = require('net'),
websocket = require('websocket-driver');
var server = net.createServer(function(connection) {
var driver = websocket.server();
driver.on('connect', function() {
if (websocket.isWebSocket(driver)) {
driver.start();
} else {
// handle other HTTP requests
}
});
driver.on('close', function() { connection.end() });
connection.on('error', function() {});
connection.pipe(driver.io).pipe(connection);
driver.messages.pipe(driver.messages);
});
server.listen(4180);
```
In the `connect` event, the driver gains several properties to describe the
request, similar to a Node request object, such as `method`, `url` and
`headers`. However you should remember it's not a real request object; you
cannot write data to it, it only tells you what request data we parsed from the
input.
If the request has a body, it will be in the `driver.body` buffer, but only as
much of the body as has been piped into the driver when the `connect` event
fires.
### Client-side
Similarly, to implement a WebSocket client you just need to make a driver by
@@ -91,8 +136,7 @@ var net = require('net'),
var driver = websocket.client('ws://www.example.com/socket'),
tcp = net.createConnection(80, 'www.example.com');
tcp.pipe(driver.io);
driver.io.pipe(tcp);
tcp.pipe(driver.io).pipe(tcp);
driver.messages.on('data', function(message) {
console.log('Got a message', message);
@@ -103,6 +147,12 @@ tcp.on('connect', function() {
});
```
Client drivers have two additional properties for reading the HTTP data that
was sent back by the server:
* `driver.statusCode` - the integer value of the HTTP status code
* `driver.headers` - an object containing the response headers
### Driver API
@@ -110,12 +160,15 @@ Drivers are created using one of the following methods:
```js
driver = websocket.http(request, options)
driver = websocket.server(options)
driver = websocket.client(url, options)
```
The `http` method returns a driver chosen using the headers from a Node HTTP
request object. The `client` method always returns a driver for the RFC version
of the protocol with masking enabled on outgoing frames.
request object. The `server` method returns a driver that will parse an HTTP
request and then decide which driver to use for it using the `http` method. The
`client` method always returns a driver for the RFC version of the protocol
with masking enabled on outgoing frames.
The `options` argument is optional, and is an object. It may contain the
following fields:
@@ -164,6 +217,12 @@ describing the error.
Sets the callback to execute when the socket becomes closed. The `event` object
has `code` and `reason` attributes.
#### `driver.setHeader(name, value)`
Sets a custom header to be sent as part of the handshake response, either from
the server or from the client. Must be called before `start()`, since this is
when the headers are serialized and sent.
#### `driver.start()`
Initiates the protocol by sending the handshake - either the response for a
+21
View File
@@ -0,0 +1,21 @@
var net = require('net'),
websocket = require('../lib/websocket/driver');
var server = net.createServer(function(connection) {
var driver = websocket.server();
driver.on('connect', function() {
if (websocket.isWebSocket(driver)) driver.start();
});
driver.on('close', function() { connection.end() });
connection.on('error', function() {});
connection.pipe(driver.io);
driver.io.pipe(connection);
driver.messages.pipe(driver.messages);
});
server.listen(process.argv[2]);
+10 -27
View File
@@ -4,45 +4,28 @@
// * http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
// * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
var Draft75 = require('./driver/draft75'),
Draft76 = require('./driver/draft76'),
Hybi = require('./driver/hybi'),
Client = require('./driver/client');
var Client = require('./driver/client'),
Server = require('./driver/server');
var Driver = {
isSecureRequest: function(request) {
if (request.headers['x-forwarded-proto']) {
return request.headers['x-forwarded-proto'] === 'https';
} else {
return (request.connection && request.connection.authorized !== undefined) ||
(request.socket && request.socket.secure);
}
},
determineUrl: function(request) {
var scheme = this.isSecureRequest(request) ? 'wss:' : 'ws:';
return scheme + '//' + request.headers.host + request.url;
},
client: function(url, options) {
options = options || {};
if (options.masking === undefined) options.masking = true;
return new Client(url, options);
},
http: function(request, options) {
server: function(options) {
options = options || {};
if (options.requireMasking === undefined) options.requireMasking = true;
return new Server(options);
},
var headers = request.headers,
url = this.determineUrl(request);
http: function() {
return Server.http.apply(Server, arguments);
},
if (headers['sec-websocket-version'])
return new Hybi(request, url, options);
else if (headers['sec-websocket-key1'])
return new Draft76(request, url, options);
else
return new Draft75(request, url, options);
isSecureRequest: function(request) {
return Server.isSecureRequest(request);
},
isWebSocket: function(request) {
+36 -23
View File
@@ -1,50 +1,61 @@
var Emitter = require('events').EventEmitter,
util = require('util'),
streams = require('../streams');
streams = require('../streams'),
Headers = require('./headers');
var Base = function(request, url, options) {
Emitter.call(this);
this._request = request;
this._options = options || {};
this.__headers = new Headers();
this.__queue = [];
this.readyState = 0;
this.url = url;
var self = this;
this.io = new streams.IO(this);
this.messages = new streams.Messages(this);
// Protocol errors are informational and do not have to be handled
this.messages.on('error', function() {});
this.on('message', function(event) {
var messages = self.messages;
if (messages.readable) messages.emit('data', event.data);
});
this.on('error', function(error) {
var messages = self.messages;
if (messages.readable) messages.emit('error', error);
});
this.on('close', function() {
var messages = self.messages;
if (!messages.readable) return;
messages.readable = messages.writable = false;
messages.emit('end');
});
this._bindEventListeners();
};
util.inherits(Base, Emitter);
var instance = {
STATES: ['connecting', 'open', 'closing', 'closed'],
_bindEventListeners: function() {
var self = this;
// Protocol errors are informational and do not have to be handled
this.messages.on('error', function() {});
this.on('message', function(event) {
var messages = self.messages;
if (messages.readable) messages.emit('data', event.data);
});
this.on('error', function(error) {
var messages = self.messages;
if (messages.readable) messages.emit('error', error);
});
this.on('close', function() {
var messages = self.messages;
if (!messages.readable) return;
messages.readable = messages.writable = false;
messages.emit('end');
});
},
getState: function() {
return this.STATES[this.readyState] || null;
},
setHeader: function(name, value) {
if (this.readyState > 0) return false;
this.__headers.set(name, value);
return true;
},
start: function() {
if (this.readyState !== 0) return false;
this._write(this._handshakeResponse());
@@ -93,6 +104,8 @@ for (var key in instance)
Base.prototype[key] = instance[key];
Base.ConnectEvent = function() {};
Base.OpenEvent = function() {};
Base.CloseEvent = function(code, reason) {
+19 -39
View File
@@ -1,6 +1,6 @@
var HTTPParser = process.binding('http_parser').HTTPParser,
url = require('url'),
var url = require('url'),
util = require('util'),
HttpParser = require('./http_parser'),
Base = require('./base'),
Hybi = require('./hybi');
@@ -11,31 +11,7 @@ var Client = function(url, options) {
this.readyState = -1;
this._key = Client.generateKey();
this._accept = Hybi.generateAccept(this._key);
this._http = new HTTPParser(HTTPParser.RESPONSE || 'response');
this._node = HTTPParser.RESPONSE ? 6 : 4;
this._complete = false;
this._headers = {};
var currentHeader = null,
self = this;
this._http.onHeaderField = function(b, start, length) {
currentHeader = b.toString('utf8', start, start + length);
};
this._http.onHeaderValue = function(b, start, length) {
self._headers[currentHeader] = b.toString('utf8', start, start + length);
};
this._http.onHeadersComplete = function(info) {
self._status = info.statusCode;
var headers = info.headers;
if (!headers) return;
for (var i = 0, n = headers.length; i < n; i += 2)
self._headers[headers[i]] = headers[i+1];
};
this._http.onMessageComplete = function() {
self._complete = true;
};
this._http = new HttpParser('response');
};
util.inherits(Client, Hybi);
@@ -56,11 +32,11 @@ var instance = {
parse: function(data) {
if (this.readyState > 0) return Hybi.prototype.parse.call(this, data);
var consumed = this._http.execute(data, 0, data.length),
offset = (this._node < 6) ? 1 : 0;
if (consumed <= data.length) this._validateHandshake();
if (consumed < data.length) this.parse(data.slice(consumed + offset));
this._http.parse(data);
if (!this._http.isComplete()) return;
this._validateHandshake();
this.parse(this._http.body);
},
_handshakeRequest: function() {
@@ -77,7 +53,7 @@ var instance = {
if (this._protocols.length > 0)
headers.push('Sec-WebSocket-Protocol: ' + this._protocols.join(', '));
return new Buffer(headers.concat('','').join('\r\n'), 'utf8');
return new Buffer(headers.concat(this.__headers.toString(), '').join('\r\n'), 'utf8');
},
_failHandshake: function(message) {
@@ -88,13 +64,17 @@ var instance = {
},
_validateHandshake: function() {
if (this._status !== 101)
return this._failHandshake('Unexpected response code: ' + this._status);
this.statusCode = this._http.statusCode;
this.headers = this._http.headers;
var upgrade = this._headers.Upgrade || '',
connection = this._headers.Connection || '',
accept = this._headers['Sec-WebSocket-Accept'] || '',
protocol = this._headers['Sec-WebSocket-Protocol'] || '';
if (this._http.statusCode !== 101)
return this._failHandshake('Unexpected response code: ' + this._http.statusCode);
var headers = this._http.headers,
upgrade = headers['upgrade'] || '',
connection = headers['connection'] || '',
accept = headers['sec-websocket-accept'] || '',
protocol = headers['sec-websocket-protocol'] || '';
if (upgrade === '')
return this._failHandshake("'Upgrade' header is missing");
+1
View File
@@ -84,6 +84,7 @@ var instance = {
'Connection: Upgrade\r\n' +
'WebSocket-Origin: ' + this._request.headers.origin + '\r\n' +
'WebSocket-Location: ' + this.url + '\r\n' +
this.__headers.toString() +
'\r\n',
'utf8');
},
+1
View File
@@ -53,6 +53,7 @@ var instance = {
'Connection: Upgrade\r\n' +
'Sec-WebSocket-Origin: ' + this._request.headers.origin + '\r\n' +
'Sec-WebSocket-Location: ' + this.url + '\r\n' +
this.__headers.toString() +
'\r\n',
'binary');
},
+30
View File
@@ -0,0 +1,30 @@
var Headers = function() {
this._sent = {};
this._lines = [];
};
Headers.prototype.ALLOWED_DUPLICATES = ['set-cookie', 'set-cookie2', 'warning', 'www-authenticate']
Headers.prototype.set = function(name, value) {
if (value === undefined) return;
name = this._strip(name);
value = this._strip(value);
var key = name.toLowerCase();
if (!this._sent.hasOwnProperty(key) || this.ALLOWED_DUPLICATES.indexOf(key) < 0) {
this._sent[key] = true;
this._lines.push(name + ': ' + value + '\r\n');
}
};
Headers.prototype.toString = function() {
return this._lines.join('');
};
Headers.prototype._strip = function(string) {
return string.toString().replace(/^ */, '').replace(/ *$/, '');
};
module.exports = Headers;
+57
View File
@@ -0,0 +1,57 @@
var HTTPParser = process.binding('http_parser').HTTPParser,
version = HTTPParser.RESPONSE ? 6 : 4;
var HttpParser = function(type) {
if (type === 'request')
this._parser = new HTTPParser(HTTPParser.REQUEST || 'request');
else
this._parser = new HTTPParser(HTTPParser.RESPONSE || 'response');
this._type = type;
this._complete = false;
this.headers = {};
var current = null,
self = this;
this._parser.onHeaderField = function(b, start, length) {
current = b.toString('utf8', start, start + length).toLowerCase();
};
this._parser.onHeaderValue = function(b, start, length) {
self.headers[current] = b.toString('utf8', start, start + length);
};
this._parser.onHeadersComplete = function(info) {
self.method = info.method;
self.statusCode = info.statusCode;
self.url = info.url;
var headers = info.headers;
if (!headers) return;
for (var i = 0, n = headers.length; i < n; i += 2)
self.headers[headers[i].toLowerCase()] = headers[i+1];
};
this._parser.onMessageComplete = function() {
self._complete = true;
};
};
HttpParser.prototype.isComplete = function() {
return this._complete;
};
HttpParser.prototype.parse = function(data) {
var offset = (version < 6) ? 1 : 0,
consumed = this._parser.execute(data, 0, data.length) + offset;
if (this._complete)
this.body = (consumed < data.length)
? data.slice(consumed)
: new Buffer(0);
};
module.exports = HttpParser;
+2 -2
View File
@@ -129,7 +129,7 @@ var instance = {
},
frame: function(data, type, code) {
if (this.readyState === 0) return this._queue([data, type, code]);
if (this.readyState <= 0) return this._queue([data, type, code]);
if (this.readyState !== 1) return false;
if (data instanceof Array) data = new Buffer(data);
@@ -239,7 +239,7 @@ var instance = {
}
}
return new Buffer(headers.concat('','').join('\r\n'), 'utf8');
return new Buffer(headers.concat(this.__headers.toString(), '').join('\r\n'), 'utf8');
},
_shutdown: function(code, reason) {
+104
View File
@@ -0,0 +1,104 @@
var util = require('util'),
HttpParser = require('./http_parser'),
Base = require('./base'),
Draft75 = require('./draft75'),
Draft76 = require('./draft76'),
Hybi = require('./hybi');
var Server = function(options) {
Base.call(this, null, null, options);
this._http = new HttpParser('request');
};
util.inherits(Server, Base);
var instance = {
EVENTS: ['open', 'message', 'error', 'close'],
_bindEventListeners: function() {
this.messages.on('error', function() {});
this.on('error', function() {});
},
parse: function(data) {
if (this._delegate) return this._delegate.parse(data);
this._http.parse(data);
if (!this._http.isComplete()) return;
this.method = this._http.method;
this.url = this._http.url;
this.headers = this._http.headers;
this.body = this._http.body;
var self = this;
this._delegate = Server.http(this, this._options);
this._delegate.messages = this.messages;
this._delegate.io = this.io;
this._delegate.on('open', function() { self._open() });
this.EVENTS.forEach(function(event) {
this._delegate.on(event, function(e) { self.emit(event, e) });
}, this);
this.parse(this._http.body);
this.emit('connect', new Base.ConnectEvent());
},
_open: function() {
this.__queue.forEach(function(msg) {
this._delegate[msg[0]].apply(this._delegate, msg[1]);
}, this);
this.__queue = [];
}
};
['setHeader', 'start', 'state', 'frame', 'text', 'binary', 'ping', 'close'].forEach(function(method) {
instance[method] = function() {
if (this._delegate) {
return this._delegate[method].apply(this._delegate, arguments);
} else {
this.__queue.push([method, arguments]);
return true;
}
};
});
for (var key in instance)
Server.prototype[key] = instance[key];
Server.isSecureRequest = function(request) {
if (request.connection && request.connection.authorized !== undefined) return true;
if (request.socket && request.socket.secure) return true;
var headers = request.headers;
if (!headers) return false;
if (headers['https'] === 'on') return true;
if (headers['x-forwarded-ssl'] === 'on') return true;
if (headers['x-forwarded-scheme'] === 'https') return true;
if (headers['x-forwarded-proto'] === 'https') return true;
return false;
};
Server.determineUrl = function(request) {
var scheme = this.isSecureRequest(request) ? 'wss:' : 'ws:';
return scheme + '//' + request.headers.host + request.url;
};
Server.http = function(request, options) {
options = options || {};
if (options.requireMasking === undefined) options.requireMasking = true;
var headers = request.headers,
url = this.determineUrl(request);
if (headers['sec-websocket-version'])
return new Hybi(request, url, options);
else if (headers['sec-websocket-key1'])
return new Draft76(request, url, options);
else
return new Draft75(request, url, options);
};
module.exports = Server;
+8 -13
View File
@@ -3,24 +3,19 @@
, "homepage" : "http://github.com/faye/websocket-driver-node"
, "author" : "James Coglan <jcoglan@gmail.com> (http://jcoglan.com/)"
, "keywords" : ["websocket"]
, "license" : "MIT"
, "version" : "0.1.0"
, "version" : "0.2.2"
, "engines" : {"node": ">=0.4.0"}
, "main" : "./lib/websocket/driver"
, "devDependencies" : {"jsclass": ""}
, "devDependencies" : {"jstest": ""}
, "scripts" : {"test": "node spec/runner.js"}
, "scripts" : {"test": "jstest spec/runner.js"}
, "repository" : { "type" : "git"
, "url" : "git://github.com/faye/websocket-driver-node.git"
}
, "bugs" : "http://github.com/faye/websocket-driver-node/issues"
, "licenses" : [ { "type" : "MIT"
, "url" : "http://www.opensource.org/licenses/mit-license.php"
}
]
, "repositories" : [ { "type" : "git"
, "url" : "git://github.com/faye/websocket-driver-node.git"
}
]
}
+15 -19
View File
@@ -1,6 +1,5 @@
require('jsclass')
var Stream = require('stream').Stream,
var test = require('jstest').Test,
Stream = require('stream').Stream,
util = require('util')
var BufferMatcher = function(data) {
@@ -29,21 +28,18 @@ Collector.prototype.write = function(buffer) {
return true
}
JS.require('JS.Test', function() {
JS.Test.Unit.TestCase.include({
buffer: function(data) {
return new BufferMatcher(data)
},
collector: function() {
return this._collector = this._collector || new Collector()
}
})
require('./websocket/driver/draft75_examples')
require('./websocket/driver/draft75_spec')
require('./websocket/driver/draft76_spec')
require('./websocket/driver/hybi_spec')
require('./websocket/driver/client_spec')
JS.Test.autorun()
test.Unit.TestCase.include({
buffer: function(data) {
return new BufferMatcher(data)
},
collector: function() {
return this._collector = this._collector || new Collector()
}
})
require('./websocket/driver/draft75_examples')
require('./websocket/driver/draft75_spec')
require('./websocket/driver/draft76_spec')
require('./websocket/driver/hybi_spec')
require('./websocket/driver/client_spec')
+44 -2
View File
@@ -1,6 +1,7 @@
var Client = require("../../../lib/websocket/driver/client")
var Client = require("../../../lib/websocket/driver/client"),
test = require('jstest').Test
JS.Test.describe("Client", function() { with(this) {
test.describe("Client", function() { with(this) {
define("options", function() {
return this._options = this._options || {protocols: this.protocols()}
})
@@ -79,6 +80,25 @@ JS.Test.describe("Client", function() { with(this) {
}})
}})
describe("with custom headers", function() { with(this) {
before(function() { with(this) {
driver().setHeader("User-Agent", "Chrome")
}})
it("writes the handshake with custom headers", function() { with(this) {
expect(driver().io, "emit").given("data", buffer(
"GET /socket HTTP/1.1\r\n" +
"Host: www.example.com\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Key: 2vBVWg4Qyk3ZoM/5d3QD9Q==\r\n" +
"Sec-WebSocket-Version: 13\r\n" +
"User-Agent: Chrome\r\n" +
"\r\n"))
driver().start()
}})
}})
it("changes the state to connecting", function() { with(this) {
driver().start()
assertEqual( "connecting", driver().getState() )
@@ -97,6 +117,14 @@ JS.Test.describe("Client", function() { with(this) {
assertEqual( false, close )
assertEqual( "open", driver().getState() )
}})
it("makes the response status available", function() { with(this) {
assertEqual( 101, driver().statusCode )
}})
it("makes the response headers available", function() { with(this) {
assertEqual( "websocket", driver().headers.upgrade )
}})
}})
describe("with a valid response followed by a frame", function() { with(this) {
@@ -118,6 +146,20 @@ JS.Test.describe("Client", function() { with(this) {
}})
}})
describe("with a bad status line", function() { with(this) {
before(function() {
var resp = this.response().replace(/101/g, "4")
this.driver().parse(new Buffer(resp))
})
it("changes the state to closed", function() { with(this) {
assertEqual( false, open )
assertEqual( "Error during WebSocket handshake: Unexpected response code: 4", error.message )
assertEqual( [1002, "Error during WebSocket handshake: Unexpected response code: 4"], close )
assertEqual( "closed", driver().getState() )
}})
}})
describe("with a bad Upgrade header", function() { with(this) {
before(function() {
var resp = this.response().replace(/websocket/g, "wrong")
+3 -1
View File
@@ -1,4 +1,6 @@
JS.Test.describe("draft-75", function() { with(this) {
var test = require('jstest').Test
test.describe("draft-75", function() { with(this) {
sharedExamplesFor("draft-75 protocol", function() { with(this) {
describe("in the open state", function() { with(this) {
before(function() { this.driver().start() })
+3 -2
View File
@@ -1,6 +1,7 @@
var Draft75 = require("../../../lib/websocket/driver/draft75")
var Draft75 = require("../../../lib/websocket/driver/draft75"),
test = require('jstest').Test
JS.Test.describe("Draft75", function() { with(this) {
test.describe("Draft75", function() { with(this) {
define("request", function() {
return this._request = this._request || {
headers: {
+3 -2
View File
@@ -1,6 +1,7 @@
var Draft76 = require("../../../lib/websocket/driver/draft76")
var Draft76 = require("../../../lib/websocket/driver/draft76"),
test = require('jstest').Test
JS.Test.describe("Draft76", function() { with(this) {
test.describe("Draft76", function() { with(this) {
BODY = new Buffer([0x91, 0x25, 0x3e, 0xd3, 0xa9, 0xe7, 0x6a, 0x88])
define("body", function() {
+20 -2
View File
@@ -1,6 +1,7 @@
var Hybi = require("../../../lib/websocket/driver/hybi")
var Hybi = require("../../../lib/websocket/driver/hybi"),
test = require('jstest').Test
JS.Test.describe("Hybi", function() { with(this) {
test.describe("Hybi", function() { with(this) {
define("request", function() {
return this._request = this._request || {
headers: {
@@ -78,6 +79,23 @@ JS.Test.describe("Hybi", function() { with(this) {
}})
}})
describe("with custom headers", function() { with(this) {
before(function() { with(this) {
driver().setHeader("Authorization", "Bearer WAT")
}})
it("writes the handshake with Sec-WebSocket-Protocol", function() { with(this) {
expect(driver().io, "emit").given("data", buffer(
"HTTP/1.1 101 Switching Protocols\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Accept: JdiiuafpBKRqD7eol0y4vJDTsTs=\r\n" +
"Authorization: Bearer WAT\r\n" +
"\r\n"))
driver().start()
}})
}})
it("triggers the onopen event", function() { with(this) {
driver().start()
assertEqual( true, open )