/node_modules/node-forge/flash/SocketPool.as
ActionScript | 754 lines | 486 code | 73 blank | 195 comment | 31 complexity | f34aa60196db3e3f3554d8dc47e0a883 MD5 | raw file
- /*
- * Copyright (c) 2009-2010 Digital Bazaar, Inc. All rights reserved.
- *
- * @author Dave Longley
- */
- package
- {
- import flash.display.Sprite;
-
- /**
- * A SocketPool is a flash object that can be embedded in a web page to
- * allow javascript access to pools of Sockets.
- *
- * Javascript can create a pool and then as many Sockets as it desires. Each
- * Socket will be assigned a unique ID that allows continued javascript
- * access to it. There is no limit on the number of persistent socket
- * connections.
- */
- public class SocketPool extends Sprite
- {
- import flash.events.Event;
- import flash.events.EventDispatcher;
- import flash.errors.IOError;
- import flash.events.IOErrorEvent;
- import flash.events.ProgressEvent;
- import flash.events.SecurityErrorEvent;
- import flash.events.TextEvent;
- import flash.external.ExternalInterface;
- import flash.net.SharedObject;
- import flash.system.Security;
- import flash.utils.ByteArray;
- import mx.utils.Base64Decoder;
- import mx.utils.Base64Encoder;
-
- // a map of ID to Socket
- private var mSocketMap:Object;
-
- // a counter for Socket IDs (Note: assumes there will be no overflow)
- private var mNextId:uint;
-
- // an event dispatcher for sending events to javascript
- private var mEventDispatcher:EventDispatcher;
-
- /**
- * Creates a new, unitialized SocketPool.
- *
- * @throws Error - if no external interface is available to provide
- * javascript access.
- */
- public function SocketPool()
- {
- if(!ExternalInterface.available)
- {
- trace("ExternalInterface is not available");
- throw new Error(
- "Flash's ExternalInterface is not available. This is a " +
- "requirement of SocketPool and therefore, it will be " +
- "unavailable.");
- }
- else
- {
- try
- {
- // set up javascript access:
-
- // initializes/cleans up the SocketPool
- ExternalInterface.addCallback("init", init);
- ExternalInterface.addCallback("cleanup", cleanup);
-
- // creates/destroys a socket
- ExternalInterface.addCallback("create", create);
- ExternalInterface.addCallback("destroy", destroy);
-
- // connects/closes a socket
- ExternalInterface.addCallback("connect", connect);
- ExternalInterface.addCallback("close", close);
-
- // checks for a connection
- ExternalInterface.addCallback("isConnected", isConnected);
-
- // sends/receives data over the socket
- ExternalInterface.addCallback("send", send);
- ExternalInterface.addCallback("receive", receive);
-
- // gets the number of bytes available on a socket
- ExternalInterface.addCallback(
- "getBytesAvailable", getBytesAvailable);
-
- // add a callback for subscribing to socket events
- ExternalInterface.addCallback("subscribe", subscribe);
-
- // add callbacks for deflate/inflate
- ExternalInterface.addCallback("deflate", deflate);
- ExternalInterface.addCallback("inflate", inflate);
-
- // add callbacks for local disk storage
- ExternalInterface.addCallback("setItem", setItem);
- ExternalInterface.addCallback("getItem", getItem);
- ExternalInterface.addCallback("removeItem", removeItem);
- ExternalInterface.addCallback("clearItems", clearItems);
-
- // socket pool is now ready
- ExternalInterface.call("window.forge.socketPool.ready");
- }
- catch(e:Error)
- {
- log("error=" + e.errorID + "," + e.name + "," + e.message);
- throw e;
- }
-
- log("ready");
- }
- }
-
- /**
- * A debug logging function.
- *
- * @param obj the string or error to log.
- */
- CONFIG::debugging
- private function log(obj:Object):void
- {
- if(obj is String)
- {
- var str:String = obj as String;
- ExternalInterface.call("console.log", "SocketPool", str);
- }
- else if(obj is Error)
- {
- var e:Error = obj as Error;
- log("error=" + e.errorID + "," + e.name + "," + e.message);
- }
- }
-
- CONFIG::release
- private function log(obj:Object):void
- {
- // log nothing in release mode
- }
-
- /**
- * Called by javascript to initialize this SocketPool.
- *
- * @param options:
- * marshallExceptions: true to pass exceptions to and from
- * javascript.
- */
- private function init(... args):void
- {
- log("init()");
-
- // get options from first argument
- var options:Object = args.length > 0 ? args[0] : null;
-
- // create socket map, set next ID, and create event dispatcher
- mSocketMap = new Object();
- mNextId = 1;
- mEventDispatcher = new EventDispatcher();
-
- // enable marshalling exceptions if appropriate
- if(options != null &&
- "marshallExceptions" in options &&
- options.marshallExceptions === true)
- {
- try
- {
- // Note: setting marshallExceptions in IE, even inside of a
- // try-catch block will terminate flash. Don't set this on IE.
- ExternalInterface.marshallExceptions = true;
- }
- catch(e:Error)
- {
- log(e);
- }
- }
-
- log("init() done");
- }
-
- /**
- * Called by javascript to clean up a SocketPool.
- */
- private function cleanup():void
- {
- log("cleanup()");
-
- mSocketMap = null;
- mNextId = 1;
- mEventDispatcher = null;
-
- log("cleanup() done");
- }
-
- /**
- * Handles events.
- *
- * @param e the event to handle.
- */
- private function handleEvent(e:Event):void
- {
- // dispatch socket event
- var message:String = (e is TextEvent) ? (e as TextEvent).text : null;
- mEventDispatcher.dispatchEvent(
- new SocketEvent(e.type, e.target as PooledSocket, message));
- }
-
- /**
- * Called by javascript to create a Socket.
- *
- * @return the Socket ID.
- */
- private function create():String
- {
- log("create()");
-
- // create a Socket
- var id:String = "" + mNextId++;
- var s:PooledSocket = new PooledSocket();
- s.id = id;
- s.addEventListener(Event.CONNECT, handleEvent);
- s.addEventListener(Event.CLOSE, handleEvent);
- s.addEventListener(ProgressEvent.SOCKET_DATA, handleEvent);
- s.addEventListener(IOErrorEvent.IO_ERROR, handleEvent);
- s.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleEvent);
- mSocketMap[id] = s;
-
- log("socket " + id + " created");
- log("create() done");
-
- return id;
- }
-
- /**
- * Called by javascript to clean up a Socket.
- *
- * @param id the ID of the Socket to clean up.
- */
- private function destroy(id:String):void
- {
- log("destroy(" + id + ")");
-
- if(id in mSocketMap)
- {
- // remove Socket
- delete mSocketMap[id];
- log("socket " + id + " destroyed");
- }
-
- log("destroy(" + id + ") done");
- }
-
- /**
- * Connects the Socket with the given ID to the given host and port,
- * using the given socket policy port.
- *
- * @param id the ID of the Socket.
- * @param host the host to connect to.
- * @param port the port to connect to.
- * @param spPort the security policy port to use, 0 to use a url.
- * @param spUrl the http URL to the policy file to use, null for default.
- */
- private function connect(
- id:String, host:String, port:uint, spPort:uint,
- spUrl:String = null):void
- {
- log("connect(" +
- id + "," + host + "," + port + "," + spPort + "," + spUrl + ")");
-
- if(id in mSocketMap)
- {
- // get the Socket
- var s:PooledSocket = mSocketMap[id];
-
- // load socket policy file
- // (permits socket access to backend)
- if(spPort !== 0)
- {
- spUrl = "xmlsocket://" + host + ":" + spPort;
- log("using cross-domain url: " + spUrl);
- Security.loadPolicyFile(spUrl);
- }
- else if(spUrl !== null && typeof(spUrl) !== undefined)
- {
- log("using cross-domain url: " + spUrl);
- Security.loadPolicyFile(spUrl);
- }
- else
- {
- log("not loading any cross-domain url");
- }
-
- // connect
- s.connect(host, port);
- }
- else
- {
- // no such socket
- log("socket " + id + " does not exist");
- }
-
- log("connect(" + id + ") done");
- }
-
- /**
- * Closes the Socket with the given ID.
- *
- * @param id the ID of the Socket.
- */
- private function close(id:String):void
- {
- log("close(" + id + ")");
-
- if(id in mSocketMap)
- {
- // close the Socket
- var s:PooledSocket = mSocketMap[id];
- if(s.connected)
- {
- s.close();
- }
- }
- else
- {
- // no such socket
- log("socket " + id + " does not exist");
- }
-
- log("close(" + id + ") done");
- }
-
- /**
- * Determines if the Socket with the given ID is connected or not.
- *
- * @param id the ID of the Socket.
- *
- * @return true if the socket is connected, false if not.
- */
- private function isConnected(id:String):Boolean
- {
- var rval:Boolean = false;
- log("isConnected(" + id + ")");
-
- if(id in mSocketMap)
- {
- // check the Socket
- var s:PooledSocket = mSocketMap[id];
- rval = s.connected;
- }
- else
- {
- // no such socket
- log("socket " + id + " does not exist");
- }
-
- log("isConnected(" + id + ") done");
- return rval;
- }
-
- /**
- * Writes bytes to a Socket.
- *
- * @param id the ID of the Socket.
- * @param bytes the string of base64-encoded bytes to write.
- *
- * @return true on success, false on failure.
- */
- private function send(id:String, bytes:String):Boolean
- {
- var rval:Boolean = false;
- log("send(" + id + ")");
-
- if(id in mSocketMap)
- {
- // write bytes to socket
- var s:PooledSocket = mSocketMap[id];
- try
- {
- var b64:Base64Decoder = new Base64Decoder();
- b64.decode(bytes);
- var b:ByteArray = b64.toByteArray();
- s.writeBytes(b, 0, b.length);
- s.flush();
- rval = true;
- }
- catch(e:IOError)
- {
- log(e);
-
- // dispatch IO error event
- mEventDispatcher.dispatchEvent(new SocketEvent(
- IOErrorEvent.IO_ERROR, s, e.message));
- if(s.connected)
- {
- s.close();
- }
- }
- }
- else
- {
- // no such socket
- log("socket " + id + " does not exist");
- }
-
- log("send(" + id + ") done");
- return rval;
- }
-
- /**
- * Receives bytes from a Socket.
- *
- * @param id the ID of the Socket.
- * @param count the maximum number of bytes to receive.
- *
- * @return an object with 'rval' set to the received bytes,
- * base64-encoded, or set to null on error.
- */
- private function receive(id:String, count:uint):Object
- {
- var rval:String = null;
- log("receive(" + id + "," + count + ")");
-
- if(id in mSocketMap)
- {
- // only read what is available
- var s:PooledSocket = mSocketMap[id];
- if(count > s.bytesAvailable)
- {
- count = s.bytesAvailable;
- }
-
- try
- {
- // read bytes from socket
- var b:ByteArray = new ByteArray();
- s.readBytes(b, 0, count);
- b.position = 0;
- var b64:Base64Encoder = new Base64Encoder();
- b64.insertNewLines = false;
- b64.encodeBytes(b, 0, b.length);
- rval = b64.toString();
- }
- catch(e:IOError)
- {
- log(e);
-
- // dispatch IO error event
- mEventDispatcher.dispatchEvent(new SocketEvent(
- IOErrorEvent.IO_ERROR, s, e.message));
- if(s.connected)
- {
- s.close();
- }
- }
- }
- else
- {
- // no such socket
- log("socket " + id + " does not exist");
- }
-
- log("receive(" + id + "," + count + ") done");
- return {rval: rval};
- }
-
- /**
- * Gets the number of bytes available from a Socket.
- *
- * @param id the ID of the Socket.
- *
- * @return the number of available bytes.
- */
- private function getBytesAvailable(id:String):uint
- {
- var rval:uint = 0;
- log("getBytesAvailable(" + id + ")");
-
- if(id in mSocketMap)
- {
- var s:PooledSocket = mSocketMap[id];
- rval = s.bytesAvailable;
- }
- else
- {
- // no such socket
- log("socket " + id + " does not exist");
- }
-
- log("getBytesAvailable(" + id +") done");
- return rval;
- }
-
- /**
- * Registers a javascript function as a callback for an event.
- *
- * @param eventType the type of event (socket event types).
- * @param callback the name of the callback function.
- */
- private function subscribe(eventType:String, callback:String):void
- {
- log("subscribe(" + eventType + "," + callback + ")");
-
- switch(eventType)
- {
- case Event.CONNECT:
- case Event.CLOSE:
- case IOErrorEvent.IO_ERROR:
- case SecurityErrorEvent.SECURITY_ERROR:
- case ProgressEvent.SOCKET_DATA:
- {
- log(eventType + " => " + callback);
- mEventDispatcher.addEventListener(
- eventType, function(event:SocketEvent):void
- {
- log("event dispatched: " + eventType);
-
- // build event for javascript
- var e:Object = new Object();
- e.id = event.socket ? event.socket.id : 0;
- e.type = eventType;
- if(event.socket && event.socket.connected)
- {
- e.bytesAvailable = event.socket.bytesAvailable;
- }
- else
- {
- e.bytesAvailable = 0;
- }
- if(event.message)
- {
- e.message = event.message;
- }
-
- // send event to javascript
- ExternalInterface.call(callback, e);
- });
- break;
- }
- default:
- throw new ArgumentError(
- "Could not subscribe to event. " +
- "Invalid event type specified: " + eventType);
- }
-
- log("subscribe(" + eventType + "," + callback + ") done");
- }
-
- /**
- * Deflates the given data.
- *
- * @param data the base64-encoded data to deflate.
- *
- * @return an object with 'rval' set to deflated data, base64-encoded.
- */
- private function deflate(data:String):Object
- {
- log("deflate");
-
- var b64d:Base64Decoder = new Base64Decoder();
- b64d.decode(data);
- var b:ByteArray = b64d.toByteArray();
- b.compress();
- b.position = 0;
- var b64e:Base64Encoder = new Base64Encoder();
- b64e.insertNewLines = false;
- b64e.encodeBytes(b, 0, b.length);
-
- log("deflate done");
- return {rval: b64e.toString()};
- }
-
- /**
- * Inflates the given data.
- *
- * @param data the base64-encoded data to inflate.
- *
- * @return an object with 'rval' set to the inflated data,
- * base64-encoded, null on error.
- */
- private function inflate(data:String):Object
- {
- log("inflate");
- var rval:Object = {rval: null};
-
- try
- {
- var b64d:Base64Decoder = new Base64Decoder();
- b64d.decode(data);
- var b:ByteArray = b64d.toByteArray();
- b.uncompress();
- b.position = 0;
- var b64e:Base64Encoder = new Base64Encoder();
- b64e.insertNewLines = false;
- b64e.encodeBytes(b, 0, b.length);
- rval.rval = b64e.toString();
- }
- catch(e:Error)
- {
- log(e);
- rval.error = {
- id: e.errorID,
- name: e.name,
- message: e.message
- };
- }
-
- log("inflate done");
- return rval;
- }
-
- /**
- * Stores an item with a key and arbitrary base64-encoded data on local
- * disk.
- *
- * @param key the key for the item.
- * @param data the base64-encoded item data.
- * @param storeId the storage ID to use, defaults to "forge.storage".
- *
- * @return an object with rval set to true on success, false on failure
- * with error included.
- */
- private function setItem(
- key:String, data:String, storeId:String = "forge.storage"):Object
- {
- var rval:Object = {rval: false};
- try
- {
- var store:SharedObject = SharedObject.getLocal(storeId);
- if(!('keys' in store.data))
- {
- store.data.keys = {};
- }
- store.data.keys[key] = data;
- store.flush();
- rval.rval = true;
- }
- catch(e:Error)
- {
- log(e);
- rval.error = {
- id: e.errorID,
- name: e.name,
- message: e.message
- };
- }
- return rval;
- }
-
- /**
- * Gets an item from the local disk.
- *
- * @param key the key for the item.
- * @param storeId the storage ID to use, defaults to "forge.storage".
- *
- * @return an object with rval set to the item data (which may be null),
- * check for error object if null.
- */
- private function getItem(
- key:String, storeId:String = "forge.storage"):Object
- {
- var rval:Object = {rval: null};
- try
- {
- var store:SharedObject = SharedObject.getLocal(storeId);
- if('keys' in store.data && key in store.data.keys)
- {
- rval.rval = store.data.keys[key];
- }
- }
- catch(e:Error)
- {
- log(e);
- rval.error = {
- id: e.errorID,
- name: e.name,
- message: e.message
- };
- }
- return rval;
- }
-
- /**
- * Removes an item from the local disk.
- *
- * @param key the key for the item.
- * @param storeId the storage ID to use, defaults to "forge.storage".
- *
- * @return an object with rval set to true if removed, false if not.
- */
- private function removeItem(
- key:String, storeId:String = "forge.storage"):Object
- {
- var rval:Object = {rval: false};
- try
- {
- var store:SharedObject = SharedObject.getLocal(storeId);
- if('keys' in store.data && key in store.data.keys)
- {
- delete store.data.keys[key];
-
- // clean up storage entirely if empty
- var empty:Boolean = true;
- for(var prop:String in store.data.keys)
- {
- empty = false;
- break;
- }
- if(empty)
- {
- store.clear();
- }
- rval.rval = true;
- }
- }
- catch(e:Error)
- {
- log(e);
- rval.error = {
- id: e.errorID,
- name: e.name,
- message: e.message
- };
- }
- return rval;
- }
-
- /**
- * Clears an entire store of all of its items.
- *
- * @param storeId the storage ID to use, defaults to "forge.storage".
- *
- * @return an object with rval set to true if cleared, false if not.
- */
- private function clearItems(storeId:String = "forge.storage"):Object
- {
- var rval:Object = {rval: false};
- try
- {
- var store:SharedObject = SharedObject.getLocal(storeId);
- store.clear();
- rval.rval = true;
- }
- catch(e:Error)
- {
- log(e);
- rval.error = {
- id: e.errorID,
- name: e.name,
- message: e.message
- };
- }
- return rval;
- }
- }
- }