blob: eecaefd4054b44ad154c20e906d69664254a2ccb [file] [log] [blame]
Wentao Shangc0311e52012-12-03 10:38:23 -08001/**
Jeff Thompson5b265a72012-11-12 01:13:08 -08002 * @author: Meki Cherkaoui, Jeff Thompson, Wentao Shang
Jeff Thompson745026e2012-10-13 12:49:20 -07003 * See COPYING for copyright and distribution information.
4 * This class represents the top-level object for communicating with an NDN host.
5 */
Meki Cherkaoui8f173612012-06-06 01:05:40 -07006
Jeff Thompson3c263812012-12-01 17:20:28 -08007var LOG = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -08008
Jeff Thompsone06b31e2012-09-30 17:19:19 -07009/**
Jeff Thompson5b265a72012-11-12 01:13:08 -080010 * settings is an associative array with the following defaults:
11 * {
Jeff Thompson5b265a72012-11-12 01:13:08 -080012 * getTransport: function() { return new WebSocketTransport(); }
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080013 * getHostAndPort: transport.defaultGetHostAndPort,
14 * host: 'localhost', // If null, use getHostAndPort when connecting.
15 * port: 9696,
Wentao Shangc0311e52012-12-03 10:38:23 -080016 * onopen: function() { if (LOG > 3) console.log("NDN connection established."); }
17 * onclose: function() { if (LOG > 3) console.log("NDN connection closed."); }
Jeff Thompson5b265a72012-11-12 01:13:08 -080018 * }
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080019 *
20 * getHostAndPort is a function, on each call it returns a new { host: host, port: port } or
21 * null if there are no more hosts.
Jeff Thompsone06b31e2012-09-30 17:19:19 -070022 */
Jeff Thompson5b265a72012-11-12 01:13:08 -080023var NDN = function NDN(settings) {
24 settings = (settings || {});
Jeff Thompson5b265a72012-11-12 01:13:08 -080025 var getTransport = (settings.getTransport || function() { return new WebSocketTransport(); });
Wentao Shang0e291c82012-12-02 23:36:29 -080026 this.transport = getTransport();
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080027 this.getHostAndPort = (settings.getHostAndPort || this.transport.defaultGetHostAndPort);
28 this.host = (settings.host !== undefined ? settings.host : 'localhost');
29 this.port = (settings.port || 9696);
Wentao Shang0e291c82012-12-02 23:36:29 -080030 this.readyStatus = NDN.UNOPEN;
31 // Event handler
Wentao Shangc0311e52012-12-03 10:38:23 -080032 this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
33 this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("NDN connection closed."); });
Meki Cherkaoui8f173612012-06-06 01:05:40 -070034};
35
Wentao Shang0e291c82012-12-02 23:36:29 -080036NDN.UNOPEN = 0; // created but not opened yet
37NDN.OPENED = 1; // connection to ccnd opened
38NDN.CLOSED = 2; // connection to ccnd closed
Jeff Thompson5b265a72012-11-12 01:13:08 -080039
Wentao Shangb42483a2013-01-03 15:32:32 -080040NDN.ccndIdFetcher = new Name('/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY');
Jeff Thompson5b265a72012-11-12 01:13:08 -080041
Wentao Shangb42483a2013-01-03 15:32:32 -080042NDN.prototype.createRoute = function(host, port) {
Meki Cherkaoui8f173612012-06-06 01:05:40 -070043 this.host=host;
44 this.port=port;
Wentao Shangb42483a2013-01-03 15:32:32 -080045};
Meki Cherkaoui8f173612012-06-06 01:05:40 -070046
Wentao Shang82854bd2012-12-27 14:14:41 -080047
48NDN.KeyStore = new Array();
49
Wentao Shangb5d0c3e2012-12-30 11:12:03 -080050var KeyStoreEntry = function KeyStoreEntry(name, rsa, time) {
Wentao Shang82854bd2012-12-27 14:14:41 -080051 this.keyName = name; // KeyName
Wentao Shang82854bd2012-12-27 14:14:41 -080052 this.rsaKey = rsa; // RSA key
Wentao Shangb5d0c3e2012-12-30 11:12:03 -080053 this.timeStamp = time; // Time Stamp
54};
55
56NDN.addKeyEntry = function(/* KeyStoreEntry */ keyEntry) {
57 var result = NDN.getKeyByName(keyEntry.keyName);
58 if (result == null)
59 NDN.KeyStore.push(keyEntry);
60 else
61 result = keyEntry;
Wentao Shang82854bd2012-12-27 14:14:41 -080062};
63
64NDN.getKeyByName = function(/* KeyName */ name) {
65 var result = null;
66
67 for (var i = 0; i < NDN.KeyStore.length; i++) {
Wentao Shangb42483a2013-01-03 15:32:32 -080068 if (NDN.KeyStore[i].keyName.contentName.match(name.contentName)) {
Wentao Shang82854bd2012-12-27 14:14:41 -080069 if (result == null ||
70 NDN.KeyStore[i].keyName.contentName.components.length > result.keyName.contentName.components.length)
71 result = NDN.KeyStore[i];
72 }
73 }
74
75 return result;
76};
77
Jeff Thompsonbe85be62012-12-13 22:32:01 -080078// For fetching data
79NDN.PITTable = new Array();
80
81var PITEntry = function PITEntry(interest, closure) {
82 this.interest = interest; // Interest
83 this.closure = closure; // Closure
84};
85
86// Return the longest entry from NDN.PITTable that matches name.
87NDN.getEntryForExpressedInterest = function(/*Name*/ name) {
88 // TODO: handle multiple matches? Maybe not from registerPrefix because multiple ContentObject
89 // could be sent for one Interest?
90 var result = null;
91
92 for (var i = 0; i < NDN.PITTable.length; i++) {
93 if (NDN.PITTable[i].interest.matches_name(name)) {
94 if (result == null ||
95 NDN.PITTable[i].interest.name.components.length > result.interest.name.components.length)
96 result = NDN.PITTable[i];
97 }
98 }
99
100 return result;
101};
102
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800103/*
104 * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
105 * If no more hosts remain, return null.
106 */
107NDN.makeShuffledGetHostAndPort = function(hostList, port) {
108 // Make a copy.
109 hostList = hostList.slice(0, hostList.length);
110 DataUtils.shuffle(hostList);
111
112 return function() {
113 if (hostList.length == 0)
114 return null;
115
116 return { host: hostList.splice(0, 1)[0], port: port };
117 };
118};
119
Jeff Thompson34419762012-10-15 22:24:12 -0700120/** Encode name as an Interest. If template is not null, use its attributes.
121 * Send the interest to host:port, read the entire response and call
122 * closure.upcall(Closure.UPCALL_CONTENT (or Closure.UPCALL_CONTENT_UNVERIFIED),
Jeff Thompson97f27432012-10-16 00:28:03 -0700123 * new UpcallInfo(this, interest, 0, contentObject)).
Jeff Thompson34419762012-10-15 22:24:12 -0700124 */
125NDN.prototype.expressInterest = function(
126 // Name
127 name,
128 // Closure
129 closure,
130 // Interest
131 template) {
Jeff Thompson5b265a72012-11-12 01:13:08 -0800132 var interest = new Interest(name);
Jeff Thompson34419762012-10-15 22:24:12 -0700133 if (template != null) {
Jeff Thompson4404ab52012-10-21 10:29:48 -0700134 interest.minSuffixComponents = template.minSuffixComponents;
135 interest.maxSuffixComponents = template.maxSuffixComponents;
136 interest.publisherPublicKeyDigest = template.publisherPublicKeyDigest;
137 interest.exclude = template.exclude;
138 interest.childSelector = template.childSelector;
139 interest.answerOriginKind = template.answerOriginKind;
140 interest.scope = template.scope;
141 interest.interestLifetime = template.interestLifetime;
Jeff Thompson34419762012-10-15 22:24:12 -0700142 }
143 else
Jeff Thompson42806a12012-12-29 18:19:39 -0800144 interest.interestLifetime = 4000; // default interest timeout value in milliseconds.
Jeff Thompson34419762012-10-15 22:24:12 -0700145
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800146 if (this.host == null || this.port == null) {
147 if (this.getHostAndPort == null)
148 console.log('ERROR: host OR port NOT SET');
149 else
150 this.connectAndExpressInterest(interest, closure);
151 }
152 else
153 this.transport.expressInterest(this, interest, closure);
154};
Jeff Thompson5b265a72012-11-12 01:13:08 -0800155
156NDN.prototype.registerPrefix = function(name, closure, flag) {
157 return this.transport.registerPrefix(this, name, closure, flag);
Wentao Shangb42483a2013-01-03 15:32:32 -0800158};
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800159
160/*
161 * Assume this.getHostAndPort is not null. This is called when this.host is null or its host
162 * is not alive. Get a host and port, connect, then express callerInterest with callerClosure.
163 */
164NDN.prototype.connectAndExpressInterest = function(callerInterest, callerClosure) {
165 var hostAndPort = this.getHostAndPort();
166 if (hostAndPort == null) {
167 console.log('ERROR: No more hosts from getHostAndPort');
168 this.host = null;
169 return;
170 }
171
172 if (hostAndPort.host == this.host && hostAndPort.port == this.port) {
173 console.log('ERROR: The host returned by getHostAndPort is not alive: ' +
174 this.host + ":" + this.port);
175 return;
176 }
177
178 this.host = hostAndPort.host;
179 this.port = hostAndPort.port;
180 console.log("Trying host from getHostAndPort: " + this.host);
181
182 // Fetch the ccndId.
183 var interest = new Interest(new Name(NDN.ccndIdFetcher));
Jeff Thompson42806a12012-12-29 18:19:39 -0800184 interest.interestLifetime = 4000; // milliseconds
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800185
186 var thisNDN = this;
187 var timerID = setTimeout(function() {
188 console.log("Timeout waiting for host " + thisNDN.host);
189 // Try again.
190 thisNDN.connectAndExpressInterest(callerInterest, callerClosure);
191 }, 3000);
192
193 this.transport.expressInterest
194 (this, interest, new NDN.ConnectClosure(this, callerInterest, callerClosure, timerID));
Wentao Shangb42483a2013-01-03 15:32:32 -0800195};
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800196
197NDN.ConnectClosure = function ConnectClosure(ndn, callerInterest, callerClosure, timerID) {
198 // Inherit from Closure.
199 Closure.call(this);
200
201 this.ndn = ndn;
202 this.callerInterest = callerInterest;
203 this.callerClosure = callerClosure;
204 this.timerID = timerID;
205};
206
207NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
208 if (!(kind == Closure.UPCALL_CONTENT ||
209 kind == Closure.UPCALL_CONTENT_UNVERIFIED ||
210 kind == Closure.UPCALL_INTEREST))
211 // The upcall is not for us.
212 return Closure.RESULT_ERR;
213
214 // The host is alive, so cancel the timeout and issue the caller's interest.
215 clearTimeout(this.timerID);
216 console.log(this.ndn.host + ": Host is alive. Fetching callerInterest.");
217 this.ndn.transport.expressInterest(this.ndn, this.callerInterest, this.callerClosure);
218
219 return Closure.RESULT_OK;
220};
221