Added if (LOG>3) messages for receiving data.
diff --git a/js/tools/build/ndn-js-uncomp.js b/js/tools/build/ndn-js-uncomp.js
index 6f1bc95..781fb39 100644
--- a/js/tools/build/ndn-js-uncomp.js
+++ b/js/tools/build/ndn-js-uncomp.js
@@ -19,7 +19,6 @@
 	this.ndn_data = null;  // this holds the ndn_closure
     this.ndn_data_dirty = false;
     
-    //this.timerID = -1;  // Store the interest timer; used to cancel the timer upon receiving interest
 };
 
 // Upcall result
@@ -71,11 +70,7 @@
 
 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.bufferOffset = 0;
-	this.structureDecoder = new BinaryXMLStructureDecoder();
+    this.elementReader = null;
     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"],
@@ -91,6 +86,7 @@
 	
 	this.ws.binaryType = "arraybuffer";
 	
+    this.elementReader = new BinaryXmlElementReader(ndn);
 	var self = this;
 	this.ws.onmessage = function(ev) {
 		var result = ev.data;
@@ -104,237 +100,12 @@
 			if (LOG>3) console.log('BINARY RESPONSE IS ' + DataUtils.toHex(bytearray));
 			
 			try {
-				if (bytearray.length + self.bufferOffset >= self.buffer.byteLength) {
-					if (LOG>3) {
-						console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: " + self.bufferOffset 
-							+ ". Current packet length: " + bytearray.length + ".");
-					}
-					
-					// Purge and quit.
-					delete self.structureDecoder;
-					delete self.buffer;
-					self.structureDecoder = new BinaryXMLStructureDecoder();
-					self.buffer = new Uint8Array(self.maxBufferSize);
-					self.bufferOffset = 0;
-					return;
-				}
-				
-				/*for (var i = 0; i < bytearray.length; i++) {
-					self.buffer.push(bytearray[i]);
-				}*/
-				self.buffer.set(bytearray, self.bufferOffset);
-				self.bufferOffset += bytearray.length;
-				
-				if (!self.structureDecoder.findElementEnd(self.buffer.subarray(0, self.bufferOffset))) {
-					// Need more data to decode
-					if (LOG>3) console.log('Incomplete packet received. Length ' + bytearray.length + '. Wait for more input.');
-					return;
-				}
-				if (LOG>3) console.log('Complete packet received. Length ' + bytearray.length + '. Start decoding.');
+                // Find the end of the binary XML element and call ndn.onReceivedElement.
+                self.elementReader.onReceivedData(bytearray);
 			} 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
