You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					288 lines
				
				8.1 KiB
			
		
		
			
		
	
	
					288 lines
				
				8.1 KiB
			| 
								 
											2 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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;
							 | 
						||
| 
								 | 
							
								};
							 |