blob: 8b78329967573c743fbcdf9b5cd84bf125620933 [file] [log] [blame]
Andrew Brown3831baf2015-01-19 13:38:52 -08001/*
andrewsbrown533c6ef2015-03-03 16:08:41 -08002 * jndn-mock
Alexander Afanasyev83a26d32016-01-26 01:04:32 -08003 * Copyright (c) 2013-2015 Regents of the University of California.
andrewsbrown533c6ef2015-03-03 16:08:41 -08004 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 3, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT ANY
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
12 * more details.
Andrew Brown3831baf2015-01-19 13:38:52 -080013 */
14package com.intel.jndn.mock;
15
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080016import net.named_data.jndn.*;
17import net.named_data.jndn.encoding.ElementListener;
18import net.named_data.jndn.encoding.ElementReader;
19import net.named_data.jndn.encoding.EncodingException;
20import net.named_data.jndn.encoding.TlvWireFormat;
21import net.named_data.jndn.encoding.tlv.Tlv;
22import net.named_data.jndn.encoding.tlv.TlvDecoder;
23import net.named_data.jndn.encoding.tlv.TlvEncoder;
24import net.named_data.jndn.security.KeyChain;
25import net.named_data.jndn.security.SecurityException;
26import net.named_data.jndn.transport.Transport;
27import net.named_data.jndn.util.Blob;
28
Andrew Brown3831baf2015-01-19 13:38:52 -080029import java.io.IOException;
andrewsbrown1f28bcf2015-04-20 13:29:20 -070030import java.nio.ByteBuffer;
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080031import java.util.ArrayList;
32import java.util.LinkedList;
33import java.util.List;
Andrew Brownec1b0d02015-02-21 13:11:42 -080034import java.util.logging.Logger;
Alexander Afanasyev8e9330f2016-01-25 19:13:40 -080035
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080036/** A client-side face for unit testing
Andrew Brown3831baf2015-01-19 13:38:52 -080037 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080038public class MockFace extends Face
39{
40 public interface SignalOnSendInterest {
41 void emit(Interest interest) throws EncodingException, SecurityException;
42 }
43
44 public interface SignalOnSendData {
45 void emit(Data data);
46 }
47
48 /**
49 * Options for MockFace
50 */
51 public static class Options
52 {
53 /** If true, packets sent out of MockFace will be appended to a container
54 */
55 boolean enablePacketLogging = false;
56
57 /**
58 * If true, prefix registration command will be automatically replied with a successful response
59 */
60 boolean enableRegistrationReply = false;
61 }
62
63 final public static Options DEFAULT_OPTIONS = new Options(){{ enablePacketLogging=true; enableRegistrationReply=true; }};
64
65 /**
66 * Create MockFace that logs packets in sentInterests and sentData and emulates NFD prefix registration
67 */
68 public MockFace() throws SecurityException {
69 this(DEFAULT_OPTIONS);
70 }
71
72 /**
73 * Create MockFace with the specified options
74 * <p>
75 * To create Face that does not log packets:
76 * <pre>
77 * new MockFace(new Options());
78 * // use onSendInterest.add(handler) and onSendData.add(handler)
79 * // to add custom logic when Interest or Data packet are sent
80 * // from the upper level (to transport)
81 * </pre>
82 *
83 * To create Face that just logs packets in sentInterests and sentData:
84 * <pre>
85 * new MockFace(new Options(){{ enablePacketLogging=true; }});
86 * </pre>
87 */
88 public MockFace(Options options) throws SecurityException {
89 super(new MockFaceTransport(), null);
90 m_transport = (MockFaceTransport)node_.getTransport();
91 m_keychain = MockKeyChain.configure(new Name("/mock/key"));
92 setCommandSigningInfo(m_keychain, m_keychain.getDefaultCertificateName());
93
94 m_transport.onSendBlock = new MockFaceTransport.OnSendBlockSignal() {
95 @Override
96 public void emit(ByteBuffer buffer) throws EncodingException, SecurityException {
97 // @todo Implement NDNLP processing
98
99 if (buffer.get(0) == Tlv.Interest || buffer.get(0) == Tlv.Data) {
100 TlvDecoder decoder = new TlvDecoder(buffer);
101 if (decoder.peekType(Tlv.Interest, buffer.remaining())) {
102 Interest interest = new Interest();
103 interest.wireDecode(buffer, TlvWireFormat.get());
104
105 for (SignalOnSendInterest signal : onSendInterest) {
106 signal.emit(interest);
107 }
108 } else if (decoder.peekType(Tlv.Data, buffer.remaining())) {
109 Data data = new Data();
110 data.wireDecode(buffer, TlvWireFormat.get());
111
112 for (SignalOnSendData signal : onSendData) {
113 signal.emit(data);
114 }
115 }
116 }
117 else {
118 logger.info("Received an unknown packet");
119 }
120 }
121 };
122
123 if (options.enablePacketLogging) {
124 onSendInterest.add(new SignalOnSendInterest() {
125 @Override
126 public void emit(Interest interest) {
127 sentInterests.add(interest);
128 }
129 });
130
131 onSendData.add(new SignalOnSendData() {
132 @Override
133 public void emit(Data data) {
134 sentData.add(data);
135 }
136 });
137 }
138
139 if (options.enableRegistrationReply) {
140 onSendInterest.add(new SignalOnSendInterest() {
141 @Override
142 public void emit(Interest interest) throws EncodingException, SecurityException {
143 final Name localhostRegistration = new Name("/localhost/nfd/rib");
144 if (!interest.getName().getPrefix(3).equals(localhostRegistration)) {
145 return;
146 }
147
148 ControlParameters params = new ControlParameters();
149 params.wireDecode(interest.getName().get(-5).getValue());
150 params.setFaceId(1);
151 params.setOrigin(0);
152
153 if (interest.getName().get(3).toString().equals("register")) {
154 params.setCost(0);
155 }
156
157 // TODO: replace with jNDN ControlResponse encoding when available
158 // http://redmine.named-data.net/issues/3455
159 TlvEncoder encoder = new TlvEncoder(256);
160 int saveLength = encoder.getLength();
161 encoder.writeBuffer(params.wireEncode().buf());
162 encoder.writeBlobTlv(Tlv.NfdCommand_StatusText, new Blob("OK").buf());
163 encoder.writeNonNegativeIntegerTlv(Tlv.NfdCommand_StatusCode, 200);
164 encoder.writeTypeAndLength(Tlv.NfdCommand_ControlResponse, encoder.getLength() - saveLength);
165
166 Data data = new Data();
167 data.setName(interest.getName());
168 data.setContent(new Blob(encoder.getOutput(), false));
169 m_keychain.sign(data);
170
171 receive(data);
172 }
173 });
174 }
175 }
176
177 /**
178 * Mock reception of the Interest packet on the Face (from transport)
179 */
180 public void receive(Interest interest) throws EncodingException {
181 m_transport.receive(interest.wireEncode().buf());
182 }
183
184 /**
185 * Mock reception of the Data packet on the Face (from transport)
186 */
187 public void receive(Data data) throws EncodingException {
188 m_transport.receive(data.wireEncode().buf());
189 }
190
191 public Transport getTransport() {
192 return m_transport;
193 }
194
195 //////////////////////////////////////////////////////////////////////////////
196 //////////////////////////////////////////////////////////////////////////////
197 //////////////////////////////////////////////////////////////////////////////
198
199 /**
200 * Internal transport for MockFace
201 */
202 private static class MockFaceTransport extends Transport {
203 public interface OnSendBlockSignal {
204 void emit(ByteBuffer buffer) throws EncodingException, SecurityException;
205 }
206
207 public void
208 receive(ByteBuffer block) throws EncodingException {
209 synchronized (m_recvBuffer) {
210 m_recvBuffer.add(block.duplicate());
211 }
212 }
213
214 // from Transport
215
216 @Override
217 public boolean isLocal(ConnectionInfo connectionInfo) {
218 return true;
219 }
220
221 @Override
222 public boolean isAsync() {
223 return false;
224 }
225
226 @Override
227 public void connect(Transport.ConnectionInfo connectionInfo,
228 ElementListener elementListener, Runnable onConnected) {
229 logger.fine("Connecting...");
230 connected = true;
231 elementReader = new ElementReader(elementListener);
232 if (onConnected != null) {
233 onConnected.run();
234 }
235 }
236
237 @Override
238 public void send(ByteBuffer data) throws IOException {
239 logger.fine("Sending " + (data.capacity() - data.position()) + " bytes");
240
241 try {
242 onSendBlock.emit(data);
243 } catch (EncodingException e) {
244 logger.info("TLV decoding error: " + e.toString());
245 } catch (SecurityException e) {
246 logger.info("Signing error: " + e.toString());
247 }
248 }
249
250 @Override
251 public void processEvents() throws IOException, EncodingException {
252 if (!getIsConnected()) {
253 logger.warning("Not connnected...");
254 }
255
256 while (true) {
257 ByteBuffer block = null;
258 synchronized (m_recvBuffer) {
259 if (!m_recvBuffer.isEmpty()) {
260 block = m_recvBuffer.remove(0);
261 }
262 }
263 if (block == null) {
264 break;
265 }
266 elementReader.onReceivedData(block);
267 }
268 }
269
270 @Override
271 public boolean getIsConnected() {
272 return connected;
273 }
274
275 @Override
276 public void close() throws IOException {
277 logger.fine("Closing...");
278 connected = false;
279 }
280
281 //////////////////////////////////////////////////////////////////////////////
282
283 public OnSendBlockSignal onSendBlock;
284
285 //////////////////////////////////////////////////////////////////////////////
286
287 private static final Logger logger = Logger.getLogger(MockFaceTransport.class.getName());
288
289 private boolean connected;
290 private ElementReader elementReader;
291 private final List<ByteBuffer> m_recvBuffer = new LinkedList<ByteBuffer>();
292 }
293
294 //////////////////////////////////////////////////////////////////////////////
295 //////////////////////////////////////////////////////////////////////////////
296 //////////////////////////////////////////////////////////////////////////////
297
298 /**
299 * Interests sent out of this MockFace
300 * <p>
301 * Sent Interests are appended to this container if options.enablePacketLogger is true.
302 * User of this class is responsible for cleaning up the container, if necessary.
303 * After .expressInterest, .processEvents must be called before the Interest would show up here.
304 */
305 public List<Interest> sentInterests = new ArrayList<Interest>();
306
307 /**
308 * Data sent out of this MockFace
309 * <p>
310 * Sent Data are appended to this container if options.enablePacketLogger is true.
311 * User of this class is responsible for cleaning up the container, if necessary.
312 * After .put, .processEvents must be called before the Data would show up here.
313 */
314 public List<Data> sentData = new ArrayList<Data>();
315
316 /**
317 * Emits whenever an Interest is sent
318 * <p>
319 * After .expressInterest, .processEvents must be called before this signal would be emitted.
320 */
321 public List<SignalOnSendInterest> onSendInterest = new ArrayList<SignalOnSendInterest>();
322
323 /**
324 * Emits whenever a Data packet is sent
325 * <p>
326 * After .putData, .processEvents must be called before this signal would be emitted.
327 */
328 public List<SignalOnSendData> onSendData = new ArrayList<SignalOnSendData>();
329
330 //////////////////////////////////////////////////////////////////////////////
331 //////////////////////////////////////////////////////////////////////////////
332 //////////////////////////////////////////////////////////////////////////////
Andrew Brown3831baf2015-01-19 13:38:52 -0800333
Andrew Brownec1b0d02015-02-21 13:11:42 -0800334 private static final Logger logger = Logger.getLogger(MockFace.class.getName());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800335 private MockFaceTransport m_transport;
336 private KeyChain m_keychain;
Andrew Brown3831baf2015-01-19 13:38:52 -0800337}