blob: cf2f477c6e80d940b4413f52f75e1a4328df6008 [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 Brown58191872016-02-05 22:16:57 -080034import java.util.logging.Level;
Andrew Brownec1b0d02015-02-21 13:11:42 -080035import java.util.logging.Logger;
Alexander Afanasyev8e9330f2016-01-25 19:13:40 -080036
Andrew Brown58191872016-02-05 22:16:57 -080037/**
38 * A client-side face for unit testing
Andrew Brown3831baf2015-01-19 13:38:52 -080039 */
Andrew Brown58191872016-02-05 22:16:57 -080040public class MockFace extends Face {
41
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080042 public interface SignalOnSendInterest {
Andrew Brown58191872016-02-05 22:16:57 -080043
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080044 void emit(Interest interest) throws EncodingException, SecurityException;
45 }
46
47 public interface SignalOnSendData {
Andrew Brown58191872016-02-05 22:16:57 -080048
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080049 void emit(Data data);
50 }
51
52 /**
53 * Options for MockFace
54 */
Andrew Brown58191872016-02-05 22:16:57 -080055 public static class Options {
56
57 /**
58 * If true, packets sent out of MockFace will be appended to a container
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080059 */
60 boolean enablePacketLogging = false;
61
62 /**
Andrew Brown58191872016-02-05 22:16:57 -080063 * If true, prefix registration command will be automatically replied with a
64 * successful response
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080065 */
66 boolean enableRegistrationReply = false;
67 }
68
Andrew Brown58191872016-02-05 22:16:57 -080069 final public static Options DEFAULT_OPTIONS = new Options() {
70 {
71 enablePacketLogging = true;
72 enableRegistrationReply = true;
73 }
74 };
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080075
76 /**
Andrew Brown58191872016-02-05 22:16:57 -080077 * Create MockFace that logs packets in sentInterests and sentData and
78 * emulates NFD prefix registration
Alexander Afanasyev83a26d32016-01-26 01:04:32 -080079 */
80 public MockFace() throws SecurityException {
81 this(DEFAULT_OPTIONS);
82 }
83
84 /**
85 * Create MockFace with the specified options
86 * <p>
87 * To create Face that does not log packets:
88 * <pre>
89 * new MockFace(new Options());
90 * // use onSendInterest.add(handler) and onSendData.add(handler)
91 * // to add custom logic when Interest or Data packet are sent
92 * // from the upper level (to transport)
93 * </pre>
94 *
95 * To create Face that just logs packets in sentInterests and sentData:
96 * <pre>
97 * new MockFace(new Options(){{ enablePacketLogging=true; }});
98 * </pre>
99 */
100 public MockFace(Options options) throws SecurityException {
101 super(new MockFaceTransport(), null);
Andrew Brown58191872016-02-05 22:16:57 -0800102 m_transport = (MockFaceTransport) node_.getTransport();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800103 m_keychain = MockKeyChain.configure(new Name("/mock/key"));
104 setCommandSigningInfo(m_keychain, m_keychain.getDefaultCertificateName());
105
106 m_transport.onSendBlock = new MockFaceTransport.OnSendBlockSignal() {
107 @Override
108 public void emit(ByteBuffer buffer) throws EncodingException, SecurityException {
109 // @todo Implement NDNLP processing
110
111 if (buffer.get(0) == Tlv.Interest || buffer.get(0) == Tlv.Data) {
112 TlvDecoder decoder = new TlvDecoder(buffer);
113 if (decoder.peekType(Tlv.Interest, buffer.remaining())) {
114 Interest interest = new Interest();
115 interest.wireDecode(buffer, TlvWireFormat.get());
116
117 for (SignalOnSendInterest signal : onSendInterest) {
118 signal.emit(interest);
119 }
120 } else if (decoder.peekType(Tlv.Data, buffer.remaining())) {
121 Data data = new Data();
122 data.wireDecode(buffer, TlvWireFormat.get());
123
124 for (SignalOnSendData signal : onSendData) {
125 signal.emit(data);
126 }
127 }
Andrew Brown58191872016-02-05 22:16:57 -0800128 } else {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800129 logger.info("Received an unknown packet");
130 }
131 }
132 };
133
134 if (options.enablePacketLogging) {
135 onSendInterest.add(new SignalOnSendInterest() {
136 @Override
137 public void emit(Interest interest) {
138 sentInterests.add(interest);
139 }
140 });
141
142 onSendData.add(new SignalOnSendData() {
143 @Override
144 public void emit(Data data) {
145 sentData.add(data);
146 }
147 });
148 }
149
150 if (options.enableRegistrationReply) {
151 onSendInterest.add(new SignalOnSendInterest() {
152 @Override
153 public void emit(Interest interest) throws EncodingException, SecurityException {
154 final Name localhostRegistration = new Name("/localhost/nfd/rib");
155 if (!interest.getName().getPrefix(3).equals(localhostRegistration)) {
156 return;
157 }
158
159 ControlParameters params = new ControlParameters();
160 params.wireDecode(interest.getName().get(-5).getValue());
161 params.setFaceId(1);
162 params.setOrigin(0);
163
164 if (interest.getName().get(3).toString().equals("register")) {
165 params.setCost(0);
166 }
167
168 // TODO: replace with jNDN ControlResponse encoding when available
169 // http://redmine.named-data.net/issues/3455
170 TlvEncoder encoder = new TlvEncoder(256);
171 int saveLength = encoder.getLength();
172 encoder.writeBuffer(params.wireEncode().buf());
173 encoder.writeBlobTlv(Tlv.NfdCommand_StatusText, new Blob("OK").buf());
174 encoder.writeNonNegativeIntegerTlv(Tlv.NfdCommand_StatusCode, 200);
175 encoder.writeTypeAndLength(Tlv.NfdCommand_ControlResponse, encoder.getLength() - saveLength);
176
177 Data data = new Data();
178 data.setName(interest.getName());
179 data.setContent(new Blob(encoder.getOutput(), false));
180 m_keychain.sign(data);
181
182 receive(data);
183 }
184 });
185 }
186 }
187
188 /**
189 * Mock reception of the Interest packet on the Face (from transport)
Andrew Brown58191872016-02-05 22:16:57 -0800190 * @param interest the mock-remote interest to add to the PIT
191 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800192 */
193 public void receive(Interest interest) throws EncodingException {
194 m_transport.receive(interest.wireEncode().buf());
195 }
196
197 /**
198 * Mock reception of the Data packet on the Face (from transport)
Andrew Brown58191872016-02-05 22:16:57 -0800199 * @param data the mock-remote data to add to the CS
200 * @throws EncodingException if packet encoding fails (it should not)
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800201 */
202 public void receive(Data data) throws EncodingException {
203 m_transport.receive(data.wireEncode().buf());
204 }
205
Andrew Brown58191872016-02-05 22:16:57 -0800206 /**
207 * @return the transport for this face
208 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800209 public Transport getTransport() {
210 return m_transport;
211 }
212
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800213 /**
Andrew Brown58191872016-02-05 22:16:57 -0800214 * Internal transport for {@link MockFace}
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800215 */
216 private static class MockFaceTransport extends Transport {
Andrew Brown58191872016-02-05 22:16:57 -0800217
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800218 public interface OnSendBlockSignal {
Andrew Brown58191872016-02-05 22:16:57 -0800219
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800220 void emit(ByteBuffer buffer) throws EncodingException, SecurityException;
221 }
222
Andrew Brown58191872016-02-05 22:16:57 -0800223 /**
224 * Receive some bytes to add to the mock socket
225 * @param block the byte buffer
226 * @throws EncodingException
227 */
228 public void receive(ByteBuffer block) throws EncodingException {
229 synchronized (receiveBuffer) {
230 receiveBuffer.add(block.duplicate());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800231 }
232 }
233
Andrew Brown58191872016-02-05 22:16:57 -0800234 /**
235 * {@inheritDoc}
236 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800237 @Override
238 public boolean isLocal(ConnectionInfo connectionInfo) {
239 return true;
240 }
241
Andrew Brown58191872016-02-05 22:16:57 -0800242 /**
243 * {@inheritDoc}
244 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800245 @Override
246 public boolean isAsync() {
247 return false;
248 }
249
Andrew Brown58191872016-02-05 22:16:57 -0800250 /**
251 * {@inheritDoc}
252 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800253 @Override
254 public void connect(Transport.ConnectionInfo connectionInfo,
Andrew Brown58191872016-02-05 22:16:57 -0800255 ElementListener elementListener, Runnable onConnected) {
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800256 logger.fine("Connecting...");
257 connected = true;
258 elementReader = new ElementReader(elementListener);
259 if (onConnected != null) {
260 onConnected.run();
261 }
262 }
263
Andrew Brown58191872016-02-05 22:16:57 -0800264 /**
265 * {@inheritDoc}
266 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800267 @Override
268 public void send(ByteBuffer data) throws IOException {
Andrew Brown58191872016-02-05 22:16:57 -0800269 logger.log(Level.FINE, "Sending {0} bytes", (data.capacity() - data.position()));
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800270
271 try {
272 onSendBlock.emit(data);
273 } catch (EncodingException e) {
Andrew Brown58191872016-02-05 22:16:57 -0800274 logger.log(Level.WARNING, "Failed to decode packet", e);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800275 } catch (SecurityException e) {
Andrew Brown58191872016-02-05 22:16:57 -0800276 logger.log(Level.WARNING, "Failed signature", e);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800277 }
278 }
279
Andrew Brown58191872016-02-05 22:16:57 -0800280 /**
281 * {@inheritDoc}
282 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800283 @Override
284 public void processEvents() throws IOException, EncodingException {
285 if (!getIsConnected()) {
286 logger.warning("Not connnected...");
287 }
288
289 while (true) {
290 ByteBuffer block = null;
Andrew Brown58191872016-02-05 22:16:57 -0800291 synchronized (receiveBuffer) {
292 if (!receiveBuffer.isEmpty()) {
293 block = receiveBuffer.remove(0);
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800294 }
295 }
296 if (block == null) {
297 break;
298 }
299 elementReader.onReceivedData(block);
300 }
301 }
302
Andrew Brown58191872016-02-05 22:16:57 -0800303 /**
304 * {@inheritDoc}
305 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800306 @Override
307 public boolean getIsConnected() {
308 return connected;
309 }
310
Andrew Brown58191872016-02-05 22:16:57 -0800311 /**
312 * {@inheritDoc}
313 */
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800314 @Override
315 public void close() throws IOException {
316 logger.fine("Closing...");
317 connected = false;
318 }
319
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800320 public OnSendBlockSignal onSendBlock;
321
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800322 private static final Logger logger = Logger.getLogger(MockFaceTransport.class.getName());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800323 private boolean connected;
324 private ElementReader elementReader;
Andrew Brown58191872016-02-05 22:16:57 -0800325 private final List<ByteBuffer> receiveBuffer = new LinkedList<>();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800326 }
327
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800328 /**
329 * Interests sent out of this MockFace
330 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800331 * Sent Interests are appended to this container if options.enablePacketLogger
332 * is true. User of this class is responsible for cleaning up the container,
333 * if necessary. After .expressInterest, .processEvents must be called before
334 * the Interest would show up here.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800335 */
Andrew Brown58191872016-02-05 22:16:57 -0800336 public List<Interest> sentInterests = new ArrayList<>();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800337
338 /**
339 * Data sent out of this MockFace
340 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800341 * Sent Data are appended to this container if options.enablePacketLogger is
342 * true. User of this class is responsible for cleaning up the container, if
343 * necessary. After .put, .processEvents must be called before the Data would
344 * show up here.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800345 */
Andrew Brown58191872016-02-05 22:16:57 -0800346 public List<Data> sentData = new ArrayList<>();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800347
348 /**
349 * Emits whenever an Interest is sent
350 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800351 * After .expressInterest, .processEvents must be called before this signal
352 * would be emitted.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800353 */
Andrew Brown58191872016-02-05 22:16:57 -0800354 public List<SignalOnSendInterest> onSendInterest = new ArrayList<>();
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800355
356 /**
357 * Emits whenever a Data packet is sent
358 * <p>
Andrew Brown58191872016-02-05 22:16:57 -0800359 * After .putData, .processEvents must be called before this signal would be
360 * emitted.
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800361 */
Andrew Brown58191872016-02-05 22:16:57 -0800362 public List<SignalOnSendData> onSendData = new ArrayList<>();
Andrew Brown3831baf2015-01-19 13:38:52 -0800363
Andrew Brownec1b0d02015-02-21 13:11:42 -0800364 private static final Logger logger = Logger.getLogger(MockFace.class.getName());
Alexander Afanasyev83a26d32016-01-26 01:04:32 -0800365 private MockFaceTransport m_transport;
366 private KeyChain m_keychain;
Andrew Brown3831baf2015-01-19 13:38:52 -0800367}