Spaces:
Configuration error
Configuration error
/** | |
* 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; | |
}; | |