For XPCOM (the Firefox add-on), connect to a random testbed host.
diff --git a/js/tools/build/ndn-js-uncomp.js b/js/tools/build/ndn-js-uncomp.js
index c2f610c..73d3862 100644
--- a/js/tools/build/ndn-js-uncomp.js
+++ b/js/tools/build/ndn-js-uncomp.js
@@ -75,19 +75,25 @@
/**
* settings is an associative array with the following defaults:
* {
- * host: 'localhost',
- * port: 9696,
* getTransport: function() { return new WebSocketTransport(); }
+ * getHostAndPort: transport.defaultGetHostAndPort,
+ * host: 'localhost', // If null, use getHostAndPort when connecting.
+ * port: 9696,
* onopen: function() { if (LOG > 3) console.log("NDN connection established."); }
* onclose: function() { if (LOG > 3) console.log("NDN connection closed."); }
* }
+ *
+ * getHostAndPort is a function, on each call it returns a new { host: host, port: port } or
+ * null if there are no more hosts.
*/
var NDN = function NDN(settings) {
settings = (settings || {});
- this.host = (settings.host || "localhost");
- this.port = (settings.port || 9696);
var getTransport = (settings.getTransport || function() { return new WebSocketTransport(); });
this.transport = getTransport();
+ this.getHostAndPort = (settings.getHostAndPort || this.transport.defaultGetHostAndPort);
+ this.host = (settings.host !== undefined ? settings.host : 'localhost');
+ dump("this.host " + this.host + "\n");
+ this.port = (settings.port || 9696);
this.readyStatus = NDN.UNOPEN;
// Event handler
this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
@@ -98,13 +104,55 @@
NDN.OPENED = 1; // connection to ccnd opened
NDN.CLOSED = 2; // connection to ccnd closed
-/* Java Socket Bridge and XPCOM transport */
+NDN.ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
NDN.prototype.createRoute = function(host,port){
this.host=host;
this.port=port;
}
+// For fetching data
+NDN.PITTable = new Array();
+
+var PITEntry = function PITEntry(interest, closure) {
+ this.interest = interest; // Interest
+ this.closure = closure; // Closure
+};
+
+// Return the longest entry from NDN.PITTable that matches name.
+NDN.getEntryForExpressedInterest = function(/*Name*/ name) {
+ // TODO: handle multiple matches? Maybe not from registerPrefix because multiple ContentObject
+ // could be sent for one Interest?
+ var result = null;
+
+ for (var i = 0; i < NDN.PITTable.length; i++) {
+ if (NDN.PITTable[i].interest.matches_name(name)) {
+ if (result == null ||
+ NDN.PITTable[i].interest.name.components.length > result.interest.name.components.length)
+ result = NDN.PITTable[i];
+ }
+ }
+
+ return result;
+};
+
+/*
+ * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
+ * If no more hosts remain, return null.
+ */
+NDN.makeShuffledGetHostAndPort = function(hostList, port) {
+ // Make a copy.
+ hostList = hostList.slice(0, hostList.length);
+ DataUtils.shuffle(hostList);
+
+ return function() {
+ if (hostList.length == 0)
+ return null;
+
+ return { host: hostList.splice(0, 1)[0], port: port };
+ };
+};
+
/** Encode name as an Interest. If template is not null, use its attributes.
* Send the interest to host:port, read the entire response and call
* closure.upcall(Closure.UPCALL_CONTENT (or Closure.UPCALL_CONTENT_UNVERIFIED),
@@ -117,11 +165,6 @@
closure,
// Interest
template) {
- if (this.host == null || this.port == null) {
- dump('ERROR host OR port NOT SET\n');
- return;
- }
-
var interest = new Interest(name);
if (template != null) {
interest.minSuffixComponents = template.minSuffixComponents;
@@ -135,14 +178,83 @@
}
else
interest.interestLifetime = 4.0; // default interest timeout value in seconds.
-
- this.transport.expressInterest(this, interest, closure);
-};
+ if (this.host == null || this.port == null) {
+ if (this.getHostAndPort == null)
+ console.log('ERROR: host OR port NOT SET');
+ else
+ this.connectAndExpressInterest(interest, closure);
+ }
+ else
+ this.transport.expressInterest(this, interest, closure);
+};
NDN.prototype.registerPrefix = function(name, closure, flag) {
return this.transport.registerPrefix(this, name, closure, flag);
}
+
+/*
+ * Assume this.getHostAndPort is not null. This is called when this.host is null or its host
+ * is not alive. Get a host and port, connect, then express callerInterest with callerClosure.
+ */
+NDN.prototype.connectAndExpressInterest = function(callerInterest, callerClosure) {
+ var hostAndPort = this.getHostAndPort();
+ if (hostAndPort == null) {
+ console.log('ERROR: No more hosts from getHostAndPort');
+ this.host = null;
+ return;
+ }
+
+ if (hostAndPort.host == this.host && hostAndPort.port == this.port) {
+ console.log('ERROR: The host returned by getHostAndPort is not alive: ' +
+ this.host + ":" + this.port);
+ return;
+ }
+
+ this.host = hostAndPort.host;
+ this.port = hostAndPort.port;
+ console.log("Trying host from getHostAndPort: " + this.host);
+
+ // Fetch the ccndId.
+ var interest = new Interest(new Name(NDN.ccndIdFetcher));
+ interest.interestLifetime = 4.0; // seconds
+
+ var thisNDN = this;
+ var timerID = setTimeout(function() {
+ console.log("Timeout waiting for host " + thisNDN.host);
+ // Try again.
+ thisNDN.connectAndExpressInterest(callerInterest, callerClosure);
+ }, 3000);
+
+ this.transport.expressInterest
+ (this, interest, new NDN.ConnectClosure(this, callerInterest, callerClosure, timerID));
+}
+
+NDN.ConnectClosure = function ConnectClosure(ndn, callerInterest, callerClosure, timerID) {
+ // Inherit from Closure.
+ Closure.call(this);
+
+ this.ndn = ndn;
+ this.callerInterest = callerInterest;
+ this.callerClosure = callerClosure;
+ this.timerID = timerID;
+};
+
+NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
+ if (!(kind == Closure.UPCALL_CONTENT ||
+ kind == Closure.UPCALL_CONTENT_UNVERIFIED ||
+ kind == Closure.UPCALL_INTEREST))
+ // The upcall is not for us.
+ return Closure.RESULT_ERR;
+
+ // The host is alive, so cancel the timeout and issue the caller's interest.
+ clearTimeout(this.timerID);
+ console.log(this.ndn.host + ": Host is alive. Fetching callerInterest.");
+ this.ndn.transport.expressInterest(this.ndn, this.callerInterest, this.callerClosure);
+
+ return Closure.RESULT_OK;
+};
+
/**
* @author: Wentao Shang
* See COPYING for copyright and distribution information.
@@ -155,10 +267,12 @@
this.buffer = new Uint8Array(this.maxBufferSize);
this.bufferOffset = 0;
this.structureDecoder = new BinaryXMLStructureDecoder();
+ this.defaultGetHostAndPort = NDN.makeShuffledGetHostAndPort
+ (["A.ws.ndn.ucla.edu", "B.ws.ndn.ucla.edu", "C.ws.ndn.ucla.edu", "D.ws.ndn.ucla.edu",
+ "E.ws.ndn.ucla.edu"],
+ 9696);
};
-var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
-
WebSocketTransport.prototype.connectWebSocket = function(ndn) {
if (this.ws != null)
delete this.ws;
@@ -239,7 +353,7 @@
nameStr = co.name.getName();
if (LOG > 3) console.log(nameStr);
- if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
+ if (self.ccndid == null && nameStr.match(NDN.ccndIdFetcher) != null) {
// We are in starting phase, record publisherPublicKeyDigest in self.ccndid
if(!co.signedInfo || !co.signedInfo.publisher
|| !co.signedInfo.publisher.publisherPublicKeyDigest) {
@@ -260,7 +374,7 @@
//console.log("NDN.onopen event fired.");
}
} else {
- var pitEntry = getEntryForExpressedInterest(nameStr);
+ var pitEntry = NDN.getEntryForExpressedInterest(co.name);
if (pitEntry != null) {
//console.log(pitEntry);
@@ -269,9 +383,10 @@
//console.log("Clear interest timer");
//console.log(pitEntry.closure.timerID);
- // Remove PIT entry from PITTable
- var index = PITTable.indexOf(pitEntry);
- PITTable.splice(index, 1);
+ // Remove PIT entry from NDN.PITTable
+ var index = NDN.PITTable.indexOf(pitEntry);
+ if (index >= 0)
+ NDN.PITTable.splice(index, 1);
// Raise callback
pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
@@ -298,7 +413,7 @@
if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
// Fetch ccndid now
- var interest = new Interest(new Name(ccndIdFetcher));
+ var interest = new Interest(new Name(NDN.ccndIdFetcher));
interest.interestLifetime = 4.0; // seconds
var subarray = encodeToBinaryInterest(interest);
@@ -323,25 +438,7 @@
ndn.onclose();
//console.log("NDN.onclose event fired.");
}
-}
-
-
-// For fetching data
-var PITTable = new Array();
-
-var PITEntry = function PITEntry(interest, closure) {
- this.interest = interest; // String
- this.closure = closure; // Closure
-}
-
-function getEntryForExpressedInterest(name) {
- for (var i = 0; i < PITTable.length; i++) {
- if (name.match(PITTable[i].interest) != null)
- return PITTable[i];
- // TODO: handle multiple matching prefixes
- }
- return null;
-}
+};
WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
if (this.ws != null) {
@@ -351,8 +448,8 @@
var bytearray = new Uint8Array(binaryInterest.length);
bytearray.set(binaryInterest);
- var pitEntry = new PITEntry(interest.name.getName(), closure);
- PITTable.push(pitEntry);
+ var pitEntry = new PITEntry(interest, closure);
+ NDN.PITTable.push(pitEntry);
this.ws.send(bytearray.buffer);
if (LOG > 3) console.log('ws.send() returned.');
@@ -361,11 +458,12 @@
closure.timerID = setTimeout(function() {
if (LOG > 3) console.log("Interest time out.");
- // Remove PIT entry from PITTable
- var index = PITTable.indexOf(pitEntry);
- //console.log(PITTable);
- PITTable.splice(index, 1);
- //console.log(PITTable);
+ // Remove PIT entry from NDN.PITTable
+ var index = NDN.PITTable.indexOf(pitEntry);
+ //console.log(NDN.PITTable);
+ if (index >= 0)
+ NDN.PITTable.splice(index, 1);
+ //console.log(NDN.PITTable);
// Raise closure callback
closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
}, interest.interestLifetime * 1000); // convert interestLifetime from seconds to ms.
@@ -382,7 +480,7 @@
var CSEntry = function CSEntry(name, closure) {
this.name = name; // String
this.closure = closure; // Closure
-}
+};
function getEntryForRegisteredPrefix(name) {
for (var i = 0; i < CSTable.length; i++) {
@@ -437,7 +535,7 @@
console.log('WebSocket connection is not established.');
return -1;
}
-}
+};
/**
* @author: Meki Cheraoui
@@ -900,6 +998,22 @@
}
/*
+ * Return true if this Name has the same components as name.
+ */
+Name.prototype.equalsName = function(name) {
+ if (this.components.length != name.components.length)
+ return false;
+
+ // Start from the last component because they are more likely to differ.
+ for (var i = this.components.length - 1; i >= 0; --i) {
+ if (!DataUtils.arraysEqual(this.components[i], name.components[i]))
+ return false;
+ }
+
+ return true;
+}
+
+/*
* Find the last component in name that has a ContentDigest and return the digest value as Uint8Array,
* or null if not found.
* A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
@@ -4086,6 +4200,19 @@
}
return result.subarray(size - i, size);
};
+
+/*
+ * Modify array to randomly shuffle the elements.
+ */
+DataUtils.shuffle = function(array) {
+ for (var i = array.length - 1; i >= 1; --i) {
+ // j is from 0 to i.
+ var j = Math.floor(Math.random() * (i + 1));
+ var temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ }
+}
/**
* This file contains utilities to help encode and decode NDN objects.
* author: Meki Cheraoui