blob: 84adc2a351fbed5ff801474fbbf024a61eab1a6a [file] [log] [blame]
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2018, The University of Memphis
4 *
5 * This file is part of PSync.
6 * See AUTHORS.md for complete list of PSync authors and contributors.
7 *
8 * PSync is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20#include "consumer.hpp"
21#include "detail/state.hpp"
22
23#include <ndn-cxx/util/logger.hpp>
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050024#include <ndn-cxx/security/validator-null.hpp>
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050025
26#include <boost/algorithm/string.hpp>
27
28namespace psync {
29
30NDN_LOG_INIT(psync.Consumer);
31
32Consumer::Consumer(const ndn::Name& syncPrefix,
33 ndn::Face& face,
34 const ReceiveHelloCallback& onReceiveHelloData,
35 const UpdateCallback& onUpdate,
36 unsigned int count,
37 double false_positive = 0.001,
38 ndn::time::milliseconds helloInterestLifetime,
39 ndn::time::milliseconds syncInterestLifetime)
40 : m_face(face)
41 , m_scheduler(m_face.getIoService())
42 , m_syncPrefix(syncPrefix)
43 , m_helloInterestPrefix(ndn::Name(m_syncPrefix).append("hello"))
44 , m_syncInterestPrefix(ndn::Name(m_syncPrefix).append("sync"))
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050045 , m_syncDataContentType(ndn::tlv::ContentType_Blob)
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050046 , m_onReceiveHelloData(onReceiveHelloData)
47 , m_onUpdate(onUpdate)
48 , m_bloomFilter(count, false_positive)
49 , m_helloInterestLifetime(helloInterestLifetime)
50 , m_syncInterestLifetime(syncInterestLifetime)
51 , m_rng(std::random_device{}())
52 , m_rangeUniformRandom(100, 500)
53{
54}
55
56bool
57Consumer::addSubscription(const ndn::Name& prefix)
58{
59 auto it = m_prefixes.insert(std::pair<ndn::Name, uint64_t>(prefix, 0));
60 if (!it.second) {
61 return false;
62 }
63 m_subscriptionList.insert(prefix);
64 m_bloomFilter.insert(prefix.toUri());
65 return true;
66}
67
68void
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050069Consumer::stop()
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050070{
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050071 if (m_syncFetcher) {
72 m_syncFetcher->stop();
73 m_syncFetcher.reset();
74 }
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050075
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050076 if (m_helloFetcher) {
77 m_helloFetcher->stop();
78 m_helloFetcher.reset();
79 }
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050080}
81
82void
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050083Consumer::sendHelloInterest()
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050084{
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050085 ndn::Interest helloInterest(m_helloInterestPrefix);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050086
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050087 NDN_LOG_DEBUG("Send Hello Interest " << helloInterest);
88
89 if (m_helloFetcher) {
90 m_helloFetcher->stop();
91 }
92
93 ndn::util::SegmentFetcher::Options options;
94 options.interestLifetime = m_helloInterestLifetime;
95 options.maxTimeout = m_helloInterestLifetime;
96
97 m_helloFetcher = ndn::util::SegmentFetcher::start(m_face,
98 helloInterest,
99 ndn::security::v2::getAcceptAllValidator(),
100 options);
101
102 m_helloFetcher->afterSegmentValidated.connect([this] (const ndn::Data& data) {
103 if (data.getFinalBlock()) {
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600104 m_helloDataName = data.getName().getPrefix(-2);
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500105 }
106 });
107
108 m_helloFetcher->onComplete.connect([this] (ndn::ConstBufferPtr bufferPtr) {
109 onHelloData(bufferPtr);
110 });
111
112 m_helloFetcher->onError.connect([this] (uint32_t errorCode, const std::string& msg) {
113 NDN_LOG_TRACE("Cannot fetch hello data, error: " <<
114 errorCode << " message: " << msg);
115 ndn::time::milliseconds after(m_rangeUniformRandom(m_rng));
116 NDN_LOG_TRACE("Scheduling after " << after);
117 m_scheduler.scheduleEvent(after, [this] { sendHelloInterest(); });
118 });
119}
120
121void
122Consumer::onHelloData(const ndn::ConstBufferPtr& bufferPtr)
123{
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500124 NDN_LOG_DEBUG("On Hello Data");
125
126 // Extract IBF from name which is the last element in hello data's name
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500127 m_iblt = m_helloDataName.getSubName(m_helloDataName.size()-1, 1);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500128
129 NDN_LOG_TRACE("m_iblt: " << std::hash<std::string>{}(m_iblt.toUri()));
130
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500131 State state(ndn::Block(std::move(bufferPtr)));
132
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500133 std::vector<MissingDataInfo> updates;
134 std::vector<ndn::Name> availableSubscriptions;
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500135
Ashlesh Gawandedeb73f82018-08-09 11:08:02 -0500136 NDN_LOG_DEBUG("Hello Data: " << state);
137
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500138 for (const auto& content : state.getContent()) {
139 ndn::Name prefix = content.getPrefix(-1);
140 uint64_t seq = content.get(content.size()-1).toNumber();
Ashlesh Gawandedeb73f82018-08-09 11:08:02 -0500141 // If consumer is subscribed then prefix must already be present in
142 // m_prefixes (see addSubscription). So [] operator is safe to use.
143 if (isSubscribed(prefix) && seq > m_prefixes[prefix]) {
144 // In case we are behind on this prefix and consumer is subscribed to it
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500145 updates.push_back(MissingDataInfo{prefix, m_prefixes[prefix] + 1, seq});
146 m_prefixes[prefix] = seq;
147 }
Ashlesh Gawandedeb73f82018-08-09 11:08:02 -0500148 availableSubscriptions.push_back(prefix);
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500149 }
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500150
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500151 m_onReceiveHelloData(availableSubscriptions);
152
153 if (!updates.empty()) {
154 NDN_LOG_DEBUG("Updating application with missed updates");
155 m_onUpdate(updates);
156 }
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500157}
158
159void
160Consumer::sendSyncInterest()
161{
162 BOOST_ASSERT(!m_iblt.empty());
163
164 ndn::Name syncInterestName(m_syncInterestPrefix);
165
166 // Append subscription list
167 m_bloomFilter.appendToName(syncInterestName);
168
169 // Append IBF received in hello/sync data
170 syncInterestName.append(m_iblt);
171
172 ndn::Interest syncInterest(syncInterestName);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500173
174 NDN_LOG_DEBUG("sendSyncInterest, nonce: " << syncInterest.getNonce() <<
175 " hash: " << std::hash<std::string>{}(syncInterest.getName().toUri()));
176
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500177 ndn::util::SegmentFetcher::Options options;
178 options.interestLifetime = m_syncInterestLifetime;
179 options.maxTimeout = m_syncInterestLifetime;;
180
181 if (m_syncFetcher) {
182 m_syncFetcher->stop();
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500183 }
184
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500185 m_syncFetcher = ndn::util::SegmentFetcher::start(m_face,
186 syncInterest,
187 ndn::security::v2::getAcceptAllValidator(),
188 options);
189
190 m_syncFetcher->afterSegmentValidated.connect([this] (const ndn::Data& data) {
191 if (data.getFinalBlock()) {
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600192 m_syncDataName = data.getName().getPrefix(-2);
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500193 m_syncDataContentType = data.getContentType();
194 }
195
196 if (m_syncDataContentType == ndn::tlv::ContentType_Nack)
197 {
198 NDN_LOG_DEBUG("Received application"
199 << " Nack from producer,"
200 << " sending hello again");
201 sendHelloInterest();
202 }
203 });
204
205 m_syncFetcher->onComplete.connect([this] (ndn::ConstBufferPtr bufferPtr) {
206 if (m_syncDataContentType == ndn::tlv::ContentType_Nack) {
207 m_syncDataContentType = ndn::tlv::ContentType_Blob;
208 return;
209 }
210 NDN_LOG_TRACE("Segment fetcher got sync data");
211 onSyncData(bufferPtr);
212 });
213
214 m_syncFetcher->onError.connect([this] (uint32_t errorCode, const std::string& msg) {
215 NDN_LOG_TRACE("Cannot fetch sync data, error: "
216 << errorCode << " message: " << msg);
217 ndn::time::milliseconds after(m_rangeUniformRandom(m_rng));
218 NDN_LOG_TRACE("Scheduling after " << after);
219 m_scheduler.scheduleEvent(after, [this] { sendSyncInterest(); });
220 });
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500221}
222
223void
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500224Consumer::onSyncData(const ndn::ConstBufferPtr& bufferPtr)
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500225{
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500226 // Extract IBF from sync data name which is the last component
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500227 m_iblt = m_syncDataName.getSubName(m_syncDataName.size()-1, 1);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500228
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500229 State state(ndn::Block(std::move(bufferPtr)));
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500230
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500231 std::vector <MissingDataInfo> updates;
232
233 for (const auto& content : state.getContent()) {
234 NDN_LOG_DEBUG(content);
235 ndn::Name prefix = content.getPrefix(-1);
236 uint64_t seq = content.get(content.size()-1).toNumber();
237 if (m_prefixes.find(prefix) == m_prefixes.end() || seq > m_prefixes[prefix]) {
238 // If this is just the next seq number then we had already informed the consumer about
239 // the previous sequence number and hence seq low and seq high should be equal to current seq
240 updates.push_back(MissingDataInfo{prefix, m_prefixes[prefix] + 1, seq});
241 m_prefixes[prefix] = seq;
242 }
243 // Else updates will be empty and consumer will not be notified.
244 }
245
246 NDN_LOG_DEBUG("Sync Data: " << state);
247
248 if (!updates.empty()) {
249 m_onUpdate(updates);
250 }
251
252 sendSyncInterest();
253}
254
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500255} // namespace psync