Server.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /**
  2. * @file Represents a websocket server
  3. */
  4. 'use strict'
  5. function nop() {}
  6. var util = require('util'),
  7. net = require('net'),
  8. tls = require('tls'),
  9. events = require('events'),
  10. Connection
  11. /**
  12. * @callback SelectProtocolCallback
  13. * @param {Connection} connection
  14. * @param {Array<string>} protocols
  15. * @returns {?string}
  16. */
  17. /**
  18. * Creates a new ws server and starts listening for new connections
  19. * @class
  20. * @param {boolean} secure indicates if it should use tls
  21. * @param {Object} [options] will be passed to net.createServer() or tls.createServer()
  22. * @param {Array<string>} [options.validProtocols]
  23. * @param {SelectProtocolCallback} [options.selectProtocol]
  24. * @param {Function} [callback] will be added as "connection" listener
  25. * @inherits EventEmitter
  26. * @event listening
  27. * @event close
  28. * @event error an error object is passed
  29. * @event connection a Connection object is passed
  30. */
  31. function Server(secure, options, callback) {
  32. var that = this
  33. if (typeof options === 'function') {
  34. callback = options
  35. options = undefined
  36. }
  37. var onConnection = function (socket) {
  38. var conn = new Connection(socket, that, function () {
  39. that.connections.push(conn)
  40. conn.removeListener('error', nop)
  41. that.emit('connection', conn)
  42. })
  43. conn.on('close', function () {
  44. var pos = that.connections.indexOf(conn)
  45. if (pos !== -1) {
  46. that.connections.splice(pos, 1)
  47. }
  48. })
  49. // Ignore errors before the connection is established
  50. conn.on('error', nop)
  51. }
  52. if (secure) {
  53. this.socket = tls.createServer(options, onConnection)
  54. } else {
  55. this.socket = net.createServer(options, onConnection)
  56. }
  57. this.socket.on('close', function () {
  58. that.emit('close')
  59. })
  60. this.socket.on('error', function (err) {
  61. that.emit('error', err)
  62. })
  63. this.connections = []
  64. // super constructor
  65. events.EventEmitter.call(this)
  66. if (callback) {
  67. this.on('connection', callback)
  68. }
  69. // Add protocol agreement handling
  70. /**
  71. * @member {?SelectProtocolCallback}
  72. * @private
  73. */
  74. this._selectProtocol = null
  75. if (options && options.selectProtocol) {
  76. // User-provided logic
  77. this._selectProtocol = options.selectProtocol
  78. } else if (options && options.validProtocols) {
  79. // Default logic
  80. this._selectProtocol = this._buildSelectProtocol(options.validProtocols)
  81. }
  82. }
  83. util.inherits(Server, events.EventEmitter)
  84. module.exports = Server
  85. Connection = require('./Connection')
  86. /**
  87. * Start listening for connections
  88. * @param {number} port
  89. * @param {string} [host]
  90. * @param {Function} [callback] will be added as "connection" listener
  91. */
  92. Server.prototype.listen = function (port, host, callback) {
  93. var that = this
  94. if (typeof host === 'function') {
  95. callback = host
  96. host = undefined
  97. }
  98. if (callback) {
  99. this.on('listening', callback)
  100. }
  101. this.socket.listen(port, host, function () {
  102. that.emit('listening')
  103. })
  104. return this
  105. }
  106. /**
  107. * Stops the server from accepting new connections and keeps existing connections.
  108. * This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event.
  109. * The optional callback will be called once the 'close' event occurs.
  110. * @param {function()} [callback]
  111. */
  112. Server.prototype.close = function (callback) {
  113. if (callback) {
  114. this.once('close', callback)
  115. }
  116. this.socket.close()
  117. }
  118. /**
  119. * Create a resolver to pick the client's most preferred protocol the server recognises
  120. * @param {Array<string>} validProtocols
  121. * @returns {SelectProtocolCallback}
  122. * @private
  123. */
  124. Server.prototype._buildSelectProtocol = function (validProtocols) {
  125. return function (conn, protocols) {
  126. var i
  127. for (i = 0; i < protocols.length; i++) {
  128. if (validProtocols.indexOf(protocols[i]) !== -1) {
  129. // A valid protocol was found
  130. return protocols[i]
  131. }
  132. }
  133. // No agreement
  134. }
  135. }