ndn-cxx: NDN C++ Library 0.9.0-33-g832ea91d
Loading...
Searching...
No Matches
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
35namespace ndn {
36namespace {
37
38class DummyTransport final : public ndn::Transport
39{
40public:
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
71public:
72 signal::Signal<DummyTransport, Block> onSendBlock;
73};
74
75} // namespace
76
77struct 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
102DummyClientFace::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
110DummyClientFace::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
121
122void
123DummyClientFace::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
164void
165DummyClientFace::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
196void
197DummyClientFace::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
210void
211DummyClientFace::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
242void
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
254void
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
265void
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
279void
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
303void
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
321void
322DummyClientFace::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.
void linkTo(DummyClientFace &other)
Link another DummyClientFace through a broadcast medium.
signal::Signal< DummyClientFace, Interest > onSendInterest
Emitted whenever an Interest is sent.
void receive(const Interest &interest)
Cause the face to receive an Interest packet.
signal::Signal< DummyClientFace, lp::Nack > onSendNack
Emitted whenever a Nack is sent.
std::vector< Data > sentData
Data sent out of this DummyClientFace.
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.
std::vector< Interest > sentInterests
Interests sent out of this DummyClientFace.
std::vector< lp::Nack > sentNacks
Nacks sent out of this DummyClientFace.
Provide a communication channel with local or remote NDN forwarder.
Definition face.hpp:91
virtual void doProcessEvents(time::milliseconds timeout, bool keepRunning)
Definition face.cpp:256
boost::asio::io_context & getIoContext() const noexcept
Returns a reference to the io_context used by this face.
Definition face.hpp:422
Transport & getTransport() const
Returns the underlying transport.
Definition face.hpp:432
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.
Represents a Network Nack.
Definition nack.hpp:39
const NackHeader & getHeader() const
Definition nack.hpp:63
const Interest & getInterest() const
Definition nack.hpp:51
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
void sign(Data &data, const SigningInfo &params=SigningInfo())
Sign a Data packet according to the supplied signing information.
@ SIGNER_TYPE_SHA256
Use a SHA-256 digest only, no signer needs to be specified.
#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
@ Name
Definition tlv.hpp:71
@ Data
Definition tlv.hpp:69
@ Interest
Definition tlv.hpp:68
Definition data.cpp:25
Options for DummyClientFace.