94 lines
3.1 KiB
JavaScript
94 lines
3.1 KiB
JavaScript
// API and protocol 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
|
|
// * http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
|
// * http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
|
// * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
|
|
|
|
var Draft75Parser = require('./websocket/draft75_parser'),
|
|
Draft76Parser = require('./websocket/draft76_parser'),
|
|
HybiParser = require('./websocket/hybi_parser'),
|
|
API = require('./websocket/api'),
|
|
Event = require('./websocket/api/event');
|
|
|
|
var getParser = function(request) {
|
|
var headers = request.headers;
|
|
return headers['sec-websocket-version']
|
|
? HybiParser
|
|
: (headers['sec-websocket-key1'] && headers['sec-websocket-key2'])
|
|
? Draft76Parser
|
|
: Draft75Parser;
|
|
};
|
|
|
|
var isSecureConnection = 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);
|
|
}
|
|
};
|
|
|
|
var WebSocket = function(request, socket, head, supportedProtos, options) {
|
|
this.request = request;
|
|
this._stream = request.socket;
|
|
this._ping = options && options.ping;
|
|
this._pingId = 0;
|
|
|
|
var scheme = isSecureConnection(request) ? 'wss:' : 'ws:';
|
|
this.url = scheme + '//' + request.headers.host + request.url;
|
|
this.readyState = API.CONNECTING;
|
|
this.bufferedAmount = 0;
|
|
|
|
var Parser = getParser(request);
|
|
this._parser = new Parser(this, {protocols: supportedProtos});
|
|
|
|
var self = this;
|
|
this._sendBuffer = [];
|
|
process.nextTick(function() { self._open() });
|
|
|
|
var handshake = this._parser.handshakeResponse(head);
|
|
if (this._parser.isOpen()) this.readyState = API.OPEN;
|
|
|
|
if (this._ping)
|
|
this._pingLoop = setInterval(function() {
|
|
self._pingId += 1;
|
|
self.ping(self._pingId.toString());
|
|
}, this._ping * 1000);
|
|
|
|
this.protocol = this._parser.protocol || '';
|
|
this.version = this._parser.getVersion();
|
|
|
|
if (!this._stream || !this._stream.writable) return;
|
|
|
|
this._stream.setTimeout(0);
|
|
this._stream.setNoDelay(true);
|
|
|
|
try { this._stream.write(handshake, 'binary') } catch (e) {}
|
|
|
|
this._stream.addListener('data', function(data) {
|
|
var response = self._parser.parse(data);
|
|
if (!response) return;
|
|
try { self._stream.write(response, 'binary') } catch (e) {}
|
|
self._open();
|
|
});
|
|
['close', 'end', 'error'].forEach(function(event) {
|
|
self._stream.addListener(event, function() { self.close(1006, '', false) });
|
|
});
|
|
};
|
|
|
|
WebSocket.prototype.ping = function(message, callback, context) {
|
|
if (!this._parser.ping) return false;
|
|
return this._parser.ping(message, callback, context);
|
|
};
|
|
|
|
for (var key in API) WebSocket.prototype[key] = API[key];
|
|
|
|
WebSocket.WebSocket = WebSocket;
|
|
WebSocket.Client = require('./websocket/client');
|
|
WebSocket.EventSource = require('./eventsource');
|
|
module.exports = WebSocket;
|
|
|