peek: add --app-params and --app-params-file options

Change-Id: Ic1afb118d2e1a58d99d046e072a9df439e0020b3
diff --git a/manpages/ndnpeek.rst b/manpages/ndnpeek.rst
index 0b6683e..bebfbd1 100644
--- a/manpages/ndnpeek.rst
+++ b/manpages/ndnpeek.rst
@@ -4,7 +4,7 @@
 Synopsis
 --------
 
-**ndnpeek** [-h] [-P] [-f] [-l *lifetime*] [-p] [-w *timeout*] [-v] [-V] *name*
+**ndnpeek** [-h] [-P] [-f] [-l *lifetime*] [-A *parameters*] [-p] [-w *timeout*] [-v] [-V] *name*
 
 Description
 -----------
@@ -33,6 +33,12 @@
 ``-l, --lifetime <lifetime>``
   Set ``lifetime`` (in milliseconds) as the ``InterestLifetime``.
 
+``-A, --app-params <parameters>``
+  Set the Interest's ``ApplicationParameters`` from a base64-encoded string.
+
+``--app-params-file <file>``
+  Set the Interest's ``ApplicationParameters`` from the specified file.
+
 ``-p, --payload``
   If specified, print the received payload only, not the full packet.
 
@@ -61,6 +67,12 @@
 Example
 -------
 
-Send Interest for ``/app1/video`` and print the received payload only::
+Send an Interest for ``/app1/video`` and print the received payload only::
 
     ndnpeek -p /app1/video
+
+Send an Interest for ``/app2/foo``, requesting fresh content, with an InterestLifetime
+of 8 seconds, and with the ApplicationParameters containing the ASCII string "hello";
+print the performed operations verbosely but discard the received Data packet::
+
+    ndnpeek -vf -l 8000 -A "aGVsbG8=" /app2/foo >/dev/null
diff --git a/tests/peek/ndnpeek.t.cpp b/tests/peek/ndnpeek.t.cpp
index 24e496b..f144d5a 100644
--- a/tests/peek/ndnpeek.t.cpp
+++ b/tests/peek/ndnpeek.t.cpp
@@ -50,11 +50,19 @@
   std::streambuf* m_originalBuf;
 };
 
+static PeekOptions
+makeDefaultOptions()
+{
+  PeekOptions opt;
+  opt.name = "/peek/test";
+  return opt;
+}
+
 class NdnPeekFixture : public UnitTestTimeFixture
 {
 protected:
   void
-  initialize(const PeekOptions& opts)
+  initialize(const PeekOptions& opts = makeDefaultOptions())
   {
     peek = make_unique<NdnPeek>(face, opts);
   }
@@ -66,15 +74,6 @@
   unique_ptr<NdnPeek> peek;
 };
 
-static PeekOptions
-makeDefaultOptions()
-{
-  PeekOptions opt;
-  opt.name = "ndn:/peek/test";
-  opt.timeout = 200_ms;
-  return opt;
-}
-
 class OutputFull
 {
 public:
@@ -227,6 +226,24 @@
   BOOST_CHECK(peek->getResult() == NdnPeek::Result::NACK);
 }
 
+BOOST_AUTO_TEST_CASE(ApplicationParameters)
+{
+  auto options = makeDefaultOptions();
+  options.applicationParameters = make_shared<Buffer>("hello", 5);
+  initialize(options);
+
+  peek->start();
+  this->advanceClocks(io, 25_ms, 4);
+
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getCanBePrefix(), false);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getMustBeFresh(), false);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getForwardingHint().empty(), true);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().hasApplicationParameters(), true);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getApplicationParameters(), "2405 68656C6C6F"_block);
+}
+
 BOOST_AUTO_TEST_CASE(NoTimeout)
 {
   auto options = makeDefaultOptions();
@@ -282,6 +299,18 @@
   BOOST_CHECK(peek->getResult() == NdnPeek::Result::TIMEOUT);
 }
 
