face: no exceptions in NDNLP PartialMessageStore

refs #2261

Change-Id: I711502be23bb4b3691eb44fc63332ffd0bfb8d34
diff --git a/daemon/face/ethernet-face.cpp b/daemon/face/ethernet-face.cpp
index d2cd9a1..a2b10e3 100644
--- a/daemon/face/ethernet-face.cpp
+++ b/daemon/face/ethernet-face.cpp
@@ -360,11 +360,10 @@
 EthernetFace::processIncomingPacket(const pcap_pkthdr* header, const uint8_t* packet)
 {
   size_t length = header->caplen;
-  if (length < ethernet::HDR_LEN + ethernet::MIN_DATA_LEN)
-    {
-      NFD_LOG_FACE_WARN("Received frame is too short (" << length << " bytes)");
-      return;
-    }
+  if (length < ethernet::HDR_LEN + ethernet::MIN_DATA_LEN) {
+    NFD_LOG_FACE_WARN("Received frame is too short (" << length << " bytes)");
+    return;
+  }
 
   const ether_header* eh = reinterpret_cast<const ether_header*>(packet);
   const ethernet::Address sourceAddress(eh->ether_shost);
@@ -382,31 +381,30 @@
 
   /// \todo Reserve space in front and at the back of the underlying buffer
   bool isOk = false;
-  Block fragment;
-  std::tie(isOk, fragment) = Block::fromBuffer(packet, length);
-  if (!isOk)
-    {
-      NFD_LOG_FACE_WARN("Block received from " << sourceAddress.toString()
-                        << " is invalid or too large to process");
-      return;
-    }
+  Block fragmentBlock;
+  std::tie(isOk, fragmentBlock) = Block::fromBuffer(packet, length);
+  if (!isOk) {
+    NFD_LOG_FACE_WARN("Block received from " << sourceAddress.toString()
+                      << " is invalid or too large to process");
+    return;
+  }
 
-  NFD_LOG_FACE_TRACE("Received: " << fragment.size() << " bytes from " << sourceAddress.toString());
-  this->getMutableCounters().getNInBytes() += fragment.size();
+  NFD_LOG_FACE_TRACE("Received: " << fragmentBlock.size() << " bytes from "
+                     << sourceAddress.toString());
+  this->getMutableCounters().getNInBytes() += fragmentBlock.size();
 
   Reassembler& reassembler = m_reassemblers[sourceAddress];
-  if (!reassembler.pms)
-    {
-      // new sender, setup a PartialMessageStore for it
-      reassembler.pms.reset(new ndnlp::PartialMessageStore);
-      reassembler.pms->onReceive.connect(
-        [this, sourceAddress] (const Block& block) {
-          NFD_LOG_FACE_TRACE("All fragments received from " << sourceAddress.toString());
-          if (!decodeAndDispatchInput(block))
-            NFD_LOG_FACE_WARN("Received unrecognized TLV block of type " << block.type()
-                              << " from " << sourceAddress.toString());
-        });
-    }
+  if (!reassembler.pms) {
+    // new sender, setup a PartialMessageStore for it
+    reassembler.pms.reset(new ndnlp::PartialMessageStore);
+    reassembler.pms->onReceive.connect(
+      [this, sourceAddress] (const Block& block) {
+        NFD_LOG_FACE_TRACE("All fragments received from " << sourceAddress.toString());
+        if (!decodeAndDispatchInput(block))
+          NFD_LOG_FACE_WARN("Received unrecognized TLV block of type " << block.type()
+                            << " from " << sourceAddress.toString());
+      });
+  }
 
   scheduler::cancel(reassembler.expireEvent);
   reassembler.expireEvent = scheduler::schedule(REASSEMBLER_LIFETIME,
@@ -414,13 +412,15 @@
       BOOST_VERIFY(m_reassemblers.erase(sourceAddress) == 1);
     });
 
-  try {
-    reassembler.pms->receiveNdnlpData(fragment);
-  }
-  catch (const ndnlp::ParseError& e) {
+  ndnlp::NdnlpData fragment;
+  std::tie(isOk, fragment) = ndnlp::NdnlpData::fromBlock(fragmentBlock);
+  if (!isOk) {
     NFD_LOG_FACE_WARN("Received invalid NDNLP fragment from "
-                      << sourceAddress.toString() << " : " << e.what());
+                      << sourceAddress.toString());
+    return;
   }
+
+  reassembler.pms->receive(fragment);
 }
 
 void
