dummy-client-face.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2024 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
23 
24 #include "ndn-cxx/impl/lp-field-tag.hpp"
25 #include "ndn-cxx/lp/fields.hpp"
26 #include "ndn-cxx/lp/packet.hpp"
27 #include "ndn-cxx/lp/tags.hpp"
31 
32 #include <boost/asio/io_context.hpp>
33 #include <boost/asio/post.hpp>
34 
35 namespace ndn {
36 namespace {
37 
38 class DummyTransport final : public ndn::Transport
39 {
40 public:
41  void
42  receive(Block block) const
43  {
44  block.encode();
45  if (m_receiveCallback) {
46  m_receiveCallback(block);
47  }
48  }
49 
50  void
51  send(const Block& block) final
52  {
53  onSendBlock(block);
54  }
55 
56  void
57  close() final
58  {
59  }
60 
61  void
62  pause() final
63  {
64  }
65 
66  void
67  resume() final
68  {
69  }
70 
71 public:
72  signal::Signal<DummyTransport, Block> onSendBlock;
73 };
74 
75 } // namespace
76 
77 struct DummyClientFace::BroadcastLink
78 {
79  std::vector<DummyClientFace*> faces;
80 };
81 
83  : Error("Face has already been linked to another face")
84 {
85 }
86 
88  : Face(make_shared<DummyTransport>())
89  , m_internalKeyChain(make_unique<KeyChain>())
90  , m_keyChain(*m_internalKeyChain)
91 {
92  this->construct(options);
93 }
94 
96  : Face(make_shared<DummyTransport>(), keyChain)
97  , m_keyChain(keyChain)
98 {
99  this->construct(options);
100 }
101 
102 DummyClientFace::DummyClientFace(boost::asio::io_context& ioCtx, const Options& options)
103  : Face(make_shared<DummyTransport>(), ioCtx)
104  , m_internalKeyChain(make_unique<KeyChain>())
105  , m_keyChain(*m_internalKeyChain)
106 {
107  this->construct(options);
108 }
109 
110 DummyClientFace::DummyClientFace(boost::asio::io_context& ioCtx, KeyChain& keyChain, const Options& options)
111  : Face(make_shared<DummyTransport>(), ioCtx, keyChain)
112  , m_keyChain(keyChain)
113 {
114  this->construct(options);
115 }
116 
118 {
119  unlink();
120 }
121 
122 void
123 DummyClientFace::construct(const Options& options)
124 {
125  static_cast<DummyTransport&>(getTransport()).onSendBlock.connect([this] (Block packet) {
126  packet.encode();
127  lp::Packet lpPacket(packet);
128  auto frag = lpPacket.get<lp::FragmentField>();
129  Block block({frag.first, frag.second});
130 
131  if (block.type() == tlv::Interest) {
132  auto interest = make_shared<Interest>(block);
133  if (lpPacket.has<lp::NackField>()) {
134  auto nack = make_shared<lp::Nack>(std::move(*interest));
135  nack->setHeader(lpPacket.get<lp::NackField>());
136  addTagFromField<lp::CongestionMarkTag, lp::CongestionMarkField>(*nack, lpPacket);
137  onSendNack(*nack);
138  }
139  else {
140  addTagFromField<lp::NextHopFaceIdTag, lp::NextHopFaceIdField>(*interest, lpPacket);
141  addTagFromField<lp::CongestionMarkTag, lp::CongestionMarkField>(*interest, lpPacket);
142  onSendInterest(*interest);
143  }
144  }
145  else if (block.type() == tlv::Data) {
146  auto data = make_shared<Data>(block);
147  addTagFromField<lp::CachePolicyTag, lp::CachePolicyField>(*data, lpPacket);
148  addTagFromField<lp::CongestionMarkTag, lp::CongestionMarkField>(*data, lpPacket);
149  onSendData(*data);
150  }
151  });
152 
153  if (options.enablePacketLogging)
154  enablePacketLogging();
155 
156  if (options.enableRegistrationReply)
157  enableRegistrationReply(options.registrationReplyFaceId);
158 
159  m_processEventsOverride = options.processEventsOverride;
160 
161  enableBroadcastLink();
162 }
163 
164 void
165 DummyClientFace::enableBroadcastLink()
166 {
167  this->onSendInterest.connect([this] (const Interest& interest) {
168  if (m_bcastLink != nullptr) {
169  for (auto otherFace : m_bcastLink->faces) {
170  if (otherFace != this) {
171  otherFace->receive(interest);
172  }
173  }
174  }
175  });
176  this->onSendData.connect([this] (const Data& data) {
177  if (m_bcastLink != nullptr) {
178  for (auto otherFace : m_bcastLink->faces) {
179  if (otherFace != this) {
180  otherFace->receive(data);
181  }
182  }
183  }
184  });
185  this->onSendNack.connect([this] (const lp::Nack& nack) {
186  if (m_bcastLink != nullptr) {
187  for (auto otherFace : m_bcastLink->faces) {
188  if (otherFace != this) {
189  otherFace->receive(nack);
190  }
191  }
192  }
193  });
194 }
195 
196 void
197 DummyClientFace::enablePacketLogging()
198 {
199  onSendInterest.connect([this] (const Interest& interest) {
200  this->sentInterests.push_back(interest);
201  });
202  onSendData.connect([this] (const Data& data) {
203  this->sentData.push_back(data);
204  });
205  onSendNack.connect([this] (const lp::Nack& nack) {
206  this->sentNacks.push_back(nack);
207  });
208 }
209 
210 void
211 DummyClientFace::enableRegistrationReply(uint64_t faceId)
212 {
213  onSendInterest.connect([=] (const Interest& interest) {
214  static const Name localhostRibPrefix("/localhost/nfd/rib");
215  static const name::Component registerVerb("register");
216  const auto& name = interest.getName();
217  if (name.size() <= 4 || !localhostRibPrefix.isPrefixOf(name))
218  return;
219 
220  nfd::ControlParameters params(name[4].blockFromValue());
221  if (!params.hasFaceId()) {
222  params.setFaceId(faceId);
223  }
224  if (!params.hasOrigin()) {
225  params.setOrigin(nfd::ROUTE_ORIGIN_APP);
226  }
227  if (!params.hasCost() && name[3] == registerVerb) {
228  params.setCost(0);
229  }
230 
232  resp.setCode(200);
233  resp.setBody(params.wireEncode());
234 
235  auto data = make_shared<Data>(name);
236  data->setContent(resp.wireEncode());
237  m_keyChain.sign(*data, security::SigningInfo(security::SigningInfo::SIGNER_TYPE_SHA256));
238  boost::asio::post(getIoContext(), [this, data] { this->receive(*data); });
239  });
240 }
241 
242 void
243 DummyClientFace::receive(const Interest& interest)
244 {
245  lp::Packet lpPacket(interest.wireEncode());
246 
247  addFieldFromTag<lp::IncomingFaceIdField, lp::IncomingFaceIdTag>(lpPacket, interest);
248  addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, interest);
249  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, interest);
250 
251  static_cast<DummyTransport&>(getTransport()).receive(lpPacket.wireEncode());
252 }
253 
254 void
255 DummyClientFace::receive(const Data& data)
256 {
257  lp::Packet lpPacket(data.wireEncode());
258 
259  addFieldFromTag<lp::IncomingFaceIdField, lp::IncomingFaceIdTag>(lpPacket, data);
260  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, data);
261 
262  static_cast<DummyTransport&>(getTransport()).receive(lpPacket.wireEncode());
263 }
264 
265 void
266 DummyClientFace::receive(const lp::Nack& nack)
267 {
268  lp::Packet lpPacket;
269  lpPacket.add<lp::NackField>(nack.getHeader());
270  Block interest = nack.getInterest().wireEncode();
271  lpPacket.add<lp::FragmentField>({interest.begin(), interest.end()});
272 
273  addFieldFromTag<lp::IncomingFaceIdField, lp::IncomingFaceIdTag>(lpPacket, nack);
274  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, nack);
275 
276  static_cast<DummyTransport&>(getTransport()).receive(lpPacket.wireEncode());
277 }
278 
279 void
280 DummyClientFace::linkTo(DummyClientFace& other)
281 {
282  if (m_bcastLink != nullptr && other.m_bcastLink != nullptr) {
283  if (m_bcastLink != other.m_bcastLink) {
284  // already on different links
286  }
287  }
288  else if (m_bcastLink == nullptr && other.m_bcastLink != nullptr) {
289  m_bcastLink = other.m_bcastLink;
290  m_bcastLink->faces.push_back(this);
291  }
292  else if (m_bcastLink != nullptr && other.m_bcastLink == nullptr) {
293  other.m_bcastLink = m_bcastLink;
294  m_bcastLink->faces.push_back(&other);
295  }
296  else {
297  m_bcastLink = other.m_bcastLink = make_shared<BroadcastLink>();
298  m_bcastLink->faces.push_back(this);
299  m_bcastLink->faces.push_back(&other);
300  }
301 }
302 
303 void
304 DummyClientFace::unlink()
305 {
306  if (m_bcastLink == nullptr) {
307  return;
308  }
309 
310  auto it = std::find(m_bcastLink->faces.begin(), m_bcastLink->faces.end(), this);
311  BOOST_ASSERT(it != m_bcastLink->faces.end());
312  m_bcastLink->faces.erase(it);
313 
314  if (m_bcastLink->faces.size() == 1) {
315  m_bcastLink->faces[0]->m_bcastLink = nullptr;
316  m_bcastLink->faces.clear();
317  }
318  m_bcastLink = nullptr;
319 }
320 
321 void
322 DummyClientFace::doProcessEvents(time::milliseconds timeout, bool keepRunning)
323 {
324  if (m_processEventsOverride != nullptr) {
325  m_processEventsOverride(timeout);
326  }
327  else {
328  Face::doProcessEvents(timeout, keepRunning);
329  }
330 }
331 
332 } // namespace ndn
Represents a TLV element of the NDN packet format.
Definition: block.hpp:45
void encode()
Encode sub-elements into TLV-VALUE.
Definition: block.cpp:353
Represents a Data packet.
Definition: data.hpp:39
size_t wireEncode(EncodingImpl< TAG > &encoder, bool wantUnsignedPortionOnly=false) const
Prepend wire encoding to encoder.
Definition: data.cpp:39
A client-side face for unit testing.
signal::Signal< DummyClientFace, Interest > onSendInterest
Emitted whenever an Interest is sent.
signal::Signal< DummyClientFace, lp::Nack > onSendNack
Emitted whenever a Nack is sent.
signal::Signal< DummyClientFace, Data > onSendData
Emitted whenever a Data packet is sent.
DummyClientFace(const Options &options={})
Create a dummy face with an internal I/O context.
void unlink()
Unlink the broadcast medium if previously linked.
Provide a communication channel with local or remote NDN forwarder.
Definition: face.hpp:91
Transport & getTransport() const
Returns the underlying transport.
Definition: face.hpp:442
Represents an Interest packet.
Definition: interest.hpp:50
size_t wireEncode(EncodingImpl< TAG > &encoder) const
Prepend wire encoding to encoder.
Definition: interest.cpp:53
Provides a "TLV-oriented" delivery service.
Definition: transport.hpp:37
Declare a field.
Definition: field-decl.hpp:174
Represents a Network Nack.
Definition: nack.hpp:39
const Interest & getInterest() const
Definition: nack.hpp:51
const NackHeader & getHeader() const
Definition: nack.hpp:63
Packet & add(const typename FIELD::ValueType &value)
Add a FIELD with value.
Definition: packet.hpp:152
Block wireEncode() const
Encode packet into wire format.
Definition: packet.cpp:91
ControlResponse & setBody(const Block &body)
ControlResponse & setCode(uint32_t code)
The main interface for signing key management.
Definition: key-chain.hpp:87
#define NDN_THROW(e)
Definition: exception.hpp:56
FieldDecl< field_location_tags::Header, NackHeader, tlv::Nack > NackField
Definition: fields.hpp:73
mgmt::ControlResponse ControlResponse
::boost::chrono::milliseconds milliseconds
Definition: time.hpp:52
@ ControlParameters
Definition: tlv-nfd.hpp:35
@ Name
Definition: tlv.hpp:71
@ Data
Definition: tlv.hpp:69
@ Interest
Definition: tlv.hpp:68
Definition: data.cpp:25
Options for DummyClientFace.