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.
287 lines
8.1 KiB
287 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; |
|
};
|
|
|