288 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Socket implementation that uses flash SocketPool class as a backend.
 | |
|  *
 | |
|  * @author Dave Longley
 | |
|  *
 | |
|  * Copyright (c) 2010-2013 Digital Bazaar, Inc.
 | |
|  */
 | |
| var forge = require('./forge');
 | |
| require('./util');
 | |
| 
 | |
| // define net namespace
 | |
| var net = module.exports = forge.net = forge.net || {};
 | |
| 
 | |
| // map of flash ID to socket pool
 | |
| net.socketPools = {};
 | |
| 
 | |
| /**
 | |
|  * Creates a flash socket pool.
 | |
|  *
 | |
|  * @param options:
 | |
|  *          flashId: the dom ID for the flash object element.
 | |
|  *          policyPort: the default policy port for sockets, 0 to use the
 | |
|  *            flash default.
 | |
|  *          policyUrl: the default policy file URL for sockets (if provided
 | |
|  *            used instead of a policy port).
 | |
|  *          msie: true if the browser is msie, false if not.
 | |
|  *
 | |
|  * @return the created socket pool.
 | |
|  */
 | |
| net.createSocketPool = function(options) {
 | |
|   // set default
 | |
|   options.msie = options.msie || false;
 | |
| 
 | |
|   // initialize the flash interface
 | |
|   var spId = options.flashId;
 | |
|   var api = document.getElementById(spId);
 | |
|   api.init({marshallExceptions: !options.msie});
 | |
| 
 | |
|   // create socket pool entry
 | |
|   var sp = {
 | |
|     // ID of the socket pool
 | |
|     id: spId,
 | |
|     // flash interface
 | |
|     flashApi: api,
 | |
|     // map of socket ID to sockets
 | |
|     sockets: {},
 | |
|     // default policy port
 | |
|     policyPort: options.policyPort || 0,
 | |
|     // default policy URL
 | |
|     policyUrl: options.policyUrl || null
 | |
|   };
 | |
|   net.socketPools[spId] = sp;
 | |
| 
 | |
|   // create event handler, subscribe to flash events
 | |
|   if(options.msie === true) {
 | |
|     sp.handler = function(e) {
 | |
|       if(e.id in sp.sockets) {
 | |
|         // get handler function
 | |
|         var f;
 | |
|         switch(e.type) {
 | |
|         case 'connect':
 | |
|           f = 'connected';
 | |
|           break;
 | |
|         case 'close':
 | |
|           f = 'closed';
 | |
|           break;
 | |
|         case 'socketData':
 | |
|           f = 'data';
 | |
|           break;
 | |
|         default:
 | |
|           f = 'error';
 | |
|           break;
 | |
|         }
 | |
|         /* IE calls javascript on the thread of the external object
 | |
|           that triggered the event (in this case flash) ... which will
 | |
|           either run concurrently with other javascript or pre-empt any
 | |
|           running javascript in the middle of its execution (BAD!) ...
 | |
|           calling setTimeout() will schedule the javascript to run on
 | |
|           the javascript thread and solve this EVIL problem. */
 | |
|         setTimeout(function() {sp.sockets[e.id][f](e);}, 0);
 | |
|       }
 | |
|     };
 | |
|   } else {
 | |
|     sp.handler = function(e) {
 | |
|       if(e.id in sp.sockets) {
 | |
|         // get handler function
 | |
|         var f;
 | |
|         switch(e.type) {
 | |
|         case 'connect':
 | |
|           f = 'connected';
 | |
|           break;
 | |
|         case 'close':
 | |
|           f = 'closed';
 | |
|           break;
 | |
|         case 'socketData':
 | |
|           f = 'data';
 | |
|           break;
 | |
|         default:
 | |
|           f = 'error';
 | |
|           break;
 | |
|         }
 | |
|         sp.sockets[e.id][f](e);
 | |
|       }
 | |
|     };
 | |
|   }
 | |
|   var handler = 'forge.net.socketPools[\'' + spId + '\'].handler';
 | |
|   api.subscribe('connect', handler);
 | |
|   api.subscribe('close', handler);
 | |
|   api.subscribe('socketData', handler);
 | |
|   api.subscribe('ioError', handler);
 | |
|   api.subscribe('securityError', handler);
 | |
| 
 | |
|   /**
 | |
|    * Destroys a socket pool. The socket pool still needs to be cleaned
 | |
|    * up via net.cleanup().
 | |
|    */
 | |
|   sp.destroy = function() {
 | |
|     delete net.socketPools[options.flashId];
 | |
|     for(var id in sp.sockets) {
 | |
|       sp.sockets[id].destroy();
 | |
|     }
 | |
|     sp.sockets = {};
 | |
|     api.cleanup();
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Creates a new socket.
 | |
|    *
 | |
|    * @param options:
 | |
|    *          connected: function(event) called when the socket connects.
 | |
|    *          closed: function(event) called when the socket closes.
 | |
|    *          data: function(event) called when socket data has arrived,
 | |
|    *            it can be read from the socket using receive().
 | |
|    *          error: function(event) called when a socket error occurs.
 | |
|    */
 | |
|    sp.createSocket = function(options) {
 | |
|      // default to empty options
 | |
|      options = options || {};
 | |
| 
 | |
|      // create flash socket
 | |
|      var id = api.create();
 | |
| 
 | |
|      // create javascript socket wrapper
 | |
|      var socket = {
 | |
|        id: id,
 | |
|        // set handlers
 | |
|        connected: options.connected || function(e) {},
 | |
|        closed: options.closed || function(e) {},
 | |
|        data: options.data || function(e) {},
 | |
|        error: options.error || function(e) {}
 | |
|      };
 | |
| 
 | |
|      /**
 | |
|       * Destroys this socket.
 | |
|       */
 | |
|      socket.destroy = function() {
 | |
|        api.destroy(id);
 | |
|        delete sp.sockets[id];
 | |
|      };
 | |
| 
 | |
|      /**
 | |
|       * Connects this socket.
 | |
|       *
 | |
|       * @param options:
 | |
|       *          host: the host to connect to.
 | |
|       *          port: the port to connect to.
 | |
|       *          policyPort: the policy port to use (if non-default), 0 to
 | |
|       *            use the flash default.
 | |
|       *          policyUrl: the policy file URL to use (instead of port).
 | |
|       */
 | |
|      socket.connect = function(options) {
 | |
|        // give precedence to policy URL over policy port
 | |
|        // if no policy URL and passed port isn't 0, use default port,
 | |
|        // otherwise use 0 for the port
 | |
|        var policyUrl = options.policyUrl || null;
 | |
|        var policyPort = 0;
 | |
|        if(policyUrl === null && options.policyPort !== 0) {
 | |
|          policyPort = options.policyPort || sp.policyPort;
 | |
|        }
 | |
|        api.connect(id, options.host, options.port, policyPort, policyUrl);
 | |
|      };
 | |
| 
 | |
|      /**
 | |
|       * Closes this socket.
 | |
|       */
 | |
|      socket.close = function() {
 | |
|        api.close(id);
 | |
|        socket.closed({
 | |
|          id: socket.id,
 | |
|          type: 'close',
 | |
|          bytesAvailable: 0
 | |
|        });
 | |
|      };
 | |
| 
 | |
|      /**
 | |
|       * Determines if the socket is connected or not.
 | |
|       *
 | |
|       * @return true if connected, false if not.
 | |
|       */
 | |
|      socket.isConnected = function() {
 | |
|        return api.isConnected(id);
 | |
|      };
 | |
| 
 | |
|      /**
 | |
|       * Writes bytes to this socket.
 | |
|       *
 | |
|       * @param bytes the bytes (as a string) to write.
 | |
|       *
 | |
|       * @return true on success, false on failure.
 | |
|       */
 | |
|      socket.send = function(bytes) {
 | |
|        return api.send(id, forge.util.encode64(bytes));
 | |
|      };
 | |
| 
 | |
|      /**
 | |
|       * Reads bytes from this socket (non-blocking). Fewer than the number
 | |
|       * of bytes requested may be read if enough bytes are not available.
 | |
|       *
 | |
|       * This method should be called from the data handler if there are
 | |
|       * enough bytes available. To see how many bytes are available, check
 | |
|       * the 'bytesAvailable' property on the event in the data handler or
 | |
|       * call the bytesAvailable() function on the socket. If the browser is
 | |
|       * msie, then the bytesAvailable() function should be used to avoid
 | |
|       * race conditions. Otherwise, using the property on the data handler's
 | |
|       * event may be quicker.
 | |
|       *
 | |
|       * @param count the maximum number of bytes to read.
 | |
|       *
 | |
|       * @return the bytes read (as a string) or null on error.
 | |
|       */
 | |
|      socket.receive = function(count) {
 | |
|        var rval = api.receive(id, count).rval;
 | |
|        return (rval === null) ? null : forge.util.decode64(rval);
 | |
|      };
 | |
| 
 | |
|      /**
 | |
|       * Gets the number of bytes available for receiving on the socket.
 | |
|       *
 | |
|       * @return the number of bytes available for receiving.
 | |
|       */
 | |
|      socket.bytesAvailable = function() {
 | |
|        return api.getBytesAvailable(id);
 | |
|      };
 | |
| 
 | |
|      // store and return socket
 | |
|      sp.sockets[id] = socket;
 | |
|      return socket;
 | |
|   };
 | |
| 
 | |
|   return sp;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Destroys a flash socket pool.
 | |
|  *
 | |
|  * @param options:
 | |
|  *          flashId: the dom ID for the flash object element.
 | |
|  */
 | |
| net.destroySocketPool = function(options) {
 | |
|   if(options.flashId in net.socketPools) {
 | |
|     var sp = net.socketPools[options.flashId];
 | |
|     sp.destroy();
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Creates a new socket.
 | |
|  *
 | |
|  * @param options:
 | |
|  *          flashId: the dom ID for the flash object element.
 | |
|  *          connected: function(event) called when the socket connects.
 | |
|  *          closed: function(event) called when the socket closes.
 | |
|  *          data: function(event) called when socket data has arrived, it
 | |
|  *            can be read from the socket using receive().
 | |
|  *          error: function(event) called when a socket error occurs.
 | |
|  *
 | |
|  * @return the created socket.
 | |
|  */
 | |
| net.createSocket = function(options) {
 | |
|   var socket = null;
 | |
|   if(options.flashId in net.socketPools) {
 | |
|     // get related socket pool
 | |
|     var sp = net.socketPools[options.flashId];
 | |
|     socket = sp.createSocket(options);
 | |
|   }
 | |
|   return socket;
 | |
| };
 |