blob: 0e473b318fcae093d0fe4456ba5e96fee851efba [file] [log] [blame]
susmit6dd649b2015-08-25 14:21:43 -06001/** NDN-Atmos: Cataloging Service for distributed data originally developed
2 * for atmospheric science data
3 * Copyright (C) 2015 Colorado State University
4 *
5 * NDN-Atmos is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * NDN-Atmos is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with NDN-Atmos. If not, see <http://www.gnu.org/licenses/>.
17**/
18
19"use strict";
20var ndn = require("ndn-js");
21var key = require("./key");
22var fs = require("fs");
23
24function RetrieveData(){
25 this.pipelineSize = 4;
26 this.timeoutDict = {};
27 this.retransmissionDict = {};
28 this.existingFileList = [];
29 //FIXME: get this from the config file
30 this.face = new ndn.Face({host: "localhost", port: "6363"});
31
32 this.identityStorage = new ndn.MemoryIdentityStorage();
33 this.privateKeyStorage = new ndn.MemoryPrivateKeyStorage();
34
35 this.keyChain = new ndn.KeyChain(new ndn.IdentityManager(this.identityStorage,
36 this.privateKeyStorage),
37 new ndn.SelfVerifyPolicyManager(this.identityStorage));
38
39 //FIXME:this should be based on the key
40 this.keyName = new ndn.Name("/retrieve/DSK-123");
41 this.certificateName = this.keyName.getSubName(0, this.keyName.size() - 1)
42 .append("KEY").append(this.keyName.get(-1))
43 .append("ID-CERT").append("0");
44
45 this.identityStorage.addKey(this.keyName, ndn.KeyType.RSA,
46 new ndn.Blob(key.DEFAULT_RSA_PUBLIC_KEY_DER, false));
47
48 this.privateKeyStorage.setKeyPairForKeyName(this.keyName,
49 ndn.KeyType.RSA,
50 key.DEFAULT_RSA_PUBLIC_KEY_DER,
51 key.DEFAULT_RSA_PRIVATE_KEY_DER);
52
53 this.face.setCommandSigningInfo(this.keyChain, this.certificateName);
54
55 this.retrievePrefix = new ndn.Name("/retrieve/nwsc");
56 this.face.registerPrefix(this.retrievePrefix, this.onUiInterest.bind(this),
57 this.onRegisterFailed.bind(this));
58 console.log("Registering prefix " + this.retrievePrefix.toUri());
59}
60
61
62RetrieveData.prototype.onUiInterest = function(prefix, interest, face, interestFilterId, filter) {
63 //FIXME: Authenticate Interest
64 var interestName = new ndn.Name(interest.getName());
65 var outgoingInterestName = interestName.getSubName(2).appendSegment(0);
66 console.log("Received Interest" + interest.getName().toUri() +
67 " Requesting " + outgoingInterestName.getName());
68 //send this Interest is from the UI, bind it to a different onData call
69 face.expressInterest(outgoingInterestName, this.onUiData.bind(this), this.onTimeout.bind(this));
70 //using HTTP status codes
71 var retrieveStatus = "200";
72 var statusData = new ndn.Data(interest.getName());
73 statusData.setContent(retrieveStatus);
74 this.keyChain.sign(statusData, this.certificateName);
75 this.face.putData(statusData);
76
77}
78
79RetrieveData.prototype.onUiData = function(Interest, data){
80//this only handles UI data
81 var payload = JSON.parse(data.getContent());
82 console.log("Data is " + payload);
83
84 //go over the loop and ask for each data object in the list
85 for (var entry in payload) {
86 //FIXME: if files exist locally, don't pull them.
87 //FIXME: What if data is more than one packet? Make the UI create packets upto a certain size
susmit83821aa2015-11-09 10:56:52 -070088 console.log("Object to request: ", payload[entry]); //"aa", bb", "cc"
susmit6dd649b2015-08-25 14:21:43 -060089 var outgoingInterestName = new ndn.Name(payload[entry]);
susmit83821aa2015-11-09 10:56:52 -070090 for(var outgoingSegmentNum = 0; outgoingSegmentNum < this.pipelineSize; outgoingSegmentNum++) {
91 console.log("Expressing Interest: ", outgoingInterestName.getName());
92 if(outgoingSegmentNum == 0){
93 this.face.expressInterest(outgoingInterestName.appendSegment(outgoingSegmentNum), this.onData.bind(this),
94 this.onTimeout.bind(this));
95 }
96 else {
97 this.face.expressInterest(outgoingInterestName.getPrefix(-1).appendSegment(outgoingSegmentNum), this.onData.bind(this),
98 this.onTimeout.bind(this));
99 }
susmit6dd649b2015-08-25 14:21:43 -0600100
101 }
102 }
susmit83821aa2015-11-09 10:56:52 -0700103}
susmit6dd649b2015-08-25 14:21:43 -0600104
105
106RetrieveData.prototype.onData = function(interest, data) {
107//this handles normal data
108 var payload = data.getContent();
109 var dataName = new ndn.Name(data.getName());
110 var segment = dataName.get(-1).toSegment();
111 var truncatedName = dataName.getPrefix(-1);
112 var dataFileName = truncatedName.toUri().replace(/\//g, "_");
113 //how do you data is from the UI? Last component is UI
114 var lastComponent = new ndn.Name(truncatedName.getSubName(-1));
115
116 //keep a hashmap of data names and check if data is out of order
117 //if so, drop and rerequest
118
119 //fetch the actual data
120 if (segment === 0){
121 this.retransmissionDict[dataName] = 0;
susmit83821aa2015-11-09 10:56:52 -0700122 console.log("Logged first data segment" + dataName.getName());
susmit6dd649b2015-08-25 14:21:43 -0600123 fs.writeFile(dataFileName, payload, function(err){
124 if(err){
125 return console.log(err);
126 }
127 });
128 }
129 else{
susmit83821aa2015-11-09 10:56:52 -0700130// console.log("Logged data segment" + dataName.getName());
susmit6dd649b2015-08-25 14:21:43 -0600131 fs.appendFile(dataFileName, payload, function(err){
132 if(err){
133 return console.log(err);
134 }
135 });
136 }
137
138 if (data.getMetaInfo().getFinalBlockID() == data.getName()[-1]){
139 fs.close(dataFileName);
140 console.log("Closed file" + dataFileName);
141 }
142 else{
143 //if out of order segment, rerequest
144 if (segment - this.retransmissionDict[dataName] > 1){
145 var reexpressInterest = truncatedName.appendSegment(this.retransmissionDict[dataName] + 1);
146 this.face.expressInterest(reexpressInterest, this.onData.bind(this), this.onTimeout.bind(this));
147 }
148 else{
149 this.retransmissionDict[dataName] = segment;
150 var nextName = truncatedName.appendSegment(segment + this.pipelineSize);
151 this.face.expressInterest(nextName, this.onData.bind(this), this.onTimeout.bind(this));
152 }
153 }
154
155}
156
157RetrieveData.prototype.onTimeout = function(interest) {
158 var dupInterestName = interest.getName().toUri();
159 console.log("Interest timeout for : " + dupInterestName);
160
161 if(!Boolean(this.timeoutDict.hasOwnProperty(dupInterestName))){
162 this.timeoutDict[dupInterestName] = 1;
163 console.log("Logged timeout Interest" + dupInterestName);
164 }
165 if(this.timeoutDict[dupInterestName]!== 3){
166 this.timeoutDict[dupInterestName] = this.timeoutDict[dupInterestName]++;
167 console.log("Retry " + dupInterestName);
168 this.face.expressInterest(interest, this.onData.bind(this), this.onTimeout.bind(this));
169 }
170 else{
171 console.log("No data received for " + dupInterestName + ", giving up");
172 }
173}
174
175RetrieveData.prototype.onRegisterFailed = function(prefix) {
176 console.log("Registration failed for URI: " + prefix.toUri() + " Closing face");
177 this.face.close();
178 }
179
180
181var main = function(){
182 var run = new RetrieveData();
183}
184
185if (require.main === module) {
186 main();
187}