+BOOST_AUTO_TEST_CASE(OversizedPacket)
+{
+  auto options = makeDefaultOptions();
+  options.applicationParameters = make_shared<Buffer>(MAX_NDN_PACKET_SIZE);
+  initialize(options);
+
+  peek->start();
+  BOOST_CHECK_THROW(this->advanceClocks(io, 1_ms, 10), Face::OversizedPacketError);
+
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestNdnPeek
 BOOST_AUTO_TEST_SUITE_END() // Peek
 
diff --git a/tools/peek/ndnpeek/main.cpp b/tools/peek/ndnpeek/main.cpp
index 989887a..89b2222 100644
--- a/tools/peek/ndnpeek/main.cpp
+++ b/tools/peek/ndnpeek/main.cpp
@@ -24,6 +24,7 @@
  *
  * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  * @author Zhuo Li <zhuoli@email.arizona.edu>
+ * @author Davide Pesavento <davidepesa@gmail.com>
  */
 
 #include "ndnpeek.hpp"
@@ -83,6 +84,8 @@
     ("link-file",  po::value<std::string>(), "set ForwardingHint from a raw binary file")
     ("lifetime,l", po::value<time::milliseconds::rep>()->default_value(options.interestLifetime.count()),
                    "set InterestLifetime, in milliseconds")
+    ("app-params,A",    po::value<std::string>(), "set ApplicationParameters from a base64-encoded string")
+    ("app-params-file", po::value<std::string>(), "set ApplicationParameters from a file")
   ;
 
   po::options_description visibleOptDesc;
@@ -159,6 +162,37 @@
     return 2;
   }
 
+  if (vm.count("app-params") > 0) {
+    if (vm.count("app-params-file") > 0) {
+      std::cerr << "ERROR: cannot specify both '--app-params' and '--app-params-file'" << std::endl;
+      return 2;
+    }
+    std::istringstream is(vm["app-params"].as<std::string>());
+    try {
+      options.applicationParameters = io::loadBuffer(is, io::BASE64);
+    }
+    catch (const io::Error& e) {
+      std::cerr << "ERROR: invalid ApplicationParameters string: " << e.what() << std::endl;
+      return 2;
+    }
+  }
+
+  if (vm.count("app-params-file") > 0) {
+    auto filename = vm["app-params-file"].as<std::string>();
+    std::ifstream paramsFile = openBinaryFile(filename);
+    if (!paramsFile) {
+      return 2;
+    }
+    try {
+      options.applicationParameters = io::loadBuffer(paramsFile, io::NO_ENCODING);
+    }
+    catch (const io::Error& e) {
+      std::cerr << "ERROR: cannot read ApplicationParameters from file '" << filename
+                << "': " << e.what() << std::endl;
+      return 2;
+    }
+  }
+
   try {
     Face face;
     NdnPeek program(face, options);
diff --git a/tools/peek/ndnpeek/ndnpeek.cpp b/tools/peek/ndnpeek/ndnpeek.cpp
index ff7f7a0..7bcb137 100644
--- a/tools/peek/ndnpeek/ndnpeek.cpp
+++ b/tools/peek/ndnpeek/ndnpeek.cpp
@@ -24,6 +24,7 @@
  *
  * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  * @author Zhuo Li <zhuoli@email.arizona.edu>
+ * @author Davide Pesavento <davidepesa@gmail.com>
  */
 
 #include "ndnpeek.hpp"
@@ -66,6 +67,9 @@
   if (m_options.link) {
     interest.setForwardingHint(m_options.link->getDelegationList());
   }
+  if (m_options.applicationParameters) {
+    interest.setApplicationParameters(m_options.applicationParameters);
+  }
 
   if (m_options.isVerbose) {
     std::cerr << "INTEREST: " << interest << std::endl;
diff --git a/tools/peek/ndnpeek/ndnpeek.hpp b/tools/peek/ndnpeek/ndnpeek.hpp
index d02155f..357597e 100644
--- a/tools/peek/ndnpeek/ndnpeek.hpp
+++ b/tools/peek/ndnpeek/ndnpeek.hpp
@@ -24,6 +24,7 @@
  *
  * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  * @author Zhuo Li <zhuoli@email.arizona.edu>
+ * @author Davide Pesavento <davidepesa@gmail.com>
  */
 
 #ifndef NDN_TOOLS_NDNPEEK_NDNPEEK_HPP
@@ -48,6 +49,7 @@
   bool mustBeFresh = false;
   shared_ptr<Link> link;
   time::milliseconds interestLifetime = DEFAULT_INTEREST_LIFETIME;
+  shared_ptr<Buffer> applicationParameters;
 
   // program behavior options
   bool isVerbose = false;
@@ -107,7 +109,7 @@
   onTimeout();
 
 private:
-  const PeekOptions& m_options;
+  const PeekOptions m_options;
   Face& m_face;
   Scheduler m_scheduler;
   time::steady_clock::TimePoint m_sendTime;