blob: 0a60c6e42b9a7a3c33d7995c344ced4b07daa798 [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);
13 this.structureDecoder = new BinaryXMLStructureDecoder();
14};
15
Jeff Thompson5b265a72012-11-12 01:13:08 -080016var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
17
18WebSocketTransport.prototype.connectWebSocket = function(ndn) {
19 if (this.ws != null)
20 delete this.ws;
21
22 this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
Jeff Thompson3c263812012-12-01 17:20:28 -080023 if (LOG > 0) console.log('ws connection created.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080024
25 this.ws.binaryType = "arraybuffer";
26
27 var self = this;
28 this.ws.onmessage = function(ev) {
29 var result = ev.data;
30 //console.log('RecvHandle called.');
31
32 if(result == null || result == undefined || result == "" ) {
33 console.log('INVALID ANSWER');
34 } else if (result instanceof ArrayBuffer) {
35 var bytearray = new Uint8Array(result);
36
Jeff Thompson13c2e7b2012-12-01 17:33:30 -080037 if (LOG>3) console.log('BINARY RESPONSE IS ' + DataUtils.toHex(bytearray));
Jeff Thompson5b265a72012-11-12 01:13:08 -080038
39 try {
40 if (bytearray.length + self.buffer.byteOffset >= self.buffer.byteLength) {
41 console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: " + self.buffer.byteOffset
42 + ". Current packet length: " + bytearray.length + ".");
43 // Purge and quit.
44 delete self.structureDecoder;
45 delete self.buffer;
46 self.structureDecoder = new BinaryXMLStructureDecoder();
47 self.buffer = new Uint8Array(self.maxBufferSize);
48 return;
49 }
50
51 /*for (var i = 0; i < bytearray.length; i++) {
52 self.buffer.push(bytearray[i]);
53 }*/
54 self.buffer.set(bytearray, self.buffer.byteOffset);
55
56 if (!self.structureDecoder.findElementEnd(self.buffer)) {
57 // Need more data to decode
58 console.log('Incomplete packet received. Length ' + bytearray.length + '. Wait for more input.');
59 console.log('self.buffer length: ' + self.buffer.length);
60 return;
61 }
62 } catch (ex) {
63 console.log("NDN.ws.onmessage exception: " + ex);
64 return;
65 }
66
67 var decoder = new BinaryXMLDecoder(self.buffer);
68 // Dispatch according to packet type
69 if (decoder.peekStartElement(CCNProtocolDTags.Interest)) { // Interest packet
Jeff Thompson3c263812012-12-01 17:20:28 -080070 if (LOG > 3) console.log('Interest packet received.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080071
72 var interest = new Interest();
73 interest.from_ccnb(decoder);
74 if (LOG>3) console.log(interest);
75 var nameStr = escape(interest.name.getName());
Jeff Thompson3c263812012-12-01 17:20:28 -080076 if (LOG > 3) console.log(nameStr);
Jeff Thompson5b265a72012-11-12 01:13:08 -080077
78 var entry = getEntryForRegisteredPrefix(nameStr);
79 if (entry != null) {
80 //console.log(entry);
81 entry.closure.upcall(Closure.UPCALL_INTEREST, new UpcallInfo(ndn, interest, 0, null));
82 }
83
84 } else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) { // Content packet
Jeff Thompson3c263812012-12-01 17:20:28 -080085 if (LOG > 3) console.log('ContentObject packet received.');
Jeff Thompson5b265a72012-11-12 01:13:08 -080086
87 var co = new ContentObject();
88 co.from_ccnb(decoder);
Jeff Thompson3c263812012-12-01 17:20:28 -080089 if (LOG > 3) console.log(co);
Jeff Thompson5b265a72012-11-12 01:13:08 -080090 nameStr = co.name.getName();
Jeff Thompson3c263812012-12-01 17:20:28 -080091 if (LOG > 3) console.log(nameStr);
Jeff Thompson5b265a72012-11-12 01:13:08 -080092
93 if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
94 // We are in starting phase, record publisherPublicKeyDigest in self.ccndid
95 if(!co.signedInfo || !co.signedInfo.publisher
96 || !co.signedInfo.publisher.publisherPublicKeyDigest) {
Wentao Shangaf25c6b2012-12-03 00:09:30 -080097 console.log("Cannot contact router, close NDN now.");
Wentao Shang0e291c82012-12-02 23:36:29 -080098
99 // Close NDN if we fail to connect to a ccn router
100 ndn.readyStatus = NDN.CLOSED;
101 ndn.onclose();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800102 //console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800103 } else {
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800104 //console.log('Connected to ccnd.');
Jeff Thompson5b265a72012-11-12 01:13:08 -0800105 self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
106 if (LOG>3) console.log(self.ccndid);
Wentao Shang0e291c82012-12-02 23:36:29 -0800107
108 // Call NDN.onopen after success
109 ndn.readyStatus = NDN.OPENED;
110 ndn.onopen();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800111 //console.log("NDN.onopen event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800112 }
113 } else {
114 var pitEntry = getEntryForExpressedInterest(nameStr);
115 if (pitEntry != null) {
116 //console.log(pitEntry);
Wentao Shangc0311e52012-12-03 10:38:23 -0800117
118 // Cancel interest timer
119 clearTimeout(pitEntry.closure.timerID);
120 //console.log("Clear interest timer");
121 //console.log(pitEntry.closure.timerID);
122 // Raise callback
Jeff Thompson5b265a72012-11-12 01:13:08 -0800123 pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
124 }
125 }
126 } else {
127 console.log('Incoming packet is not Interest or ContentObject. Discard now.');
128 }
129
130 delete decoder;
131
132 // Renew StrcutureDecoder and buffer after we process a full packet
133 delete self.structureDecoder;
134 delete self.buffer;
135 self.structureDecoder = new BinaryXMLStructureDecoder();
136 self.buffer = new Uint8Array(self.maxBufferSize);
137 }
138 }
139
140 this.ws.onopen = function(ev) {
Jeff Thompson3c263812012-12-01 17:20:28 -0800141 if (LOG > 3) console.log(ev);
142 if (LOG > 3) console.log('ws.onopen: WebSocket connection opened.');
143 if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800144
145 // Fetch ccndid now
Jeff Thompson3c263812012-12-01 17:20:28 -0800146 var interest = new Interest(new Name(ccndIdFetcher));
Jeff Thompson5b265a72012-11-12 01:13:08 -0800147 interest.InterestLifetime = 4200;
148 //var hex = encodeToHexInterest(interest);
149 var hex = encodeToBinaryInterest(interest);
150
151 /*var bytes = new Uint8Array(hex.length / 2);
152 for (var i = 0; i < hex.length; i = i + 2) {
153 bytes[i / 2] = '0x' + hex.substr(i, 2);
154 }*/
155 var bytes = new Uint8Array(hex.length);
156 bytes.set(hex);
157
158 self.ws.send(bytes.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800159 }
160
161 this.ws.onerror = function(ev) {
162 console.log('ws.onerror: ReadyState: ' + this.readyState);
163 console.log(ev);
164 console.log('ws.onerror: WebSocket error: ' + ev.data);
165 }
166
167 this.ws.onclose = function(ev) {
168 console.log('ws.onclose: WebSocket connection closed.');
169 self.ws = null;
Wentao Shang0e291c82012-12-02 23:36:29 -0800170
171 // Close NDN when WebSocket is closed
172 ndn.readyStatus = NDN.CLOSED;
173 ndn.onclose();
Wentao Shangaf25c6b2012-12-03 00:09:30 -0800174 //console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800175 }
176}
177
178
179// For fetching data
180var PITTable = new Array();
181
182var PITEntry = function PITEntry(interest, closure) {
183 this.interest = interest; // String
184 this.closure = closure; // Closure
185}
186
187function getEntryForExpressedInterest(name) {
188 for (var i = 0; i < PITTable.length; i++) {
189 if (name.match(PITTable[i].interest) != null)
190 return PITTable[i];
191 // TODO: handle multiple matching prefixes
192 }
193 return null;
194}
195
Wentao Shangc0311e52012-12-03 10:38:23 -0800196WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
197 if (this.ws != null) {
198 //TODO: check local content store first
199
200 var binaryInterest = encodeToBinaryInterest(interest);
201 var bytearray = new Uint8Array(binaryInterest.length);
202 bytearray.set(binaryInterest);
203
204 var pitEntry = new PITEntry(interest.name.getName(), closure);
205 PITTable.push(pitEntry);
206
207 this.ws.send(bytearray.buffer);
208 if (LOG > 3) console.log('ws.send() returned.');
209
210 // Set interest timer
211 closure.timerID = setTimeout(function() {
212 console.log("Interest time out.");
213
214 // Remove PIT entry from PITTable
215 index = PITTable.indexOf(pitEntry);
216 //console.log(PITTable);
217 PITTable.splice(index, 1);
218 //console.log(PITTable);
219 // Raise closure callback
220 closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
221 }, NDN.InterestTimeOut);
222 //console.log(closure.timerID);
223 }
224 else
225 console.log('WebSocket connection is not established.');
226};
227
Jeff Thompson5b265a72012-11-12 01:13:08 -0800228
229// For publishing data
230var CSTable = new Array();
231
232var CSEntry = function CSEntry(name, closure) {
233 this.name = name; // String
234 this.closure = closure; // Closure
235}
236
237function getEntryForRegisteredPrefix(name) {
238 for (var i = 0; i < CSTable.length; i++) {
239 if (CSTable[i].name.match(name) != null)
240 return CSTable[i];
241 }
242 return null;
243}
244
245WebSocketTransport.prototype.registerPrefix = function(ndn, name, closure, flag) {
246 if (this.ws != null) {
247 if (this.ccndid == null) {
248 console.log('ccnd node ID unkonwn. Cannot register prefix.');
Jeff Thompson3c263812012-12-01 17:20:28 -0800249 return -1;
Jeff Thompson5b265a72012-11-12 01:13:08 -0800250 }
251
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800252 var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800253 var bytes = encodeForwardingEntry(fe);
254
255 var si = new SignedInfo();
256 si.setFields();
257
258 var co = new ContentObject(new Name(), si, bytes, new Signature());
259 co.sign();
260 var coBinary = encodeToBinaryContentObject(co);
261
Jeff Thompsonbd829262012-11-30 22:28:37 -0800262 //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');
263 var nodename = this.ccndid;
264 var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800265
266 var interest = new Interest(interestName);
267 interest.scope = 1;
268 //var hex = encodeToHexInterest(int);
269 var binaryInterest = encodeToBinaryInterest(interest);
Wentao Shangc05dc532012-11-19 12:00:33 -0800270 // If we directly use binaryInterest.buffer to feed ws.send(),
271 // WebSocket will end up sending a packet with 10000 bytes of data.
272 // That is, WebSocket will flush the entire buffer in BinaryXMLEncoder
273 // regardless of the offset of the Uint8Array. So we have to create
274 // a new Uint8Array buffer with just the right size and copy the
275 // content from binaryInterest to the new buffer.
276 // ---Wentao
Jeff Thompson5b265a72012-11-12 01:13:08 -0800277 var bytearray = new Uint8Array(binaryInterest.length);
278 bytearray.set(binaryInterest);
Jeff Thompson3c263812012-12-01 17:20:28 -0800279 if (LOG > 3) console.log('Send Interest registration packet.');
Jeff Thompson5b265a72012-11-12 01:13:08 -0800280
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800281 var csEntry = new CSEntry(name.getName(), closure);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800282 CSTable.push(csEntry);
283
284 this.ws.send(bytearray.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800285
286 return 0;
287 } else {
288 console.log('WebSocket connection is not established.');
289 return -1;
290 }
291}
292