face: Implementation of encode/decode of LocalControlHeader

LocalControlHeader can only be used on faces that are derived from
LocalFace.  UnixStreamFace is directly inherited from LocalFace,
TCP face has two specializations: generic TcpFace (strictly not local),
and LocalTcpFace.

refs #1213

Change-Id: I8a158c3bc4bb929eedd15757cfddecc0d1049f9f
diff --git a/tests/face/unix-stream.cpp b/tests/face/unix-stream.cpp
index fee5827..9d6a6cf 100644
--- a/tests/face/unix-stream.cpp
+++ b/tests/face/unix-stream.cpp
@@ -61,7 +61,7 @@
 
     this->afterIo();
   }
-  
+
   void
   face1_onReceiveInterest(const Interest& interest)
   {
@@ -69,7 +69,7 @@
 
     this->afterIo();
   }
-  
+
   void
   face1_onReceiveData(const Data& data)
   {
@@ -85,7 +85,7 @@
 
     this->afterIo();
   }
-  
+
   void
   face2_onReceiveData(const Data& data)
   {
@@ -310,6 +310,138 @@
   BOOST_CHECK_EQUAL(m_face2_receivedDatas    [0].getName(), data1.getName());
 }
 
+static inline void
+noOp()
+{
+}
+
+BOOST_FIXTURE_TEST_CASE(UnixStreamFaceLocalControlHeader, EndToEndFixture)
+{
+  UnixStreamChannelFactory factory(m_ioService);
+  Scheduler scheduler(m_ioService); // to limit the amount of time the test may take
+
+  EventId abortEvent =
+    scheduler.scheduleEvent(time::seconds(1),
+                            bind(&EndToEndFixture::abortTestCase, this,
+                                 "UnixStreamChannel error: cannot connect or cannot accept connection"));
+
+  shared_ptr<UnixStreamChannel> channel1 = factory.create("foo");
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  shared_ptr<stream_protocol::socket> client =
+      make_shared<stream_protocol::socket>(boost::ref(m_ioService));
+  client->async_connect(stream_protocol::endpoint("foo"),
+                        bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  m_ioRemaining = 2;
+  m_ioService.run();
+  m_ioService.reset();
+  scheduler.cancelEvent(abortEvent);
+
+  BOOST_REQUIRE(static_cast<bool>(m_face1));
+
+  abortEvent =
+    scheduler.scheduleEvent(time::seconds(1),
+                            bind(&EndToEndFixture::abortTestCase, this,
+                                 "UnixStreamChannel error: cannot send or receive Interest/Data packets"));
+
+  m_face2 = make_shared<UnixStreamFace>(client);
+  m_face2->onReceiveInterest +=
+    bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+  m_face2->onReceiveData +=
+    bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+
+  m_face1->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID);
+  m_face1->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID);
+
+  BOOST_CHECK(m_face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(m_face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+
+  m_face2->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID);
+  m_face2->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID);
+
+  BOOST_CHECK(m_face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(m_face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+
+  ////////////////////////////////////////////////////////
+
+  interest1.setIncomingFaceId(11);
+  interest1.setNextHopFaceId(111);
+
+  m_face1->sendInterest(interest1);
+
+  data1.setIncomingFaceId(22);
+  data1.getLocalControlHeader().setNextHopFaceId(222);
+
+  m_face1->sendData    (data1);
+
+  //
+
+  m_ioRemaining = 2;
+  m_ioService.run();
+  m_ioService.reset();
+
+  BOOST_REQUIRE_EQUAL(m_face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(m_face2_receivedDatas    .size(), 1);
+
+  // sending allows only IncomingFaceId, receiving allows only NextHopFaceId
+  BOOST_CHECK_EQUAL(m_face2_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(m_face2_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), false);
+
+  BOOST_CHECK_EQUAL(m_face2_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(m_face2_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
+
+  ////////////////////////////////////////////////////////
+
+  using namespace boost::asio;
+
+  std::vector<const_buffer> interestWithHeader;
+  Block iHeader  = interest1.getLocalControlHeader().wireEncode(interest1, true, true);
+  Block iPayload = interest1.wireEncode();
+  interestWithHeader.push_back(buffer(iHeader.wire(),  iHeader.size()));
+  interestWithHeader.push_back(buffer(iPayload.wire(), iPayload.size()));
+
+  std::vector<const_buffer> dataWithHeader;
+  Block dHeader  = data1.getLocalControlHeader().wireEncode(data1, true, true);
+  Block dPayload = data1.wireEncode();
+  dataWithHeader.push_back(buffer(dHeader.wire(),  dHeader.size()));
+  dataWithHeader.push_back(buffer(dPayload.wire(), dPayload.size()));
+
+  //
+
+  client->async_send(interestWithHeader, bind(&noOp));
+  client->async_send(dataWithHeader, bind(&noOp));
+
+  m_ioRemaining = 2;
+  m_ioService.run();
+  m_ioService.reset();
+
+  BOOST_REQUIRE_EQUAL(m_face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(m_face1_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(m_face1_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(m_face1_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), true);
+  BOOST_CHECK_EQUAL(m_face1_receivedInterests[0].getNextHopFaceId(), 111);
+
+  BOOST_CHECK_EQUAL(m_face1_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(m_face1_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace nfd