| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 | 'use strict';var Buffer     = require('safe-buffer').Buffer,    crypto     = require('crypto'),    url        = require('url'),    util       = require('util'),    HttpParser = require('../http_parser'),    Base       = require('./base'),    Hybi       = require('./hybi'),    Proxy      = require('./proxy');var Client = function(_url, options) {  this.version = 'hybi-' + Hybi.VERSION;  Hybi.call(this, null, _url, options);  this.readyState = -1;  this._key       = Client.generateKey();  this._accept    = Hybi.generateAccept(this._key);  this._http      = new HttpParser('response');  var uri  = url.parse(this.url),      auth = uri.auth && Buffer.from(uri.auth, 'utf8').toString('base64');  if (this.VALID_PROTOCOLS.indexOf(uri.protocol) < 0)    throw new Error(this.url + ' is not a valid WebSocket URL');  this._pathname = (uri.pathname || '/') + (uri.search || '');  this._headers.set('Host', uri.host);  this._headers.set('Upgrade', 'websocket');  this._headers.set('Connection', 'Upgrade');  this._headers.set('Sec-WebSocket-Key', this._key);  this._headers.set('Sec-WebSocket-Version', Hybi.VERSION);  if (this._protocols.length > 0)    this._headers.set('Sec-WebSocket-Protocol', this._protocols.join(', '));  if (auth)    this._headers.set('Authorization', 'Basic ' + auth);};util.inherits(Client, Hybi);Client.generateKey = function() {  return crypto.randomBytes(16).toString('base64');};var instance = {  VALID_PROTOCOLS: ['ws:', 'wss:'],  proxy: function(origin, options) {    return new Proxy(this, origin, options);  },  start: function() {    if (this.readyState !== -1) return false;    this._write(this._handshakeRequest());    this.readyState = 0;    return true;  },  parse: function(chunk) {    if (this.readyState === 3) return;    if (this.readyState > 0) return Hybi.prototype.parse.call(this, chunk);    this._http.parse(chunk);    if (!this._http.isComplete()) return;    this._validateHandshake();    if (this.readyState === 3) return;    this._open();    this.parse(this._http.body);  },  _handshakeRequest: function() {    var extensions = this._extensions.generateOffer();    if (extensions)      this._headers.set('Sec-WebSocket-Extensions', extensions);    var start   = 'GET ' + this._pathname + ' HTTP/1.1',        headers = [start, this._headers.toString(), ''];    return Buffer.from(headers.join('\r\n'), 'utf8');  },  _failHandshake: function(message) {    message = 'Error during WebSocket handshake: ' + message;    this.readyState = 3;    this.emit('error', new Error(message));    this.emit('close', new Base.CloseEvent(this.ERRORS.protocol_error, message));  },  _validateHandshake: function() {    this.statusCode = this._http.statusCode;    this.headers    = this._http.headers;    if (this._http.error)      return this._failHandshake(this._http.error.message);    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");    if (upgrade.toLowerCase() !== 'websocket')      return this._failHandshake("'Upgrade' header value is not 'WebSocket'");    if (connection === '')      return this._failHandshake("'Connection' header is missing");    if (connection.toLowerCase() !== 'upgrade')      return this._failHandshake("'Connection' header value is not 'Upgrade'");    if (accept !== this._accept)      return this._failHandshake('Sec-WebSocket-Accept mismatch');    this.protocol = null;    if (protocol !== '') {      if (this._protocols.indexOf(protocol) < 0)        return this._failHandshake('Sec-WebSocket-Protocol mismatch');      else        this.protocol = protocol;    }    try {      this._extensions.activate(this.headers['sec-websocket-extensions']);    } catch (e) {      return this._failHandshake(e.message);    }  }};for (var key in instance)  Client.prototype[key] = instance[key];module.exports = Client;
 |