blob: 0d53aaf62fcdb26deec7bd29af9f7ce9ac4283d3 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013-2018 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
* ndn-cxx library is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received copies of the GNU General Public License and GNU Lesser
* General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
* <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
*/
#include "segment-fetcher.hpp"
#include "../name-component.hpp"
#include "../lp/nack.hpp"
#include "../lp/nack-header.hpp"
#include <boost/lexical_cast.hpp>
#include <cmath>
namespace ndn {
namespace util {
const uint32_t SegmentFetcher::MAX_INTEREST_REEXPRESS = 3;
SegmentFetcher::SegmentFetcher(Face& face, security::v2::Validator& validator)
: m_face(face)
, m_scheduler(m_face.getIoService())
, m_validator(validator)
{
}
shared_ptr<SegmentFetcher>
SegmentFetcher::start(Face& face,
const Interest& baseInterest,
security::v2::Validator& validator)
{
shared_ptr<SegmentFetcher> fetcher(new SegmentFetcher(face, validator));
fetcher->fetchFirstSegment(baseInterest, fetcher);
return fetcher;
}
shared_ptr<SegmentFetcher>
SegmentFetcher::fetch(Face& face,
const Interest& baseInterest,
security::v2::Validator& validator,
const CompleteCallback& completeCallback,
const ErrorCallback& errorCallback)
{
shared_ptr<SegmentFetcher> fetcher = start(face, baseInterest, validator);
fetcher->onComplete.connect(completeCallback);
fetcher->onError.connect(errorCallback);
return fetcher;
}
shared_ptr<SegmentFetcher>
SegmentFetcher::fetch(Face& face,
const Interest& baseInterest,
shared_ptr<security::v2::Validator> validator,
const CompleteCallback& completeCallback,
const ErrorCallback& errorCallback)
{
auto fetcher = fetch(face, baseInterest, *validator, completeCallback, errorCallback);
fetcher->onComplete.connect([validator] (ConstBufferPtr) {});
return fetcher;
}
void
SegmentFetcher::fetchFirstSegment(const Interest& baseInterest,
shared_ptr<SegmentFetcher> self)
{
Interest interest(baseInterest);
interest.setChildSelector(1);
interest.setMustBeFresh(true);
m_face.expressInterest(interest,
bind(&SegmentFetcher::afterSegmentReceivedCb, this, _1, _2, true, self),
bind(&SegmentFetcher::afterNackReceivedCb, this, _1, _2, 0, self),
bind([this] { onError(INTEREST_TIMEOUT, "Timeout"); }));
}
void
SegmentFetcher::fetchNextSegment(const Interest& origInterest, const Name& dataName,
uint64_t segmentNo,
shared_ptr<SegmentFetcher> self)
{
Interest interest(origInterest); // to preserve any selectors
interest.refreshNonce();
interest.setChildSelector(0);
interest.setMustBeFresh(false);
interest.setName(dataName.getPrefix(-1).appendSegment(segmentNo));
m_face.expressInterest(interest,
bind(&SegmentFetcher::afterSegmentReceivedCb, this, _1, _2, false, self),
bind(&SegmentFetcher::afterNackReceivedCb, this, _1, _2, 0, self),
bind([this] { onError(INTEREST_TIMEOUT, "Timeout"); }));
}
void
SegmentFetcher::afterSegmentReceivedCb(const Interest& origInterest,
const Data& data, bool isSegmentZeroExpected,
shared_ptr<SegmentFetcher> self)
{
afterSegmentReceived(data);
m_validator.validate(data,
bind(&SegmentFetcher::afterValidationSuccess, this, _1,
isSegmentZeroExpected, origInterest, self),
bind(&SegmentFetcher::afterValidationFailure, this, _1, _2));
}
void
SegmentFetcher::afterValidationSuccess(const Data& data,
bool isSegmentZeroExpected,
const Interest& origInterest,
shared_ptr<SegmentFetcher> self)
{
name::Component currentSegment = data.getName().get(-1);
if (currentSegment.isSegment()) {
if (isSegmentZeroExpected && currentSegment.toSegment() != 0) {
fetchNextSegment(origInterest, data.getName(), 0, self);
}
else {
m_buffer.write(reinterpret_cast<const char*>(data.getContent().value()),
data.getContent().value_size());
afterSegmentValidated(data);
const auto& finalBlockId = data.getFinalBlock();
if (!finalBlockId || (*finalBlockId > currentSegment)) {
fetchNextSegment(origInterest, data.getName(), currentSegment.toSegment() + 1, self);
}
else {
onComplete(m_buffer.buf());
}
}
}
else {
onError(DATA_HAS_NO_SEGMENT, "Data Name has no segment number.");
}
}
void
SegmentFetcher::afterValidationFailure(const Data& data, const security::v2::ValidationError& error)
{
onError(SEGMENT_VALIDATION_FAIL, "Segment validation fail " +
boost::lexical_cast<std::string>(error));
}
void
SegmentFetcher::afterNackReceivedCb(const Interest& origInterest, const lp::Nack& nack,
uint32_t reExpressCount, shared_ptr<SegmentFetcher> self)
{
if (reExpressCount >= MAX_INTEREST_REEXPRESS) {
onError(NACK_ERROR, "Nack Error");
}
else {
switch (nack.getReason()) {
case lp::NackReason::DUPLICATE:
reExpressInterest(origInterest, reExpressCount, self);
break;
case lp::NackReason::CONGESTION:
using ms = time::milliseconds;
m_scheduler.scheduleEvent(ms(static_cast<ms::rep>(std::pow(2, reExpressCount + 1))),
bind(&SegmentFetcher::reExpressInterest, this,
origInterest, reExpressCount, self));
break;
default:
onError(NACK_ERROR, "Nack Error");
break;
}
}
}
void
SegmentFetcher::reExpressInterest(Interest interest, uint32_t reExpressCount,
shared_ptr<SegmentFetcher> self)
{
interest.refreshNonce();
BOOST_ASSERT(interest.hasNonce());
bool isSegmentZeroExpected = true;
if (!interest.getName().empty()) {
name::Component lastComponent = interest.getName().get(-1);
isSegmentZeroExpected = !lastComponent.isSegment();
}
m_face.expressInterest(interest,
bind(&SegmentFetcher::afterSegmentReceivedCb, this, _1, _2,
isSegmentZeroExpected, self),
bind(&SegmentFetcher::afterNackReceivedCb, this, _1, _2,
++reExpressCount, self),
bind([this] { onError(INTEREST_TIMEOUT, "Timeout"); }));
}
} // namespace util
} // namespace ndn