PageRenderTime 36ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/js/lib/Socket.IO-node/lib/socket.io/transports/websocket/index.js

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
JavaScript | 203 lines | 125 code | 37 blank | 41 comment | 18 complexity | fc87814577db5783fdbb0589aa7f9f0f MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /*!
  2. * Socket.IO - transports - WebSocket
  3. * Copyright (c) 2010-2011 Guillermo Rauch <guillermo@learnboost.com>
  4. * MIT Licensed
  5. */
  6. var Client = require('../../client')
  7. , Stream = require('net').Stream
  8. , EventEmitter = require('events').EventEmitter
  9. , Parser = require('./parser')
  10. , crypto = require('crypto')
  11. , url = require('url');
  12. /**
  13. * Expose `WebSocket`.
  14. */
  15. module.exports = WebSocket;
  16. /**
  17. * Initialize a `WebSocket` client.
  18. *
  19. * @api private
  20. */
  21. function WebSocket(){
  22. Client.apply(this, arguments);
  23. };
  24. /**
  25. * Inherit from `Client.prototype`.
  26. */
  27. WebSocket.prototype.__proto__ = Client.prototype;
  28. WebSocket.prototype._onConnect = function(req, socket){
  29. var self = this
  30. , headers = [];
  31. if (!req.connection.setTimeout){
  32. req.connection.end();
  33. return false;
  34. }
  35. this.parser = new Parser();
  36. this.parser.on('data', self._onMessage.bind(this));
  37. this.parser.on('error', self._onClose.bind(this));
  38. Client.prototype._onConnect.call(this, req);
  39. if (this.request.headers.upgrade !== 'WebSocket' || !this._verifyOrigin(this.request.headers.origin)){
  40. this.listener.log('WebSocket connection invalid or Origin not verified');
  41. this._onClose();
  42. return false;
  43. }
  44. var origin = this.request.headers.origin,
  45. location = (this.request.socket.encrypted ? 'wss' : 'ws')
  46. + '://' + this.request.headers.host + this.request.url;
  47. this.waitingForNonce = false;
  48. if ('sec-websocket-key1' in this.request.headers){
  49. /* We need to send the 101 response immediately when using Draft 76 with
  50. a load balancing proxy, such as HAProxy. In order to protect an
  51. unsuspecting non-websocket HTTP server, HAProxy will not send the
  52. 8-byte nonce through the connection until the Upgrade: WebSocket
  53. request has been confirmed by the WebSocket server by a 101 response
  54. indicating that the server can handle the upgraded protocol. We
  55. therefore must send the 101 response immediately, and then wait for
  56. the nonce to be forwarded to us afterward in order to finish the
  57. Draft 76 handshake.
  58. */
  59. // If we don't have the nonce yet, wait for it.
  60. if (!(this.upgradeHead && this.upgradeHead.length >= 8)) {
  61. this.waitingForNonce = true;
  62. }
  63. headers = [
  64. 'HTTP/1.1 101 WebSocket Protocol Handshake'
  65. , 'Upgrade: WebSocket'
  66. , 'Connection: Upgrade'
  67. , 'Sec-WebSocket-Origin: ' + origin
  68. , 'Sec-WebSocket-Location: ' + location
  69. ];
  70. if ('sec-websocket-protocol' in this.request.headers){
  71. headers.push('Sec-WebSocket-Protocol: ' + this.request.headers['sec-websocket-protocol']);
  72. }
  73. } else {
  74. headers = [
  75. 'HTTP/1.1 101 Web Socket Protocol Handshake'
  76. , 'Upgrade: WebSocket'
  77. , 'Connection: Upgrade'
  78. , 'WebSocket-Origin: ' + origin
  79. , 'WebSocket-Location: ' + location
  80. ];
  81. }
  82. try {
  83. this.connection.write(headers.concat('', '').join('\r\n'));
  84. this.connection.setTimeout(0);
  85. this.connection.setNoDelay(true);
  86. this.connection.setEncoding('utf-8');
  87. } catch(e){
  88. this._onClose();
  89. return;
  90. }
  91. if (this.waitingForNonce) {
  92. // Since we will be receiving the binary nonce through the normal HTTP
  93. // data event, set the connection to 'binary' temporarily
  94. this.connection.setEncoding('binary');
  95. this._headers = headers;
  96. }
  97. else {
  98. if (this._proveReception(headers)) this._payload();
  99. }
  100. this.buffer = '';
  101. this.connection.on('data', function(data){
  102. if (self.waitingForNonce) {
  103. self.buffer += data;
  104. if (self.buffer.length < 8) return;
  105. // Restore the connection to utf8 encoding after receiving the nonce
  106. self.connection.setEncoding('utf8');
  107. self.waitingForNonce = false;
  108. // Stuff the nonce into the location where it's expected to be
  109. self.upgradeHead = self.buffer.substr(0,8);
  110. self.buffer = '';
  111. if (self._proveReception(self._headers)) self._payload();
  112. return;
  113. }
  114. self.parser.write(data);
  115. });
  116. };
  117. // http://www.whatwg.org/specs/web-apps/current-work/complete/network.html#opening-handshake
  118. WebSocket.prototype._proveReception = function(headers){
  119. var self = this
  120. , k1 = this.request.headers['sec-websocket-key1']
  121. , k2 = this.request.headers['sec-websocket-key2'];
  122. if (k1 && k2){
  123. var md5 = crypto.createHash('md5');
  124. [k1, k2].forEach(function(k){
  125. var n = parseInt(k.replace(/[^\d]/g, '')),
  126. spaces = k.replace(/[^ ]/g, '').length;
  127. if (spaces === 0 || n % spaces !== 0){
  128. self.listener.log('Invalid WebSocket key: "' + k + '". Dropping connection');
  129. self._onClose();
  130. return false;
  131. }
  132. n /= spaces;
  133. md5.update(String.fromCharCode(
  134. n >> 24 & 0xff,
  135. n >> 16 & 0xff,
  136. n >> 8 & 0xff,
  137. n & 0xff));
  138. });
  139. md5.update(this.upgradeHead.toString('binary'));
  140. try {
  141. this.connection.write(md5.digest('binary'), 'binary');
  142. } catch(e){
  143. this._onClose();
  144. }
  145. }
  146. return true;
  147. };
  148. /**
  149. * Write implementation.
  150. *
  151. * @param {String} message
  152. * @api private
  153. */
  154. WebSocket.prototype._write = function(message){
  155. try {
  156. this.connection.write('\u0000', 'binary');
  157. this.connection.write(message, 'utf8');
  158. this.connection.write('\uffff', 'binary');
  159. } catch(e){
  160. this._onClose();
  161. }
  162. };
  163. /**
  164. * Requires upgrade.
  165. */
  166. WebSocket.httpUpgrade = true;