websocket-driver
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -7,22 +7,6 @@ // Protocol references: | ||
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) { | ||
@@ -34,15 +18,10 @@ options = 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); | ||
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); | ||
http: function() { | ||
return Server.http.apply(Server, arguments); | ||
}, | ||
@@ -49,0 +28,0 @@ |
var Emitter = require('events').EventEmitter, | ||
util = require('util'), | ||
streams = require('../streams'); | ||
streams = require('../streams'), | ||
Headers = require('./headers'); | ||
@@ -10,2 +11,3 @@ var Base = function(request, url, options) { | ||
this._options = options || {}; | ||
this.__headers = new Headers(); | ||
this.__queue = []; | ||
@@ -15,26 +17,5 @@ this.readyState = 0; | ||
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(); | ||
}; | ||
@@ -46,2 +27,26 @@ util.inherits(Base, Emitter); | ||
_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() { | ||
@@ -51,2 +56,8 @@ 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() { | ||
@@ -100,2 +111,4 @@ if (this.readyState !== 0) return false; | ||
Base.ConnectEvent = function() {}; | ||
Base.OpenEvent = function() {}; | ||
@@ -102,0 +115,0 @@ |
@@ -1,4 +0,4 @@ | ||
var HTTPParser = process.binding('http_parser').HTTPParser, | ||
url = require('url'), | ||
var url = require('url'), | ||
util = require('util'), | ||
HttpParser = require('./http_parser'), | ||
Base = require('./base'), | ||
@@ -14,27 +14,3 @@ Hybi = require('./hybi'); | ||
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'); | ||
}; | ||
@@ -60,7 +36,7 @@ util.inherits(Client, Hybi); | ||
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); | ||
}, | ||
@@ -82,3 +58,3 @@ | ||
return new Buffer(headers.concat('','').join('\r\n'), 'utf8'); | ||
return new Buffer(headers.concat(this.__headers.toString(), '').join('\r\n'), 'utf8'); | ||
}, | ||
@@ -94,10 +70,14 @@ | ||
_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 === '') | ||
@@ -104,0 +84,0 @@ return this._failHandshake("'Upgrade' header is missing"); |
@@ -87,2 +87,3 @@ var Base = require('./base'), | ||
'WebSocket-Location: ' + this.url + '\r\n' + | ||
this.__headers.toString() + | ||
'\r\n', | ||
@@ -89,0 +90,0 @@ 'utf8'); |
@@ -56,2 +56,3 @@ var Base = require('./base'), | ||
'Sec-WebSocket-Location: ' + this.url + '\r\n' + | ||
this.__headers.toString() + | ||
'\r\n', | ||
@@ -58,0 +59,0 @@ 'binary'); |
@@ -242,3 +242,3 @@ var crypto = require('crypto'), | ||
return new Buffer(headers.concat('','').join('\r\n'), 'utf8'); | ||
return new Buffer(headers.concat(this.__headers.toString(), '').join('\r\n'), 'utf8'); | ||
}, | ||
@@ -245,0 +245,0 @@ |
@@ -7,3 +7,3 @@ { "name" : "websocket-driver" | ||
, "version" : "0.1.0" | ||
, "version" : "0.2.0" | ||
, "engines" : {"node": ">=0.4.0"} | ||
@@ -10,0 +10,0 @@ , "main" : "./lib/websocket/driver" |
@@ -46,3 +46,3 @@ # websocket-driver [![Build Status](https://travis-ci.org/faye/websocket-driver-node.png)](https://travis-ci.org/faye/websocket-driver-node) | ||
### Server-side | ||
### Server-side with HTTP | ||
@@ -81,2 +81,49 @@ A Node webserver emits a special event for 'upgrade' requests, and this is | ||
### 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); | ||
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 | ||
@@ -108,3 +155,9 @@ | ||
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 | ||
@@ -116,2 +169,3 @@ | ||
driver = websocket.http(request, options) | ||
driver = websocket.server(options) | ||
driver = websocket.client(url, options) | ||
@@ -121,4 +175,6 @@ ``` | ||
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. | ||
@@ -172,2 +228,8 @@ The `options` argument is optional, and is an object. It may contain the | ||
#### `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()` | ||
@@ -174,0 +236,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49059
15
1032
309
1