listener.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. var url = require('url')
  2. , util = require(process.binding('natives').util ? 'util' : 'sys')
  3. , fs = require('fs')
  4. , options = require('./utils').options
  5. , Client = require('./client')
  6. , clientVersion = require('./../../support/socket.io-client/lib/io').io.version
  7. , transports = {};
  8. var Listener = module.exports = function(server, options){
  9. process.EventEmitter.call(this);
  10. var self = this;
  11. this.server = server;
  12. this.options({
  13. origins: '*:*',
  14. resource: 'socket.io',
  15. flashPolicyServer: true,
  16. transports: ['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart',
  17. 'xhr-polling', 'jsonp-polling'],
  18. transportOptions: {},
  19. log: util.log
  20. }, options);
  21. if (!this.options.log) this.options.log = function(){};
  22. this.clients = this.clientsIndex = {};
  23. this._clientCount = 0;
  24. this._clientFiles = {};
  25. var listeners = this.server.listeners('request');
  26. this.server.removeAllListeners('request');
  27. this.server.addListener('request', function(req, res){
  28. if (self.check(req, res)) return;
  29. for (var i = 0, len = listeners.length; i < len; i++){
  30. listeners[i].call(this, req, res);
  31. }
  32. });
  33. this.server.addListener('upgrade', function(req, socket, head){
  34. if (!self.check(req, socket, true, head)){
  35. socket.end();
  36. socket.destroy();
  37. }
  38. });
  39. this.options.transports.forEach(function(name) {
  40. if (!(name in transports))
  41. transports[name] = require('./transports/' + name);
  42. if ('init' in transports[name]) transports[name].init(self);
  43. });
  44. this.options.log('socket.io ready - accepting connections');
  45. };
  46. util.inherits(Listener, process.EventEmitter);
  47. for (var i in options) Listener.prototype[i] = options[i];
  48. Listener.prototype.broadcast = function(message, except){
  49. for (var i = 0, k = Object.keys(this.clients), l = k.length; i < l; i++){
  50. if (!except || ((typeof except == 'number' || typeof except == 'string') && k[i] != except)
  51. || (Array.isArray(except) && except.indexOf(k[i]) == -1)){
  52. this.clients[k[i]].send(message);
  53. }
  54. }
  55. return this;
  56. };
  57. Listener.prototype.check = function(req, res, httpUpgrade, head){
  58. var path = url.parse(req.url).pathname, parts, cn;
  59. if (path && path.indexOf('/' + this.options.resource) === 0){
  60. parts = path.substr(2 + this.options.resource.length).split('/');
  61. if (this._serveClient(parts.join('/'), req, res)) return true;
  62. if (!(parts[0] in transports)) return false;
  63. if (parts[1]){
  64. cn = this.clients[parts[1]];
  65. if (cn){
  66. cn._onConnect(req, res);
  67. } else {
  68. req.connection.end();
  69. req.connection.destroy();
  70. this.options.log('Couldnt find client with session id "' + parts[1] + '"');
  71. }
  72. } else {
  73. this._onConnection(parts[0], req, res, httpUpgrade, head);
  74. }
  75. return true;
  76. }
  77. return false;
  78. };
  79. Listener.prototype._serveClient = function(file, req, res){
  80. var self = this
  81. , clientPaths = {
  82. 'socket.io.js': 'socket.io.js',
  83. 'lib/vendor/web-socket-js/WebSocketMain.swf': 'lib/vendor/web-socket-js/WebSocketMain.swf', // for compat with old clients
  84. 'WebSocketMain.swf': 'lib/vendor/web-socket-js/WebSocketMain.swf'
  85. }
  86. , types = {
  87. swf: 'application/x-shockwave-flash',
  88. js: 'text/javascript'
  89. };
  90. function write(path){
  91. if (req.headers['if-none-match'] == clientVersion){
  92. res.writeHead(304);
  93. res.end();
  94. } else {
  95. res.writeHead(200, self._clientFiles[path].headers);
  96. res.end(self._clientFiles[path].content, self._clientFiles[path].encoding);
  97. }
  98. };
  99. var path = clientPaths[file];
  100. if (req.method == 'GET' && path !== undefined){
  101. if (path in this._clientFiles){
  102. write(path);
  103. return true;
  104. }
  105. fs.readFile(__dirname + '/../../support/socket.io-client/' + path, function(err, data){
  106. if (err){
  107. res.writeHead(404);
  108. res.end('404');
  109. } else {
  110. var ext = path.split('.').pop();
  111. self._clientFiles[path] = {
  112. headers: {
  113. 'Content-Length': data.length,
  114. 'Content-Type': types[ext],
  115. 'ETag': clientVersion
  116. },
  117. content: data,
  118. encoding: ext == 'swf' ? 'binary' : 'utf8'
  119. };
  120. write(path);
  121. }
  122. });
  123. return true;
  124. }
  125. return false;
  126. };
  127. Listener.prototype._onClientConnect = function(client){
  128. this.clients[client.sessionId] = client;
  129. this.options.log('Client '+ client.sessionId +' connected');
  130. this.emit('clientConnect', client);
  131. this.emit('connection', client);
  132. };
  133. Listener.prototype._onClientMessage = function(data, client){
  134. this.emit('clientMessage', data, client);
  135. };
  136. Listener.prototype._onClientDisconnect = function(client){
  137. delete this.clients[client.sessionId];
  138. this.options.log('Client '+ client.sessionId +' disconnected');
  139. this.emit('clientDisconnect', client);
  140. };
  141. Listener.prototype._onConnection = function(transport, req, res, httpUpgrade, head){
  142. this.options.log('Initializing client with transport "'+ transport +'"');
  143. new transports[transport](this, req, res, this.options.transportOptions[transport], head);
  144. };