faye-websocket
Advanced tools
Comparing version 0.1.2 to 0.2.0
@@ -6,4 +6,3 @@ var WebSocket = require('../lib/faye/websocket'); | ||
cases = 0, | ||
skip = [247,248,249,250,251,252,253,254,255, | ||
256,257,258,259,260,261,262,263,264]; | ||
skip = []; | ||
@@ -10,0 +9,0 @@ var socket = new WebSocket.Client(host + '/getCaseCount'); |
@@ -28,4 +28,4 @@ var WebSocket = require('../lib/faye/websocket'), | ||
server.addListener('upgrade', function(request, socket, head) { | ||
var ws = new WebSocket(request, socket, head); | ||
console.log('open', ws.url, ws.version); | ||
var ws = new WebSocket(request, socket, head, ['irc', 'xmpp']); | ||
console.log('open', ws.url, ws.version, ws.protocol); | ||
@@ -32,0 +32,0 @@ ws.onmessage = function(event) { |
@@ -10,6 +10,6 @@ // API and protocol references: | ||
var Draft75Parser = require('./websocket/draft75_parser'), | ||
Draft76Parser = require('./websocket/draft76_parser'), | ||
Protocol8Parser = require('./websocket/protocol8_parser'), | ||
API = require('./websocket/api'); | ||
var Draft75Parser = require('./websocket/draft75_parser'), | ||
Draft76Parser = require('./websocket/draft76_parser'), | ||
HybiParser = require('./websocket/hybi_parser'), | ||
API = require('./websocket/api'); | ||
@@ -19,3 +19,3 @@ var getParser = function(request) { | ||
return headers['sec-websocket-version'] | ||
? Protocol8Parser | ||
? HybiParser | ||
: (headers['sec-websocket-key1'] && headers['sec-websocket-key2']) | ||
@@ -35,3 +35,3 @@ ? Draft76Parser | ||
var WebSocket = function(request, socket, head) { | ||
var WebSocket = function(request, socket, head, supportedProtos) { | ||
this.request = request; | ||
@@ -46,5 +46,8 @@ this._stream = request.socket; | ||
var Parser = getParser(request); | ||
this._parser = new Parser(this, this._stream); | ||
this._parser.handshakeResponse(head); | ||
this._parser = new Parser(this, {protocols: supportedProtos}); | ||
var handshake = this._parser.handshakeResponse(head); | ||
try { this._stream.write(handshake, 'binary') } catch (e) {} | ||
this.protocol = this._parser.protocol || ''; | ||
this.readyState = API.OPEN; | ||
@@ -60,3 +63,5 @@ this.version = this._parser.getVersion(); | ||
this._stream.addListener('data', function(data) { | ||
self._parser.parse(data); | ||
var response = self._parser.parse(data); | ||
if (!response) return; | ||
try { self._stream.write(response, 'binary') } catch (e) {} | ||
}); | ||
@@ -63,0 +68,0 @@ ['close', 'end', 'error'].forEach(function(event) { |
@@ -11,2 +11,3 @@ var API = { | ||
onclose: null, | ||
protocol: null, | ||
@@ -23,3 +24,9 @@ receive: function(data) { | ||
if (this.readyState === API.CLOSED) return false; | ||
return this._parser.frame(data, type, errorType); | ||
var frame = this._parser.frame(data, type, errorType); | ||
try { | ||
this._stream.write(frame, 'binary'); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
}, | ||
@@ -26,0 +33,0 @@ |
@@ -5,12 +5,13 @@ var API = require('./api'), | ||
var Protocol8Parser = require('./protocol8_parser'); | ||
var HybiParser = require('./hybi_parser'); | ||
var Client = function(url) { | ||
this.url = url; | ||
this.uri = require('url').parse(url); | ||
var Client = function(url, protocols) { | ||
this.url = url; | ||
this._uri = require('url').parse(url); | ||
this.protocol = ''; | ||
this.readyState = API.CONNECTING; | ||
this.bufferedAmount = 0; | ||
var secure = (this.uri.protocol === 'wss:'), | ||
var secure = (this._uri.protocol === 'wss:'), | ||
self = this, | ||
@@ -20,6 +21,6 @@ onConnect = function() { self._onConnect() }, | ||
connection = secure | ||
? tls.connect(this.uri.port || 443, this.uri.hostname, onConnect) | ||
: net.createConnection(this.uri.port || 80, this.uri.hostname); | ||
? tls.connect(this._uri.port || 443, this._uri.hostname, onConnect) | ||
: net.createConnection(this._uri.port || 80, this._uri.hostname); | ||
this._parser = new Protocol8Parser(this, connection, {masking: true}); | ||
this._parser = new HybiParser(this, {masking: true, protocols: protocols}); | ||
this._stream = connection; | ||
@@ -39,5 +40,7 @@ | ||
Client.prototype._onConnect = function() { | ||
this._handshake = this._parser.createHandshake(this.uri); | ||
this._handshake = this._parser.createHandshake(this._uri, this._stream); | ||
this._message = []; | ||
this._handshake.requestData(); | ||
try { | ||
this._stream.write(this._handshake.requestData(), 'binary'); | ||
} catch (e) {} | ||
}; | ||
@@ -55,2 +58,3 @@ | ||
if (this._handshake.isValid()) { | ||
this.protocol = this._handshake.protocol || ''; | ||
this.readyState = API.OPEN; | ||
@@ -65,3 +69,3 @@ var event = new API.Event('open'); | ||
this.readyState = API.CLOSED; | ||
var event = new API.Event('close'); | ||
var event = new API.Event('close', {code: 1006, reason: ''}); | ||
event.initEvent('close', false, false); | ||
@@ -68,0 +72,0 @@ this.dispatchEvent(event); |
@@ -1,60 +0,87 @@ | ||
var Draft75Parser = function(webSocket, stream) { | ||
this._socket = webSocket; | ||
this._stream = stream; | ||
this._buffer = []; | ||
this._buffering = false; | ||
var Draft75Parser = function(webSocket) { | ||
this._socket = webSocket; | ||
this._stage = 0; | ||
}; | ||
var instance = { | ||
FRAME_START : new Buffer([0x00]), | ||
FRAME_END : new Buffer([0xFF]), | ||
getVersion: function() { | ||
return 'draft-75'; | ||
return 'hixie-75'; | ||
}, | ||
handshakeResponse: function() { | ||
var stream = this._stream; | ||
try { | ||
stream.write( 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + | ||
'Upgrade: WebSocket\r\n' + | ||
'Connection: Upgrade\r\n' + | ||
'WebSocket-Origin: ' + this._socket.request.headers.origin + '\r\n' + | ||
'WebSocket-Location: ' + this._socket.url + '\r\n\r\n'); | ||
} catch (e) {} | ||
return new Buffer('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + | ||
'Upgrade: WebSocket\r\n' + | ||
'Connection: Upgrade\r\n' + | ||
'WebSocket-Origin: ' + this._socket.request.headers.origin + '\r\n' + | ||
'WebSocket-Location: ' + this._socket.url + '\r\n\r\n', | ||
'utf8'); | ||
}, | ||
parse: function(data) { | ||
for (var i = 0, n = data.length; i < n; i++) | ||
this._handleChar(data[i]); | ||
parse: function(buffer) { | ||
var data, message, value; | ||
for (var i = 0, n = buffer.length; i < n; i++) { | ||
data = buffer[i]; | ||
switch (this._stage) { | ||
case 0: | ||
this._parseLeadingByte(data); | ||
break; | ||
case 1: | ||
value = (data & 0x7F); | ||
this._length = value + 128 * this._length; | ||
if (this._closing && this._length === 0) { | ||
this._socket.close(null, null, false); | ||
} | ||
else if ((0x80 & data) !== 0x80) { | ||
if (this._length === 0) { | ||
this._socket.receive(''); | ||
this._stage = 0; | ||
} | ||
else { | ||
this._buffer = []; | ||
this._stage = 2; | ||
} | ||
} | ||
break; | ||
case 2: | ||
if (data === 0xFF) { | ||
message = new Buffer(this._buffer); | ||
this._socket.receive(message.toString('utf8', 0, this._buffer.length)); | ||
this._stage = 0; | ||
} | ||
else { | ||
this._buffer.push(data); | ||
if (this._length && this._buffer.length === this._length) | ||
this._stage = 0; | ||
} | ||
break; | ||
} | ||
} | ||
}, | ||
frame: function(data) { | ||
var stream = this._stream; | ||
try { | ||
stream.write(this.FRAME_START, 'binary'); | ||
stream.write(new Buffer(data), 'utf8'); | ||
stream.write(this.FRAME_END, 'binary'); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
_parseLeadingByte: function(data) { | ||
if ((0x80 & data) === 0x80) { | ||
this._length = 0; | ||
this._stage = 1; | ||
} else { | ||
delete this._length; | ||
this._buffer = []; | ||
this._stage = 2; | ||
} | ||
}, | ||
_handleChar: function(data) { | ||
switch (data) { | ||
case 0x00: | ||
this._buffering = true; | ||
break; | ||
case 0xFF: | ||
this._buffer = new Buffer(this._buffer); | ||
this._socket.receive(this._buffer.toString('utf8', 0, this._buffer.length)); | ||
this._buffer = []; | ||
this._buffering = false; | ||
break; | ||
default: | ||
if (this._buffering) this._buffer.push(data); | ||
} | ||
frame: function(data) { | ||
if (Buffer.isBuffer(data)) return data; | ||
var buffer = new Buffer(data, 'utf8'), | ||
frame = new Buffer(buffer.length + 2); | ||
frame[0] = 0x00; | ||
frame[buffer.length + 1] = 0xFF; | ||
buffer.copy(frame, 1); | ||
return frame; | ||
} | ||
@@ -61,0 +88,0 @@ }; |
@@ -26,27 +26,31 @@ var crypto = require('crypto'), | ||
Draft76Parser.prototype.getVersion = function() { | ||
return 'draft-76'; | ||
return 'hixie-76'; | ||
}; | ||
Draft76Parser.prototype.handshakeResponse = function(head) { | ||
var request = this._socket.request, | ||
stream = this._stream; | ||
var request = this._socket.request, tmp; | ||
try { | ||
stream.write( 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + | ||
'Upgrade: WebSocket\r\n' + | ||
'Connection: Upgrade\r\n' + | ||
'Sec-WebSocket-Origin: ' + request.headers.origin + '\r\n' + | ||
'Sec-WebSocket-Location: ' + this._socket.url + '\r\n\r\n', | ||
'binary'); | ||
} catch (e) {} | ||
var response = new Buffer('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + | ||
'Upgrade: WebSocket\r\n' + | ||
'Connection: Upgrade\r\n' + | ||
'Sec-WebSocket-Origin: ' + request.headers.origin + '\r\n' + | ||
'Sec-WebSocket-Location: ' + this._socket.url + '\r\n\r\n', | ||
'binary'); | ||
this.handshakeSignature(head); | ||
var signature = this.handshakeSignature(head); | ||
if (signature) { | ||
tmp = new Buffer(response.length + signature.length); | ||
response.copy(tmp, 0); | ||
signature.copy(tmp, response.length); | ||
response = tmp; | ||
} | ||
return response; | ||
}; | ||
Draft76Parser.prototype.handshakeSignature = function(head) { | ||
if (head.length === 0) return; | ||
if (head.length === 0) return null; | ||
this._handshakeComplete = true; | ||
var request = this._socket.request, | ||
stream = this._stream, | ||
@@ -65,5 +69,3 @@ key1 = request.headers['sec-websocket-key1'], | ||
try { | ||
stream.write(MD5.digest('binary'), 'binary'); | ||
} catch (e) {}; | ||
return new Buffer(MD5.digest('binary'), 'binary'); | ||
}; | ||
@@ -75,6 +77,22 @@ | ||
this.handshakeSignature(data); | ||
return this.handshakeSignature(data); | ||
}; | ||
Draft76Parser.prototype._parseLeadingByte = function(data) { | ||
if (data !== 0xFF) | ||
return Draft75Parser.prototype._parseLeadingByte.call(this, data); | ||
this._closing = true; | ||
this._length = 0; | ||
this._stage = 1; | ||
}; | ||
Draft76Parser.prototype.close = function(code, reason, callback, context) { | ||
if (this._closed) return; | ||
if (this._closing) this._socket.send(new Buffer([0xFF, 0x00])); | ||
this._closed = true; | ||
if (callback) callback.call(context); | ||
}; | ||
module.exports = Draft76Parser; | ||
{ "name" : "faye-websocket" | ||
, "description" : "Robust general-purpose WebSocket server and client" | ||
, "description" : "Standards-compliant WebSocket server and client" | ||
, "homepage" : "http://github.com/jcoglan/faye-websocket-node" | ||
@@ -7,6 +7,6 @@ , "author" : "James Coglan <jcoglan@gmail.com> (http://jcoglan.com/)" | ||
, "version" : "0.1.2" | ||
, "version" : "0.2.0" | ||
, "engines" : {"node": ">=0.4.0"} | ||
, "main" : "./lib/faye/websocket" | ||
, "devDependencies" : {"jsclass": ">=3.0.4"} | ||
, "devDependencies" : {"jsclass": ""} | ||
@@ -13,0 +13,0 @@ , "bugs" : "http://github.com/jcoglan/faye-websocket-node/issues" |
@@ -16,3 +16,3 @@ var Client = require('../../../lib/faye/websocket/client') | ||
open_socket: function(url, callback) { | ||
open_socket: function(url, protocols, callback) { | ||
var done = false, | ||
@@ -28,3 +28,3 @@ self = this, | ||
this._ws = new Client(url) | ||
this._ws = new Client(url, protocols) | ||
@@ -54,2 +54,7 @@ this._ws.onopen = function() { resume(true) } | ||
check_protocol: function(protocol, callback) { | ||
this.assertEqual( protocol, this._ws.protocol ) | ||
callback() | ||
}, | ||
listen_for_message: function(callback) { | ||
@@ -82,2 +87,3 @@ var self = this | ||
before(function() { | ||
this.protocols = ["foo", "echo"] | ||
this.plain_text_url = "ws://localhost:8000/bayeux" | ||
@@ -89,13 +95,19 @@ this.secure_url = "wss://localhost:8000/bayeux" | ||
it("can open a connection", function() { with(this) { | ||
open_socket(socket_url) | ||
open_socket(socket_url, protocols) | ||
check_open() | ||
check_protocol("echo") | ||
}}) | ||
it("cannot open a connection to the wrong host", function() { with(this) { | ||
open_socket(blocked_url) | ||
open_socket(blocked_url, protocols) | ||
check_closed() | ||
}}) | ||
it("cannot open a connection with unacceptable protocols", function() { with(this) { | ||
open_socket(socket_url, ["foo"]) | ||
check_closed() | ||
}}) | ||
it("can close the connection", function() { with(this) { | ||
open_socket(socket_url) | ||
open_socket(socket_url, protocols) | ||
close_socket() | ||
@@ -107,3 +119,3 @@ check_closed() | ||
before(function() { with(this) { | ||
open_socket(socket_url) | ||
open_socket(socket_url, protocols) | ||
}}) | ||
@@ -120,3 +132,3 @@ | ||
before(function() { with(this) { | ||
open_socket(socket_url) | ||
open_socket(socket_url, protocols) | ||
close_socket() | ||
@@ -123,0 +135,0 @@ }}) |
@@ -6,21 +6,56 @@ var Draft75Parser = require('../../../lib/faye/websocket/draft75_parser') | ||
this.webSocket = {dispatchEvent: function() {}} | ||
this.socket = new FakeSocket | ||
this.parser = new Draft75Parser(webSocket, socket) | ||
this.parser = new Draft75Parser(webSocket) | ||
}}) | ||
describe("parse", function() { with(this) { | ||
it("parses text frames", function() { with(this) { | ||
expect(webSocket, "receive").given("Hello") | ||
parser.parse([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]) | ||
sharedBehavior("draft-75 parser", function() { with(this) { | ||
it("parses text frames", function() { with(this) { | ||
expect(webSocket, "receive").given("Hello") | ||
parser.parse([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]) | ||
}}) | ||
it("parses multiple frames from the same packet", function() { with(this) { | ||
expect(webSocket, "receive").given("Hello").exactly(2) | ||
parser.parse([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]) | ||
}}) | ||
it("parses text frames beginning 0x00-0x7F", function() { with(this) { | ||
expect(webSocket, "receive").given("Hello") | ||
parser.parse([0x66, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]) | ||
}}) | ||
it("ignores frames with a length header", function() { with(this) { | ||
expect(webSocket, "receive").exactly(0) | ||
parser.parse([0x80, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]) | ||
}}) | ||
it("parses text following an ignored block", function() { with(this) { | ||
expect(webSocket, "receive").given("Hello") | ||
parser.parse([0x80, 0x02, 0x48, 0x65, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]) | ||
}}) | ||
it("parses multibyte text frames", function() { with(this) { | ||
expect(webSocket, "receive").given("Apple = ") | ||
parser.parse([0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]) | ||
}}) | ||
it("parses frames received in several packets", function() { with(this) { | ||
expect(webSocket, "receive").given("Apple = ") | ||
parser.parse([0x00, 0x41, 0x70, 0x70, 0x6c, 0x65]) | ||
parser.parse([0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]) | ||
}}) | ||
it("parses fragmented frames", function() { with(this) { | ||
expect(webSocket, "receive").given("Hello") | ||
parser.parse([0x00, 0x48, 0x65, 0x6c]) | ||
parser.parse([0x6c, 0x6f, 0xff]) | ||
}}) | ||
}}) | ||
it("parses multibyte text frames", function() { with(this) { | ||
expect(webSocket, "receive").given("Apple = ") | ||
parser.parse([0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]) | ||
}}) | ||
behavesLike("draft-75 parser") | ||
it("parses fragmented frames", function() { with(this) { | ||
expect(webSocket, "receive").given("Hello") | ||
parser.parse([0x00, 0x48, 0x65, 0x6c]) | ||
parser.parse([0x6c, 0x6f, 0xff]) | ||
it("does not close the socket if a 76 close frame is received", function() { with(this) { | ||
expect(webSocket, "close").exactly(0) | ||
expect(webSocket, "receive").given("") | ||
parser.parse([0xFF, 0x00]) | ||
}}) | ||
@@ -31,11 +66,9 @@ }}) | ||
it("returns the given string formatted as a WebSocket frame", function() { with(this) { | ||
parser.frame("Hello") | ||
assertEqual( [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], socket.read() ) | ||
assertBufferEqual( [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], parser.frame("Hello") ) | ||
}}) | ||
it("encodes multibyte characters correctly", function() { with(this) { | ||
parser.frame("Apple = ") | ||
assertEqual( [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff], socket.read() ) | ||
assertBufferEqual( [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff], parser.frame("Apple = ") ) | ||
}}) | ||
}}) | ||
}}) |
@@ -9,19 +9,2 @@ require('jsclass') | ||
JS.ENV.FakeSocket = function() { | ||
this._fragments = [] | ||
} | ||
FakeSocket.prototype.write = function(buffer, encoding) { | ||
this._fragments.push([buffer, encoding]) | ||
} | ||
FakeSocket.prototype.read = function() { | ||
var output = [] | ||
this._fragments.forEach(function(buffer, i) { | ||
for (var j = 0, n = buffer[0].length; j < n; j++) | ||
output.push(buffer[0][j]) | ||
}) | ||
return output | ||
} | ||
FakeSocket.prototype.addListener = function() {} | ||
JS.ENV.EchoServer = function() {} | ||
@@ -37,3 +20,3 @@ EchoServer.prototype.listen = function(port, ssl) { | ||
server.addListener('upgrade', function(request, socket, head) { | ||
var ws = new WebSocket(request, socket, head) | ||
var ws = new WebSocket(request, socket, head, ["echo"]) | ||
ws.onmessage = function(event) { | ||
@@ -60,7 +43,15 @@ ws.send(event.data) | ||
JS.require('JS.Test', function() { | ||
JS.Test.Unit.Assertions.define("assertBufferEqual", function(array, buffer) { | ||
this.assertEqual(array.length, buffer.length); | ||
var ary = [], n = buffer.length; | ||
while (n--) ary[n] = buffer[n]; | ||
this.assertEqual(array, ary); | ||
}) | ||
JS.require( 'ClientSpec', | ||
'Draft75ParserSpec', | ||
'Protocol8ParserSpec', | ||
'Draft76ParserSpec', | ||
'HybiParserSpec', | ||
JS.Test.method('autorun')) | ||
}) | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1224
145
59441
24
7