blob: 1ef056724d155da9c23aeca4f2963f1a160b8c2b [file] [log] [blame]
Jeff Thompson5b265a72012-11-12 01:13:08 -08001/*
2 * @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
16WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
17 if (this.ws != null) {
18 //TODO: check local content store first
19
20 var binaryInterest = encodeToBinaryInterest(interest);
21 var bytearray = new Uint8Array(binaryInterest.length);
22 bytearray.set(binaryInterest);
23
24 var pitEntry = new PITEntry(interest.name.getName(), closure);
25 PITTable.push(pitEntry);
26
27 this.ws.send(bytearray.buffer);
28 console.log('ws.send() returned.');
29 }
30 else{
31 console.log('WebSocket connection is not established.');
32 return null;
33 }
34};
35
36
37var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
38
39WebSocketTransport.prototype.connectWebSocket = function(ndn) {
40 if (this.ws != null)
41 delete this.ws;
42
43 this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
44 console.log('ws connection created.');
45
46 this.ws.binaryType = "arraybuffer";
47
48 var self = this;
49 this.ws.onmessage = function(ev) {
50 var result = ev.data;
51 //console.log('RecvHandle called.');
52
53 if(result == null || result == undefined || result == "" ) {
54 console.log('INVALID ANSWER');
55 } else if (result instanceof ArrayBuffer) {
56 var bytearray = new Uint8Array(result);
57
58 if (LOG>3) console.log('BINARY RESPONSE IS ' + bytearray);
59
60 try {
61 if (bytearray.length + self.buffer.byteOffset >= self.buffer.byteLength) {
62 console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: " + self.buffer.byteOffset
63 + ". Current packet length: " + bytearray.length + ".");
64 // Purge and quit.
65 delete self.structureDecoder;
66 delete self.buffer;
67 self.structureDecoder = new BinaryXMLStructureDecoder();
68 self.buffer = new Uint8Array(self.maxBufferSize);
69 return;
70 }
71
72 /*for (var i = 0; i < bytearray.length; i++) {
73 self.buffer.push(bytearray[i]);
74 }*/
75 self.buffer.set(bytearray, self.buffer.byteOffset);
76
77 if (!self.structureDecoder.findElementEnd(self.buffer)) {
78 // Need more data to decode
79 console.log('Incomplete packet received. Length ' + bytearray.length + '. Wait for more input.');
80 console.log('self.buffer length: ' + self.buffer.length);
81 return;
82 }
83 } catch (ex) {
84 console.log("NDN.ws.onmessage exception: " + ex);
85 return;
86 }
87
88 var decoder = new BinaryXMLDecoder(self.buffer);
89 // Dispatch according to packet type
90 if (decoder.peekStartElement(CCNProtocolDTags.Interest)) { // Interest packet
91 console.log('Interest packet received.');
92
93 var interest = new Interest();
94 interest.from_ccnb(decoder);
95 if (LOG>3) console.log(interest);
96 var nameStr = escape(interest.name.getName());
97 console.log(nameStr);
98
99 var entry = getEntryForRegisteredPrefix(nameStr);
100 if (entry != null) {
101 //console.log(entry);
102 entry.closure.upcall(Closure.UPCALL_INTEREST, new UpcallInfo(ndn, interest, 0, null));
103 }
104
105 } else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) { // Content packet
106 console.log('ContentObject packet received.');
107
108 var co = new ContentObject();
109 co.from_ccnb(decoder);
110 if (LOG>3) console.log(co);
111 nameStr = co.name.getName();
112 console.log(nameStr);
113
114 if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
115 // We are in starting phase, record publisherPublicKeyDigest in self.ccndid
116 if(!co.signedInfo || !co.signedInfo.publisher
117 || !co.signedInfo.publisher.publisherPublicKeyDigest) {
118 console.log("Cannot contact router");
Wentao Shang0e291c82012-12-02 23:36:29 -0800119
120 // Close NDN if we fail to connect to a ccn router
121 ndn.readyStatus = NDN.CLOSED;
122 ndn.onclose();
123 console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800124 } else {
125 console.log('Connected to ccnd.');
126 self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
127 if (LOG>3) console.log(self.ccndid);
Wentao Shang0e291c82012-12-02 23:36:29 -0800128
129 // Call NDN.onopen after success
130 ndn.readyStatus = NDN.OPENED;
131 ndn.onopen();
132 console.log("NDN.onopen event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800133 }
134 } else {
135 var pitEntry = getEntryForExpressedInterest(nameStr);
136 if (pitEntry != null) {
137 //console.log(pitEntry);
138 pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
139 }
140 }
141 } else {
142 console.log('Incoming packet is not Interest or ContentObject. Discard now.');
143 }
144
145 delete decoder;
146
147 // Renew StrcutureDecoder and buffer after we process a full packet
148 delete self.structureDecoder;
149 delete self.buffer;
150 self.structureDecoder = new BinaryXMLStructureDecoder();
151 self.buffer = new Uint8Array(self.maxBufferSize);
152 }
153 }
154
155 this.ws.onopen = function(ev) {
156 console.log(ev);
157 console.log('ws.onopen: WebSocket connection opened.');
158 console.log('ws.onopen: ReadyState: ' + this.readyState);
159
160 // Fetch ccndid now
161 interest = new Interest(new Name(ccndIdFetcher));
162 interest.InterestLifetime = 4200;
163 //var hex = encodeToHexInterest(interest);
164 var hex = encodeToBinaryInterest(interest);
165
166 /*var bytes = new Uint8Array(hex.length / 2);
167 for (var i = 0; i < hex.length; i = i + 2) {
168 bytes[i / 2] = '0x' + hex.substr(i, 2);
169 }*/
170 var bytes = new Uint8Array(hex.length);
171 bytes.set(hex);
172
173 self.ws.send(bytes.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800174 }
175
176 this.ws.onerror = function(ev) {
177 console.log('ws.onerror: ReadyState: ' + this.readyState);
178 console.log(ev);
179 console.log('ws.onerror: WebSocket error: ' + ev.data);
180 }
181
182 this.ws.onclose = function(ev) {
183 console.log('ws.onclose: WebSocket connection closed.');
184 self.ws = null;
Wentao Shang0e291c82012-12-02 23:36:29 -0800185
186 // Close NDN when WebSocket is closed
187 ndn.readyStatus = NDN.CLOSED;
188 ndn.onclose();
189 console.log("NDN.onclose event fired.");
Jeff Thompson5b265a72012-11-12 01:13:08 -0800190 }
191}
192
193
194// For fetching data
195var PITTable = new Array();
196
197var PITEntry = function PITEntry(interest, closure) {
198 this.interest = interest; // String
199 this.closure = closure; // Closure
200}
201
202function getEntryForExpressedInterest(name) {
203 for (var i = 0; i < PITTable.length; i++) {
204 if (name.match(PITTable[i].interest) != null)
205 return PITTable[i];
206 // TODO: handle multiple matching prefixes
207 }
208 return null;
209}
210
211
212// For publishing data
213var CSTable = new Array();
214
215var CSEntry = function CSEntry(name, closure) {
216 this.name = name; // String
217 this.closure = closure; // Closure
218}
219
220function getEntryForRegisteredPrefix(name) {
221 for (var i = 0; i < CSTable.length; i++) {
222 if (CSTable[i].name.match(name) != null)
223 return CSTable[i];
224 }
225 return null;
226}
227
228WebSocketTransport.prototype.registerPrefix = function(ndn, name, closure, flag) {
229 if (this.ws != null) {
230 if (this.ccndid == null) {
231 console.log('ccnd node ID unkonwn. Cannot register prefix.');
232 return;
233 }
234
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800235 var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800236 var bytes = encodeForwardingEntry(fe);
237
238 var si = new SignedInfo();
239 si.setFields();
240
241 var co = new ContentObject(new Name(), si, bytes, new Signature());
242 co.sign();
243 var coBinary = encodeToBinaryContentObject(co);
244
Jeff Thompsonbd829262012-11-30 22:28:37 -0800245 //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');
246 var nodename = this.ccndid;
247 var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800248
249 var interest = new Interest(interestName);
250 interest.scope = 1;
251 //var hex = encodeToHexInterest(int);
252 var binaryInterest = encodeToBinaryInterest(interest);
Wentao Shangc05dc532012-11-19 12:00:33 -0800253 // If we directly use binaryInterest.buffer to feed ws.send(),
254 // WebSocket will end up sending a packet with 10000 bytes of data.
255 // That is, WebSocket will flush the entire buffer in BinaryXMLEncoder
256 // regardless of the offset of the Uint8Array. So we have to create
257 // a new Uint8Array buffer with just the right size and copy the
258 // content from binaryInterest to the new buffer.
259 // ---Wentao
Jeff Thompson5b265a72012-11-12 01:13:08 -0800260 var bytearray = new Uint8Array(binaryInterest.length);
261 bytearray.set(binaryInterest);
262 console.log('Send Interest registration packet.');
263
Jeff Thompson48ba4ff2012-11-12 01:23:13 -0800264 var csEntry = new CSEntry(name.getName(), closure);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800265 CSTable.push(csEntry);
266
267 this.ws.send(bytearray.buffer);
Jeff Thompson5b265a72012-11-12 01:13:08 -0800268
269 return 0;
270 } else {
271 console.log('WebSocket connection is not established.');
272 return -1;
273 }
274}
275