-				if (LOG > 3) 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());
-				if (LOG > 3) console.log(nameStr);
-				
-				var entry = getEntryForRegisteredPrefix(nameStr);
-				if (entry != null) {
-					//console.log(entry);
-					var info = new UpcallInfo(ndn, interest, 0, null);
-					var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
-					if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null) { 
-						var coBinary = encodeToBinaryContentObject(info.contentObject);
-						// If we directly use coBinary.buffer to feed ws.send(), WebSocket 
-						// will end up sending a packet with 10000 bytes of data. That 
-						// is, WebSocket will flush the entire buffer in BinaryXMLEncoder
-						// regardless of the offset of the Uint8Array. So we have to
-						// create a new Uint8Array buffer with just the right size and
-						// copy the content from coBinary to the new buffer.
-						//    ---Wentao
-						var bytearray = new Uint8Array(coBinary.length);
-						bytearray.set(coBinary);
-						
-						self.ws.send(bytearray.buffer);
-					}
-				}
-				
-			} else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {  // Content packet
-				if (LOG > 3) console.log('ContentObject packet received.');
-				
-				var co = new ContentObject();
-				co.from_ccnb(decoder);
-				//console.log(co);
-				//var nameStr = co.name.getName();
-				//console.log(nameStr);
-				
-				if (self.ccndid == null && NDN.ccndIdFetcher.match(co.name)) {
-					// 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, close NDN now.");
-						
-						// Close NDN if we fail to connect to a ccn router
-						ndn.readyStatus = NDN.CLOSED;
-						ndn.onclose();
-						//console.log("NDN.onclose event fired.");
-					} else {
-						//console.log('Connected to ccnd.');
-						self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
-						if (LOG>3) console.log(self.ccndid);
-						
-						// Call NDN.onopen after success
-						ndn.readyStatus = NDN.OPENED;
-						ndn.onopen();
-						//console.log("NDN.onopen event fired.");
-					}
-				} else {
-					var pitEntry = NDN.getEntryForExpressedInterest(co.name);
-					if (pitEntry != null) {
-						//console.log(pitEntry);
-						// Remove PIT entry from NDN.PITTable
-						var index = NDN.PITTable.indexOf(pitEntry);
-						if (index >= 0)
-							NDN.PITTable.splice(index, 1);
-						
-						var currentClosure = pitEntry.closure;
-						
-						// Cancel interest timer
-						clearTimeout(pitEntry.timerID);
-						//console.log("Clear interest timer");
-						//console.log(currentClosure.timerID);
-						
-						// Key verification
-						
-						// Recursive key fetching & verification closure
-						var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
-							this.contentObject = content;  // unverified content object
-							this.closure = closure;  // closure corresponding to the contentObject
-							this.keyName = key;  // name of current key to be fetched
-							this.sigHex = sig;  // hex signature string to be verified
-							this.witness = wit;
-							
-							Closure.call(this);
-						};
-						
-						KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
-							if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
-								console.log("In KeyFetchClosure.upcall: interest time out.");
-								console.log(this.keyName.contentName.getName());
-							} else if (kind == Closure.UPCALL_CONTENT) {
-								//console.log("In KeyFetchClosure.upcall: signature verification passed");
-								
-								var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
-								var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
-								
-								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-								//console.log("raise encapsulated closure");
-								this.closure.upcall(flag, new UpcallInfo(ndn, null, 0, this.contentObject));
-								
-								// Store key in cache
-								var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
-								NDN.addKeyEntry(keyEntry);
-								//console.log(NDN.KeyStore);
-							} else if (kind == Closure.UPCALL_CONTENT_BAD) {
-								console.log("In KeyFetchClosure.upcall: signature verification failed");
-							}
-						};
-						
-						if (co.signedInfo && co.signedInfo.locator && co.signature) {
-							if (LOG > 3) console.log("Key verification...");
-							var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
-							
-							var wit = null;
-							if (co.signature.Witness != null) {
-								wit = new Witness();
-								wit.decode(co.signature.Witness);
-							}
-							
-							var keylocator = co.signedInfo.locator;
-							if (keylocator.type == KeyLocatorType.KEYNAME) {
-								if (LOG > 3) console.log("KeyLocator contains KEYNAME");
-								//var keyname = keylocator.keyName.contentName.getName();
-								//console.log(nameStr);
-								//console.log(keyname);
-								
-								if (keylocator.keyName.contentName.match(co.name)) {
-									if (LOG > 3) console.log("Content is key itself");
-									
-									var rsakey = decodeSubjectPublicKeyInfo(co.content);
-									var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-									var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-									
-									currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
-									
-									// SWT: We don't need to store key here since the same key will be
-									//      stored again in the closure.
-									//var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
-									//NDN.addKeyEntry(keyEntry);
-									//console.log(NDN.KeyStore);
-								} else {
-									// Check local key store
-									var keyEntry = NDN.getKeyByName(keylocator.keyName);
-									if (keyEntry) {
-										// Key found, verify now
-										if (LOG > 3) console.log("Local key cache hit");
-										var rsakey = keyEntry.rsaKey;
-										var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-										var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-										
-										// Raise callback
-										currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
-									} else {
-										// Not found, fetch now
-										if (LOG > 3) console.log("Fetch key according to keylocator");
-										var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
-										var interest = new Interest(keylocator.keyName.contentName.getPrefix(4));
-										interest.interestLifetime = 4000;  // milliseconds
-										self.expressInterest(ndn, interest, nextClosure);
-									}
-								}
-							} else if (keylocator.type == KeyLocatorType.KEY) {
-								if (LOG > 3) console.log("Keylocator contains KEY");
-								
-								var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
-								var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-								
-								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-								// Raise callback
-								currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
-								
-								// Since KeyLocator does not contain key name for this key,
-								// we have no way to store it as a key entry in KeyStore.
-							} else {
-								var cert = keylocator.certificate;
-								console.log("KeyLocator contains CERT");
-								console.log(cert);
-								
-								// TODO: verify certificate
-							}
-						}
-					}
-				}
-			} 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);
-			self.bufferOffset = 0;
 		}
 	}
 	
