| /** |
| * Socket implementation that uses flash SocketPool class as a backend. |
| * |
| * @author Dave Longley |
| * |
| * Copyright (c) 2010-2013 Digital Bazaar, Inc. |
| */ |
| (function() { |
| /* ########## Begin module implementation ########## */ |
| function initModule(forge) { |
| |
| // define net namespace |
| var net = 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; |
| }; |
| |
| } // end module implementation |
| |
| /* ########## Begin module wrapper ########## */ |
| var name = 'net'; |
| if(typeof define !== 'function') { |
| // NodeJS -> AMD |
| if(typeof module === 'object' && module.exports) { |
| var nodeJS = true; |
| define = function(ids, factory) { |
| factory(require, module); |
| }; |
| } else { |
| // <script> |
| if(typeof forge === 'undefined') { |
| forge = {}; |
| } |
| return initModule(forge); |
| } |
| } |
| // AMD |
| var deps; |
| var defineFunc = function(require, module) { |
| module.exports = function(forge) { |
| var mods = deps.map(function(dep) { |
| return require(dep); |
| }).concat(initModule); |
| // handle circular dependencies |
| forge = forge || {}; |
| forge.defined = forge.defined || {}; |
| if(forge.defined[name]) { |
| return forge[name]; |
| } |
| forge.defined[name] = true; |
| for(var i = 0; i < mods.length; ++i) { |
| mods[i](forge); |
| } |
| return forge[name]; |
| }; |
| }; |
| var tmpDefine = define; |
| define = function(ids, factory) { |
| deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2); |
| if(nodeJS) { |
| delete define; |
| return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0)); |
| } |
| define = tmpDefine; |
| return define.apply(null, Array.prototype.slice.call(arguments, 0)); |
| }; |
| define(['require', 'module'], function() { |
| defineFunc.apply(null, Array.prototype.slice.call(arguments, 0)); |
| }); |
| })(); |