diff --git a/daemon/face/ndnlp-data.cpp b/daemon/face/ndnlp-data.cpp
new file mode 100644
index 0000000..7cfacac
--- /dev/null
+++ b/daemon/face/ndnlp-data.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ndnlp-data.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+std::tuple<bool, NdnlpData>
+NdnlpData::fromBlock(const Block& wire)
+{
+  if (wire.type() != tlv::NdnlpData) {
+    // top element is not NdnlpData
+    return std::make_tuple(false, NdnlpData());
+  }
+  wire.parse();
+  const Block::element_container& elements = wire.elements();
+  if (elements.size() < 2) {
+    // NdnlpData element has incorrect number of children
+    return std::make_tuple(false, NdnlpData());
+  }
+
+  NdnlpData parsed;
+
+  const Block& sequenceElement = elements.front();
+  if (sequenceElement.type() != tlv::NdnlpSequence) {
+    // NdnlpSequence element is missing
+    return std::make_tuple(false, NdnlpData());
+  }
+  if (sequenceElement.value_size() != sizeof(uint64_t)) {
+    // NdnlpSequence element has incorrect length
+    return std::make_tuple(false, NdnlpData());
+  }
+  parsed.seq = be64toh(*reinterpret_cast<const uint64_t*>(&*sequenceElement.value_begin()));
+
+  const Block& payloadElement = elements.back();
+  if (payloadElement.type() != tlv::NdnlpPayload) {
+    // NdnlpPayload element is missing
+    return std::make_tuple(false, NdnlpData());
+  }
+  parsed.payload = payloadElement;
+
+  if (elements.size() == 2) { // single wire packet
+    parsed.fragIndex = 0;
+    parsed.fragCount = 1;
+    return std::make_tuple(true, parsed);
+  }
+  if (elements.size() != 4) {
+    // NdnlpData element has incorrect number of children
+    return std::make_tuple(false, NdnlpData());
+  }
+
+  const Block& fragIndexElement = elements.at(1);
+  if (fragIndexElement.type() != tlv::NdnlpFragIndex) {
+    // NdnlpFragIndex element is missing
+    return std::make_tuple(false, NdnlpData());
+  }
+  uint64_t fragIndex = ndn::readNonNegativeInteger(fragIndexElement);
+  if (fragIndex > std::numeric_limits<uint16_t>::max()) {
+    // NdnlpFragIndex is too large
+    return std::make_tuple(false, NdnlpData());
+  }
+  parsed.fragIndex = static_cast<uint16_t>(fragIndex);
+
+  const Block& fragCountElement = elements.at(2);
+  if (fragCountElement.type() != tlv::NdnlpFragCount) {
+    // NdnlpFragCount element is missing
+    return std::make_tuple(false, NdnlpData());
+  }
+  uint64_t fragCount = ndn::readNonNegativeInteger(fragCountElement);
+  if (fragCount > std::numeric_limits<uint16_t>::max()) {
+    // NdnlpFragCount is too large
+    return std::make_tuple(false, NdnlpData());
+  }
+  parsed.fragCount = static_cast<uint16_t>(fragCount);
+
+  if (parsed.fragIndex >= parsed.fragCount) {
+    // NdnlpFragIndex must be less than NdnlpFragCount
+    return std::make_tuple(false, NdnlpData());
+  }
+
+  return std::make_tuple(true, parsed);
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/daemon/face/ndnlp-parse.hpp b/daemon/face/ndnlp-data.hpp
similarity index 67%
rename from daemon/face/ndnlp-parse.hpp
rename to daemon/face/ndnlp-data.hpp
index f40c910..fd921e2 100644
--- a/daemon/face/ndnlp-parse.hpp
+++ b/daemon/face/ndnlp-data.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #ifndef NFD_DAEMON_FACE_NDNLP_PARSE_HPP
 #define NFD_DAEMON_FACE_NDNLP_PARSE_HPP
@@ -31,14 +32,6 @@
 namespace nfd {
 namespace ndnlp {
 
-struct ParseError : public std::runtime_error
-{
-  ParseError(const std::string& what)
-    : std::runtime_error(what)
-  {
-  }
-};
-
 /** \brief represents a NdnlpData packet
  *
  *  NdnlpData ::= NDNLP-DATA-TYPE TLV-LENGTH
@@ -51,17 +44,16 @@
 {
 public:
   /** \brief parse a NdnlpData packet
-   *
-   *  \exception ParseError packet is malformated
+   *  \return whether \p wire has a valid NdnlpData packet, and the parsed packet
    */
-  void
-  wireDecode(const Block& wire);
+  static std::tuple<bool, NdnlpData>
+  fromBlock(const Block& wire);
 
 public:
-  uint64_t m_seq;
-  uint16_t m_fragIndex;
-  uint16_t m_fragCount;
-  Block m_payload;
+  uint64_t seq;
+  uint16_t fragIndex;
+  uint16_t fragCount;
+  Block payload;
 };
 
 } // namespace ndnlp
diff --git a/daemon/face/ndnlp-parse.cpp b/daemon/face/ndnlp-parse.cpp
deleted file mode 100644
index 21d959f..0000000
--- a/daemon/face/ndnlp-parse.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#include "ndnlp-parse.hpp"
-
-namespace nfd {
-namespace ndnlp {
-
-void
-NdnlpData::wireDecode(const Block& wire)
-{
-  if (wire.type() != tlv::NdnlpData) {
-    throw ParseError("top element is not NdnlpData");
-  }
-  wire.parse();
-  const Block::element_container& elements = wire.elements();
-  if (elements.size() < 2) {
-    throw ParseError("NdnlpData element has incorrect number of children");
-  }
-
-  const Block& sequenceElement = elements.front();
-  if (sequenceElement.type() != tlv::NdnlpSequence) {
-    throw ParseError("NdnlpSequence element is missing");
-  }
-  if (sequenceElement.value_size() != sizeof(uint64_t)) {
-    throw ParseError("NdnlpSequence element has incorrect length");
-  }
-  m_seq = be64toh(*reinterpret_cast<const uint64_t*>(&*sequenceElement.value_begin()));
-
-  const Block& payloadElement = elements.back();
-  if (payloadElement.type() != tlv::NdnlpPayload) {
-    throw ParseError("NdnlpPayload element is missing");
-  }
-  m_payload = payloadElement;
-
-  if (elements.size() == 2) { // single wire packet
-    m_fragIndex = 0;
-    m_fragCount = 1;
-    return;
-  }
-  if (elements.size() != 4) {
-    throw ParseError("NdnlpData element has incorrect number of children");
-  }
-
-  const Block& fragIndexElement = elements.at(1);
-  if (fragIndexElement.type() != tlv::NdnlpFragIndex) {
-    throw ParseError("NdnlpFragIndex element is missing");
-  }
-  uint64_t fragIndex = ndn::readNonNegativeInteger(fragIndexElement);
-  if (fragIndex > std::numeric_limits<uint16_t>::max()) {
-    throw ParseError("NdnlpFragIndex is too large");
-  }
-  m_fragIndex = static_cast<uint16_t>(fragIndex);
-
-  const Block& fragCountElement = elements.at(2);
-  if (fragCountElement.type() != tlv::NdnlpFragCount) {
-    throw ParseError("NdnlpFragCount element is missing");
-  }
-  uint64_t fragCount = ndn::readNonNegativeInteger(fragCountElement);
-  if (fragCount > std::numeric_limits<uint16_t>::max()) {
-    throw ParseError("NdnlpFragCount is too large");
-  }
-  m_fragCount = static_cast<uint16_t>(fragCount);
-
-  if (m_fragIndex >= m_fragCount) {
-    throw ParseError("NdnlpFragIndex must be less than NdnlpFragCount");
-  }
-}
-
-} // namespace ndnlp
-} // namespace nfd
diff --git a/daemon/face/ndnlp-partial-message-store.cpp b/daemon/face/ndnlp-partial-message-store.cpp
index fafeaf0..082f678 100644
--- a/daemon/face/ndnlp-partial-message-store.cpp
+++ b/daemon/face/ndnlp-partial-message-store.cpp
@@ -24,10 +24,13 @@
  */
 
 #include "ndnlp-partial-message-store.hpp"
+#include "core/logger.hpp"
 
 namespace nfd {
 namespace ndnlp {
 
+NFD_LOG_INIT("NdnlpPartialMessageStore");
+
 PartialMessage::PartialMessage()
   : m_fragCount(0)
   , m_received(0)
@@ -63,21 +66,32 @@
   return m_received == m_fragCount;
 }
 
-Block
+std::tuple<bool, Block>
 PartialMessage::reassemble()
 {
   BOOST_ASSERT(this->isComplete());
 
   ndn::BufferPtr buffer = make_shared<ndn::Buffer>(m_totalLength);
-  uint8_t* buf = buffer->get();
-  for (std::vector<Block>::const_iterator it = m_payloads.begin();
-       it != m_payloads.end(); ++it) {
-    const Block& payload = *it;
-    memcpy(buf, payload.value(), payload.value_size());
-    buf += payload.value_size();
+  ndn::Buffer::iterator buf = buffer->begin();
+  for (const Block& payload : m_payloads) {
+    buf = std::copy(payload.value_begin(), payload.value_end(), buf);
   }
+  BOOST_ASSERT(buf == buffer->end());
 
-  return Block(buffer);
+  Block reassembled;
+  bool isBlockOk = Block::fromBuffer(buffer, 0, reassembled);
+  return std::make_tuple(isBlockOk, reassembled);
+}
+
+std::tuple<bool, Block>
+PartialMessage::reassembleSingle(const NdnlpData& fragment)
+{
+  BOOST_ASSERT(fragment.fragCount == 1);
+
+  Block reassembled;
+  bool isBlockOk = Block::fromBuffer(fragment.payload.value(), fragment.payload.value_size(),
+                                     reassembled);
+  return std::make_tuple(isBlockOk, reassembled);
 }
 
 PartialMessageStore::PartialMessageStore(const time::nanoseconds& idleDuration)
@@ -85,28 +99,38 @@
 {
 }
 
-PartialMessageStore::~PartialMessageStore()
-{
-}
-
 void
-PartialMessageStore::receiveNdnlpData(const Block& pkt)
+PartialMessageStore::receive(const NdnlpData& pkt)
 {
-  NdnlpData parsed;
-  parsed.wireDecode(pkt);
-  if (parsed.m_fragCount == 1) { // single fragment
-    this->onReceive(parsed.m_payload.blockFromValue());
+  bool isReassembled = false;
+  Block reassembled;
+  if (pkt.fragCount == 1) { // single fragment
+    std::tie(isReassembled, reassembled) = PartialMessage::reassembleSingle(pkt);
+    if (!isReassembled) {
+      NFD_LOG_TRACE(pkt.seq << " reassemble error");
+      return;
+    }
+
+    NFD_LOG_TRACE(pkt.seq << " deliver");
+    this->onReceive(reassembled);
     return;
   }
 
-  uint64_t messageIdentifier = parsed.m_seq - parsed.m_fragIndex;
+  uint64_t messageIdentifier = pkt.seq - pkt.fragIndex;
   PartialMessage& pm = m_partialMessages[messageIdentifier];
   this->scheduleCleanup(messageIdentifier, pm);
 
-  pm.add(parsed.m_fragIndex, parsed.m_fragCount, parsed.m_payload);
+  pm.add(pkt.fragIndex, pkt.fragCount, pkt.payload);
   if (pm.isComplete()) {
-    this->onReceive(pm.reassemble());
-    this->cleanup(messageIdentifier);
+    std::tie(isReassembled, reassembled) = pm.reassemble();
+    if (!isReassembled) {
+      NFD_LOG_TRACE(messageIdentifier << " reassemble error");
+      return;
+    }
+
+    NFD_LOG_TRACE(messageIdentifier << " deliver");
+    this->onReceive(reassembled);
+    m_partialMessages.erase(messageIdentifier);
   }
 }
 
@@ -121,6 +145,7 @@
 void
 PartialMessageStore::cleanup(uint64_t messageIdentifier)
 {
+  NFD_LOG_TRACE(messageIdentifier << " cleanup");
   m_partialMessages.erase(messageIdentifier);
 }
 
diff --git a/daemon/face/ndnlp-partial-message-store.hpp b/daemon/face/ndnlp-partial-message-store.hpp
index 6fbf0f7..25afcd8 100644
--- a/daemon/face/ndnlp-partial-message-store.hpp
+++ b/daemon/face/ndnlp-partial-message-store.hpp
@@ -26,7 +26,7 @@
 #ifndef NFD_DAEMON_FACE_NDNLP_PARTIAL_MESSAGE_STORE_HPP
 #define NFD_DAEMON_FACE_NDNLP_PARTIAL_MESSAGE_STORE_HPP
 
-#include "ndnlp-parse.hpp"
+#include "ndnlp-data.hpp"
 #include "core/scheduler.hpp"
 
 namespace nfd {
@@ -56,15 +56,19 @@
   isComplete() const;
 
   /** \brief reassemble network layer packet
-   *
-   *  isComplete() must be true before calling this method
-   *
-   *  \exception ndn::Block::Error packet is malformated
-   *  \return network layer packet
+   *  \pre isComplete() == true
+   *  \return whether success, network layer packet
    */
-  Block
+  std::tuple<bool, Block>
   reassemble();
 
+  /** \brief reassemble network layer packet from a single fragment
+   *  \pre fragment.fragCount == 1
+   *  \return whether success, network layer packet
+   */
+  static std::tuple<bool, Block>
+  reassembleSingle(const NdnlpData& fragment);
+
 public:
   scheduler::ScopedEventId expiry;
 
@@ -83,16 +87,12 @@
   explicit
   PartialMessageStore(const time::nanoseconds& idleDuration = time::milliseconds(100));
 
-  virtual
-  ~PartialMessageStore();
-
   /** \brief receive a NdnlpData packet
    *
-   *  \exception ParseError NDNLP packet is malformated
-   *  \exception ndn::Block::Error network layer packet is malformated
+   *  Reassembly errors will be ignored.
    */
   void
-  receiveNdnlpData(const Block& pkt);
+  receive(const NdnlpData& pkt);
 
   /** \brief fires when network layer packet is received
    */