blob: a482775d8b66a791fdec136299250e8715540c6e [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.
4 * Implement getAsync and putAsync used by NDN using nsISocketTransportService.
5 * This is used inside Firefox XPCOM modules.
6 */
7
8var WebSocketTransport = function WebSocketTransport() {
9 this.ws = null;
10 this.ccndid = null;
11 this.maxBufferSize = 10000; // Currently support 10000 bytes data input, consistent with BinaryXMLEncoder
12 this.buffer = new Uint8Array(this.maxBufferSize);
Wentao Shang2b740e62012-12-07 00:02:53 -080013 this.bufferOffset = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -080014 this.structureDecoder = new BinaryXMLStructureDecoder();
15};
16
Jeff Thompson5b265a72012-11-12 01:13:08 -080017var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
18
19WebSocketTransport.prototype.connectWebSocket = function(ndn) {
20 if (this.ws != null)
21 delete this.ws;
22
23 this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
Jeff Thompson3c263812012-12-01 17:20:28 -080024 if (LOG > 0) console.log('ws connection created.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080025
26 this.ws.binaryType = "arraybuffer";
27
28 var self = this;
29 this.ws.onmessage = function(ev) {
30 var result = ev.data;
31 //console.log('RecvHandle called.');
32
33 if(result == null || result == undefined || result == "" ) {
34 console.log('INVALID ANSWER');
35 } else if (result instanceof ArrayBuffer) {
36 var bytearray = new Uint8Array(result);
37
Jeff Thompson13c2e7b2012-12-01 17:33:30 -080038 if (LOG>3) console.log('BINARY RESPONSE IS ' + DataUtils.toHex(bytearray));
Jeff Thompson5b265a72012-11-12 01:13:08 -080039
40 try {
Wentao Shang2b740e62012-12-07 00:02:53 -080041 if (bytearray.length + self.bufferOffset >= self.buffer.byteLength) {
42 if (LOG>3) {
43 console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: " + self.bufferOffset
44 + ". Current packet length: " + bytearray.length + ".");
45 }
46
Jeff Thompson5b265a72012-11-12 01:13:08 -080047 // Purge and quit.
48 delete self.structureDecoder;
49 delete self.buffer;
50 self.structureDecoder = new BinaryXMLStructureDecoder();
51 self.buffer = new Uint8Array(self.maxBufferSize);
Wentao Shang2b740e62012-12-07 00:02:53 -080052 self.bufferOffset = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -080053 return;
54 }
55
56 /*for (var i = 0; i < bytearray.length; i++) {
57 self.buffer.push(bytearray[i]);
58 }*/
Wentao Shang2b740e62012-12-07 00:02:53 -080059 self.buffer.set(bytearray, self.bufferOffset);
60 self.bufferOffset += bytearray.length;
Jeff Thompson5b265a72012-11-12 01:13:08 -080061
Wentao Shang2b740e62012-12-07 00:02:53 -080062 if (!self.structureDecoder.findElementEnd(self.buffer.subarray(0, self.bufferOffset))) {
Jeff Thompson5b265a72012-11-12 01:13:08 -080063 // Need more data to decode
Wentao Shang2b740e62012-12-07 00:02:53 -080064 if (LOG>3) console.log('Incomplete packet received. Length ' + bytearray.length + '. Wait for more input.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080065 return;
66 }
Wentao Shang2b740e62012-12-07 00:02:53 -080067 if (LOG>3) console.log('Complete packet received. Length ' + bytearray.length + '. Start decoding.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080068 } catch (ex) {
69 console.log("NDN.ws.onmessage exception: " + ex);
70 return;
71 }
72
73 var decoder = new BinaryXMLDecoder(self.buffer);
74 // Dispatch according to packet type
75 if (decoder.peekStartElement(CCNProtocolDTags.Interest)) { // Interest packet
Jeff Thompson3c263812012-12-01 17:20:28 -080076 if (LOG > 3) console.log('Interest packet received.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080077
78 var interest = new Interest();
79 interest.from_ccnb(decoder);
80 if (LOG>3) console.log(interest);
81 var nameStr = escape(interest.name.getName());
Jeff Thompson3c263812012-12-01 17:20:28 -080082 if (LOG > 3) console.log(nameStr);
Jeff Thompson5b265a72012-11-12 01:13:08 -080083
84 var entry = getEntryForRegisteredPrefix(nameStr);
85 if (entry != null) {
86 //console.log(entry);
87 entry.closure.upcall(Closure.UPCALL_INTEREST, new UpcallInfo(ndn, interest, 0, null));
88 }
89
90 } else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) { // Content packet
Jeff Thompson3c263812012-12-01 17:20:28 -080091 if (LOG > 3) console.log('ContentObject packet received.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080092
93 var co = new ContentObject();
94 co.from_ccnb(decoder);
Jeff Thompson3c263812012-12-01 17:20:28 -080095 if (LOG > 3) console.log(co);
Jeff Thompson5b265a72012-11-12 01:13:08 -080096 nameStr = co.name.getName();
Jeff Thompson3c263812012-12-01 17:20:28 -080097 if (LOG > 3) console.log(nameStr);
Jeff Thompson5b265a72012-11-12 01:13:08 -080098
99 if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
100 // We are in starting phase, record publisherPublicKeyDigest in self.ccndid
101 if(!co.signedInfo || !co.signedInfo.publisher
102 || !co.signedInfo.publisher.publisherPublicKeyDigest) {
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800103 console.log("Cannot contact router, close NDN now.");
Wentao Shang0e291c82012-12-02 23:36:29 -0800104
105 // Close NDN if we fail to connect to a ccn router
106 ndn.readyStatus = NDN.CLOSED;
107 ndn.onclose();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800108 //console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800109 } else {
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800110 //console.log('Connected to ccnd.');
Jeff Thompson5b265a72012-11-12 01:13:08 -0800111 self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
112 if (LOG>3) console.log(self.ccndid);
Wentao Shang0e291c82012-12-02 23:36:29 -0800113
114 // Call NDN.onopen after success
115 ndn.readyStatus = NDN.OPENED;
116 ndn.onopen();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800117 //console.log("NDN.onopen event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800118 }
119 } else {
120 var pitEntry = getEntryForExpressedInterest(nameStr);
121 if (pitEntry != null) {
122 //console.log(pitEntry);
Wentao Shangc0311e52012-12-03 10:38:23 -0800123
124 // Cancel interest timer
125 clearTimeout(pitEntry.closure.timerID);
126 //console.log("Clear interest timer");
127 //console.log(pitEntry.closure.timerID);
Wentao Shangbd63e462012-12-03 16:19:33 -0800128
129 // Remove PIT entry from PITTable
130 index = PITTable.indexOf(pitEntry);
131 PITTable.splice(index, 1);
132
Wentao Shangc0311e52012-12-03 10:38:23 -0800133 // Raise callback
Jeff Thompson5b265a72012-11-12 01:13:08 -0800134 pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
135 }
136 }
137 } else {
138 console.log('Incoming packet is not Interest or ContentObject. Discard now.');
139 }
140
141 delete decoder;
142
143 // Renew StrcutureDecoder and buffer after we process a full packet
144 delete self.structureDecoder;
145 delete self.buffer;
146 self.structureDecoder = new BinaryXMLStructureDecoder();
147 self.buffer = new Uint8Array(self.maxBufferSize);
Wentao Shang2b740e62012-12-07 00:02:53 -0800148 self.bufferOffset = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -0800149 }
150 }
151
152 this.ws.onopen = function(ev) {
Jeff Thompson3c263812012-12-01 17:20:28 -0800153 if (LOG > 3) console.log(ev);
154 if (LOG > 3) console.log('ws.onopen: WebSocket connection opened.');
155 if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800156
157 // Fetch ccndid now
Jeff Thompson3c263812012-12-01 17:20:28 -0800158 var interest = new Interest(new Name(ccndIdFetcher));
Jeff Thompson5b265a72012-11-12 01:13:08 -0800159 interest.InterestLifetime = 4200;
160 //var hex = encodeToHexInterest(interest);
161 var hex = encodeToBinaryInterest(interest);
162
163 /*var bytes = new Uint8Array(hex.length / 2);
164 for (var i = 0; i < hex.length; i = i + 2) {
165 bytes[i / 2] = '0x' + hex.substr(i, 2);
166 }*/
167 var bytes = new Uint8Array(hex.length);
168 bytes.set(hex);
169
170 self.ws.send(bytes.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800171 }
172
173 this.ws.onerror = function(ev) {
174 console.log('ws.onerror: ReadyState: ' + this.readyState);
175 console.log(ev);
176 console.log('ws.onerror: WebSocket error: ' + ev.data);
177 }
178
179 this.ws.onclose = function(ev) {
180 console.log('ws.onclose: WebSocket connection closed.');
181 self.ws = null;
Wentao Shang0e291c82012-12-02 23:36:29 -0800182
183 // Close NDN when WebSocket is closed
184 ndn.readyStatus = NDN.CLOSED;
185 ndn.onclose();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800186 //console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800187 }
188}
189
190
191// For fetching data
192var PITTable = new Array();
193
194var PITEntry = function PITEntry(interest, closure) {
195 this.interest = interest; // String
196 this.closure = closure; // Closure
197}
198
199function getEntryForExpressedInterest(name) {
200 for (var i = 0; i < PITTable.length; i++) {
201 if (name.match(PITTable[i].interest) != null)
202 return PITTable[i];
203 // TODO: handle multiple matching prefixes
204 }
205 return null;
206}
207
Wentao Shangc0311e52012-12-03 10:38:23 -0800208WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
209 if (this.ws != null) {
210 //TODO: check local content store first
211
212 var binaryInterest = encodeToBinaryInterest(interest);
213 var bytearray = new Uint8Array(binaryInterest.length);
214 bytearray.set(binaryInterest);
215
216 var pitEntry = new PITEntry(interest.name.getName(), closure);
217 PITTable.push(pitEntry);
218
219 this.ws.send(bytearray.buffer);
220 if (LOG > 3) console.log('ws.send() returned.');
221
222 // Set interest timer
223 closure.timerID = setTimeout(function() {
Wentao Shangbd63e462012-12-03 16:19:33 -0800224 if (LOG > 3) console.log("Interest time out.");
Wentao Shangc0311e52012-12-03 10:38:23 -0800225
226 // Remove PIT entry from PITTable
227 index = PITTable.indexOf(pitEntry);
228 //console.log(PITTable);
229 PITTable.splice(index, 1);
230 //console.log(PITTable);
231 // Raise closure callback
232 closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
Wentao Shangbd63e462012-12-03 16:19:33 -0800233 }, interest.interestLifetime);
Wentao Shangc0311e52012-12-03 10:38:23 -0800234 //console.log(closure.timerID);
235 }
236 else
237 console.log('WebSocket connection is not established.');
238};
239
Jeff Thompson5b265a72012-11-12 01:13:08 -0800240
241// For publishing data
242var CSTable = new Array();
243
244var CSEntry = function CSEntry(name, closure) {
245 this.name = name; // String
246 this.closure = closure; // Closure
247}
248
249function getEntryForRegisteredPrefix(name) {
250 for (var i = 0; i < CSTable.length; i++) {
251 if (CSTable[i].name.match(name) != null)
252 return CSTable[i];
253 }
254 return null;
255}
256
257WebSocketTransport.prototype.registerPrefix = function(ndn, name, closure, flag) {
258 if (this.ws != null) {
259 if (this.ccndid == null) {
260 console.log('ccnd node ID unkonwn. Cannot register prefix.');
Jeff Thompson3c263812012-12-01 17:20:28 -0800261 return -1;
Jeff Thompson5b265a72012-11-12 01:13:08 -0800262 }
263
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800264 var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800265 var bytes = encodeForwardingEntry(fe);
266
267 var si = new SignedInfo();
268 si.setFields();
269
270 var co = new ContentObject(new Name(), si, bytes, new Signature());
271 co.sign();
272 var coBinary = encodeToBinaryContentObject(co);
273
Jeff Thompsonbd829262012-11-30 22:28:37 -0800274 //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');
275 var nodename = this.ccndid;
276 var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800277
278 var interest = new Interest(interestName);
279 interest.scope = 1;
280 //var hex = encodeToHexInterest(int);
281 var binaryInterest = encodeToBinaryInterest(interest);
Wentao Shangc05dc532012-11-19 12:00:33 -0800282 // If we directly use binaryInterest.buffer to feed ws.send(),
283 // WebSocket will end up sending a packet with 10000 bytes of data.
284 // That is, WebSocket will flush the entire buffer in BinaryXMLEncoder
285 // regardless of the offset of the Uint8Array. So we have to create
286 // a new Uint8Array buffer with just the right size and copy the
287 // content from binaryInterest to the new buffer.
288 // ---Wentao
Jeff Thompson5b265a72012-11-12 01:13:08 -0800289 var bytearray = new Uint8Array(binaryInterest.length);
290 bytearray.set(binaryInterest);
Jeff Thompson3c263812012-12-01 17:20:28 -0800291 if (LOG > 3) console.log('Send Interest registration packet.');
Jeff Thompson5b265a72012-11-12 01:13:08 -0800292
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800293 var csEntry = new CSEntry(name.getName(), closure);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800294 CSTable.push(csEntry);
295
296 this.ws.send(bytearray.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800297
298 return 0;
299 } else {
300 console.log('WebSocket connection is not established.');
301 return -1;
302 }
303}
304