peek: Refactor ndnpeek and add unit tests
Change-Id: Ib34c4431bdc268c5a7ef2fccf0946b0d19d69ab6
Refs: #3558
diff --git a/tools/peek/ndnpeek/main.cpp b/tools/peek/ndnpeek/main.cpp
new file mode 100644
index 0000000..774d759
--- /dev/null
+++ b/tools/peek/ndnpeek/main.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, 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 ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ * @author Zhuo Li <zhuoli@email.arizona.edu>
+ */
+
+#include "ndnpeek.hpp"
+#include "core/version.hpp"
+
+#include <ndn-cxx/util/io.hpp>
+
+namespace ndn {
+namespace peek {
+
+namespace po = boost::program_options;
+
+static void
+usage(std::ostream& os, const po::options_description& options)
+{
+ os << "Usage: ndnpeek [options] ndn:/name\n"
+ "\n"
+ "Fetch one data item matching the name prefix and write it to standard output.\n"
+ "\n"
+ << options;
+}
+
+static int
+main(int argc, char* argv[])
+{
+ PeekOptions options;
+ options.isVerbose = false;
+ options.mustBeFresh = false;
+ options.wantRightmostChild = false;
+ options.wantPayloadOnly = false;
+ options.minSuffixComponents = -1;
+ options.maxSuffixComponents = -1;
+ options.interestLifetime = time::milliseconds(-1);
+ options.timeout = time::milliseconds(-1);
+
+ po::options_description genericOptDesc("Generic options");
+ genericOptDesc.add_options()
+ ("help,h", "print help and exit")
+ ("payload,p", po::bool_switch(&options.wantPayloadOnly),
+ "print payload only, instead of full packet")
+ ("timeout,w", po::value<int>(),
+ "set timeout (in milliseconds)")
+ ("verbose,v", po::bool_switch(&options.isVerbose),
+ "turn on verbose output")
+ ("version,V", "print version and exit")
+ ;
+
+ po::options_description interestOptDesc("Interest construction");
+ interestOptDesc.add_options()
+ ("fresh,f", po::bool_switch(&options.mustBeFresh),
+ "set MustBeFresh")
+ ("rightmost,r", po::bool_switch(&options.wantRightmostChild),
+ "set ChildSelector to rightmost")
+ ("minsuffix,m", po::value<int>(&options.minSuffixComponents),
+ "set MinSuffixComponents")
+ ("maxsuffix,M", po::value<int>(&options.maxSuffixComponents),
+ "set MaxSuffixComponents")
+ ("lifetime,l", po::value<int>(),
+ "set InterestLifetime (in milliseconds)")
+ ("link-file", po::value<std::string>(),
+ "set Link from a file")
+ ;
+
+ po::options_description visibleOptDesc;
+ visibleOptDesc.add(genericOptDesc).add(interestOptDesc);
+
+ po::options_description hiddenOptDesc;
+ hiddenOptDesc.add_options()
+ ("prefix", po::value<std::string>(), "Interest name");
+
+ po::options_description optDesc;
+ optDesc.add(visibleOptDesc).add(hiddenOptDesc);
+
+ po::positional_options_description optPos;
+ optPos.add("prefix", -1);
+
+ po::variables_map vm;
+ try {
+ po::store(po::command_line_parser(argc, argv).options(optDesc).positional(optPos).run(), vm);
+ po::notify(vm);
+ }
+ catch (const po::error& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 2;
+ }
+
+ if (vm.count("help") > 0) {
+ usage(std::cout, visibleOptDesc);
+ return 0;
+ }
+
+ if (vm.count("version") > 0) {
+ std::cout << "ndnpeek " << tools::VERSION << std::endl;
+ return 0;
+ }
+
+ if (vm.count("prefix") > 0) {
+ options.prefix = vm["prefix"].as<std::string>();
+ }
+ else {
+ std::cerr << "ERROR: Interest name is missing" << std::endl;
+ usage(std::cerr, visibleOptDesc);
+ return 2;
+ }
+
+ if (vm.count("minsuffix") > 0 && options.minSuffixComponents < 0) {
+ std::cerr << "ERROR: MinSuffixComponents must be a non-negative integer" << std::endl;
+ usage(std::cerr, visibleOptDesc);
+ return 2;
+ }
+
+ if (vm.count("maxsuffix") > 0 && options.maxSuffixComponents < 0) {
+ std::cerr << "ERROR: MaxSuffixComponents must be a non-negative integer" << std::endl;
+ usage(std::cerr, visibleOptDesc);
+ return 2;
+ }
+
+ if (vm.count("lifetime") > 0) {
+ if (vm["lifetime"].as<int>() >= 0) {
+ options.interestLifetime = time::milliseconds(vm["lifetime"].as<int>());
+ }
+ else {
+ std::cerr << "ERROR: InterestLifetime must be a non-negative integer" << std::endl;
+ usage(std::cerr, visibleOptDesc);
+ return 2;
+ }
+ }
+
+ if (vm.count("timeout") > 0) {
+ if (vm["timeout"].as<int>() > 0) {
+ options.timeout = time::milliseconds(vm["timeout"].as<int>());
+ }
+ else {
+ std::cerr << "ERROR: Timeout must be a positive integer" << std::endl;
+ usage(std::cerr, visibleOptDesc);
+ return 2;
+ }
+ }
+
+ if (vm.count("link-file") > 0) {
+ options.link = io::load<Link>(vm["link-file"].as<std::string>());
+ if (options.link == nullptr) {
+ std::cerr << "ERROR: Cannot read Link object from the specified file" << std::endl;
+ usage(std::cerr, visibleOptDesc);
+ return 2;
+ }
+ }
+
+ Face face;
+ NdnPeek program(face, options);
+
+ try {
+ program.start();
+ face.processEvents(program.getTimeout());
+ }
+ catch (const std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 1;
+ }
+
+ ResultCode result = program.getResultCode();
+ if (result == ResultCode::TIMEOUT && options.isVerbose) {
+ std::cerr << "TIMEOUT" << std::endl;
+ }
+ return static_cast<int>(result);
+}
+
+} // namespace peek
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+ return ndn::peek::main(argc, argv);
+}
diff --git a/tools/peek/ndnpeek/ndnpeek.cpp b/tools/peek/ndnpeek/ndnpeek.cpp
new file mode 100644
index 0000000..c9f89b2
--- /dev/null
+++ b/tools/peek/ndnpeek/ndnpeek.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, 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 ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ * @author Zhuo Li <zhuoli@email.arizona.edu>
+ */
+
+#include "ndnpeek.hpp"
+
+namespace ndn {
+namespace peek {
+
+NdnPeek::NdnPeek(Face& face, const PeekOptions& options)
+ : m_face(face)
+ , m_options(options)
+ , m_timeout(options.timeout)
+ , m_resultCode(ResultCode::TIMEOUT)
+{
+ if (m_timeout < time::milliseconds::zero()) {
+ m_timeout = m_options.interestLifetime < time::milliseconds::zero() ?
+ DEFAULT_INTEREST_LIFETIME : m_options.interestLifetime;
+ }
+}
+
+time::milliseconds
+NdnPeek::getTimeout() const
+{
+ return m_timeout;
+}
+
+ResultCode
+NdnPeek::getResultCode() const
+{
+ return m_resultCode;
+}
+
+void
+NdnPeek::start()
+{
+ m_face.expressInterest(createInterest(),
+ bind(&NdnPeek::onData, this, _2),
+ bind(&NdnPeek::onNack, this, _2),
+ nullptr);
+ m_expressInterestTime = time::steady_clock::now();
+}
+
+Interest
+NdnPeek::createInterest() const
+{
+ Interest interest(m_options.prefix);
+
+ if (m_options.minSuffixComponents >= 0)
+ interest.setMinSuffixComponents(m_options.minSuffixComponents);
+
+ if (m_options.maxSuffixComponents >= 0)
+ interest.setMaxSuffixComponents(m_options.maxSuffixComponents);
+
+ if (m_options.interestLifetime >= time::milliseconds::zero())
+ interest.setInterestLifetime(m_options.interestLifetime);
+
+ if (m_options.link != nullptr)
+ interest.setLink(m_options.link->wireEncode());
+
+ if (m_options.mustBeFresh)
+ interest.setMustBeFresh(true);
+
+ if (m_options.wantRightmostChild)
+ interest.setChildSelector(1);
+
+ if (m_options.isVerbose) {
+ std::cerr << "INTEREST: " << interest << std::endl;
+ }
+
+ return interest;
+}
+
+void
+NdnPeek::onData(const Data& data)
+{
+ m_resultCode = ResultCode::DATA;
+
+ if (m_options.isVerbose) {
+ std::cerr << "DATA, RTT: "
+ << time::duration_cast<time::milliseconds>(time::steady_clock::now() - m_expressInterestTime).count()
+ << "ms" << std::endl;
+ }
+
+ if (m_options.wantPayloadOnly) {
+ const Block& block = data.getContent();
+ std::cout.write(reinterpret_cast<const char*>(block.value()), block.value_size());
+ }
+ else {
+ const Block& block = data.wireEncode();
+ std::cout.write(reinterpret_cast<const char*>(block.wire()), block.size());
+ }
+}
+
+void
+NdnPeek::onNack(const lp::Nack& nack)
+{
+ m_resultCode = ResultCode::NACK;
+ lp::NackHeader header = nack.getHeader();
+
+ if (m_options.isVerbose) {
+ std::cerr << "NACK, RTT: "
+ << time::duration_cast<time::milliseconds>(time::steady_clock::now() - m_expressInterestTime).count()
+ << "ms" << std::endl;
+ }
+
+ if (m_options.wantPayloadOnly) {
+ std::cout << header.getReason() << std::endl;
+ }
+ else {
+ const Block& block = header.wireEncode();
+ std::cout.write(reinterpret_cast<const char*>(block.wire()), block.size());
+ }
+}
+
+} // namespace peek
+} // namespace ndn
diff --git a/tools/peek/ndnpeek/ndnpeek.hpp b/tools/peek/ndnpeek/ndnpeek.hpp
new file mode 100644
index 0000000..2b0a85e
--- /dev/null
+++ b/tools/peek/ndnpeek/ndnpeek.hpp
@@ -0,0 +1,112 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, 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 ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ * @author Zhuo Li <zhuoli@email.arizona.edu>
+ */
+
+#ifndef NDN_TOOLS_NDNPEEK_NDNPEEK_HPP
+#define NDN_TOOLS_NDNPEEK_NDNPEEK_HPP
+
+#include "core/common.hpp"
+
+namespace ndn {
+namespace peek {
+
+/**
+ * @brief options for NdnPeek
+ */
+struct PeekOptions
+{
+ std::string prefix;
+ int minSuffixComponents;
+ int maxSuffixComponents;
+ time::milliseconds interestLifetime;
+ time::milliseconds timeout;
+ shared_ptr<Link> link;
+ bool isVerbose;
+ bool mustBeFresh;
+ bool wantRightmostChild;
+ bool wantPayloadOnly;
+};
+
+enum class ResultCode {
+ NONE = -1,
+ DATA = 0,
+ NACK = 4,
+ TIMEOUT = 3
+};
+
+class NdnPeek : boost::noncopyable
+{
+public:
+ NdnPeek(Face& face, const PeekOptions& options);
+
+ /**
+ * @return the timeout
+ */
+ time::milliseconds
+ getTimeout() const;
+
+ /**
+ * @return the result of Peek execution
+ */
+ ResultCode
+ getResultCode() const;
+
+ /**
+ * @brief express the Interest
+ * @note The caller must invoke face.processEvents() afterwards
+ */
+ void
+ start();
+
+private:
+ Interest
+ createInterest() const;
+
+ /**
+ * @brief called when a Data packet is received
+ */
+ void
+ onData(const Data& data);
+
+ /**
+ * @brief called when a Nack packet is received
+ */
+ void
+ onNack(const lp::Nack& nack);
+
+private:
+ Face& m_face;
+ const PeekOptions& m_options;
+ time::steady_clock::TimePoint m_expressInterestTime;
+ time::milliseconds m_timeout;
+ ResultCode m_resultCode;
+};
+
+} // namespace peek
+} // namespace ndn
+
+#endif // NDN_TOOLS_NDNPEEK_NDNPEEK_HPP