blob: 0396b93fbbd7e9502dac761f5d7b2a0a5b39a2a7 [file] [log] [blame]
Wentao Shangc0311e52012-12-03 10:38:23 -08001/**
Jeff Thompson5b265a72012-11-12 01:13:08 -08002 * @author: Wentao Shang
3 * See COPYING for copyright and distribution information.
Jeff Thompson5b265a72012-11-12 01:13:08 -08004 */
5
6var WebSocketTransport = function WebSocketTransport() {
7 this.ws = null;
8 this.ccndid = null;
9 this.maxBufferSize = 10000; // Currently support 10000 bytes data input, consistent with BinaryXMLEncoder
10 this.buffer = new Uint8Array(this.maxBufferSize);
Wentao Shang2b740e62012-12-07 00:02:53 -080011 this.bufferOffset = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -080012 this.structureDecoder = new BinaryXMLStructureDecoder();
13};
14
Jeff Thompson5b265a72012-11-12 01:13:08 -080015var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
16
17WebSocketTransport.prototype.connectWebSocket = function(ndn) {
18 if (this.ws != null)
19 delete this.ws;
20
21 this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
Jeff Thompson3c263812012-12-01 17:20:28 -080022 if (LOG > 0) console.log('ws connection created.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080023
24 this.ws.binaryType = "arraybuffer";
25
26 var self = this;
27 this.ws.onmessage = function(ev) {
28 var result = ev.data;
29 //console.log('RecvHandle called.');
30
31 if(result == null || result == undefined || result == "" ) {
32 console.log('INVALID ANSWER');
33 } else if (result instanceof ArrayBuffer) {
34 var bytearray = new Uint8Array(result);
35
Jeff Thompson13c2e7b2012-12-01 17:33:30 -080036 if (LOG>3) console.log('BINARY RESPONSE IS ' + DataUtils.toHex(bytearray));
Jeff Thompson5b265a72012-11-12 01:13:08 -080037
38 try {
Wentao Shang2b740e62012-12-07 00:02:53 -080039 if (bytearray.length + self.bufferOffset >= self.buffer.byteLength) {
40 if (LOG>3) {
41 console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: " + self.bufferOffset
42 + ". Current packet length: " + bytearray.length + ".");
43 }
44
Jeff Thompson5b265a72012-11-12 01:13:08 -080045 // Purge and quit.
46 delete self.structureDecoder;
47 delete self.buffer;
48 self.structureDecoder = new BinaryXMLStructureDecoder();
49 self.buffer = new Uint8Array(self.maxBufferSize);
Wentao Shang2b740e62012-12-07 00:02:53 -080050 self.bufferOffset = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -080051 return;
52 }
53
54 /*for (var i = 0; i < bytearray.length; i++) {
55 self.buffer.push(bytearray[i]);
56 }*/
Wentao Shang2b740e62012-12-07 00:02:53 -080057 self.buffer.set(bytearray, self.bufferOffset);
58 self.bufferOffset += bytearray.length;
Jeff Thompson5b265a72012-11-12 01:13:08 -080059
Wentao Shang2b740e62012-12-07 00:02:53 -080060 if (!self.structureDecoder.findElementEnd(self.buffer.subarray(0, self.bufferOffset))) {
Jeff Thompson5b265a72012-11-12 01:13:08 -080061 // Need more data to decode
Wentao Shang2b740e62012-12-07 00:02:53 -080062 if (LOG>3) console.log('Incomplete packet received. Length ' + bytearray.length + '. Wait for more input.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080063 return;
64 }
Wentao Shang2b740e62012-12-07 00:02:53 -080065 if (LOG>3) console.log('Complete packet received. Length ' + bytearray.length + '. Start decoding.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080066 } catch (ex) {
67 console.log("NDN.ws.onmessage exception: " + ex);
68 return;
69 }
70
71 var decoder = new BinaryXMLDecoder(self.buffer);
72 // Dispatch according to packet type
73 if (decoder.peekStartElement(CCNProtocolDTags.Interest)) { // Interest packet
Jeff Thompson3c263812012-12-01 17:20:28 -080074 if (LOG > 3) console.log('Interest packet received.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080075
76 var interest = new Interest();
77 interest.from_ccnb(decoder);
78 if (LOG>3) console.log(interest);
79 var nameStr = escape(interest.name.getName());
Jeff Thompson3c263812012-12-01 17:20:28 -080080 if (LOG > 3) console.log(nameStr);
Jeff Thompson5b265a72012-11-12 01:13:08 -080081
82 var entry = getEntryForRegisteredPrefix(nameStr);
83 if (entry != null) {
84 //console.log(entry);
85 entry.closure.upcall(Closure.UPCALL_INTEREST, new UpcallInfo(ndn, interest, 0, null));
86 }
87
88 } else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) { // Content packet
Jeff Thompson3c263812012-12-01 17:20:28 -080089 if (LOG > 3) console.log('ContentObject packet received.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080090
91 var co = new ContentObject();
92 co.from_ccnb(decoder);
Jeff Thompson3c263812012-12-01 17:20:28 -080093 if (LOG > 3) console.log(co);
Jeff Thompson5b265a72012-11-12 01:13:08 -080094 nameStr = co.name.getName();
Jeff Thompson3c263812012-12-01 17:20:28 -080095 if (LOG > 3) console.log(nameStr);
Jeff Thompson5b265a72012-11-12 01:13:08 -080096
97 if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
98 // We are in starting phase, record publisherPublicKeyDigest in self.ccndid
99 if(!co.signedInfo || !co.signedInfo.publisher
100 || !co.signedInfo.publisher.publisherPublicKeyDigest) {
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800101 console.log("Cannot contact router, close NDN now.");
Wentao Shang0e291c82012-12-02 23:36:29 -0800102
103 // Close NDN if we fail to connect to a ccn router
104 ndn.readyStatus = NDN.CLOSED;
105 ndn.onclose();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800106 //console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800107 } else {
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800108 //console.log('Connected to ccnd.');
Jeff Thompson5b265a72012-11-12 01:13:08 -0800109 self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
110 if (LOG>3) console.log(self.ccndid);
Wentao Shang0e291c82012-12-02 23:36:29 -0800111
112 // Call NDN.onopen after success
113 ndn.readyStatus = NDN.OPENED;
114 ndn.onopen();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800115 //console.log("NDN.onopen event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800116 }
117 } else {
118 var pitEntry = getEntryForExpressedInterest(nameStr);
119 if (pitEntry != null) {
120 //console.log(pitEntry);
Wentao Shangc0311e52012-12-03 10:38:23 -0800121
122 // Cancel interest timer
123 clearTimeout(pitEntry.closure.timerID);
124 //console.log("Clear interest timer");
125 //console.log(pitEntry.closure.timerID);
Wentao Shangbd63e462012-12-03 16:19:33 -0800126
127 // Remove PIT entry from PITTable
Jeff Thompson53733fb2012-12-09 16:34:56 -0800128 var index = PITTable.indexOf(pitEntry);
Wentao Shangbd63e462012-12-03 16:19:33 -0800129 PITTable.splice(index, 1);
130
Wentao Shangc0311e52012-12-03 10:38:23 -0800131 // Raise callback
Jeff Thompson5b265a72012-11-12 01:13:08 -0800132 pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
133 }
134 }
135 } else {
136 console.log('Incoming packet is not Interest or ContentObject. Discard now.');
137 }
138
139 delete decoder;
140
141 // Renew StrcutureDecoder and buffer after we process a full packet
142 delete self.structureDecoder;
143 delete self.buffer;
144 self.structureDecoder = new BinaryXMLStructureDecoder();
145 self.buffer = new Uint8Array(self.maxBufferSize);
Wentao Shang2b740e62012-12-07 00:02:53 -0800146 self.bufferOffset = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -0800147 }
148 }
149
150 this.ws.onopen = function(ev) {
Jeff Thompson3c263812012-12-01 17:20:28 -0800151 if (LOG > 3) console.log(ev);
152 if (LOG > 3) console.log('ws.onopen: WebSocket connection opened.');
153 if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800154
155 // Fetch ccndid now
Jeff Thompson3c263812012-12-01 17:20:28 -0800156 var interest = new Interest(new Name(ccndIdFetcher));
Jeff Thompson84db2632012-12-09 22:31:39 -0800157 interest.interestLifetime = 4.0; // seconds
Jeff Thompson64db7c02012-12-09 21:56:57 -0800158 var subarray = encodeToBinaryInterest(interest);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800159
Jeff Thompson64db7c02012-12-09 21:56:57 -0800160 var bytes = new Uint8Array(subarray.length);
161 bytes.set(subarray);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800162
163 self.ws.send(bytes.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800164 }
165
166 this.ws.onerror = function(ev) {
167 console.log('ws.onerror: ReadyState: ' + this.readyState);
168 console.log(ev);
169 console.log('ws.onerror: WebSocket error: ' + ev.data);
170 }
171
172 this.ws.onclose = function(ev) {
173 console.log('ws.onclose: WebSocket connection closed.');
174 self.ws = null;
Wentao Shang0e291c82012-12-02 23:36:29 -0800175
176 // Close NDN when WebSocket is closed
177 ndn.readyStatus = NDN.CLOSED;
178 ndn.onclose();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800179 //console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800180 }
181}
182
183
184// For fetching data
185var PITTable = new Array();
186
187var PITEntry = function PITEntry(interest, closure) {
188 this.interest = interest; // String
189 this.closure = closure; // Closure
190}
191
192function getEntryForExpressedInterest(name) {
193 for (var i = 0; i < PITTable.length; i++) {
194 if (name.match(PITTable[i].interest) != null)
195 return PITTable[i];
196 // TODO: handle multiple matching prefixes
197 }
198 return null;
199}
200
Wentao Shangc0311e52012-12-03 10:38:23 -0800201WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
202 if (this.ws != null) {
203 //TODO: check local content store first
204
205 var binaryInterest = encodeToBinaryInterest(interest);
206 var bytearray = new Uint8Array(binaryInterest.length);
207 bytearray.set(binaryInterest);
208
209 var pitEntry = new PITEntry(interest.name.getName(), closure);
210 PITTable.push(pitEntry);
211
212 this.ws.send(bytearray.buffer);
213 if (LOG > 3) console.log('ws.send() returned.');
214
215 // Set interest timer
216 closure.timerID = setTimeout(function() {
Wentao Shangbd63e462012-12-03 16:19:33 -0800217 if (LOG > 3) console.log("Interest time out.");
Wentao Shangc0311e52012-12-03 10:38:23 -0800218
219 // Remove PIT entry from PITTable
Jeff Thompson53733fb2012-12-09 16:34:56 -0800220 var index = PITTable.indexOf(pitEntry);
Wentao Shangc0311e52012-12-03 10:38:23 -0800221 //console.log(PITTable);
222 PITTable.splice(index, 1);
223 //console.log(PITTable);
224 // Raise closure callback
225 closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
Jeff Thompson84db2632012-12-09 22:31:39 -0800226 }, interest.interestLifetime * 1000); // convert interestLifetime from seconds to ms.
Wentao Shangc0311e52012-12-03 10:38:23 -0800227 //console.log(closure.timerID);
228 }
229 else
230 console.log('WebSocket connection is not established.');
231};
232
Jeff Thompson5b265a72012-11-12 01:13:08 -0800233
234// For publishing data
235var CSTable = new Array();
236
237var CSEntry = function CSEntry(name, closure) {
238 this.name = name; // String
239 this.closure = closure; // Closure
240}
241
242function getEntryForRegisteredPrefix(name) {
243 for (var i = 0; i < CSTable.length; i++) {
244 if (CSTable[i].name.match(name) != null)
245 return CSTable[i];
246 }
247 return null;
248}
249
250WebSocketTransport.prototype.registerPrefix = function(ndn, name, closure, flag) {
251 if (this.ws != null) {
252 if (this.ccndid == null) {
253 console.log('ccnd node ID unkonwn. Cannot register prefix.');
Jeff Thompson3c263812012-12-01 17:20:28 -0800254 return -1;
Jeff Thompson5b265a72012-11-12 01:13:08 -0800255 }
256
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800257 var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800258 var bytes = encodeForwardingEntry(fe);
259
260 var si = new SignedInfo();
261 si.setFields();
262
263 var co = new ContentObject(new Name(), si, bytes, new Signature());
264 co.sign();
265 var coBinary = encodeToBinaryContentObject(co);
266
Jeff Thompsonbd829262012-11-30 22:28:37 -0800267 //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');
268 var nodename = this.ccndid;
269 var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800270
271 var interest = new Interest(interestName);
272 interest.scope = 1;
Jeff Thompson5b265a72012-11-12 01:13:08 -0800273 var binaryInterest = encodeToBinaryInterest(interest);
Wentao Shangc05dc532012-11-19 12:00:33 -0800274 // If we directly use binaryInterest.buffer to feed ws.send(),
275 // WebSocket will end up sending a packet with 10000 bytes of data.
276 // That is, WebSocket will flush the entire buffer in BinaryXMLEncoder
277 // regardless of the offset of the Uint8Array. So we have to create
278 // a new Uint8Array buffer with just the right size and copy the
279 // content from binaryInterest to the new buffer.
280 // ---Wentao
Jeff Thompson5b265a72012-11-12 01:13:08 -0800281 var bytearray = new Uint8Array(binaryInterest.length);
282 bytearray.set(binaryInterest);
Jeff Thompson3c263812012-12-01 17:20:28 -0800283 if (LOG > 3) console.log('Send Interest registration packet.');
Jeff Thompson5b265a72012-11-12 01:13:08 -0800284
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800285 var csEntry = new CSEntry(name.getName(), closure);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800286 CSTable.push(csEntry);
287
288 this.ws.send(bytearray.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800289
290 return 0;
291 } else {
292 console.log('WebSocket connection is not established.');
293 return -1;
294 }
295}
296