draft76.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. 'use strict';
  2. var Base = require('./base'),
  3. Draft75 = require('./draft75'),
  4. crypto = require('crypto'),
  5. util = require('util');
  6. var numberFromKey = function(key) {
  7. return parseInt(key.match(/[0-9]/g).join(''), 10);
  8. };
  9. var spacesInKey = function(key) {
  10. return key.match(/ /g).length;
  11. };
  12. var Draft76 = function(request, url, options) {
  13. Draft75.apply(this, arguments);
  14. this._stage = -1;
  15. this._body = [];
  16. this.version = 'hixie-76';
  17. this._headers.clear();
  18. this._headers.set('Upgrade', 'WebSocket');
  19. this._headers.set('Connection', 'Upgrade');
  20. this._headers.set('Sec-WebSocket-Origin', this._request.headers.origin);
  21. this._headers.set('Sec-WebSocket-Location', this.url);
  22. };
  23. util.inherits(Draft76, Draft75);
  24. var instance = {
  25. BODY_SIZE: 8,
  26. start: function() {
  27. if (!Draft75.prototype.start.call(this)) return false;
  28. this._started = true;
  29. this._sendHandshakeBody();
  30. return true;
  31. },
  32. close: function() {
  33. if (this.readyState === 3) return false;
  34. this._write(new Buffer([0xFF, 0x00]));
  35. this.readyState = 3;
  36. this.emit('close', new Base.CloseEvent(null, null));
  37. return true;
  38. },
  39. _handshakeResponse: function() {
  40. var headers = this._request.headers,
  41. key1 = headers['sec-websocket-key1'],
  42. number1 = numberFromKey(key1),
  43. spaces1 = spacesInKey(key1),
  44. key2 = headers['sec-websocket-key2'],
  45. number2 = numberFromKey(key2),
  46. spaces2 = spacesInKey(key2);
  47. if (number1 % spaces1 !== 0 || number2 % spaces2 !== 0) {
  48. this.emit('error', new Error('Client sent invalid Sec-WebSocket-Key headers'));
  49. this.close();
  50. return null;
  51. }
  52. this._keyValues = [number1 / spaces1, number2 / spaces2];
  53. var start = 'HTTP/1.1 101 WebSocket Protocol Handshake',
  54. headers = [start, this._headers.toString(), ''];
  55. return new Buffer(headers.join('\r\n'), 'binary');
  56. },
  57. _handshakeSignature: function() {
  58. if (this._body.length < this.BODY_SIZE) return null;
  59. var md5 = crypto.createHash('md5'),
  60. buffer = new Buffer(8 + this.BODY_SIZE);
  61. buffer.writeUInt32BE(this._keyValues[0], 0);
  62. buffer.writeUInt32BE(this._keyValues[1], 4);
  63. new Buffer(this._body).copy(buffer, 8, 0, this.BODY_SIZE);
  64. md5.update(buffer);
  65. return new Buffer(md5.digest('binary'), 'binary');
  66. },
  67. _sendHandshakeBody: function() {
  68. if (!this._started) return;
  69. var signature = this._handshakeSignature();
  70. if (!signature) return;
  71. this._write(signature);
  72. this._stage = 0;
  73. this._open();
  74. if (this._body.length > this.BODY_SIZE)
  75. this.parse(this._body.slice(this.BODY_SIZE));
  76. },
  77. _parseLeadingByte: function(octet) {
  78. if (octet !== 0xFF)
  79. return Draft75.prototype._parseLeadingByte.call(this, octet);
  80. this._closing = true;
  81. this._length = 0;
  82. this._stage = 1;
  83. }
  84. };
  85. for (var key in instance)
  86. Draft76.prototype[key] = instance[key];
  87. module.exports = Draft76;