@@ -346,12 +117,7 @@
 		// Fetch ccndid now
 		var interest = new Interest(NDN.ccndIdFetcher);
 		interest.interestLifetime = 4000; // milliseconds
-		var subarray = encodeToBinaryInterest(interest);
-		
-		var bytes = new Uint8Array(subarray.length);
-		bytes.set(subarray);
-		
-		self.ws.send(bytes.buffer);
+        this.send(encodeToBinaryInterest(interest));
 	}
 	
 	this.ws.onerror = function(ev) {
@@ -371,108 +137,62 @@
 	}
 };
 
-WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+/*
+ * Send the Uint8Array data.
+ */
+WebSocketTransport.prototype.send = function(data) {
 	if (this.ws != null) {
-		//TODO: check local content store first
-
-        var binaryInterest = encodeToBinaryInterest(interest);
-		var bytearray = new Uint8Array(binaryInterest.length);
-		bytearray.set(binaryInterest);
-		
-		if (closure != null) {
-			var pitEntry = new PITEntry(interest, closure);
-			NDN.PITTable.push(pitEntry);
-			closure.pitEntry = pitEntry;
-		}
-		
-		this.ws.send(bytearray.buffer);
+        // If we directly use data.buffer to feed ws.send(), 
+        // WebSocket may end up sending a packet with 10000 bytes of data.
+        // That is, WebSocket will flush the entire buffer
+        // regardless of the offset of the Uint8Array. So we have to create
+        // a new Uint8Array buffer with just the right size and copy the 
+        // content from binaryInterest to the new buffer.
+        //    ---Wentao
+        var bytearray = new Uint8Array(data.length);
+        bytearray.set(data);
+        this.ws.send(bytearray.buffer);
 		if (LOG > 3) console.log('ws.send() returned.');
-		
-		// Set interest timer
-		if (closure != null) {
-			pitEntry.timerID = setTimeout(function() {
-				if (LOG > 3) console.log("Interest time out.");
-				
-				// 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);
-				//console.log(pitEntry.interest.name.getName());
-				
-				// Raise closure callback
-				closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
-			}, interest.interestLifetime);  // interestLifetime is in milliseconds.
-			//console.log(closure.timerID);
-		}
 	}
 	else
 		console.log('WebSocket connection is not established.');
-};
-
-
-// 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 -1;
-		}
-		
-		var fe = new ForwardingEntry('selfreg', 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 nodename = 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 nodename = this.ccndid;
-		var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
-
-		var interest = new Interest(interestName);
-		interest.scope = 1;
-		var binaryInterest = encodeToBinaryInterest(interest);
-		// If we directly use binaryInterest.buffer to feed ws.send(), 
-		// WebSocket will end up sending a packet with 10000 bytes of data.
-		// That is, WebSocket will flush the entire buffer in BinaryXMLEncoder
-		// regardless of the offset of the Uint8Array. So we have to create
-		// a new Uint8Array buffer with just the right size and copy the 
-		// content from binaryInterest to the new buffer.
-		//    ---Wentao
-    	var bytearray = new Uint8Array(binaryInterest.length);
-		bytearray.set(binaryInterest);
-		if (LOG > 3) console.log('Send Interest registration packet.');
-    	
-    	var csEntry = new CSEntry(name.getName(), closure);
-		CSTable.push(csEntry);
-    	
-    	this.ws.send(bytearray.buffer);
-		
-		return 0;
-	} else {
-		console.log('WebSocket connection is not established.');
-		return -1;
+WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+    if (ndn.readyStatus != NDN.OPENED) {
+		console.log('Connection is not established.');
+        return;
+    }
+    
+	//TODO: check local content store first
+	if (closure != null) {
+		var pitEntry = new PITEntry(interest, closure);
+        // TODO: This needs to be a single thread-safe transaction on a global object.
+		NDN.PITTable.push(pitEntry);
+		closure.pitEntry = pitEntry;
 	}
+
+	// Set interest timer
+	if (closure != null) {
+		pitEntry.timerID = setTimeout(function() {
+			if (LOG > 3) console.log("Interest time out.");
+				
+			// Remove PIT entry from NDN.PITTable.
+            // TODO: Make this a thread-safe operation on the global PITTable.
+			var index = NDN.PITTable.indexOf(pitEntry);
+			//console.log(NDN.PITTable);
+			if (index >= 0) 
+	            NDN.PITTable.splice(index, 1);
+			//console.log(NDN.PITTable);
+			//console.log(pitEntry.interest.name.getName());
+				
+			// Raise closure callback
+			closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
+		}, interest.interestLifetime);  // interestLifetime is in milliseconds.
+		//console.log(closure.timerID);
+	}
+
+	this.send(encodeToBinaryInterest(interest));
 };
 
 /**
@@ -7809,6 +7529,7 @@
     // Event handler
     this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
     this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("NDN connection closed."); });
+	this.ccndid = null;
 };
 
 NDN.UNOPEN = 0;  // created but not opened yet
@@ -7879,6 +7600,22 @@
 	return result;
 };
 
+// For publishing data
+NDN.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 < NDN.CSTable.length; i++) {
+		if (NDN.CSTable[i].name.match(name) != null)
+			return NDN.CSTable[i];
+	}
+	return null;
+}
+
 /*
  * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
  * If no more hosts remain, return null.
@@ -7933,7 +7670,220 @@
 };
 
 NDN.prototype.registerPrefix = function(name, closure, flag) {
-    return this.transport.registerPrefix(this, name, closure, flag);
+    if (this.readyStatus != NDN.OPENED) {
+		console.log('Connection is not established.');
+        return -1;
+    }
+
+	if (this.ccndid == null) {
+		console.log('ccnd node ID unkonwn. Cannot register prefix.');
+		return -1;
+	}
+		
+	var fe = new ForwardingEntry('selfreg', 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 nodename = 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 nodename = this.ccndid;
+	var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
+
+	var interest = new Interest(interestName);
+	interest.scope = 1;
+	if (LOG > 3) console.log('Send Interest registration packet.');
+    	
+    var csEntry = new CSEntry(name.getName(), closure);
+	NDN.CSTable.push(csEntry);
+    
+    this.transport.send(encodeToBinaryInterest(interest));
+		
+	return 0;
+};
+
+/*
+ * This is called when an entire binary XML element is received, such as a ContentObject or Interest.
+ * Look up in the PITTable and call the closure callback.
+ */
+NDN.prototype.onReceivedElement = function(element) {
+    if (LOG>3) console.log('Complete element received. Length ' + element.length + '. Start decoding.');
+	var decoder = new BinaryXMLDecoder(element);
+	// Dispatch according to packet type
+	if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {  // Interest packet
+		if (LOG > 3) 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());
+		if (LOG > 3) console.log(nameStr);
+				
+		var entry = getEntryForRegisteredPrefix(nameStr);
+		if (entry != null) {
+			//console.log(entry);
+			var info = new UpcallInfo(this, interest, 0, null);
+			var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
+			if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null) 
+				this.transport.send(encodeToBinaryContentObject(info.contentObject));
+		}				
+	} else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {  // Content packet
+		if (LOG > 3) console.log('ContentObject packet received.');
+				
+		var co = new ContentObject();
+		co.from_ccnb(decoder);
+				
+		if (this.ccndid == null && NDN.ccndIdFetcher.match(co.name)) {
+			// We are in starting phase, record publisherPublicKeyDigest in ccndid
+			if(!co.signedInfo || !co.signedInfo.publisher 
+				|| !co.signedInfo.publisher.publisherPublicKeyDigest) {
+				console.log("Cannot contact router, close NDN now.");
+						
+				// Close NDN if we fail to connect to a ccn router
+				this.readyStatus = NDN.CLOSED;
+				this.onclose();
+				//console.log("NDN.onclose event fired.");
+			} else {
+				//console.log('Connected to ccnd.');
+				this.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
+				if (LOG>3) console.log(ndn.ccndid);
+						
+				// Call NDN.onopen after success
+				this.readyStatus = NDN.OPENED;
+				this.onopen();
+				//console.log("NDN.onopen event fired.");
+			}
+		} else {
+			var pitEntry = NDN.getEntryForExpressedInterest(co.name);
+			if (pitEntry != null) {
+				//console.log(pitEntry);
+				// Remove PIT entry from NDN.PITTable
+				var index = NDN.PITTable.indexOf(pitEntry);
+				if (index >= 0)
+					NDN.PITTable.splice(index, 1);
+						
+				var currentClosure = pitEntry.closure;
+						
+				// Cancel interest timer
+				clearTimeout(pitEntry.timerID);
+				//console.log("Clear interest timer");
+				//console.log(currentClosure.timerID);
+						
+				// Key verification
+						
+				// Recursive key fetching & verification closure
+				var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
+					this.contentObject = content;  // unverified content object
+					this.closure = closure;  // closure corresponding to the contentObject
+					this.keyName = key;  // name of current key to be fetched
+					this.sigHex = sig;  // hex signature string to be verified
+					this.witness = wit;
+						
+					Closure.call(this);
+				};
+						
+                var thisNdn = this;
+				KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
+					if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
+						console.log("In KeyFetchClosure.upcall: interest time out.");
+						console.log(this.keyName.contentName.getName());
+					} else if (kind == Closure.UPCALL_CONTENT) {
+						//console.log("In KeyFetchClosure.upcall: signature verification passed");
+								
+						var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
+						var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
+								
+						var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+						//console.log("raise encapsulated closure");
+						this.closure.upcall(flag, new UpcallInfo(thisNdn, null, 0, this.contentObject));
+								
+						// Store key in cache
+						var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
+						NDN.addKeyEntry(keyEntry);
+						//console.log(NDN.KeyStore);
+					} else if (kind == Closure.UPCALL_CONTENT_BAD) {
+						console.log("In KeyFetchClosure.upcall: signature verification failed");
+					}
+				};
+						
+				if (co.signedInfo && co.signedInfo.locator && co.signature) {
+					if (LOG > 3) console.log("Key verification...");
+					var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
+							
+					var wit = null;
+					if (co.signature.Witness != null) {
+						wit = new Witness();
+						wit.decode(co.signature.Witness);
+					}
+							
+					var keylocator = co.signedInfo.locator;
+					if (keylocator.type == KeyLocatorType.KEYNAME) {
+						if (LOG > 3) console.log("KeyLocator contains KEYNAME");
+						//var keyname = keylocator.keyName.contentName.getName();
+						//console.log(nameStr);
+						//console.log(keyname);
+								
+						if (keylocator.keyName.contentName.match(co.name)) {
+							if (LOG > 3) console.log("Content is key itself");
+									
+							var rsakey = decodeSubjectPublicKeyInfo(co.content);
+							var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
+							var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+									
+							currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
+									
+							// SWT: We don't need to store key here since the same key will be
+							//      stored again in the closure.
+							//var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
+							//NDN.addKeyEntry(keyEntry);
+							//console.log(NDN.KeyStore);
+						} else {
+							// Check local key store
+							var keyEntry = NDN.getKeyByName(keylocator.keyName);
+							if (keyEntry) {
+								// Key found, verify now
+								if (LOG > 3) console.log("Local key cache hit");
+								var rsakey = keyEntry.rsaKey;
+								var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
+								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+										
+								// Raise callback
+								currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
+							} else {
+								// Not found, fetch now
+								if (LOG > 3) console.log("Fetch key according to keylocator");
+								var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
+								this.expressInterest(keylocator.keyName.contentName.getPrefix(4), nextClosure);
+							}
+						}
+					} else if (keylocator.type == KeyLocatorType.KEY) {
+						if (LOG > 3) console.log("Keylocator contains KEY");
+								
+						var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
+						var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
+								
+						var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+						// Raise callback
+						currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(this, null, 0, co));
+								
+						// Since KeyLocator does not contain key name for this key,
+						// we have no way to store it as a key entry in KeyStore.
+					} else {
+						var cert = keylocator.certificate;
+						console.log("KeyLocator contains CERT");
+						console.log(cert);
+								
+						// TODO: verify certificate
+					}
+				}
+			}
+		}
+	} else
+		console.log('Incoming packet is not Interest or ContentObject. Discard now.');
 };
 
 /*
@@ -7958,8 +7908,8 @@
     this.port = hostAndPort.port;   
     console.log("Trying host from getHostAndPort: " + this.host);
     
-    // Fetch the ccndId.
-    var interest = new Interest(NDN.ccndIdFetcher);
+    // Fetch any content.
+    var interest = new Interest(new Name("/"));
 	interest.interestLifetime = 4000; // milliseconds    
 
     var thisNDN = this;
@@ -7985,8 +7935,7 @@
 
 NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
     if (!(kind == Closure.UPCALL_CONTENT ||
-          kind == Closure.UPCALL_CONTENT_UNVERIFIED ||
-          kind == Closure.UPCALL_INTEREST))
+          kind == Closure.UPCALL_CONTENT_UNVERIFIED))
         // The upcall is not for us.
         return Closure.RESULT_ERR;
         
@@ -7998,3 +7947,43 @@
     return Closure.RESULT_OK;
 };
 
+/*
+ * A BinaryXmlElementReader lets you call onReceivedData multiple times which uses a
+ *   BinaryXMLStructureDecoder to detect the end of a binary XML element and calls
+ *   elementListener.onReceivedElement(element) with the element. 
+ * This handles the case where a single call to onReceivedData may contain multiple elements.
+ */
+var BinaryXmlElementReader = function BinaryXmlElementReader(elementListener) {
+    this.elementListener = elementListener;
+	this.dataParts = [];
+    this.structureDecoder = new BinaryXMLStructureDecoder();
+};
+
+BinaryXmlElementReader.prototype.onReceivedData = function(/* Uint8Array */ rawData) {
+    // Process multiple objects in the data.
+    while(true) {
+        // Scan the input to check if a whole ccnb object has been read.
+        this.structureDecoder.seek(0);
+        if (this.structureDecoder.findElementEnd(rawData)) {
+            // Got the remainder of an object.  Report to the caller.
+            this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
+            this.elementListener.onReceivedElement(DataUtils.concatArrays(this.dataParts));
+        
+            // Need to read a new object.
+            rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
+            this.dataParts = [];
+            this.structureDecoder = new BinaryXMLStructureDecoder();
+            if (rawData.length == 0)
+                // No more data in the packet.
+                return;
+            
+            // else loop back to decode.
+        }
+        else {
+            // Save for a later call to concatArrays so that we only copy data once.
+            this.dataParts.push(rawData);
+			if (LOG>3) console.log('Incomplete packet received. Length ' + rawData.length + '. Wait for more input.');
+            return;
+        }
+    }    
+}
\ No newline at end of file