Compare commits

..

13 Commits

Author SHA1 Message Date
James Coglan 11a9b75185 Bump version to 0.3.1. 2013-12-03 00:46:42 +00:00
James Coglan d93c853414 Don't pre-allocate a huge Buffer before we know there's enough chunks in the read queue to complete a message. 2013-12-02 21:07:33 +00:00
James Coglan 2dff35d3e4 Map Node v0.11's magic numbers for HTTP methods. 2013-12-02 13:02:38 +00:00
James Coglan 6588928445 Make HttpParser work on v0.11.6+. 2013-11-29 00:42:49 +00:00
James Coglan 151fddd206 Make the maximum frame length into an option that the user can set. 2013-11-28 23:38:42 +00:00
James Coglan 0b1f16a7ee Merge pull request #2 from meteor/limit-frame-to-max-buffer
Reduce frame max length to max Node Buffer length.
2013-11-28 15:25:05 -08:00
David Glasser f15b331a34 Reduce frame max length to max Node Buffer length. 2013-10-11 19:21:14 -07:00
James Coglan 95261a1779 Bump version to 0.3.0. 2013-09-09 20:15:10 +01:00
James Coglan 12f9f4d444 Add support for Basic Auth URLs to the client driver. 2013-08-13 17:16:49 +01:00
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
13 changed files with 123 additions and 59 deletions
+14
View File
@@ -1,3 +1,17 @@
### 0.3.1 / 2013-12-03
* Add a `maxLength` option to limit allowed frame size
* Don't pre-allocate a message buffer until the whole frame has arrived
* Fix compatibility with Node v0.11 `HTTPParser`
### 0.3.0 / 2013-09-09
* Support client URLs with Basic Auth credentials
### 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
+6 -7
View File
@@ -62,15 +62,14 @@ 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
@@ -104,8 +103,7 @@ var server = net.createServer(function(connection) {
driver.on('close', function() { connection.end() });
connection.on('error', function() {});
connection.pipe(driver.io);
driver.io.pipe(connection);
connection.pipe(driver.io).pipe(connection);
driver.messages.pipe(driver.messages);
});
@@ -138,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);
@@ -176,6 +173,8 @@ with masking enabled on outgoing frames.
The `options` argument is optional, and is an object. It may contain the
following fields:
* `maxLength` - the maximum allowed size of incoming message frames, in bytes.
The default value is `2^30 - 1`, or 1 byte short of 1 GiB.
* `protocols` - an array of strings representing acceptable subprotocols for
use over the socket. The driver will negotiate one of these to use via the
`Sec-WebSocket-Protocol` header if supported by the other peer.
+3
View File
@@ -53,6 +53,9 @@ var instance = {
if (this._protocols.length > 0)
headers.push('Sec-WebSocket-Protocol: ' + this._protocols.join(', '));
if (uri.auth)
headers.push('Authorization: Basic ' + new Buffer(uri.auth, 'utf8').toString('base64'));
return new Buffer(headers.concat(this.__headers.toString(), '').join('\r\n'), 'utf8');
},
+27 -3
View File
@@ -22,8 +22,8 @@ var HttpParser = function(type) {
self.headers[current] = b.toString('utf8', start, start + length);
};
this._parser.onHeadersComplete = function(info) {
self.method = info.method;
this._parser.onHeadersComplete = this._parser[HTTPParser.kOnHeadersComplete] = function(info) {
self.method = (typeof info.method === 'number') ? HttpParser.METHODS[info.method] : info.method;
self.statusCode = info.statusCode;
self.url = info.url;
@@ -34,11 +34,35 @@ var HttpParser = function(type) {
self.headers[headers[i].toLowerCase()] = headers[i+1];
};
this._parser.onMessageComplete = function() {
this._parser.onMessageComplete = this._parser[HTTPParser.kOnMessageComplete] = function() {
self._complete = true;
};
};
HttpParser.METHODS = {
0: 'DELETE',
1: 'GET',
2: 'HEAD',
3: 'POST',
4: 'PUT',
5: 'CONNECT',
6: 'OPTIONS',
7: 'TRACE',
8: 'COPY',
9: 'LOCK',
10: 'MKCOL',
11: 'MOVE',
12: 'PROPFIND',
13: 'PROPPATCH',
14: 'SEARCH',
15: 'UNLOCK',
16: 'REPORT',
17: 'MKACTIVITY',
18: 'CHECKOUT',
19: 'MERGE',
24: 'PATCH'
};
HttpParser.prototype.isComplete = function() {
return this._complete;
};
+5 -2
View File
@@ -11,6 +11,7 @@ var Hybi = function(request, url, options) {
this._stage = 0;
this._masking = this._options.masking;
this._protocols = this._options.protocols || [];
this._maxLength = this._options.maxLength || this.MAX_LENGTH;
if (typeof this._protocols === 'string')
this._protocols = this._protocols.split(/\s*,\s*/);
@@ -66,7 +67,9 @@ var instance = {
FRAGMENTED_OPCODES: [0, 1, 2],
OPENING_OPCODES: [1, 2],
MAX_LENGTH: Math.pow(2, 53) - 1,
// This is the maximum length of a Node buffer:
// https://github.com/joyent/node/blob/master/src/smalloc.h#L40
MAX_LENGTH: 0x3fffffff,
TWO_POWERS: [0, 1, 2, 3, 4, 5, 6, 7].map(function(n) { return Math.pow(2, 8 * n) }),
ERRORS: {
@@ -303,7 +306,7 @@ var instance = {
if (this.FRAGMENTED_OPCODES.indexOf(this._opcode) < 0 && this._length > 125)
return this._fail('protocol_error', 'Received control frame having too long payload: ' + this._length);
if (this._length > this.MAX_LENGTH)
if (this._length > this._maxLength)
return this._fail('too_large', 'WebSocket frame length too large');
this._stage = this._masked ? 3 : 4;
+7 -4
View File
@@ -1,6 +1,7 @@
var StreamReader = function() {
this._queue = [];
this._cursor = 0;
this._queue = [];
this._queueSize = 0;
this._cursor = 0;
};
StreamReader.prototype.read = function(bytes) {
@@ -11,9 +12,12 @@ StreamReader.prototype.put = function(buffer) {
if (!buffer || buffer.length === 0) return;
if (!buffer.copy) buffer = new Buffer(buffer);
this._queue.push(buffer);
this._queueSize += buffer.length;
};
StreamReader.prototype._readBuffer = function(length) {
if (length > this._queueSize) return null;
var buffer = new Buffer(length),
queue = this._queue,
remain = length,
@@ -29,11 +33,10 @@ StreamReader.prototype._readBuffer = function(length) {
size = Math.min(remain, chunk.length - offset);
chunk.copy(buffer, length - remain, offset, offset + size);
remain -= size;
this._queueSize -= size;
i += 1;
}
if (remain > 0) return null;
queue.splice(0, i-1);
this._cursor = (i === 1 ? this._cursor : 0) + size;
+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.2.1"
, "version" : "0.3.1"
, "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')
+25 -3
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()}
})
@@ -9,9 +10,13 @@ JS.Test.describe("Client", function() { with(this) {
null
})
define("url", function() {
return "ws://www.example.com/socket"
})
define("driver", function() {
if (this._driver) return this._driver
this._driver = new Client("ws://www.example.com/socket", this.options())
this._driver = new Client(this.url(), this.options())
var self = this
this._driver.on('open', function(e) { self.open = true })
this._driver.on('message', function(e) { self.message += e.data })
@@ -79,6 +84,23 @@ JS.Test.describe("Client", function() { with(this) {
}})
}})
describe("with basic auth", function() { with(this) {
define("url", function() { return "ws://user:pass@www.example.com/socket" })
it("writes the handshake with Authorization", 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" +
"Authorization: Basic dXNlcjpwYXNz\r\n" +
"\r\n"))
driver().start()
}})
}})
describe("with custom headers", function() { with(this) {
before(function() { with(this) {
driver().setHeader("User-Agent", "Chrome")
+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() {
+4 -3
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: {
@@ -307,7 +308,7 @@ JS.Test.describe("Hybi", function() { with(this) {
}})
it("returns an error for too-large frames", function() { with(this) {
driver().parse([0x81, 0x7f, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
driver().parse([0x81, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00])
assertEqual( "WebSocket frame length too large", error.message )
assertEqual( [1009, "WebSocket frame length too large"], close )
assertEqual( "closed", driver().getState() )