blob: 57f9dd9ba6df7f0b083b913ab09dd9bb6ccda77e [file] [log] [blame]
Wentao Shangbd63e462012-12-03 16:19:33 -08001/**
Jeff Thompson17a9da82012-11-12 01:11:01 -08002 * @author: Jeff Thompson
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
8// Assume already imported the following:
9// Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
10// Components.utils.import("resource://gre/modules/NetUtil.jsm");
11
Jeff Thompson3b6bf982013-01-13 20:00:03 -080012var XpcomTransport = function XpcomTransport() {
13 this.ndn = null;
14 this.socket = null; // nsISocketTransport
15 this.outStream = null;
16 this.connectedHost = null;
17 this.connectedPort = null;
18
Jeff Thompson3fa84152012-12-16 17:11:42 -080019 this.defaultGetHostAndPort = NDN.makeShuffledGetHostAndPort
20 (["A.hub.ndn.ucla.edu", "B.hub.ndn.ucla.edu", "C.hub.ndn.ucla.edu", "D.hub.ndn.ucla.edu",
21 "E.hub.ndn.ucla.edu", "F.hub.ndn.ucla.edu", "G.hub.ndn.ucla.edu", "H.hub.ndn.ucla.edu"],
22 9695);
Jeff Thompson17a9da82012-11-12 01:11:01 -080023};
24
Jeff Thompson3b6bf982013-01-13 20:00:03 -080025/*
26 * Connect to the host and port in ndn. This replaces a previous connection.
27 * Listen on the port to read an entire binary XML encoded response and call
28 * listener.onReceivedData(data) where data is Uint8Array.
29 */
30XpcomTransport.prototype.connect = function(ndn, listener) {
31 if (this.socket != null) {
32 try {
33 this.socket.close(0);
34 } catch (ex) {
35 console.log("XpcomTransport socket.close exception: " + ex);
36 }
37 this.socket = null;
38 }
39 this.ndn = ndn;
40
41 var transportService = Components.classes["@mozilla.org/network/socket-transport-service;1"].getService
42 (Components.interfaces.nsISocketTransportService);
43 var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance
44 (Components.interfaces.nsIInputStreamPump);
45 this.socket = transportService.createTransport(null, 0, ndn.host, ndn.port, null);
46 this.connectedHost = ndn.host;
47 this.connectedPort = ndn.port;
48 this.outStream = this.socket.openOutputStream(1, 0, 0);
49
50 var inStream = this.socket.openInputStream(0, 0, 0);
51 var dataListener = {
52 dataParts: [],
53 structureDecoder: new BinaryXMLStructureDecoder(),
54
55 onStartRequest: function (request, context) {
56 },
57 onStopRequest: function (request, context, status) {
58 },
59 onDataAvailable: function (request, context, _inputStream, offset, count) {
60 try {
61 // Use readInputStreamToString to handle binary data.
62 // TODO: Can we go directly from the stream to Uint8Array?
63 var rawData = DataUtils.toNumbersFromString
64 (NetUtil.readInputStreamToString(inStream, count));
65
66 // Process multiple objects in this packet.
67 while(true) {
68 // Scan the input to check if a whole ccnb object has been read.
69 this.structureDecoder.seek(0);
70 if (this.structureDecoder.findElementEnd(rawData)) {
71 // Got the remainder of an object. Report to the caller.
72 this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
73 listener.onReceivedData(DataUtils.concatArrays(this.dataParts));
74
75 // Need to read a new object.
76 rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
77 this.dataParts = [];
78 this.structureDecoder = new BinaryXMLStructureDecoder();
79 if (rawData.length == 0)
80 // No more data in the packet.
81 return;
82
83 // else loop back to decode.
84 }
85 else {
86 // Save for a later call to concatArrays so that we only copy data once.
87 this.dataParts.push(rawData);
88 return;
89 }
90 }
91 } catch (ex) {
92 console.log("XpcomTransport.onDataAvailable exception: " + ex);
93 }
94 }
95 };
96
97 pump.init(inStream, -1, -1, 0, 0, true);
98 pump.asyncRead(dataListener, null);
99};
100
101/*
102 * Send the data over the connection created by connect.
103 */
104XpcomTransport.prototype.send = function(/* Uint8Array */ data) {
105 if (this.socket == null) {
106 console.log("XpcomTransport connection is not established.");
107 return;
108 }
Jeff Thompson17a9da82012-11-12 01:11:01 -0800109
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800110 var rawDataString = DataUtils.toString(data);
111 this.outStream.write(rawDataString, rawDataString.length);
112 this.outStream.flush();
113};
114
115XpcomTransport.prototype.expressInterest = function(ndn, interest, closure) {
116 var thisXpcomTransport = this;
117
118 if (this.socket == null || this.connectedHost != ndn.host || this.connectedPort != ndn.port) {
119 var dataListener = {
Jeff Thompson17a9da82012-11-12 01:11:01 -0800120 onReceivedData : function(data) {
121 if (data == null || data == undefined || data.length == 0)
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800122 console.log("XpcomTransport: received empty data from socket.");
Jeff Thompson17a9da82012-11-12 01:11:01 -0800123 else {
Jeff Thompson3fa84152012-12-16 17:11:42 -0800124 var decoder = new BinaryXMLDecoder(data);
125 if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800126 // TODO: handle interest properly. For now, assume the only use in getting
127 // an interest is knowing that the host is alive from NDN.ccndIdFetcher.
128 var pitEntry = NDN.getEntryForExpressedInterest(NDN.ccndIdFetcher);
129 if (pitEntry != null) {
130 // Remove PIT entry from NDN.PITTable.
131 var index = NDN.PITTable.indexOf(pitEntry);
132 if (index >= 0)
133 NDN.PITTable.splice(index, 1);
134
135 pitEntry.closure.upcall(Closure.UPCALL_INTEREST, null);
136 }
Jeff Thompson3fa84152012-12-16 17:11:42 -0800137 }
138 else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {
139 var co = new ContentObject();
140 co.from_ccnb(decoder);
Jeff Thompson17a9da82012-11-12 01:11:01 -0800141
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800142 var pitEntry = NDN.getEntryForExpressedInterest(co.name);
143 if (pitEntry != null) {
144 // Remove PIT entry from NDN.PITTable.
145 // TODO: This needs to be a single thread-safe transaction.
146 var index = NDN.PITTable.indexOf(pitEntry);
147 if (index >= 0)
148 NDN.PITTable.splice(index, 1);
Jeff Thompson3fa84152012-12-16 17:11:42 -0800149 }
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800150 if (pitEntry != null) {
151 var currentClosure = pitEntry.closure;
152
153 // TODO: verify the content object and set kind to UPCALL_CONTENT.
154 var result = currentClosure.upcall(Closure.UPCALL_CONTENT_UNVERIFIED,
155 new UpcallInfo(thisXpcomTransport.ndn, null, 0, co));
156 if (result == Closure.RESULT_OK) {
157 // success
158 }
159 else if (result == Closure.RESULT_ERR)
160 console.log("XpcomTransport: upcall returned RESULT_ERR.");
161 else if (result == Closure.RESULT_REEXPRESS) {
162 // TODO: Handl re-express interest.
163 }
164 else if (result == Closure.RESULT_VERIFY) {
165 // TODO: force verification of content.
166 }
167 else if (result == Closure.RESULT_FETCHKEY) {
168 // TODO: get the key in the key locator and re-call the interest
169 // with the key available in the local storage.
170 }
Jeff Thompson3fa84152012-12-16 17:11:42 -0800171 }
Jeff Thompson17a9da82012-11-12 01:11:01 -0800172 }
Jeff Thompson3fa84152012-12-16 17:11:42 -0800173 else
174 console.log('Incoming packet is not Interest or ContentObject. Discard now.');
Jeff Thompson17a9da82012-11-12 01:11:01 -0800175 }
176 }
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800177 }
178
179 this.connect(ndn, dataListener);
180 }
Jeff Thompson17a9da82012-11-12 01:11:01 -0800181
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800182 var binaryInterest = encodeToBinaryInterest(interest);
183
184 // TODO: This needs to be a single thread-safe transaction.
185 var pitEntry = new PITEntry(interest, closure);
186 NDN.PITTable.push(pitEntry);
187
188 this.send(binaryInterest);
Jeff Thompson17a9da82012-11-12 01:11:01 -0800189};
190
191/** Send outputData (Uint8Array) to host:port, read the entire response and call
Jeff Thompson3fa84152012-12-16 17:11:42 -0800192 * listener.onReceivedData(data) where data is Uint8Array and returns true if the data is consumed,
193 * false if need to keep reading.
Jeff Thompson17a9da82012-11-12 01:11:01 -0800194 * Code derived from http://stackoverflow.com/questions/7816386/why-nsiscriptableinputstream-is-not-working .
195 */
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800196 /*
Jeff Thompson17a9da82012-11-12 01:11:01 -0800197XpcomTransport.readAllFromSocket = function(host, port, outputData, listener) {
198 var transportService = Components.classes["@mozilla.org/network/socket-transport-service;1"].getService
199 (Components.interfaces.nsISocketTransportService);
Jeff Thompson17a9da82012-11-12 01:11:01 -0800200 var transport = transportService.createTransport(null, 0, host, port, null);
201 var outStream = transport.openOutputStream(1, 0, 0);
202 var rawDataString = DataUtils.toString(outputData);
203 outStream.write(rawDataString, rawDataString.length);
204 outStream.flush();
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800205 outStream.close();
Jeff Thompson17a9da82012-11-12 01:11:01 -0800206 var inStream = transport.openInputStream(0, 0, 0);
207 var dataListener = {
Jeff Thompsonfab4a7d2012-11-28 21:24:26 -0800208 dataParts: [],
Jeff Thompson17a9da82012-11-12 01:11:01 -0800209 structureDecoder: new BinaryXMLStructureDecoder(),
Jeff Thompson3fa84152012-12-16 17:11:42 -0800210 dataIsConsumed: false,
Jeff Thompson17a9da82012-11-12 01:11:01 -0800211
212 onStartRequest: function (request, context) {
213 },
214 onStopRequest: function (request, context, status) {
215 inStream.close();
Jeff Thompson17a9da82012-11-12 01:11:01 -0800216 },
217 onDataAvailable: function (request, context, _inputStream, offset, count) {
Jeff Thompson3fa84152012-12-16 17:11:42 -0800218 if (this.dataIsConsumed)
Jeff Thompson17a9da82012-11-12 01:11:01 -0800219 // Already finished. Ignore extra data.
220 return;
221
222 try {
223 // Ignore _inputStream and use inStream.
224 // Use readInputStreamToString to handle binary data.
Jeff Thompsonfab4a7d2012-11-28 21:24:26 -0800225 // TODO: Can we go directly from the stream to Uint8Array?
226 var rawData = DataUtils.toNumbersFromString
227 (NetUtil.readInputStreamToString(inStream, count));
Jeff Thompson17a9da82012-11-12 01:11:01 -0800228
Jeff Thompson3fa84152012-12-16 17:11:42 -0800229 // Process multiple objects in this packet.
230 while(true) {
231 // Scan the input to check if a whole ccnb object has been read.
232 this.structureDecoder.seek(0);
233 if (this.structureDecoder.findElementEnd(rawData)) {
234 // Got the remainder of an object. Report to the caller.
235 this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
236 if (listener.onReceivedData(DataUtils.concatArrays(this.dataParts))) {
237 this.dataIsConsumed = true;
238 this.onStopRequest();
239 return;
240 }
241
242 // Need to read a new object.
243 rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
244 this.dataParts = [];
245 this.structureDecoder = new BinaryXMLStructureDecoder();
246 if (rawData.length == 0)
247 // No more data in the packet.
248 return;
249 // else loop back to decode.
250 }
251 else {
252 // Save for a later call to concatArrays so that we only copy data once.
253 this.dataParts.push(rawData);
254 return;
255 }
256 }
Jeff Thompson17a9da82012-11-12 01:11:01 -0800257 } catch (ex) {
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800258 console.log("readAllFromSocket.onDataAvailable exception: " + ex);
Jeff Thompson17a9da82012-11-12 01:11:01 -0800259 }
260 }
261 };
262
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800263 var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance
264 (Components.interfaces.nsIInputStreamPump);
Jeff Thompson17a9da82012-11-12 01:11:01 -0800265 pump.init(inStream, -1, -1, 0, 0, true);
266 pump.asyncRead(dataListener, null);
267}
Jeff Thompson3b6bf982013-01-13 20:00:03 -0800268*/
Jeff Thompson17a9da82012-11-12 01:11:01 -0800269