UNPKG

ndn-js-contrib

Version:

Reusable 'Classes' for Named Data Networking: NameTree, PIT, FIB, ContentStore, Interfaces, and Transports

490 lines (489 loc) 17.2 kB
var Repository = require("./Repository.js"), ContentStore = require("./ContentStore.js"), PIT = require("./PIT.js"), FIB = require("./FIB.js"), Strategy = require("./Strategy.js"), getFileChunks = require("./util/get-file-chunks.js"), assembleFile = require("./util/assemble-file.js"), Name = require("ndn-js/js/name.js").Name, Data = require("ndn-js/js/data.js").Data, Interest = require("ndn-js/js/interest").Interest, Tlv = require('ndn-js/js/encoding/tlv/tlv.js').Tlv, TlvDecoder = require('ndn-js/js/encoding/tlv/tlv-decoder.js').TlvDecoder, listen = require("./util/server.js"), connect = require("./util/connect.js"), defaultKey_Cert = require("./util/default_key_cert.js"), ControlParameters = require("ndn-js/js/control-parameters.js").ControlParameters, Manager = require("./Manager"); function makeNonce() { if (global.crypto && global.crypto.getRandomValues) { var result = new Uint8Array(5); crypto.getRandomValues(result); return result; } else { var result = [], i = 5; while (i > 0) { result.push(Math.floor(Math.random * 255)); --i; } return result; } } function Node(params) { this._contentStore = new ContentStore(); this._pit = new PIT(); this._fib = new FIB(); this._strategy = new Strategy(); this._manager = params.manager || new Manager(); this.listen = listen; this.connect = connect; this._servers = []; this._faces = []; this._routes = []; this._interest_pool = []; this._data_pool = []; this._keyChain = params.keyChain || defaultKey_Cert.keyChain; this._certificateName = params.certificateName || defaultKey_Cert.certificateName; } Node.create = function Node_create(params) { params = params || {}; var node = new Node(params); if (params.repo) return Repository.Open(params.repo).then(function Node_create_Repository_Open(repo) { node._repository = repo; return node; }); else return Promise.resolve(new Node(params)); }; function chunkIterator(chunks) { this.curr = 0; this.size = chunks.length + 0; this.chunks = chunks; return this; } chunkIterator.prototype.next = function chunkIterator_next() { var self = this; var chunkNumber = this.curr; var done = (self.size === chunkNumber); this.curr++; var next = (!done) ? new Promise(function(resolve, reject) { resolve({ buffer: self.chunks.shift(), chunkNumber: chunkNumber }); }) : null; return { value: next, done: done }; }; Node.getStringChunks = function Node_getStringChunks(string) { return new Promise(function getStringChunks_Promise(resolve, reject) { var chunks = {}; chunks.array = []; chunks.total = 0; chunks[Symbol.iterator] = function() { return new chunkIterator(chunks.array); }; while (string.length > 0) { chunks.array.push(string.substr(0, 8000)); string = string.substr(8000, string.length); chunks.total++; } resolve(chunks); }); }; Node.getBufferChunks = function getBufferChunks(buffer) { return new Promise(function getBufferChunks_Promise(resolve, reject) { var chunks = []; var i = 0; chunks.total = Math.ceil((buffer.length / 8000)); chunks[Symbol.iterator] = function() { return new chunkIterator(chunks); }; while (i * 8000 < buffer.length) { chunks.push(buffer.slice(i * 8000, (i + 1) * 8000)); i++; } resolve(chunks); }); }; Node.getFileChunks = getFileChunks; Node.assemble = function Node_assemble(datas) { var meta = JSON.parse(datas.shift().content.toString()); if (meta.type === "string" || meta.type === "json") { var str = ""; while (datas.length) str += datas.shift().content.toString(); if (meta.type === "json") str = JSON.parse(str); return str; } else if (meta.type === "buffer") { return Buffer.concat(datas.map(function(data) { return data.content; })); } else if (meta.type === "file") { return assembleFile(datas.map(function(data) { return data.content; }), meta); } }; Node.prototype.onData = function Node_onData(data, face) { var self = this; return self._pit.lookup(data, face).then(function Node_onData_pitResults(faceArray) { for (var i in faceArray) faceArray[i].putData(data); return self._contentStore.insert(data); }).then(function Node_onContentStoreDataEvicted(data) { return data; }).catch(function(err) { return data; }); }; Node.prototype.setCommandInfo = function(face) { face.setCommandSigningInfo(this._keyChain, this._certificateName); }; Node.prototype.registerPrefix = function(prefix, remoteFace) { var self = this; if (remoteFace.commandKeyChain == null) return Promise.reject(new Error("registerPrefix: The command KeyChain has not been set. You must call setCommandSigningInfo.")); if (remoteFace.commandCertificateName.size() == 0) return Promise.reject(new Error("registerPrefix: The command certificate name has not been set. You must call setCommandSigningInfo.")); var controlParameters = new ControlParameters(); controlParameters.setName(prefix); var commandInterest = new Interest(); commandInterest.setName(new Name("/localhop/nfd/rib/register")); commandInterest.setInterestLifetimeMilliseconds(4000.0); commandInterest.getName().append(controlParameters.wireEncode()); remoteFace.nodeMakeCommandInterest(commandInterest, remoteFace.commandKeyChain, remoteFace.commandCertificateName); var promise = this._pit.insert(commandInterest).then(function(res) { self.recycleInterest(commandInterest); return remoteFace; }).catch(function(er) { throw new Error("timeout in registration command Interest"); }); remoteFace.putData(commandInterest); return promise; }; Node.ERRORS = { NO_ROUTES: 1, NO_PIT: 2, INTEREST_TIMEOUT: 3 }; Node.prototype.expressInterest = function Node_expressInterest(interest) { var t = Date.now(); if (!interest.getNonceAsBuffer()) interest.setNonce(makeNonce()); return Node_fulfillInterest.call(this, interest, {putData: function(data) { return { data: data, face: "local", rtt: Date.now() - t }; }}).catch(Node_forwardInterest); }; function repository_lookup(interest) { if (this._repository) return this._repository.lookup(interest); else return Promise.reject(interest); } function Node_fulfillInterest(interest, face) { var node = this; return this._contentStore.lookup(interest).catch(repository_lookup.bind(this)).then(face.putData.bind(face)).catch(function(er) { return Promise.reject({ interest: interest, face: face, node: node }); }); } function forwardInterest_with_strategy(results) { var face = results.pop(), node = results.pop(), interest = results.pop(), strategy = results.pop(), nextHops = results.pop(), choices = strategy.choose(nextHops); for (var i in choices) { choices[i].face.putData(interest); } return node._pit.insert(interest, face).then(function onData(res) { res.face = face; strategy.log(choices, res); return res; }).catch(function onTimeout(res) { strategy.log(choices, null); var er = new Error("Interest Timeout: " + interest.toUri()); er.code = Node.ERRORS.INTEREST_TIMEOUT; return Promise.reject(er); }); } function Node_forwardInterest(request) { var node = request.node, interest = request.interest, face = request.face; return node._fib.lookup(interest).then(function(nexthops) { return Promise.all([Promise.resolve(nexthops), node._strategy.lookup(interest), Promise.resolve(interest), Promise.resolve(node), Promise.resolve(face)]); }).catch(function(er) { node._pit.insert(interest, face).then(function() {}).catch(function() {}); var error = new Error("ForwardInterest, no FIB Entries: " + interest.toUri()); error.code = Node.ERRORS.NO_ROUTES; return Promise.reject(er); }).then(forwardInterest_with_strategy); } ; Node.prototype.add_nexthop = function Node_add_nexthop(interest, face) { var commandComponent = interest.name.get(4); var controlParameters = new ControlParameters(); controlParameters.wireDecode(commandComponent.getValueAsBuffer()); return this._fib.insert(controlParameters.name, face).then(function() { face.registeredPrefixTable.push(controlParameters.name); var ack = new Data(interest.name); ack.setContent("SUCCESS"); face.putData(ack); return interest; }); }; Node.prototype.announce = function Node_announce(prefix) { this._routes.push(prefix); var faces = this._faces; var proms = []; for (var i in faces) { proms.push(this.registerPrefix(prefix, faces[i])); } return Promise.all(proms); }; Node.prototype.peer_aware = function Node_peer_aware(interest, face) {}; Node.prototype.onInterest = function Node_onInterest(interest, face) { var self = this, intercept; if (intercept = this._manager.intercept(interest, face)) return this[intercept](interest, face); else return Node_fulfillInterest.call(this, interest, face).catch(Node_forwardInterest.bind(this)).then(function Node_onInterest_forward_Response(res) { console.log("interest response"); return interest; }).catch(function Node_onInterest_forward_Timeout(message) { console.log("interest timeout"); return interest; }); }; Node.prototype.putData = function Node_putData(data, store) { var self = this; store = store || this._contentStore; return Promise.all([store.insert(data).then(function(wrap) { return wrap[0]; }), self._pit.lookup(data).then(function Node_putPacket_PIT_Hit(faces) { for (var i in faces) faces[i].putData(data); return true; }).catch(function Node_putPacket_PIT_Miss(er) { return false; })]); }; Node.prototype.put = function Node_put(param, store) { var self = this, store = store || this._contentStore, type = param.type, data = param.data, versioned = param.versioned, prefix = new Name(param.prefix), freshnessPeriod = param.freshnessPeriod; return new Promise(function Node_put_Promise(resolve, reject) { if (!(type && data && param.prefix)) return reject(new Error("Node.put(param): param must include type, data, and prefix fields")); if (versioned) prefix.appendVersion(Date.now()); var chunkify; if (type === "json") { chunkify = Node.getStringChunks(JSON.stringify(data)); } else if (type === "string") { chunkify = Node.getStringChunks(data); } else if (type === "file") { chunkify = Node.getFileChunks(data); } else if (type === "buffer") { chunkify = Node.getBufferChunks(data); } else { return reject(new Error("Node.put(param): param.type must be json, string, file, or buffer")); } chunkify.then(function Node_put_processChunks(chunks) { var name = new Name(prefix); name.appendSegment(0); if (param.data.type) { param.mime = param.data.type; param.name = param.data.name; param.lastModified = param.data.lastModified; } delete param.data; var data0 = new Data(name, JSON.stringify(param)); data0.getMetaInfo().setFreshnessPeriod(freshnessPeriod); data0.getMetaInfo().setFinalBlockID(Name.Component.fromNumberWithMarker(chunks.total, 0x00)); var proms = [self.putData(data0, store)]; var $__3 = true; var $__4 = false; var $__5 = undefined; try { for (var $__1 = void 0, $__0 = (chunks)[$traceurRuntime.toProperty(Symbol.iterator)](); !($__3 = ($__1 = $__0.next()).done); $__3 = true) { var chunk = $__1.value; { proms.push(chunk.then(function onChunk(chunk) { var name = new Name(prefix); name.appendSegment(chunk.chunkNumber + 1); var data = new Data(name, chunk.buffer); return self.putData(data, store); })); } } } catch ($__6) { $__4 = true; $__5 = $__6; } finally { try { if (!$__3 && $__0.return != null) { $__0.return(); } } finally { if ($__4) { throw $__5; } } } Promise.all(proms).then(function Node_put_Promise_Resolve(puts) { resolve(puts); }).catch(function Node_put_Promise_Reject(err) { reject(err); }); }); }); }; Node.prototype.getMaximumPacketSendTime = function Node_getMaximumPacketSendTime() { return undefined; }; Node.prototype.pipelineFetch = function Node_pipelineFetch(params) { var name = params.prefix.append("prototype"); var pipe = []; var numberOfPackets = params.finalBlock + 1; var millisecondsPerPacket = this.getMaximumPacketSendTime() || 200; var timeToExpectedLastPacket = (millisecondsPerPacket * numberOfPackets) + params.rtt; var proms = []; for (var i = 0; i < numberOfPackets; i++) { pipe[i] = new Interest(name.getPrefix(-1).appendSegment(i)); pipe[i].setInterestLifetimeMilliseconds(timeToExpectedLastPacket); pipe[i].setMustBeFresh(params.mustBeFresh); proms.push(this.expressInterest(pipe[i]).then(function(response) { return response.data; }).catch(function(er) {})); } return Promise.all(proms); }; Node.prototype.fetch = function Node_fetch(params) { var prefix = new Name(params.prefix), versioned = params.versioned, chained = params.chained, onProgress = params.onProgress, self = this, mustBeFresh = params.mustBeFresh || true; var firstInterest = new Interest(prefix); firstInterest.setInterestLifetimeMilliseconds(4000); var minSuffix = (versioned) ? 3 : 2; var maxSuffix = minSuffix; var childSelector = (versioned) ? 1 : 0; firstInterest.setChildSelector(childSelector); firstInterest.setMustBeFresh(mustBeFresh); return self.expressInterest(firstInterest).then(function(response) { return self.pipelineFetch({ prefix: response.data.name.getPrefix(-1), rtt: response.rtt, mustBeFresh: mustBeFresh, finalBlock: response.data.getMetaInfo().getFinalBlockID().toNumberWithMarker(0x00) }); }); }; Node.prototype.get = function Node_get(params) { return this.fetch(params).then(function(datas) { return Node.assemble(datas); }); }; Node.prototype.store = function Node_store(params) { return this.put(params, this._repository); }; Node.prototype.steward = function Node_steward(params) { var self = this; return this.fetch(params).then(function(datas) { var proms = []; for (var i in datas) proms.push(self._repository.insert(datas[i])); return Promise.all(proms); }); }; Node.prototype.getRemotes = function Node_getRemotes() {}; Node.prototype.getProfile = function Node_getProfile() {}; Node.prototype.setProfile = function Node_setProfile() {}; Node.prototype.advertiseRoute = function Node_advertiseRoute() {}; Node.prototype.createInterest = function Node_createInterest(element) { var interest = this._interest_pool.pop() || new Interest(); interest.wireDecode(element); return interest; }; Node.prototype.recycleInterest = function Node_recycleInterest(interest) { this._interest_pool.push(interest); }; Node.prototype.createData = function Node_createData(element) { var data = this._data_pool.pop() || new Data(); data.wireDecode(element); return data; }; Node.prototype.recycleData = function Node_recycleData(data) { this._data_pool.push(data); }; Node.prototype.onReceivedElement = function Node_onRecievedElement(element, face) { var self = this; if (element[0] == Tlv.Interest || element[0] == Tlv.Data) { var decoder = new TlvDecoder(element); if (decoder.peekType(Tlv.Interest, element.length)) { var interest = this.createInterest(element); this.onInterest(interest, face).then(function(ret) { self.recycleInterest(ret); }); } else if (decoder.peekType(Tlv.Data, element.length)) { var data = self.createData(element); this.onData(data, face).then(function() { self.recycleData(data); }); } } }; Node.prototype.drop = function drop() {}; Node.prototype.onPeerConnect = function Node_onPeerConnect(face, meta) {}; Node.prototype.onFace = function Node_onInboundFace(face) { var self = this; face.onclose = function() { self.onFaceClose(face); }; this.setCommandInfo(face); face.set_onReceivedElement(this.onReceivedElement.bind(this)); var self = this; this._fib.insert(new Name("localhop"), face); this._faces.push(face); var routes = this._routes; var proms = []; for (var i in routes) proms.push(self.registerPrefix(routes[i], face)); return Promise.all(proms); }; Node.prototype.onFaceClose = function Node_onFaceClose(face) { this._fib.remove(face); var faces = this._faces; for (var i in faces) { if (faces[i] === face) return faces.splice(i, 1); } }; Node.prototype.onPeer = function Node_onPeer(face, peeringInfo) { console.log("stuff", peeringInfo, face); }; module.exports = Node;