Changed NDN to take getTransport and added WebSocketTransport.js which defines the default transport. Make NDN.expressInterest take the Name and call the specified transport's expressInterest.
diff --git a/js/Helper.js b/js/Helper.js
index a77aa12..52faac9 100644
--- a/js/Helper.js
+++ b/js/Helper.js
@@ -3,6 +3,8 @@
document.write('<script type="text/javascript" src="../NDN.js"></script>');
+document.write('<script type="text/javascript" src="../WebSocketTransport.js"></script>');
+
document.write('<script type="text/javascript" src="../util/CCNProtocolDTags.js"></script>');
document.write('<script type="text/javascript" src="../util/CCNTime.js"></script>');
diff --git a/js/NDN.js b/js/NDN.js
index 958a267..8dde5c3 100644
--- a/js/NDN.js
+++ b/js/NDN.js
@@ -1,18 +1,30 @@
/*
- * @author: ucla-cs
+ * @author: Meki Cherkaoui, Jeff Thompson, Wentao Shang
* See COPYING for copyright and distribution information.
* This class represents the top-level object for communicating with an NDN host.
*/
+var LOG = 3;
+
/**
- * host is default '127.0.0.1'.
- * port is default 9695.
+ * settings is an associative array with the following defaults:
+ * {
+ * host: 'localhost',
+ * port: 9696,
+ * getTransport: function() { return new WebSocketTransport(); }
+ * }
*/
-var NDN = function NDN(host, port){
- this.host = (host || '127.0.0.1');
- this.port = (port || 9695);
+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();
};
+
+/* Java Socket Bridge and XPCOM transport */
+
NDN.prototype.createRoute = function(host,port){
this.host=host;
this.port=port;
@@ -26,32 +38,32 @@
console.log('INVALID INPUT TO GET');
return null;
}
-
-
+
+
//var array = Name.createNameArray(message);
int = new Interest(new Name(message));
int.InterestLifetime = 4200;
-
+
var hex = encodeToHexInterest(int);
-
+
//var result = get_java_socket_bridge().connectAndStart(ndnurl,ndnport,hex);
-
+
var result = get(this.host,this.port, hex);
if(LOG>0)console.log('BINARY RESPONSE IS ' +result);
-
+
if(result==null || result==undefined || result =="" ){
/*if(result[0] != '0'||result[1]!='4') {
if(LOG>2)console.log('INVALID ANSWER');
}*/
return null;
}
-
+
else{
-
+
co = decodeHexContentObject(result);
if(LOG>2) {
@@ -68,76 +80,76 @@
return null;
}
-
+
}
NDN.prototype.put = function(name,content){
if(this.host!=null && this.port!=null){
-
+
var co = this.get("/%C1.M.S.localhost/%C1.M.SRV/ccnd");
-
+
if(!co || !co.signedInfo || !co.signedInfo.publisher || !co.signedInfo.publisher.publisherPublicKeyDigest){
alert("Cannot contact router");
-
+
return null;
}
-
+
var ccnxnodename = co.signedInfo.publisher.publisherPublicKeyDigest;
-
+
name = name.trim();
-
+
var fe = new ForwardingEntry('selfreg',new Name(name),null, null, 3,2147483647);
-
+
var bytes = encodeForwardingEntry(fe);
-
-
+
+
var si = new SignedInfo();
si.setFields();
-
+
var co = new ContentObject(new Name(),si,bytes,new Signature());
co.sign();
-
+
var coBinary = encodeToBinaryContentObject(co);
-
+
//var ccnxnodename = unescape('%E0%A0%1E%099h%F9t%0C%E7%F46%1B%AB%F5%BB%05%A4%E5Z%AC%A5%E5%8Fs%ED%DE%B8%E0%13%AA%8F');
-
+
var interestName = new Name(['ccnx',ccnxnodename,'selfreg',coBinary]);
int = new Interest(interestName);
int.scope = 1;
-
+
var hex = encodeToHexInterest(int);
console.log('GOING TO PUT INTEREST OBJECT');
-
+
console.log(hex);
-
+
//var result = put(this.host,this.port, hex,name);
-
+
//if(LOG>3)console.log('received interest'); //from host'+ host +':'+port+' with name '+name);
-
+
//if(LOG>3)console.log('DATA ');
-
+
//if(LOG>3)console.log(result);
-
+
//interest = decodeHexInterest(result);
-
+
//console.log('SUCCESSFULLY PARSED INTEREST');
-
+
console.log('CREATING ANSWER');
var si = new SignedInfo();
si.setFields();
-
+
var answer = DataUtils.toNumbersFromString(content);
var co = new ContentObject(new Name(name),si,answer,new Signature());
co.sign();
-
-
+
+
var outputHex = encodeToHexContentObject(co);
-
+
//console.log('SENDING ANSWER');
//return get_java_socket_bridge().putAnswer(outputHex,name);
@@ -171,7 +183,7 @@
return;
}
- interest = new Interest(name);
+ var interest = new Interest(name);
if (template != null) {
interest.minSuffixComponents = template.minSuffixComponents;
interest.maxSuffixComponents = template.maxSuffixComponents;
@@ -185,51 +197,10 @@
else
interest.interestLifetime = 4200;
- var encoder = new BinaryXMLEncoder();
- interest.to_ccnb(encoder);
- var outputData = encoder.getReducedOstream();
- encoder = null;
-
- // Make a local variable so it is not masked by an inner this.
- var ndn = this;
- var dataListener = {
- onReceivedData : function(data) {
- if (data == null || data == undefined || data.length == 0)
- dump("NDN.expressInterest: received empty data from socket.\n");
- else {
- var decoder = new BinaryXMLDecoder(data);
- var co = new ContentObject();
- co.from_ccnb(decoder);
-
- if(LOG>2) {
- dump("DECODED CONTENT OBJECT\n");
- dump(co);
- dump("\n");
- }
-
- // TODO: verify the content object and set kind to UPCALL_CONTENT.
- var result = closure.upcall(Closure.UPCALL_CONTENT_UNVERIFIED,
- new UpcallInfo(ndn, interest, 0, co));
- if (result == Closure.RESULT_OK) {
- // success
- }
- else if (result == Closure.RESULT_ERR)
- dump("NDN.expressInterest: upcall returned RESULT_ERR.\n");
- else if (result == Closure.RESULT_REEXPRESS)
- readAllFromSocket(ndn.host, ndn.port, outputData, dataListener);
- else if (result == Closure.RESULT_VERIFY) {
- // TODO: force verification of content.
- }
- else if (result == Closure.RESULT_FETCHKEY) {
- // TODO: get the key in the key locator and re-call the interest
- // with the key available in the local storage.
- }
- }
- }
- }
-
- // The application includes a source file that defines readAllFromSocket
- // according to the application's communication method.
- readAllFromSocket(this.host, this.port, outputData, dataListener);
+ this.transport.expressInterest(this, interest, closure);
};
+
+NDN.prototype.registerPrefix = function(name, closure, flag) {
+ return this.transport.registerPrefix(this, name, closure, flag);
+}
diff --git a/js/WebSocketTransport.js b/js/WebSocketTransport.js
new file mode 100644
index 0000000..0b9543e
--- /dev/null
+++ b/js/WebSocketTransport.js
@@ -0,0 +1,257 @@
+/*
+ * @author: Wentao Shang
+ * See COPYING for copyright and distribution information.
+ * Implement getAsync and putAsync used by NDN using nsISocketTransportService.
+ * This is used inside Firefox XPCOM modules.
+ */
+
+var WebSocketTransport = function WebSocketTransport() {
+ this.ws = null;
+ this.ccndid = null;
+ this.maxBufferSize = 10000; // Currently support 10000 bytes data input, consistent with BinaryXMLEncoder
+ this.buffer = new Uint8Array(this.maxBufferSize);
+ this.structureDecoder = new BinaryXMLStructureDecoder();
+};
+
+WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+ if (this.ws != null) {
+ //TODO: check local content store first
+
+ var binaryInterest = encodeToBinaryInterest(interest);
+ var bytearray = new Uint8Array(binaryInterest.length);
+ bytearray.set(binaryInterest);
+
+ var pitEntry = new PITEntry(interest.name.getName(), closure);
+ PITTable.push(pitEntry);
+
+ this.ws.send(bytearray.buffer);
+ console.log('ws.send() returned.');
+ }
+ else{
+ console.log('WebSocket connection is not established.');
+ return null;
+ }
+};
+
+
+var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
+
+WebSocketTransport.prototype.connectWebSocket = function(ndn) {
+ if (this.ws != null)
+ delete this.ws;
+
+ this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
+ console.log('ws connection created.');
+
+ this.ws.binaryType = "arraybuffer";
+
+ var self = this;
+ this.ws.onmessage = function(ev) {
+ var result = ev.data;
+ //console.log('RecvHandle called.');
+
+ if(result == null || result == undefined || result == "" ) {
+ console.log('INVALID ANSWER');
+ } else if (result instanceof ArrayBuffer) {
+ var bytearray = new Uint8Array(result);
+
+ if (LOG>3) console.log('BINARY RESPONSE IS ' + bytearray);
+
+ try {
+ if (bytearray.length + self.buffer.byteOffset >= self.buffer.byteLength) {
+ console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: " + self.buffer.byteOffset
+ + ". Current packet length: " + bytearray.length + ".");
+ // Purge and quit.
+ delete self.structureDecoder;
+ delete self.buffer;
+ self.structureDecoder = new BinaryXMLStructureDecoder();
+ self.buffer = new Uint8Array(self.maxBufferSize);
+ return;
+ }
+
+ /*for (var i = 0; i < bytearray.length; i++) {
+ self.buffer.push(bytearray[i]);
+ }*/
+ self.buffer.set(bytearray, self.buffer.byteOffset);
+
+ if (!self.structureDecoder.findElementEnd(self.buffer)) {
+ // Need more data to decode
+ console.log('Incomplete packet received. Length ' + bytearray.length + '. Wait for more input.');
+ console.log('self.buffer length: ' + self.buffer.length);
+ return;
+ }
+ } catch (ex) {
+ console.log("NDN.ws.onmessage exception: " + ex);
+ return;
+ }
+
+ var decoder = new BinaryXMLDecoder(self.buffer);
+ // Dispatch according to packet type
+ if (decoder.peekStartElement(CCNProtocolDTags.Interest)) { // Interest packet
+ console.log('Interest packet received.');
+
+ var interest = new Interest();
+ interest.from_ccnb(decoder);
+ if (LOG>3) console.log(interest);
+ var nameStr = escape(interest.name.getName());
+ console.log(nameStr);
+
+ var entry = getEntryForRegisteredPrefix(nameStr);
+ if (entry != null) {
+ //console.log(entry);
+ entry.closure.upcall(Closure.UPCALL_INTEREST, new UpcallInfo(ndn, interest, 0, null));
+ }
+
+ } else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) { // Content packet
+ console.log('ContentObject packet received.');
+
+ var co = new ContentObject();
+ co.from_ccnb(decoder);
+ if (LOG>3) console.log(co);
+ nameStr = co.name.getName();
+ console.log(nameStr);
+
+ if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
+ // We are in starting phase, record publisherPublicKeyDigest in self.ccndid
+ if(!co.signedInfo || !co.signedInfo.publisher
+ || !co.signedInfo.publisher.publisherPublicKeyDigest) {
+ console.log("Cannot contact router");
+ } else {
+ console.log('Connected to ccnd.');
+ self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
+ if (LOG>3) console.log(self.ccndid);
+ }
+ } else {
+ var pitEntry = getEntryForExpressedInterest(nameStr);
+ if (pitEntry != null) {
+ //console.log(pitEntry);
+ pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+ }
+ }
+ } else {
+ console.log('Incoming packet is not Interest or ContentObject. Discard now.');
+ }
+
+ delete decoder;
+
+ // Renew StrcutureDecoder and buffer after we process a full packet
+ delete self.structureDecoder;
+ delete self.buffer;
+ self.structureDecoder = new BinaryXMLStructureDecoder();
+ self.buffer = new Uint8Array(self.maxBufferSize);
+ }
+ }
+
+ this.ws.onopen = function(ev) {
+ console.log(ev);
+ console.log('ws.onopen: WebSocket connection opened.');
+ console.log('ws.onopen: ReadyState: ' + this.readyState);
+
+ // Fetch ccndid now
+ interest = new Interest(new Name(ccndIdFetcher));
+ interest.InterestLifetime = 4200;
+ //var hex = encodeToHexInterest(interest);
+ var hex = encodeToBinaryInterest(interest);
+
+ /*var bytes = new Uint8Array(hex.length / 2);
+ for (var i = 0; i < hex.length; i = i + 2) {
+ bytes[i / 2] = '0x' + hex.substr(i, 2);
+ }*/
+ var bytes = new Uint8Array(hex.length);
+ bytes.set(hex);
+
+ self.ws.send(bytes.buffer);
+ console.log('ws.onopen: ws.send() returned.');
+ }
+
+ this.ws.onerror = function(ev) {
+ console.log('ws.onerror: ReadyState: ' + this.readyState);
+ console.log(ev);
+ console.log('ws.onerror: WebSocket error: ' + ev.data);
+ }
+
+ this.ws.onclose = function(ev) {
+ console.log('ws.onclose: WebSocket connection closed.');
+ self.ws = null;
+ }
+}
+
+
+// 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;
+}
+
+
+// For publishing data
+var CSTable = new Array();
+
+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++) {
+ if (CSTable[i].name.match(name) != null)
+ return CSTable[i];
+ }
+ return null;
+}
+
+WebSocketTransport.prototype.registerPrefix = function(ndn, name, closure, flag) {
+ if (this.ws != null) {
+ if (this.ccndid == null) {
+ console.log('ccnd node ID unkonwn. Cannot register prefix.');
+ return;
+ }
+
+ name = name.trim();
+
+ var fe = new ForwardingEntry('selfreg', new Name(name), null, null, 3, 2147483647);
+ var bytes = encodeForwardingEntry(fe);
+
+ var si = new SignedInfo();
+ si.setFields();
+
+ var co = new ContentObject(new Name(), si, bytes, new Signature());
+ co.sign();
+ var coBinary = encodeToBinaryContentObject(co);
+
+ //var ccnxnodename = unescape('%00%88%E2%F4%9C%91%16%16%D6%21%8E%A0c%95%A5%A6r%11%E0%A0%82%89%A6%A9%85%AB%D6%E2%065%DB%AF');
+ var ccnxnodename = this.ccndid;
+ var interestName = new Name(['ccnx', ccnxnodename, 'selfreg', coBinary]);
+
+ var interest = new Interest(interestName);
+ interest.scope = 1;
+ //var hex = encodeToHexInterest(int);
+ var binaryInterest = encodeToBinaryInterest(interest);
+ var bytearray = new Uint8Array(binaryInterest.length);
+ bytearray.set(binaryInterest);
+ console.log('Send Interest registration packet.');
+
+ var csEntry = new CSEntry(name, closure);
+ CSTable.push(csEntry);
+
+ this.ws.send(bytearray.buffer);
+ console.log('ws.send() returned.');
+
+ return 0;
+ } else {
+ console.log('WebSocket connection is not established.');
+ return -1;
+ }
+}
+
diff --git a/js/testing/test-get-async.html b/js/testing/test-get-async.html
index fc7d84a..f93db6c 100644
--- a/js/testing/test-get-async.html
+++ b/js/testing/test-get-async.html
@@ -11,7 +11,7 @@
<script type="text/javascript">
var ndn = new NDN();
- ndn.connectWebSocket();
+ ndn.transport.connectWebSocket(ndn);
var AsyncGetClosure = function AsyncGetClosure() {
// Inherit from Closure.
@@ -35,7 +35,7 @@
function run() {
- ndn.expressInterestWS(document.getElementById('interest').value, new AsyncGetClosure(), null);
+ ndn.expressInterest(new Name(document.getElementById('interest').value), new AsyncGetClosure());
}
</script>
diff --git a/js/testing/test-put-async.html b/js/testing/test-put-async.html
index f94d2b6..448d8bf 100644
--- a/js/testing/test-put-async.html
+++ b/js/testing/test-put-async.html
@@ -12,7 +12,7 @@
<script type="text/javascript">
var ndn = new NDN();
- ndn.connectWebSocket();
+ ndn.transport.connectWebSocket(ndn);
var AsyncPutClosure = function AsyncPutClosure() {
// Inherit from Closure.
@@ -31,7 +31,7 @@
console.log('AsyncPutClosure.upcall() called.');
var content = document.getElementById('content').value;
var interest = upcallInfo.interest;
- nameStr = escape(interest.name.getName());
+ var nameStr = escape(interest.name.getName());
var si = new SignedInfo();
si.setFields();
@@ -47,7 +47,7 @@
bytearray[i / 2] = '0x' + hex.substr(i, 2);
}
- upcallInfo.ndn.ws.send(bytearray.buffer);
+ upcallInfo.ndn.transport.ws.send(bytearray.buffer);
console.log('ws.send() finised.');
return Closure.RESULT_INTEREST_CONSUMED;
@@ -59,7 +59,7 @@
function run() {
var contentName = document.getElementById('contentname').value;
- result = ndn.registerPrefixWS(contentName, new AsyncPutClosure(), null);
+ var result = ndn.registerPrefix(contentName, new AsyncPutClosure());
document.getElementById('result').innerHTML = 'Content name \'' + contentName
+'\' published. Result: ' + result;