socket.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /**
  2. * Socket implementation that uses flash SocketPool class as a backend.
  3. *
  4. * @author Dave Longley
  5. *
  6. * Copyright (c) 2010-2013 Digital Bazaar, Inc.
  7. */
  8. var forge = require('./forge');
  9. require('./util');
  10. // define net namespace
  11. var net = module.exports = forge.net = forge.net || {};
  12. // map of flash ID to socket pool
  13. net.socketPools = {};
  14. /**
  15. * Creates a flash socket pool.
  16. *
  17. * @param options:
  18. * flashId: the dom ID for the flash object element.
  19. * policyPort: the default policy port for sockets, 0 to use the
  20. * flash default.
  21. * policyUrl: the default policy file URL for sockets (if provided
  22. * used instead of a policy port).
  23. * msie: true if the browser is msie, false if not.
  24. *
  25. * @return the created socket pool.
  26. */
  27. net.createSocketPool = function(options) {
  28. // set default
  29. options.msie = options.msie || false;
  30. // initialize the flash interface
  31. var spId = options.flashId;
  32. var api = document.getElementById(spId);
  33. api.init({marshallExceptions: !options.msie});
  34. // create socket pool entry
  35. var sp = {
  36. // ID of the socket pool
  37. id: spId,
  38. // flash interface
  39. flashApi: api,
  40. // map of socket ID to sockets
  41. sockets: {},
  42. // default policy port
  43. policyPort: options.policyPort || 0,
  44. // default policy URL
  45. policyUrl: options.policyUrl || null
  46. };
  47. net.socketPools[spId] = sp;
  48. // create event handler, subscribe to flash events
  49. if(options.msie === true) {
  50. sp.handler = function(e) {
  51. if(e.id in sp.sockets) {
  52. // get handler function
  53. var f;
  54. switch(e.type) {
  55. case 'connect':
  56. f = 'connected';
  57. break;
  58. case 'close':
  59. f = 'closed';
  60. break;
  61. case 'socketData':
  62. f = 'data';
  63. break;
  64. default:
  65. f = 'error';
  66. break;
  67. }
  68. /* IE calls javascript on the thread of the external object
  69. that triggered the event (in this case flash) ... which will
  70. either run concurrently with other javascript or pre-empt any
  71. running javascript in the middle of its execution (BAD!) ...
  72. calling setTimeout() will schedule the javascript to run on
  73. the javascript thread and solve this EVIL problem. */
  74. setTimeout(function() {sp.sockets[e.id][f](e);}, 0);
  75. }
  76. };
  77. } else {
  78. sp.handler = function(e) {
  79. if(e.id in sp.sockets) {
  80. // get handler function
  81. var f;
  82. switch(e.type) {
  83. case 'connect':
  84. f = 'connected';
  85. break;
  86. case 'close':
  87. f = 'closed';
  88. break;
  89. case 'socketData':
  90. f = 'data';
  91. break;
  92. default:
  93. f = 'error';
  94. break;
  95. }
  96. sp.sockets[e.id][f](e);
  97. }
  98. };
  99. }
  100. var handler = 'forge.net.socketPools[\'' + spId + '\'].handler';
  101. api.subscribe('connect', handler);
  102. api.subscribe('close', handler);
  103. api.subscribe('socketData', handler);
  104. api.subscribe('ioError', handler);
  105. api.subscribe('securityError', handler);
  106. /**
  107. * Destroys a socket pool. The socket pool still needs to be cleaned
  108. * up via net.cleanup().
  109. */
  110. sp.destroy = function() {
  111. delete net.socketPools[options.flashId];
  112. for(var id in sp.sockets) {
  113. sp.sockets[id].destroy();
  114. }
  115. sp.sockets = {};
  116. api.cleanup();
  117. };
  118. /**
  119. * Creates a new socket.
  120. *
  121. * @param options:
  122. * connected: function(event) called when the socket connects.
  123. * closed: function(event) called when the socket closes.
  124. * data: function(event) called when socket data has arrived,
  125. * it can be read from the socket using receive().
  126. * error: function(event) called when a socket error occurs.
  127. */
  128. sp.createSocket = function(options) {
  129. // default to empty options
  130. options = options || {};
  131. // create flash socket
  132. var id = api.create();
  133. // create javascript socket wrapper
  134. var socket = {
  135. id: id,
  136. // set handlers
  137. connected: options.connected || function(e) {},
  138. closed: options.closed || function(e) {},
  139. data: options.data || function(e) {},
  140. error: options.error || function(e) {}
  141. };
  142. /**
  143. * Destroys this socket.
  144. */
  145. socket.destroy = function() {
  146. api.destroy(id);
  147. delete sp.sockets[id];
  148. };
  149. /**
  150. * Connects this socket.
  151. *
  152. * @param options:
  153. * host: the host to connect to.
  154. * port: the port to connect to.
  155. * policyPort: the policy port to use (if non-default), 0 to
  156. * use the flash default.
  157. * policyUrl: the policy file URL to use (instead of port).
  158. */
  159. socket.connect = function(options) {
  160. // give precedence to policy URL over policy port
  161. // if no policy URL and passed port isn't 0, use default port,
  162. // otherwise use 0 for the port
  163. var policyUrl = options.policyUrl || null;
  164. var policyPort = 0;
  165. if(policyUrl === null && options.policyPort !== 0) {
  166. policyPort = options.policyPort || sp.policyPort;
  167. }
  168. api.connect(id, options.host, options.port, policyPort, policyUrl);
  169. };
  170. /**
  171. * Closes this socket.
  172. */
  173. socket.close = function() {
  174. api.close(id);
  175. socket.closed({
  176. id: socket.id,
  177. type: 'close',
  178. bytesAvailable: 0
  179. });
  180. };
  181. /**
  182. * Determines if the socket is connected or not.
  183. *
  184. * @return true if connected, false if not.
  185. */
  186. socket.isConnected = function() {
  187. return api.isConnected(id);
  188. };
  189. /**
  190. * Writes bytes to this socket.
  191. *
  192. * @param bytes the bytes (as a string) to write.
  193. *
  194. * @return true on success, false on failure.
  195. */
  196. socket.send = function(bytes) {
  197. return api.send(id, forge.util.encode64(bytes));
  198. };
  199. /**
  200. * Reads bytes from this socket (non-blocking). Fewer than the number
  201. * of bytes requested may be read if enough bytes are not available.
  202. *
  203. * This method should be called from the data handler if there are
  204. * enough bytes available. To see how many bytes are available, check
  205. * the 'bytesAvailable' property on the event in the data handler or
  206. * call the bytesAvailable() function on the socket. If the browser is
  207. * msie, then the bytesAvailable() function should be used to avoid
  208. * race conditions. Otherwise, using the property on the data handler's
  209. * event may be quicker.
  210. *
  211. * @param count the maximum number of bytes to read.
  212. *
  213. * @return the bytes read (as a string) or null on error.
  214. */
  215. socket.receive = function(count) {
  216. var rval = api.receive(id, count).rval;
  217. return (rval === null) ? null : forge.util.decode64(rval);
  218. };
  219. /**
  220. * Gets the number of bytes available for receiving on the socket.
  221. *
  222. * @return the number of bytes available for receiving.
  223. */
  224. socket.bytesAvailable = function() {
  225. return api.getBytesAvailable(id);
  226. };
  227. // store and return socket
  228. sp.sockets[id] = socket;
  229. return socket;
  230. };
  231. return sp;
  232. };
  233. /**
  234. * Destroys a flash socket pool.
  235. *
  236. * @param options:
  237. * flashId: the dom ID for the flash object element.
  238. */
  239. net.destroySocketPool = function(options) {
  240. if(options.flashId in net.socketPools) {
  241. var sp = net.socketPools[options.flashId];
  242. sp.destroy();
  243. }
  244. };
  245. /**
  246. * Creates a new socket.
  247. *
  248. * @param options:
  249. * flashId: the dom ID for the flash object element.
  250. * connected: function(event) called when the socket connects.
  251. * closed: function(event) called when the socket closes.
  252. * data: function(event) called when socket data has arrived, it
  253. * can be read from the socket using receive().
  254. * error: function(event) called when a socket error occurs.
  255. *
  256. * @return the created socket.
  257. */
  258. net.createSocket = function(options) {
  259. var socket = null;
  260. if(options.flashId in net.socketPools) {
  261. // get related socket pool
  262. var sp = net.socketPools[options.flashId];
  263. socket = sp.createSocket(options);
  264. }
  265. return socket;
  266. };