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
						
					
					
				/**
 | 
						|
 * 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;
 | 
						|
};
 |